/* Self-hosted. A font from someone else's CDN decides how fast the page paints and
   learns who read it. */
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/lato-400.e1b3b5908c9c.woff2") format("woff2");
}
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/lato-700.de69cf9e514d.woff2") format("woff2");
}
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url("../fonts/lato-900.1c6c65523675.woff2") format("woff2");
}


/* --- preflight (the part of Tailwind's reset this stylesheet assumes) --- */
[hidden]:where(:not([hidden="until-found"])) {
  display: none !important;
}

/* Form controls do not inherit type by default: the range input came out at the browser's
   13.3px instead of the page's own size. */
button,
input,
optgroup,
select,
textarea {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}


/* --- tokens ------------------------------------------------------ */
/* Сгенерировано system/toolchain/brand_tokens.py - руками не править. */
/* Канон бренда - style guide Наталии Челак, принят 24.07.2026. */

:root {
  /* шкалы палитры */
  --cl-blue-950: #001D4C;
  --cl-blue-900: #022B6E;
  --cl-blue-800: #0A3D90;
  --cl-blue-700: #1651B2;
  --cl-blue-600: #2567D4;
  --cl-blue-500: #3880F6;
  --cl-blue-400: #66A0FF;
  --cl-blue-300: #92BCFF;
  --cl-blue-200: #BFD7FF;
  --cl-blue-100: #EBF2FF;
  --cl-navy-950: #1C2740;
  --cl-navy-900: #2E3C5D;
  --cl-navy-800: #42537A;
  --cl-navy-700: #5A6C97;
  --cl-navy-600: #7488B5;
  --cl-navy-500: #92A5D2;
  --cl-navy-400: #B2C5EF;
  --cl-navy-300: #CCD9F8;
  --cl-navy-200: #E5EDFF;
  --cl-navy-100: #F8FAFF;
  --cl-grey-950: #3A3F49;
  --cl-grey-900: #4E5460;
  --cl-grey-800: #626977;
  --cl-grey-700: #787F8D;
  --cl-grey-600: #8E95A4;
  --cl-grey-500: #A5ACBB;
  --cl-grey-400: #BDC3D1;
  --cl-grey-300: #D5DBE8;
  --cl-grey-200: #F2F4F8;
  --cl-grey-100: #FDFEFF;

  /* типографика */
  --cl-font: 'Lato',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
  --cl-mono: ui-monospace,'SF Mono',Menlo,Monaco,Consolas,'Liberation Mono',monospace;

  /* радиусы */
  --cl-r-sm: 4px;
  --cl-r-md: 8px;
  --cl-r-lg: 16px;
  --cl-r-xl: 24px;
  --cl-r-full: 9999px;
}

:root {
  --cl-bg: #F8FAFF;
  --cl-surface: #FFFFFF;
  --cl-surface2: #F2F4F8;
  --cl-surface3: #E5EDFF;
  --cl-surface4: #D5DBE8;
  --cl-line: #E5EDFF;
  --cl-line2: #CCD9F8;
  --cl-hairline: rgba(28,39,64,.07);
  --cl-text: #1C2740;
  --cl-text2: #42537A;
  --cl-text3: #5A6C97;
  --cl-accent: #3880F6;
  --cl-accent-br: #2567D4;
  --cl-accent-soft: rgba(56,128,246,.12);
  --cl-accent-tint: rgba(56,128,246,.14);
  --cl-accent-tint2: rgba(56,128,246,.07);
  --cl-blue: #2567D4;
  --cl-amber: #B57C1F;
  --cl-shadow: 0 1px 2px rgba(28,39,64,.06),0 6px 22px rgba(28,39,64,.07);
  --cl-p1: #C6373C;
  --cl-p1-soft: #FBECEC;
  --cl-p2: #B57C1F;
  --cl-p2-soft: #FAF1E0;
  --cl-p3: #5A6C97;
  --cl-p3-soft: #F2F4F8;
  --cl-scrim: rgba(28,39,64,.18);
  --cl-shade: rgba(28,39,64,.10);
}

[data-theme="dark"] {
  --cl-bg: #1C2740;
  --cl-surface: #2E3C5D;
  --cl-surface2: #42537A;
  --cl-surface3: rgba(248,250,255,.08);
  --cl-surface4: rgba(248,250,255,.14);
  --cl-line: rgba(248,250,255,.14);
  --cl-line2: rgba(248,250,255,.22);
  --cl-hairline: rgba(248,250,255,.08);
  --cl-text: #F8FAFF;
  --cl-text2: #E5EDFF;
  --cl-text3: #B2C5EF;
  --cl-accent: #3880F6;
  --cl-accent-br: #66A0FF;
  --cl-accent-soft: rgba(56,128,246,.16);
  --cl-accent-tint: rgba(56,128,246,.18);
  --cl-accent-tint2: rgba(56,128,246,.09);
  --cl-blue: #92BCFF;
  --cl-amber: #B57C1F;
  --cl-shadow: 0 10px 34px rgba(28,39,64,.16);
  --cl-p1: #E8686D;
  --cl-p1-soft: rgba(232,104,109,.16);
  --cl-p2: #D6A84E;
  --cl-p2-soft: rgba(214,168,78,.16);
  --cl-p3: #B2C5EF;
  --cl-p3-soft: rgba(248,250,255,.10);
  --cl-scrim: rgba(0,0,0,.5);
  --cl-shade: rgba(0,0,0,.45);
}



/* --- page -------------------------------------------------------- */

/* The page commits to one editorial palette on purpose: day and night are the subject
   here, not a viewer preference. A theme toggle would argue with the thesis.

   Colour comes from one fixed backdrop (Worlds.astro). Sections are transparent and only
   declare which side of the seam they sit on; their type is coloured for that side, so
   contrast is decided at authoring time rather than hoped for at runtime. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* NOT overflow-x here. Hiding overflow on the root turns it into the scroll container and
     `position: sticky` stops working for everything on the page - the pinned week simply
     scrolled away with the rest. The body carries it instead, and the real answer is that
     nothing overflows: see the note on the week's rows leaving by fade rather than by
     sliding off the edge. */
}

/* The clip lives here, not on the root and not on the body.
 *
 * `overflow-x: hidden` makes its element a scroll container, and on the root that stops
 * `position: sticky` working for the whole page. `clip` never creates one, so anything that
 * overflows inside the page is cut off and the window cannot be dragged sideways, while the
 * pinned week still sticks to the viewport. It also permits `overflow-y: visible`, which
 * `hidden` does not - that is the whole reason `clip` exists. */
main,
footer {
  overflow-x: clip;
}

/* Main takes the slack, so the footer sits at the bottom of a short page and directly under
   the content of a long one. */
main {
  flex: 1 0 auto;
}

/* The page fills the window even when the words do not. A changelog with nothing shipped
   yet used to end a third of the way down, and a footer floating in the middle of the
   screen reads as a broken page rather than a short one. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  margin: 0;
  background: transparent;
  color: var(--cl-text);
  font-family: var(--cl-font);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
}

h1,
h2,
h3,
p,
ul,
ol,
figure {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

:focus-visible {
  outline: 3px solid var(--cl-accent);
  outline-offset: 3px;
  border-radius: var(--cl-r-sm);
}

/* --- rhythm ------------------------------------------------------------- */

.wrap {
  width: min(100% - 2.5rem, 78rem);
  margin-inline: auto;
}

.section {
  position: relative;
  padding-block: clamp(4rem, 9vw, 8rem);
  background: transparent;
  color: var(--cl-text);
}

/* `content-visibility: auto` was tried here and taken out again, which is worth writing
   down so it is not tried a third time.
 *
 * It does what it promises - offscreen sections skip style, layout and paint - but the size
 * hint is a guess, and this page's sections range from 500 to 2400 points. Measured, the
 * document height moved by 720 points as the reader scrolled through it, which shows up as
 * a scrollbar that will not sit still. The page already holds 60fps without it; a jumpy
 * scrollbar is a real defect traded for a saving nobody can feel. */

.stack {
  display: flex;
  flex-direction: column;
}

/* Content columns pinned to one side of the seam.
   The night column is bounded by the seam itself, not by a fixed measure: while the seam
   is still sweeping down from the middle of the page, a fixed 46rem column starts to the
   left of it and the first words land on the wrong world. --seam-n is the same number
   without a unit, because a percentage inside calc() resolves against the element rather
   than the viewport. */
.side {
  width: min(100%, 46rem);
}

.side--wide {
  width: min(100%, 62rem);
}

/* The night column starts past the boundary AND past its fray.
 *
 * Below the hero the seam rests at 15% of the window, and it frays inward for about two
 * hundred points. Clearing only the seam is what left the first letter of every line
 * standing in a light island between about nine hundred and thirteen hundred points wide -
 * at wider sizes the column happened to start far enough right to hide the fault.
 *
 * The column is right-aligned in `.wrap`, so its left edge is the wrap's right edge minus
 * its width: capping the width by (wrap right - seam - fray) puts that edge where it
 * belongs at every size. Written from the resting seam, not from --seam-n: nothing may take
 * its size from the boundary, or the page reflows while the boundary is being carried. */
.side--night {
  margin-inline-start: auto;
  margin-inline-end: 0;
  max-width: max(
    20rem,
    calc((100vw + min(100vw - 2.5rem, 78rem)) / 2 - 15vw - 14rem)
  );
}

@media (max-width: 900px) {
  .side,
  .side--wide {
    width: 100%;
  }

  /* The narrow layout has no seam mid-page to clear (see Worlds.astro). */
  .side--night {
    max-width: none;
  }
}

/* Type made of both worlds.
 *
 * The boundary can be picked up and carried anywhere, so any text it can reach has to be
 * legible on both sides of it. This paints the glyphs with a hard stop at the seam: the part
 * standing in the day is dark, the part standing in the night is light, and it is one DOM
 * node - one tab stop, one string for a screen reader, full contrast on each side.
 *
 * `background-attachment: fixed` is what makes it general. It anchors the gradient to the
 * viewport, so the seam percentage lands on the true window x no matter where the element
 * sits or how wide it is - without it, a percentage inside the gradient resolves against the
 * element and every block would need its own offset worked out.
 *
 * Each user sets --day and --night to its own two colours.
 */
.split {
  --day: var(--cl-navy-950);
  --night: var(--cl-navy-100);
}

@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .split {
    background-image: linear-gradient(
      90deg,
      var(--day) 0 var(--seam),
      var(--night) var(--seam) 100%
    );
    background-attachment: fixed;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* Applied to the page's standard type, not element by element.
 *
 * The boundary has a handle and the handle exists on every screen, so a reader at the foot
 * of the page can carry the seam to the far right and turn the whole window into day - with
 * every heading below the hero authored light. Painting the type split is what makes that
 * harmless: wherever the boundary ends up, both halves of every line stay legible.
 *
 * Only type. Anything carrying its own background - buttons, chips, the application window -
 * is a solid object and reads on either world already. */
html .wrap :is(.h2, .h3, .lead, .prose, .label, p, li) {
  --day: var(--cl-navy-800);
  --night: var(--cl-navy-300);
}

html .wrap :is(.h2, .h3) {
  --day: var(--cl-navy-950);
  --night: var(--cl-navy-100);
}

html .wrap .label {
  --day: var(--cl-navy-700);
  --night: var(--cl-navy-400, #7e93c4);
}

@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  /* Everything that stands directly on the field, and nothing that stands on a surface of
     its own. A terminal card, an opaque closing block or the application window brings its
     own background, so its type is already decided and repainting it for the world behind
     would put dark letters on a dark card. Those opt out with `.solid`. */
  html .wrap :is(.h2, .h3, .lead, .prose, .label, p, li):not(.solid, .solid *) {
    background-image: linear-gradient(
      90deg,
      var(--day) 0 var(--seam),
      var(--night) var(--seam) 100%
    );
    background-attachment: fixed;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    /* The one !important on the page, and it earns it. Component styles are scoped, which
       makes every one of them more specific than anything written here; a single authored
       `color` left the glyphs painted solid and the gradient invisible behind them. The
       rule this layer enforces - type stays legible wherever the boundary is carried - is
       not a component's decision to override. */
    color: transparent !important;
  }
}

/* Rules and bars want the same treatment without the clip: they are the background. */
.split-rule {
  background-image: linear-gradient(
    90deg,
    var(--day, var(--cl-navy-950)) 0 var(--seam),
    var(--night, var(--cl-navy-100)) var(--seam) 100%
  );
  background-attachment: fixed;
}

/* --- type --------------------------------------------------------------- */

/* Leading held just above the glyphs rather than inside them. Tighter looked better set
   and worse used: a line box shorter than the type it holds makes the selection rectangles
   of consecutive lines overlap, and dragging across a heading produced stacked blue slabs.
   A display face loses very little going from 0.90 to 0.96. */
/* The scale is read on a laptop, not on the widescreen it was drawn on.
 *
 * 10.5vw put the headline at 158px on a 1512pt MacBook - a size that stops being a headline
 * and becomes wallpaper, because two words fill the window and the sentence has to be
 * reconstructed by scrolling. 6.6vw lands at 100px there, 127px on a 1920 monitor, and
 * still fills the hero on a phone. */
.display {
  font-weight: 900;
  font-size: clamp(2.6rem, 6.6vw, 8rem);
  line-height: 0.96;
  letter-spacing: -0.038em;
  text-wrap: balance;
}

/* Section headings sat at their 54px ceiling from 1180px upward, so every laptop got the
   widescreen size and the copy under them looked like a caption. */
.h2 {
  font-weight: 900;
  font-size: clamp(1.85rem, 3.4vw, 3rem);
  line-height: 1.12;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.h3 {
  font-weight: 700;
  font-size: 1.3125rem;
  line-height: 1.25;
  letter-spacing: -0.012em;
}

.lead {
  font-size: clamp(1.125rem, 1.9vw, 1.4375rem);
  line-height: 1.5;
  color: var(--cl-text2);
  max-width: 40ch;
}

.label {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cl-text3);
}

.label::before {
  content: "";
  width: 1.75rem;
  height: 2px;
  background: var(--cl-accent);
}

.prose {
  max-width: 60ch;
  color: var(--cl-text2);
}

.numeral {
  display: block;
  font-weight: 900;
  font-size: clamp(3.25rem, 7vw, 6rem);
  line-height: 0.8;
  letter-spacing: -0.05em;
  color: transparent;
  -webkit-text-stroke: 2px var(--cl-line2);
}

/* --- controls ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.95rem 1.7rem;
  border-radius: var(--cl-r-full);
  border: 2px solid transparent;
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -0.005em;
  text-decoration: none;
  cursor: pointer;
  transition:
    transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1),
    box-shadow 180ms ease,
    border-color 180ms ease;
}

.btn:hover {
  transform: translateY(-2px);
}

/* Except this one. The outline button's label is painted with a seam-anchored gradient, and
   any transform on the element re-anchors that gradient to the element - so the lift made
   the word flip colour under the cursor. The border already answers the hover. */
.btn--ghost:hover {
  transform: none;
}

/* accent-br is BLUE[600] in day and BLUE[400] in night: the bright brand blue fails AA
   as a fill, the scale steps do not. The token picks the right one per surface. */
.btn--primary {
  background: var(--cl-accent-br);
  color: #fff;
  box-shadow: 0 6px 20px rgba(37, 103, 212, 0.28);
}

.btn--primary:hover {
  box-shadow: 0 12px 30px rgba(37, 103, 212, 0.36);
}

[data-theme="dark"] .btn--primary {
  color: var(--cl-navy-950);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

/* The outline button stands on the field with no surface of its own, so both its border and
   its label have to survive on either world. The border is brand blue at half strength - the
   one hue that carries on white and on navy, which is why it is the brand's - and the label
   is split at the seam like every other line. Page-level tokens made it a day-only control:
   on the night half it was an invisible border around invisible text. */
.btn--ghost {
  --day: var(--cl-navy-950);
  --night: var(--cl-navy-100);
  border-color: rgba(56, 128, 246, 0.55);
  color: var(--cl-text);
}

@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .btn--ghost {
    background-image: linear-gradient(
      90deg,
      var(--day) 0 var(--seam),
      var(--night) var(--seam) 100%
    );
    background-attachment: fixed;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.btn--ghost:hover {
  border-color: var(--cl-accent);
}

/* A single pass of light across the fill on hover. It reads as the control waking rather
   than as an effect: it happens once per hover and it is over before you finish moving. */
.btn--sheen {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.btn--sheen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    100deg,
    transparent 20%,
    rgba(255, 255, 255, 0.35) 45%,
    transparent 70%
  );
  translate: -120% 0;
  transition: translate 620ms cubic-bezier(0.3, 0.8, 0.4, 1);
}

.btn--sheen:hover::after,
.btn--sheen:focus-visible::after {
  translate: 120% 0;
}

@media (prefers-reduced-motion: reduce) {
  .btn--sheen::after {
    display: none;
  }
}

/* --- surfaces ----------------------------------------------------------- */

.panel {
  border-radius: var(--cl-r-xl);
  background: var(--cl-surface);
  border: 1px solid var(--cl-line);
  box-shadow: 0 1px 2px rgba(28, 39, 64, 0.06), 0 18px 50px rgba(28, 39, 64, 0.1);
}

/* Opaque, not translucent.
 *
 * The boundary can be dragged, so any section can end up standing in the day - and a panel
 * with a see-through dark fill turns pale grey over white while its type stays light: a
 * card you cannot read, in the middle of the argument. An opaque surface is a dark card on
 * a light page, which is a normal thing to look at, and its contents keep the contrast they
 * were designed with wherever the boundary happens to be.
 *
 * The blur goes with it. It only ever showed the backdrop it is now hiding, and it was the
 * most expensive paint on the page. */
[data-theme="dark"] .panel {
  background: #26314f;
  border-color: rgba(178, 197, 239, 0.16);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
}

/* --- motion ------------------------------------------------------------- */

/* One choreography for the whole page: things rise into place as they arrive, staggered
   by their position in the group. Scattering separate effects across sections is what
   makes a page read as assembled rather than designed. */
/* Opacity, and nothing that moves.
 *
 * This used to rise a rem and a half into place, and the rise was quietly wrecking the page:
 * a transform on an element re-anchors its `background-attachment: fixed` background to the
 * element itself, so every split heading and paragraph painted its day half - dark navy - on
 * the dark world for the length of the transition. On first load that read as an unexplained
 * shadow lying across the type and the cards.
 *
 * Nothing that creates a containing block may be used here: transform, translate, scale,
 * filter and will-change all do. Fading is what is left, and the stagger still carries the
 * choreography. */
.reveal {
  opacity: 0;
  transition: opacity 700ms cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 90ms);
}

.reveal[data-shown="true"] {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- layouts/Base.astro --- */
.skip {
        position: absolute;
        left: 1rem;
        top: -4rem;
        z-index: 50;
        padding: 0.75rem 1.25rem;
        border-radius: var(--cl-r-full);
        background: var(--cl-accent-br);
        color: #fff;
        font-weight: 700;
        text-decoration: none;
        transition: top 160ms ease;
      }

      .skip:focus {
        top: 1rem;
      }

/* --- components/Field.astro --- */
.ground {
    position: fixed;
    inset: 0;
    z-index: -2;
    width: 100%;
    height: 100%;
    pointer-events: none;
    /* The fallback, and the first paint before the context is up. */
    background: linear-gradient(
      90deg,
      var(--cl-navy-100) 0 var(--seam),
      var(--cl-navy-950) var(--seam) 100%
    );
  }

/* --- components/Worlds.astro --- */
:root {
    --seam: 50%;
    --seam-n: 50;
    --fold: 200vh;
    --zone: 120px;
  }

  .field {
    position: fixed;
    inset: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    pointer-events: none;
  }

  /* The boundary, made grabbable. Narrow enough that it takes almost nothing away from the
     page, wide enough to catch. */
  .seamgrip {
    position: fixed;
    z-index: 5;
    top: 0;
    bottom: 0;
    left: var(--seam);
    width: 26px;
    translate: -50% 0;
    cursor: col-resize;
    touch-action: none;
  }

  @media (max-width: 900px), (pointer: coarse) {
    .seamgrip {
      display: none;
    }
  }

  html[data-dragging] {
    user-select: none;
  }

/* --- components/Chrome.astro --- */
.chip {
    position: absolute;
    z-index: 20;
    top: clamp(1rem, 3vw, 1.75rem);
    left: clamp(1rem, 3vw, 1.75rem);
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.5rem 1rem 0.5rem 0.6rem;
    border-radius: var(--cl-r-full);
    background: #fff;
    box-shadow: 0 1px 2px rgba(28, 39, 64, 0.06), 0 6px 22px rgba(28, 39, 64, 0.07);
    color: var(--cl-navy-950);
    font-weight: 700;
    letter-spacing: -0.01em;
    text-decoration: none;
  }

  .bar {
    position: fixed;
    z-index: 40;
    inset-inline: 0;
    top: 0;
    padding-block: 0.85rem;
    background: rgba(28, 39, 64, 0.72);
    backdrop-filter: blur(16px);
    border-bottom: 1px solid rgba(178, 197, 239, 0.14);
    color: var(--cl-navy-100);
    opacity: 0;
    transform: translateY(-100%);
    transition:
      opacity 260ms ease,
      transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
  }

  .bar[data-shown="true"] {
    opacity: 1;
    transform: none;
  }

  .bar__inner {
    display: flex;
    align-items: center;
    gap: 1.5rem;
  }

  /* The mark had shrunk to the size of a favicon in a bar that is otherwise the width of
     the screen: at 22 points it read as an icon someone forgot to finish rather than the
     brand handed over from the hero. */
  .bar__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 1.125rem;
    font-weight: 700;
    letter-spacing: -0.015em;
    text-decoration: none;
  }

  .bar__brand img {
    width: 28px;
    height: 28px;
  }

  .bar__links {
    display: flex;
    gap: 1.5rem;
    margin-inline-start: auto;
    font-size: 1rem;
  }

  .bar__links a {
    color: var(--cl-navy-300);
    text-decoration: none;
  }

  .bar__links a:hover {
    color: var(--cl-navy-100);
  }

  /* Desktop only. On a phone the bar has room for the mark and one button, and a row of
     six section names would either wrap into a second line or scroll sideways - both worse
     than the scroll itself, which is how a phone navigates a page anyway. */
  .bar__toc {
    display: none;
  }

  @media (min-width: 1080px) {
    .bar__toc {
      display: flex;
      gap: 1.5rem;
      margin-inline: auto;
      font-size: 0.9375rem;
      letter-spacing: 0.01em;
    }

    .bar__toc a {
      position: relative;
      padding-block: 0.35rem;
      color: var(--cl-navy-400, #7e93c4);
      text-decoration: none;
      white-space: nowrap;
      transition: color 200ms ease;
    }

    .bar__toc a:hover {
      color: var(--cl-navy-100);
    }

    /* The mark under the current section rather than a colour alone: at this size a change
       of shade is a guess, a line is an answer. */
    .bar__toc a::after {
      content: "";
      position: absolute;
      inset-inline: 0;
      bottom: 0;
      height: 2px;
      border-radius: 2px;
      background: var(--cl-blue-400);
      opacity: 0;
      transform: scaleX(0.4);
      transition: opacity 200ms ease, transform 200ms ease;
    }

    .bar__toc a[aria-current="true"] {
      color: var(--cl-navy-50, #f4f7ff);
    }

    .bar__toc a[aria-current="true"]::after {
      opacity: 1;
      transform: scaleX(1);
    }

    /* With an index in the middle, the plain links are a second answer to the same question. */
    .bar__toc:not([hidden]) ~ .bar__links {
      display: none;
    }
  }

  .bar__cta {
    padding: 0.6rem 1.35rem;
    border-radius: var(--cl-r-full);
    background: var(--cl-blue-400);
    color: var(--cl-navy-950);
    font-weight: 700;
    font-size: 1rem;
    text-decoration: none;
  }

  @media (max-width: 760px) {
    .bar__links {
      display: none;
    }

    .bar__cta {
      margin-inline-start: auto;
    }
  }

/* --- components/Hero.astro --- */
.hero {
    /* The inherited colour is the no-support fallback for .split: without background-clip
       on text the hero simply reads as day type. */
    color: var(--cl-navy-950);
    --gutter: clamp(1.25rem, 5vw, 5.5rem);
    position: relative;
    min-height: 100svh;
    display: grid;
    align-content: center;
    gap: clamp(0.9rem, 2vw, 1.6rem);
    /* Weighted upward. Centred content with a light top pad sat near the bottom of a tall
       window, which puts the sentence the page is about below the middle of the screen. */
    padding-block: clamp(4.5rem, 9vh, 6.5rem) clamp(5rem, 15vh, 9rem);
  }

  /* The application, in the twin's half. Anchored to the seam so it never crosses it, and
     running off the right edge so it reads as a window that continues rather than a picture
     that ended. */
  .hero__app {
    position: absolute;
    z-index: 0;
    inset-block-start: 50%;
    /* It rides the boundary again, but by transform rather than by offset. Its box is
       anchored to the window and never moves, so nothing reflows while the seam is carried;
       what follows is the paint. Damped to four fifths so it drifts with the boundary rather
       than being dragged by it, and floored at -6vw because further left is the headline -
       the screen must not cover the sentence introducing it. */
    translate: clamp(-6vw, (var(--seam-n, 50) - 50) * 0.8vw, 22vw) -46%;
    inset-inline-start: 57vw;
    /* Deliberately wider than the room it has: the window is rendered near full size and
       runs off the edge of the page. Fitting it entirely on screen would mean shrinking it
       until its own type is unreadable, and unreadable type is what makes a screenshot look
       like a drawing of software. */
    inset-inline-end: clamp(-30rem, -19vw, -9rem);
    opacity: calc(1 - var(--overture, 0) * 0.25);
    filter: drop-shadow(0 40px 90px rgba(6, 12, 28, 0.55));
    rotate: -0.6deg;
    pointer-events: none;
  }

  /* Below this the day half cannot hold the reading column and the night half cannot hold a
     1160-pixel window at a size where its type is legible. Showing it smaller would be
     showing it worse. */
  @media (max-width: 1180px), (max-height: 700px) {
    .hero__app {
      display: none;
    }
  }

  /* The overture. Centred, large, and driven out by the first screen of scroll - the bar
     picks the brand up from there, so it is never absent, only never twice at once. */
  .hero__logo {
    --out: 0; /* 0 at the top, 1 once the first screen is behind you */
    position: relative;
    z-index: 2;
    /* In the flow, so it reserves its own room. Absolute, it lay across the eyebrow and the
       first line of the headline - an overture that covers the thing it is introducing.

       Centred in the day, not in the window: centred in the window put it exactly on the
       seam, the worst square metre on the page, where the wordmark crossed from dark-on-light
       to dark-on-dark and vanished halfway. The owner's half is where a brand belongs. */
    justify-self: center;
    margin-bottom: clamp(1.2rem, 3vh, 2.2rem);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.65rem;
    text-decoration: none;
    /* Moved by translate, not by margin. Under `justify-self: center` the alignment centres
       the MARGIN box, so a margin of M shifts the border box by only M/2 - the mark sat at
       half the offset it was given. Transforms are applied after alignment, are not halved,
       and cost no layout, so following the seam here is movement rather than reflow.

       Floored at 9rem from the left edge: carried far enough, the boundary would otherwise
       drag the mark off the side of the page. */
    translate: max(calc(var(--seam-n, 50) * 0.5vw - 50vw), calc(9rem - 50vw))
      calc(var(--out) * -2.5rem);
    scale: calc(1 - var(--out) * 0.22);
    /* Two reasons to leave: the first screen is behind you, or the day half has been carried
       so narrow that the mark no longer fits inside it. A brand half-swallowed by the
       boundary is worse than a brand that has stepped away. */
    opacity: calc((1 - var(--out)) * clamp(0, (var(--seam-n, 50) - 20) / 8, 1));
  }

  .hero__logo img {
    position: relative;
    z-index: 1;
    width: clamp(4.5rem, 9vw, 7.5rem);
    height: auto;
    filter: drop-shadow(0 10px 34px rgba(37, 103, 212, 0.28));
  }

  /* Solid, not split. `.split` anchors its gradient to the viewport with
     background-attachment: fixed, and a transform on an ancestor turns that anchor into the
     ancestor's own box - this element sits inside a translated, scaled logo, so the seam
     percentage landed a few pixels from the word's left edge and painted the rest of it
     near-white on the light world. The mark loses the whole name that way; instead it keeps
     one colour and leaves when the day half gets too narrow to hold it (see .hero__logo). */
  .hero__word {
    color: var(--cl-navy-900);
    font-size: clamp(1rem, 1.5vw, 1.375rem);
    font-weight: 700;
    letter-spacing: 0.34em;
    text-indent: 0.34em;
    text-transform: uppercase;
    animation: settle 1.4s cubic-bezier(0.2, 0.8, 0.3, 1) both;
  }

  @keyframes settle {
    from {
      letter-spacing: 0.9em;
      text-indent: 0.9em;
      opacity: 0;
    }
  }

  /* A ring turning around the mark, drawn as a dashed circle rather than a border so the
     gaps stay even at any size. */
  .hero__ring {
    position: absolute;
    top: 50%;
    left: 50%;
    width: clamp(7rem, 14vw, 11.5rem);
    aspect-ratio: 1;
    translate: -50% -50%;
    margin-top: calc(clamp(1rem, 1.5vw, 1.375rem) * -0.9);
    border-radius: 50%;
    /* Brand blue at half strength, and left alone on both sides. It is the one hue that
       carries on white and on navy - which is why it is the brand's - so the ring needs no
       rule about which world it is standing in. */
    border: 1px dashed rgba(56, 128, 246, 0.5);
    animation: turn 26s linear infinite;
  }

  @keyframes turn {
    to {
      rotate: 1turn;
    }
  }

  /* Specks of the same material orbiting it: the mark is made of what the page is made of. */
  .hero__specks {
    position: absolute;
    top: 50%;
    left: 50%;
    width: clamp(8.5rem, 17vw, 14rem);
    aspect-ratio: 1;
    translate: -50% -50%;
    margin-top: calc(clamp(1rem, 1.5vw, 1.375rem) * -0.9);
    animation: turn 34s linear infinite reverse;
  }

  .hero__specks i {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 3px;
    height: 3px;
    background: var(--cl-blue-500, #3880f6);
    transform: rotate(calc(var(--s) * 36deg)) translateX(calc(clamp(8.5rem, 17vw, 14rem) / 2));
    opacity: calc(0.25 + (var(--s) / 10) * 0.6);
  }

  /* Both halves of every line are painted by .split (global.css), which anchors its gradient
     to the viewport - so these no longer need to be full-bleed for the seam to land in the
     right place, and nothing here takes a size from the boundary. */
  .hero__eyebrow,
  .hero__title {
    position: relative;
    z-index: 2;
    width: 100%;
    padding-inline: var(--gutter);
  }

  .hero__eyebrow {
    --day: var(--cl-navy-700);
    --night: var(--cl-navy-300);
    margin-bottom: -0.5rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.24em;
    text-transform: uppercase;
  }

  /* The hero owns the biggest type on the site, and 12vw made it 176px on a laptop - two
     words per line, the sentence finished below the fold, and the application window beside
     it reduced to a stripe. 7.6vw lands at 115px on that machine and still reaches 160px on
     a widescreen, where there is room for it. */
  .hero__title {
    font-size: clamp(3rem, 7.6vw, 10rem);
  }

  /* The reading column stays inside the day. Bounded by the seam itself rather than by a
     fixed measure: --seam-n is the unitless twin of --seam, because a percentage inside
     calc() resolves against this element instead of the viewport. */
  .hero__say {
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    margin-inline-start: var(--gutter);
    /* A fixed measure. Bound to the seam, this column resized on every frame of a drag and
       on every pointer move, and the whole hero reflowed under the boundary as it was
       carried - text rewrapping, buttons stacking and unstacking. NOTHING may take its size
       from the seam; crossing it is handled by paint, not by layout. */
    width: min(38rem, 42vw);
    margin-top: clamp(0.5rem, 2vh, 1.5rem);
  }

  .hero__lede {
    --day: var(--cl-navy-800);
    --night: var(--cl-navy-200, #d8e4fb);
    font-size: clamp(1.0625rem, 1.55vw, 1.3125rem);
    line-height: 1.5;
    text-wrap: pretty;
  }

  .hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
  }

  .hero__foot {
    --day: var(--cl-navy-700);
    --night: var(--cl-navy-300);
    font-size: 0.875rem;
  }

  .hero__hint {
    --day: var(--cl-navy-600);
    --night: var(--cl-navy-400, #7e93c4);
    display: flex;
    align-items: center;
    gap: 0.7rem;
    margin-inline-start: var(--gutter);
    font-size: 0.75rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
  }

  /* The rule cannot use currentColor: .split sets the colour to transparent so the gradient
     shows through the glyphs, and a bar painted with currentColor would vanish with it. It
     carries its own split instead. */
  .hero__hintline {
    width: 2.5rem;
    height: 1px;
    transform-origin: left;
    animation: pull 2.8s ease-in-out infinite;
  }

  @keyframes pull {
    0%,
    100% {
      transform: scaleX(0.4);
      opacity: 0.5;
    }
    50% {
      transform: scaleX(1);
      opacity: 1;
    }
  }

  /* Narrow: the day half is too small to hold a reading column beside the boundary, so the
     column takes the full measure and the headline alone carries the split. */
  @media (max-width: 900px) {
    /* Nothing to centre in: the narrow layout opens fully in the day, so the mark sits in the
       middle of the screen and stays until the scroll takes it. */
    .hero__logo {
      translate: 0 calc(var(--out) * -2.5rem);
      opacity: calc(1 - var(--out));
    }

    /* Centred, all of it.
     *
     * The asymmetry upstairs means something: the reading column belongs to the day half and
     * the headline crosses into the night. On a phone there are no halves - the whole screen
     * is one world - so what is left is a centred mark standing over a left-aligned column,
     * which reads as a mistake rather than as a composition. */
    .hero {
      justify-items: center;
      text-align: center;
    }

    .hero__say {
      width: min(100%, calc(100vw - var(--gutter) * 2));
      margin-inline: auto;
      align-items: center;
    }

    .hero__actions {
      justify-content: center;
    }

    .hero__title {
      font-size: clamp(2.75rem, 13vw, 5rem);
    }
  }

  @media (max-width: 700px), (pointer: coarse) {
    .hero__hint {
      display: none;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .hero__hintline {
      animation: none;
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .hero__ring,
    .hero__specks,
    .hero__word {
      animation: none;
    }
  }

/* --- components/AppWindow.astro --- */
/* Fixed geometry, scaled to the container. The product's own type size is part of the
     evidence: an app whose text is as big as the page's body copy is a drawing of an app. */
  .appwin.stage{
    --w: 1160;
    --h: 720;
    --k: 1;
    /* Floor on the scale. Below this the application's own type stops being text and starts
       being texture, and a blurred grey smear proves nothing about the product. When the
       column is narrower than the floor allows, the window overflows and is panned. */
    --kmin: 0;
    width: 100%;
    /* Never scaled up either. Beyond 1:1 the chrome grows past any real window and the type
       turns into a poster of an application; a wide column just centres it instead. */
    max-width: calc(var(--w) * 1px);
    margin-inline: auto;
    /* Scrolled, and aimed.
     *
     * The window is nearly twice the width of a phone. Cropping alone cut the session strip
     * in half and left no way to see the rest; scrolling alone dropped the reader at the far
     * left, on the rail. It does both: pressing a button scrolls the frame to that pane, and
     * from there it pans by hand. `contain` keeps the gesture from chaining out to the page
     * once the window reaches its end. */
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }

  .appwin.stage::-webkit-scrollbar{
    display: none;
  }

  .appwin .stage__in{
    position: relative;
    width: calc(var(--w) * var(--k) * 1px);
    height: calc(var(--h) * var(--k) * 1px);
  }

  @media (max-width: 900px) {
    .appwin.stage{
      --kmin: 0.66;
    }
  }

  .appwin .app{
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: calc(var(--w) * 1px);
    height: calc(var(--h) * 1px);
    transform: scale(var(--k));
    transform-origin: top left;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border-radius: 12px;
    /* The console's own tokens (system/toolchain/brand_tokens.py, dark). The application is
       dark and only dark by decision, so it stays dark here even where the page is light. */
    --bg: #1c2740;
    --surface: #2e3c5d;
    --panel: #25304e;
    --term-bg: #16203a;
    --line: rgba(248, 250, 255, 0.14);
    --hair: rgba(248, 250, 255, 0.08);
    --text: #f8faff;
    --text2: #e5edff;
    --text3: #b2c5ef;
    --accent: #3880f6;
    --accent-br: #66a0ff;
    --soft: rgba(56, 128, 246, 0.16);
    --ok: #4fbf8a;
    --p1: #e8686d;
    --p2: #d6a84e;
    --mono: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
    background: var(--bg);
    color: var(--text2);
    font-size: 13px;
    line-height: 1.45;
    /* A rim. The application's own background is the same navy as the page's night half by
       construction - they share the token - so without an edge the window dissolves into
       the world behind it instead of standing in it. */
    border: 1px solid rgba(178, 197, 239, 0.18);
    box-shadow:
      inset 0 1px 0 rgba(248, 250, 255, 0.06),
      0 2px 6px rgba(8, 14, 30, 0.5),
      0 40px 90px -20px rgba(4, 8, 20, 0.8);
    user-select: none;
  }

  /* --- titlebar = session strip ------------------------------------------ */

  .appwin .bar{
    flex: none;
    height: 38px;
    display: flex;
    align-items: flex-end;
    gap: 4px;
    padding: 0 10px;
    background: var(--surface);
    border-bottom: 1px solid var(--hair);
  }

  .appwin .lights{
    display: flex;
    gap: 8px;
    align-self: center;
    margin: 0 12px 0 4px;
  }

  .appwin .lights i{
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #ff5f57;
  }
  .appwin .lights i:nth-child(2){
    background: #febc2e;
  }
  .appwin .lights i:nth-child(3){
    background: #28c840;
  }

  .appwin .tab{
    display: flex;
    align-items: center;
    gap: 7px;
    height: 29px;
    padding: 0 10px;
    border-radius: 7px 7px 0 0;
    color: var(--text3);
    font-size: 12.5px;
    white-space: nowrap;
    cursor: default;
  }

  .appwin .app[data-mode="live"] .tab{
    cursor: pointer;
  }

  .appwin .tab.is-on{
    background: var(--bg);
    color: var(--text);
    font-weight: 700;
  }

  .appwin .dot{
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 8px rgba(56, 128, 246, 0.8);
  }
  .appwin .dot.idle{
    background: var(--text3);
    box-shadow: none;
    opacity: 0.45;
  }
  .appwin .dot.unread{
    background: var(--p2);
    box-shadow: 0 0 8px rgba(214, 168, 78, 0.7);
  }

  .appwin .tab .x, .appwin .ctab .x{
    font-size: 10px;
    color: var(--text3);
    opacity: 0.6;
  }

  .appwin .plus, .appwin .cplus{
    color: var(--text3);
    font-size: 15px;
    padding: 0 8px 4px;
  }

  /* --- rails --------------------------------------------------------------- */

  .appwin .body{
    flex: 1;
    display: flex;
    min-height: 0;
  }

  .appwin .rail{
    flex: none;
    width: 44px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 8px 0;
    background: var(--surface);
    border-right: 1px solid var(--hair);
  }

  .appwin .rail--r{
    border-right: none;
    border-left: 1px solid var(--hair);
  }

  .appwin .ric{
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border: 0;
    padding: 0;
    border-radius: 7px;
    background: none;
    color: var(--text3);
    cursor: pointer;
  }

  .appwin .ric svg{
    width: 17px;
    height: 17px;
  }

  .appwin .ric.is-on{
    background: var(--soft);
    color: var(--accent-br);
  }

  .appwin .ric--mark{
    width: 32px;
    height: 32px;
  }

  .appwin .ric--mark img{
    width: 20px;
    height: 20px;
  }

  .appwin .nub{
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: radial-gradient(circle at 34% 32%, #a9c8ff, var(--accent) 62%, #1d4ea8);
    box-shadow: 0 0 12px rgba(56, 128, 246, 0.7);
  }

  .appwin .rsp{
    flex: 1;
  }

  .appwin .rsp--gap{
    flex: none;
    height: 10px;
    width: 18px;
    border-top: 1px solid var(--hair);
    margin: 5px 0;
  }

  /* --- panes --------------------------------------------------------------- */

  .appwin .main{
    flex: 1;
    display: flex;
    min-width: 0;
  }

  .appwin [data-pane]{
    display: none;
  }

  /* Voice and search are not screens you go to - one is a column that opens beside the work
     and the other takes the tree's place - so their panes carry `voice` and `search` tags on
     the pieces that stay. Treating voice as its own view emptied the middle of the window,
     which is the opposite of what the feature does. */
  .appwin .app[data-view="work"] [data-pane~="work"], .appwin .app[data-view="voice"] [data-pane~="voice"], .appwin .app[data-view="search"] [data-pane~="search"], .appwin .app[data-view="file"] [data-pane~="file"], .appwin .app[data-view="brain"] [data-pane~="brain"], .appwin .app[data-view="heart"] [data-pane~="heart"]{
    display: var(--pane-d, flex);
  }

  .appwin .app[data-usage="true"] [data-pane~="usage"], .appwin .app[data-view="voice"] [data-pane~="voice"]{
    display: flex;
  }

  /* The status bar's own lines follow the view rather than the layout. */
  .appwin .stat{
    display: none;
  }
  .appwin .app[data-view="brain"] [data-stat="brain"], .appwin .app[data-view="voice"] [data-stat="voice"]{
    display: inline;
  }

  .appwin .tree{
    flex: none;
    width: 218px;
    flex-direction: column;
    background: var(--panel);
    border-right: 1px solid var(--hair);
    overflow: hidden;
  }

  .appwin .tree__h{
    display: flex;
    justify-content: space-between;
    padding: 9px 12px;
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text3);
    border-bottom: 1px solid var(--hair);
  }

  .appwin .tree__b{
    opacity: 0.6;
    letter-spacing: 0;
  }

  .appwin .tree__l{
    padding: 4px 0;
    display: flex;
    flex-direction: column;
  }

  .appwin .trow{
    display: flex;
    align-items: center;
    gap: 5px;
    height: 26px;
    padding-inline: calc(8px + var(--d) * 13px) 10px;
    font-size: 12.5px;
    color: var(--text2);
  }

  .appwin .app[data-mode="live"] .trow.is-file{
    cursor: pointer;
  }

  .appwin .app[data-view="file"] .trow.is-file{
    background: var(--soft);
    color: var(--text);
  }

  .appwin .tico{
    width: 14px;
    display: grid;
    place-items: center;
    color: var(--text3);
  }

  .appwin .tico svg{
    width: 13px;
    height: 13px;
  }

  .appwin .md{
    font-size: 9px;
    font-weight: 900;
    color: var(--accent-br);
    opacity: 0.75;
  }

  .appwin .tname{
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .appwin .tbadge{
    margin-inline-start: auto;
    min-width: 17px;
    height: 15px;
    padding: 0 4px;
    border-radius: 8px;
    background: var(--soft);
    color: var(--accent-br);
    font-size: 10.5px;
    font-weight: 700;
    display: grid;
    place-items: center;
  }

  .appwin .centre{
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--bg);
  }

  /* Again no `display`: the pane rules own it, and declaring it here left the file tabs
     hanging over the graph and the spaces page, where the real application has none. */
  .appwin .ctabs{
    flex: none;
    gap: 4px;
    padding: 8px 12px 0;
  }

  .appwin .ctab{
    display: flex;
    align-items: center;
    gap: 6px;
    height: 27px;
    padding: 0 10px;
    border-radius: 7px 7px 0 0;
    color: var(--text3);
    font-size: 12.5px;
  }

  .appwin .ctab.is-on{
    background: var(--term-bg);
    color: var(--text);
    font-weight: 700;
  }

  .appwin .app[data-view="file"] .ctab.is-on{
    background: none;
    color: var(--text3);
    font-weight: 400;
  }

  .appwin .app[data-view="file"] .ctab[data-ctab="file"]{
    background: var(--term-bg);
    color: var(--text);
    font-weight: 700;
  }

  .appwin .ci{
    font-family: var(--mono);
    font-size: 10.5px;
    opacity: 0.75;
  }

  /* --- terminal ------------------------------------------------------------ */

  .appwin .term{
    --pane-d: block;
    flex: 1;
    background: var(--term-bg);
    padding: 14px 18px;
    overflow: hidden;
    font-family: var(--mono);
    font-size: 12.5px;
    line-height: 1.62;
  }

  .appwin .tl{
    color: var(--text2);
    margin-bottom: 2px;
  }

  .appwin .tl--path{
    color: var(--accent-br);
    margin-bottom: 6px;
  }

  .appwin .tl__git{
    color: var(--p2);
    margin-inline-start: 12px;
    opacity: 0.9;
  }

  .appwin .pr{
    color: var(--ok);
    margin-inline-end: 8px;
  }

  .appwin .tl--say{
    color: var(--text);
    margin: 10px 0 8px;
  }

  .appwin .tl--n{
    padding-inline-start: 26px;
    text-indent: -26px;
    margin-bottom: 6px;
    max-width: 76ch;
  }

  .appwin .tl--n b{
    display: inline-block;
    width: 26px;
    color: var(--accent-br);
    text-indent: 0;
    font-weight: 700;
  }

  .appwin .tl--dim{
    color: var(--text3);
    max-width: 76ch;
    margin-top: 8px;
  }

  .appwin .tl--dim em{
    color: var(--accent-br);
    font-style: normal;
  }

  .appwin .tl:last-child{
    margin-top: 14px;
  }

  .appwin .caret{
    display: inline-block;
    width: 7px;
    height: 15px;
    margin-inline-start: 3px;
    vertical-align: -3px;
    background: var(--accent-br);
    animation: blink 1.1s steps(1) infinite;
  }

  @keyframes blink {
    50% {
      opacity: 0;
    }
  }

  /* --- editor -------------------------------------------------------------- */

  /* No `display` here: the pane rules own it. Declaring it on the pane itself sits at the
     same specificity as `[data-pane]` and comes later in the sheet, so it wins - and the
     editor was drawn on top of the terminal in the work view. */
  .appwin .ed{
    flex: 1;
    background: var(--term-bg);
    min-height: 0;
  }

  .appwin .ed__doc{
    flex: 1;
    padding: 20px 26px;
    max-width: 68ch;
  }

  .appwin .fm{
    font-family: var(--mono);
    font-size: 11.5px;
    line-height: 1.7;
    color: var(--text3);
    background: rgba(248, 250, 255, 0.04);
    border-left: 2px solid var(--accent);
    padding: 9px 12px;
    margin: 0 0 16px;
    border-radius: 0 4px 4px 0;
  }

  .appwin .ed h4{
    margin: 0 0 10px;
    font-size: 19px;
    font-weight: 900;
    color: var(--text);
    letter-spacing: -0.015em;
  }

  .appwin .ed p{
    margin: 0 0 10px;
    font-size: 13px;
    line-height: 1.62;
  }

  .appwin .wl{
    color: var(--accent-br);
    border-bottom: 1px solid rgba(102, 160, 255, 0.35);
  }

  /* Pinned to the right edge. The document is capped at a reading measure, so without this
     the slack piled up between the two columns and left the side panel floating in the
     middle of the pane with dead space behind it. */
  .appwin .ed__side{
    flex: none;
    width: 208px;
    margin-inline-start: auto;
    padding: 20px 16px;
    border-left: 1px solid var(--hair);
    background: var(--panel);
  }

  /* --- editor toolbar ------------------------------------------------------ */

  .appwin .mdbar{
    display: flex;
    align-items: center;
    gap: 6px;
    margin: -6px 0 16px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--hair);
    font-size: 12px;
    color: var(--text3);
  }

  .appwin .tsel{
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px;
    border-radius: 5px;
    border: 1px solid var(--line);
    color: var(--text2);
  }

  .appwin .tv{
    font-style: normal;
    font-size: 9px;
    opacity: 0.7;
  }

  .appwin .tbtn{
    min-width: 22px;
    padding: 3px 5px;
    border-radius: 5px;
    text-align: center;
  }

  .appwin .tb{
    font-weight: 900;
    color: var(--text2);
  }

  .appwin .ti{
    font-style: italic;
  }

  .appwin .tc{
    font-family: var(--mono);
    font-size: 10.5px;
  }

  .appwin .tsep{
    width: 1px;
    height: 14px;
    background: var(--line);
    margin: 0 3px;
  }

  .appwin .tgap{
    flex: 1;
  }

  .appwin .thint{
    font-size: 11px;
    opacity: 0.75;
    white-space: nowrap;
  }

  .appwin .tsave{
    padding: 3px 10px;
    border-radius: 999px;
    border: 1px solid var(--line);
    color: var(--ok);
    font-size: 11.5px;
  }

  /* --- search --------------------------------------------------------------- */

  .appwin .find{
    padding: 0 0 8px;
  }

  .appwin .find__in{
    display: flex;
    align-items: center;
    margin: 10px 10px 8px;
    padding: 6px 9px;
    border-radius: 6px;
    border: 1px solid var(--line);
    background: rgba(8, 14, 30, 0.35);
    color: var(--text);
    font-size: 12.5px;
  }

  .appwin .find__in .caret{
    width: 6px;
    height: 13px;
    margin-inline-start: 2px;
    vertical-align: 0;
  }

  .appwin .seg{
    display: flex;
    margin: 0 10px 10px;
    border: 1px solid var(--line);
    border-radius: 6px;
    overflow: hidden;
  }

  .appwin .segb{
    flex: 1;
    padding: 4px 0;
    text-align: center;
    font-size: 11.5px;
    color: var(--text3);
  }

  .appwin .segb.is-on{
    background: var(--soft);
    color: var(--accent-br);
  }

  .appwin .find__c{
    margin: 0 12px 6px;
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text3);
  }

  .appwin .find__q{
    display: block;
    padding: 0 12px 8px 27px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--text3);
  }

  .appwin .find__q em{
    font-style: normal;
    color: var(--accent-br);
    background: rgba(56, 128, 246, 0.16);
  }

  .appwin .sh{
    font-size: 10.5px;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--text3);
    margin: 0 0 8px;
  }

  .appwin .ed__side ul{
    margin: 0 0 20px;
    padding: 0;
    list-style: none;
    font-family: var(--mono);
    font-size: 11.5px;
    line-height: 1.85;
    color: var(--accent-br);
  }

  .appwin .sm{
    font-size: 12px;
    color: var(--text3);
    margin: 0;
  }

  /* --- brain --------------------------------------------------------------- */

  .appwin .brain{
    flex: 1;
    min-width: 0;
    flex-direction: column;
    background: var(--term-bg);
    overflow: hidden;
  }

  .appwin .bbody{
    display: flex;
    flex: 1;
    min-height: 0;
  }

  .appwin .brain canvas{
    display: block;
    flex: 1;
    min-width: 0;
    height: 100%;
  }

  /* The sector strip. Counts first, names second: the question a map answers before you
     read it is how much of each thing there is. */
  .appwin .btop{
    display: flex;
    align-items: center;
    gap: 6px;
    flex: none;
    height: 38px;
    padding-inline: 10px;
    border-bottom: 1px solid var(--hair);
    overflow: hidden;
  }

  .appwin .bchip{
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 22px;
    padding-inline: 9px;
    border-radius: 999px;
    border: 1px solid var(--hair);
    font-size: 11.5px;
    color: var(--text2);
    white-space: nowrap;
  }

  .appwin .bchip i{
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--c);
  }

  .appwin .bchip b{
    font-weight: 600;
    color: var(--text3);
  }

  .appwin .bbtn{
    display: inline-flex;
    align-items: center;
    height: 22px;
    padding-inline: 10px;
    border-radius: 6px;
    border: 1px solid var(--hair);
    background: var(--soft);
    font-size: 11.5px;
    color: var(--text2);
    white-space: nowrap;
  }

  .appwin .btop .bbtn{
    margin-inline-start: auto;
  }

  .appwin .bcen{
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0 12px;
    padding: 7px 9px;
    border-radius: 7px;
    border: 1px solid var(--hair);
    background: var(--soft);
  }

  .appwin .bcen__dot{
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent-br);
    box-shadow: 0 0 8px rgba(56, 128, 246, 0.8);
  }

  .appwin .bcen__n{
    font-size: 12.5px;
    color: var(--text);
  }

  .appwin .bhint{
    margin: 6px 12px 0;
    font-size: 10.5px;
    line-height: 1.35;
    color: var(--text3);
  }

  .appwin .btgl{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: 8px 12px 0;
    font-size: 12px;
    color: var(--text2);
  }

  .appwin .btgl i{
    position: relative;
    width: 28px;
    height: 16px;
    border-radius: 999px;
    background: var(--accent-br);
  }

  .appwin .btgl i::after{
    content: "";
    position: absolute;
    top: 2px;
    right: 2px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
  }

  .appwin .bacts{
    display: flex;
    gap: 6px;
    margin: 12px 12px 0;
  }

  .appwin .bacts .bbtn{
    flex: 1;
    justify-content: center;
  }

  .appwin .bside{
    flex: none;
    width: 214px;
    padding: 12px 0;
    background: var(--panel);
    border-right: 1px solid var(--hair);
    overflow: hidden;
  }

  .appwin .bsh{
    margin: 12px 12px 5px;
    font-size: 10.5px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text3);
  }

  .appwin .bsh:first-child{
    margin-top: 0;
  }

  .appwin .brow{
    display: flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    padding-inline: calc(10px + var(--d, 0) * 14px) 12px;
    font-size: 12.5px;
    color: var(--text2);
  }

  .appwin .brow.is-on{
    background: var(--soft);
    color: var(--accent-br);
  }

  .appwin .bcar{
    width: 0;
    height: 0;
    border-inline-start: 4px solid currentColor;
    border-block: 3.5px solid transparent;
    opacity: 0.55;
  }

  .appwin .bnm{
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .appwin .bn{
    margin-inline-start: auto;
    font-family: var(--mono);
    font-size: 11px;
    color: var(--text3);
  }

  .appwin .bseg{
    display: flex;
    margin: 0 12px;
    border: 1px solid var(--line);
    border-radius: 6px;
    overflow: hidden;
  }

  .appwin .bsegb{
    flex: 1;
    padding: 4px 0;
    text-align: center;
    font-size: 11.5px;
    color: var(--text3);
    border-inline-start: 1px solid var(--line);
  }

  .appwin .bsegb:first-child{
    border-inline-start: 0;
  }

  .appwin .bsegb.is-on{
    background: var(--soft);
    color: var(--accent-br);
  }

  .appwin .bsl{
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 0 12px;
    height: 22px;
    font-size: 11.5px;
    color: var(--text3);
  }

  .appwin .bsl span{
    width: 46px;
    flex: none;
  }

  .appwin .bsl i{
    flex: 1;
    height: 3px;
    border-radius: 2px;
    background: linear-gradient(
      90deg,
      var(--accent) 0 var(--v),
      rgba(248, 250, 255, 0.12) var(--v) 100%
    );
  }

  /* --- the twin ------------------------------------------------------------ */

  .appwin .heart{
    --pane-d: block;
    flex: 1;
    padding: 26px 30px;
    overflow: hidden;
  }

  .appwin .heart__h{
    display: flex;
    align-items: center;
    gap: 14px;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--hair);
  }

  .appwin .heart h4{
    margin: 0;
    font-size: 20px;
    font-weight: 900;
    color: var(--text);
    letter-spacing: -0.015em;
  }

  .appwin .heart__m{
    margin: 3px 0 0;
    font-size: 12.5px;
    color: var(--text2);
    display: flex;
    align-items: center;
    gap: 7px;
  }

  .appwin .dimd{
    color: var(--text3);
  }

  .appwin .hdot{
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 9px rgba(56, 128, 246, 0.85);
    animation: pulse 2.4s ease-in-out infinite;
  }

  @keyframes pulse {
    50% {
      opacity: 0.35;
    }
  }

  .appwin .btn-slim{
    margin-inline-start: auto;
    padding: 6px 16px;
    border-radius: 999px;
    border: 1px solid var(--line);
    font-size: 12.5px;
    color: var(--text2);
  }

  .appwin .tiles{
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    margin: 18px 0 22px;
  }

  .appwin .tile{
    padding: 12px 14px;
    border-radius: 8px;
    background: var(--panel);
    border: 1px solid var(--hair);
  }

  .appwin .tile .l{
    display: block;
    font-size: 11px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text3);
  }

  .appwin .tile .v{
    display: block;
    margin-top: 5px;
    font-size: 21px;
    font-weight: 900;
    color: var(--text);
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
  }

  .appwin .tile small{
    font-size: 11.5px;
    font-weight: 400;
    color: var(--text3);
    margin-inline-start: 5px;
    letter-spacing: 0;
  }

  .appwin .wakes{
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .appwin .wakes li{
    display: flex;
    align-items: baseline;
    gap: 14px;
    padding: 8px 0;
    border-bottom: 1px solid var(--hair);
    font-size: 12.5px;
  }

  .appwin .wakes .t{
    font-family: var(--mono);
    font-size: 11.5px;
    color: var(--text3);
    width: 42px;
  }

  .appwin .wakes .r{
    width: 88px;
    color: var(--accent-br);
  }

  .appwin .wakes .d{
    color: var(--text3);
  }

  /* --- voice ---------------------------------------------------------------- */

  .appwin .voice{
    flex: none;
    width: 296px;
    flex-direction: column;
    padding: 18px;
    background: var(--panel);
    border-left: 1px solid var(--hair);
  }

  .appwin .vph{
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0 0 14px;
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent-br);
  }

  .appwin .vdot{
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent-br);
    animation: pulse 1.6s ease-in-out infinite;
  }

  .appwin .wave{
    display: flex;
    align-items: center;
    gap: 3px;
    height: 46px;
    margin-bottom: 16px;
  }

  .appwin .wave i{
    flex: 1;
    border-radius: 2px;
    background: var(--accent);
    height: 18%;
    animation: talk 1.5s ease-in-out infinite;
    animation-delay: calc(var(--i) * -73ms);
  }

  @keyframes talk {
    0%,
    100% {
      height: 14%;
      opacity: 0.55;
    }
    50% {
      height: 92%;
      opacity: 1;
    }
  }

  .appwin .vsaid{
    margin: 0 0 12px;
    font-size: 12.5px;
    color: var(--text3);
  }

  .appwin .vans{
    margin: 0 0 16px;
    font-size: 13px;
    font-weight: 700;
    color: var(--text);
    line-height: 1.5;
  }

  .appwin .vhint{
    margin: auto 0 0;
    font-size: 11.5px;
    color: var(--text3);
  }

  /* --- usage ---------------------------------------------------------------- */

  .appwin .usage{
    flex: none;
    width: 208px;
    flex-direction: column;
    padding: 16px;
    background: var(--panel);
    border-left: 1px solid var(--hair);
  }

  .appwin .ub{
    margin-bottom: 14px;
  }

  .appwin .ubt{
    display: flex;
    justify-content: space-between;
    font-size: 12.5px;
    color: var(--text2);
  }

  .appwin .ubt b{
    color: var(--text);
    font-variant-numeric: tabular-nums;
  }

  .appwin .ubr{
    display: block;
    height: 5px;
    margin: 6px 0 5px;
    border-radius: 3px;
    background: rgba(248, 250, 255, 0.1);
    overflow: hidden;
  }

  .appwin .ubr i{
    display: block;
    height: 100%;
    width: var(--w);
    background: var(--accent);
  }

  .appwin .ubs{
    font-size: 11px;
    color: var(--text3);
  }

  .appwin .uk{
    display: flex;
    align-items: baseline;
    gap: 7px;
    padding: 5px 0;
    border-bottom: 1px solid var(--hair);
  }

  .appwin .uk span{
    font-size: 14px;
    font-weight: 900;
    color: var(--text);
    font-variant-numeric: tabular-nums;
  }

  .appwin .uk small{
    font-size: 11.5px;
    color: var(--text3);
  }

  /* --- status bar ------------------------------------------------------------ */

  .appwin .status{
    flex: none;
    height: 27px;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 14px;
    background: var(--surface);
    border-top: 1px solid var(--hair);
    font-size: 11.5px;
    color: var(--text3);
  }

  .appwin .status .dot.on{
    background: var(--ok);
    box-shadow: 0 0 8px rgba(79, 191, 138, 0.7);
  }

  .appwin .status .sp{
    flex: 1;
  }

  .appwin .status .hdot{
    width: 7px;
    height: 7px;
  }

  @media (prefers-reduced-motion: reduce) {
    .appwin .caret, .appwin .hdot, .appwin .vdot, .appwin .wave i{
      animation: none;
    }
  }

/* --- components/Surfaces.astro --- */
.see {
    color: var(--cl-navy-100);
  }

  /* Kept clear of the boundary. Below the hero the seam is pinned at 15% of the window, so
     a centred column on a 1680-wide screen starts a few pixels to the left of it - and the
     first letter of a light heading was painted on the light world. Padding the section, not
     the column, keeps the window and the captions on the same left edge. */
  @media (min-width: 900px) {
    /* The words clear the boundary and its fray; the window does not have to. Padding the
       whole section pushed the window right as well and shrank it for nothing - it is an
       opaque object with a rim, and a boundary passing behind it is just depth. */
    /* No bespoke clearance here any more. This section used to push its own column in by a
       formula of its own, which put its heading at a different left edge from every other
       section on the page - visible the moment you scroll from one to the next. The column
       is `.side--night`, the same as everywhere else, and the boundary is cleared by the
       same rule. */
  }

  .see__lead {
    color: var(--cl-navy-300);
    max-width: 46ch;
  }

  /* Wider than the reading column: the window is the exhibit, and an exhibit boxed into a
     text measure looks like an illustration of itself. */
  /* Two thirds of the window height for the exhibit, so the captions under it are on the
     same screen. Below the laptop band the window is already scaled by width and this never
     binds. */
  .see__app {
    --hshare: 0.46;
  }

  /* Centred in its stage, always - not centred at some sizes and left-anchored at others.
     That inconsistency is what read as the window sliding about: it moved only when the
     height happened to decide its size. Where the window is as wide as the stage this rule
     changes nothing, so the same line covers both cases. */
  .see__app > .stage__in {
    margin-inline: auto;
  }

  /* The exhibit and its label share one screen, so this section spends less on its own
     margins than a section of prose does. */
  .see {
    padding-block: clamp(3rem, 5vw, 6rem);
  }

  /* A laptop is short before it is narrow. On a 944-point window the heading, the window and
     the first caption add up to more than the screen unless the section gives back some of
     its own margin - and the caption is the part that explains what is being looked at. */
  @media (max-height: 1000px) {
    .see {
      padding-block: 2.5rem;
    }
  }

  /* A 13-inch laptop, or any window someone has shrunk. The exhibit gives up more height
     rather than pushing its own caption off the screen. */
  @media (max-height: 860px) {
    .see__app {
      --hshare: 0.38;
    }
  }

  /* A tall window can afford a bigger exhibit. Holding the laptop's share on a 1200-point
     screen left the window small in the middle of a lot of nothing. */
  @media (min-height: 1100px) {
    .see__app {
      --hshare: 0.56;
    }
  }

  /* The exhibit sits over its own captions, not over the page.
   *
   * The stage used to be centred on the window - 88rem of full-bleed with `auto` margins -
   * while the heading and the captions live in the night column further right. The result
   * was a picture whose centre was ninety points off the centre of the text explaining it,
   * which is the kind of misalignment you feel before you can name it. It now shares the
   * column's box, so centring inside it centres over the words. */
  .see__stage {
    width: min(100% - 2.5rem, 78rem);
    margin: clamp(2rem, 5vw, 3.5rem) auto clamp(2.5rem, 5vw, 4rem);
  }

  .see__phone {
    display: none;
  }

  /* Three by two. Six buttons on a wrapping flex row left one of them alone and full width
     on a second line, which reads as a mistake next to five equal ones. */
  .see__views {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.4rem;
    margin-top: 0.9rem;
  }

  .vbtn {
    min-height: 44px;
    padding: 0.5rem 0.9rem;
    border-radius: var(--cl-r-full);
    border: 1px solid rgba(178, 197, 239, 0.24);
    background: none;
    font: inherit;
    font-size: 0.8125rem;
    text-transform: capitalize;
    color: var(--cl-navy-300);
  }

  .vbtn.is-on {
    background: rgba(56, 128, 246, 0.16);
    border-color: rgba(102, 160, 255, 0.55);
    color: var(--cl-blue-300, #8cbaff);
  }

  .see__pan {
    margin-top: 0.7rem;
    font-size: 0.6875rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--cl-navy-400, #7e93c4);
  }

  @media (max-width: 900px) {
    .see__phone {
      display: block;
    }

    /* One caption at a time. The buttons above are the switch here, so six paragraphs at a
       third of full contrast are just a wall of grey under them - the current one is the
       only one that has anything to do with what is on screen. */
    .notes[data-single] .note:not(.is-on) {
      display: none;
    }

    .notes[data-single] .note {
      opacity: 1;
    }
  }

  /* Three by three, not `auto-fit`. Six captions in a column count decided by the viewport
     came out four and two - a ragged second row under a symmetrical window. The count is
     chosen so every row is full. */
  /* Six captions on a widescreen, one on anything smaller.
   *
   * The captions switch with the panel, so the current one is the only one that describes
   * what is on screen. On a laptop all six pushed the whole set below the fold: the reader
   * saw the window and no explanation at all, which is worse than seeing five explanations
   * fewer. Above 1600 there is room for the lot, and the set reads as a contents page. */
  .notes[data-single] {
    grid-template-columns: minmax(0, 1fr);
  }

  .notes[data-single] .note:not(.is-on) {
    display: none;
  }

  .notes[data-single] .note {
    opacity: 1;
    /* No marker line when there is only one caption. The blue rule tells you which of six
       is current; above a single caption it marks nothing and reads as a stray line drawn
       across the page. */
    border-top-color: transparent;
  }

  .notes[data-single] {
    border-top: 0;
    padding-top: 0;
  }

  /* All six only where all six fit beside the window: a wide screen that is also tall. On
     1920x1080 the set needs two rows, and two rows push the window's own caption off the
     bottom - the reader then sees an application and no explanation, which is the one
     outcome this section cannot afford. */
  @media (min-width: 1600px) and (min-height: 1200px) {
    /* Two rows of captions cost about a hundred points, and the section buys them back from
       its own opening margin rather than from the screen. */
    .see {
      padding-block-start: 3rem;
    }

    .notes[data-single] {
      grid-template-columns: repeat(3, 1fr);
    }

    .notes[data-single] .note:not(.is-on) {
      display: flex;
    }
  }

  .notes {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: clamp(1.25rem, 2.5vw, 2rem);
    padding-top: clamp(1rem, 2vw, 1.75rem);
    border-top: 1px solid rgba(178, 197, 239, 0.16);
  }

  @media (max-width: 1100px) {
    .notes {
      grid-template-columns: repeat(2, 1fr);
    }
  }

  @media (max-width: 620px) {
    .notes {
      grid-template-columns: 1fr;
    }
  }

  /* Every note is present for a reader without scripts; the current one is simply lit.
     Hiding five of six behind a class would leave the argument depending on JS.
     Each is a button, because it names a panel and pressing it opens that panel - a label
     that reads like a control and does nothing is the failure this section exists to
     disprove. */
  .note {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0;
    padding: 0.9rem 0 0;
    border: 0;
    border-top: 2px solid transparent;
    background: none;
    font: inherit;
    text-align: start;
    color: inherit;
    cursor: pointer;
    opacity: 0.32;
    transition:
      opacity 320ms ease,
      border-color 320ms ease;
  }

  .note:hover,
  .note:focus-visible {
    opacity: 0.7;
  }

  .note.is-on,
  .note.is-on:hover {
    opacity: 1;
    border-top-color: var(--cl-blue-400);
  }

  /* Colours come from the split layer in global.css - the seam can be carried across this
     section, and type that names its own colour would be painted solid on the wrong world. */
  .note__b {
    --day: var(--cl-navy-800);
    --night: var(--cl-navy-300);
    font-size: 0.9375rem;
    line-height: 1.55;
    text-wrap: pretty;
  }

  .see__foot {
    --day: var(--cl-navy-600);
    --night: var(--cl-navy-400, #7e93c4);
    margin-top: clamp(1.75rem, 3vw, 2.5rem);
    max-width: 54ch;
    font-size: 0.875rem;
  }

  @media (prefers-reduced-motion: reduce) {
    .note {
      opacity: 1;
    }
  }

/* --- components/Handover.astro --- */
/* Scroll drives it.
   *
   * The section is taller than the screen and its contents are pinned, so the distance you
   * travel through it maps onto the handover. Reading the page and emptying the week become
   * the same gesture - which is the point being made, so it should not need a second one.
   * The handle still works: taking hold of it wins until you let go. */
  .handover {
    /* Its own colour, because this section is not `.section` and therefore never picked one
       up: it inherited the day text colour straight from the body and painted a dark heading
       on the dark world. */
    color: var(--cl-text);
    position: relative;
    height: 260vh;
  }

  .ho__pin {
    position: sticky;
    top: 0;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: clamp(4rem, 8vh, 7rem);
  }

  .ho {
    --p: 0;
    position: relative;
    border-radius: var(--cl-r-lg);
  }

  .ho__cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 15rem);
    gap: clamp(1.5rem, 4vw, 3.5rem);
    align-items: start;
  }

  /* Two columns from the start: one column of twenty two put the point nine hundred pixels
     below the counter that was making it. Fixed rows so the block does not jump as the
     work leaves - the emptiness left behind is the argument, and a list that collapses
     hides it. */
  .ho__list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: 1.85rem;
    gap: 0 1.5rem;
    margin: 0;
  }

  .ho__kept {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem clamp(1.25rem, 3vw, 2.5rem);
    margin-top: clamp(1.25rem, 2.5vw, 2rem);
    padding-top: clamp(1rem, 2vw, 1.4rem);
    border-top: 1px solid rgba(178, 197, 239, 0.16);
  }

  .ho__keptcap {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--cl-accent);
    flex: none;
  }

  .ho__keptlist {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem clamp(1.25rem, 3vw, 2.25rem);
    margin: 0;
  }

  .ho__item {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding-block: 0.34rem;
    font-size: 0.9375rem;
    /* One mid-tone that reads on white and on navy alike. These rows are outside the split
       layer - they move, and a moving element cannot carry a fixed-position gradient - so
       they cannot be repainted per side and must simply work on both. */
    color: #7b8db8;
    transition:
      opacity 380ms ease,
      translate 520ms cubic-bezier(0.3, 0.9, 0.4, 1),
      filter 380ms ease;
    transition-delay: calc(var(--i, 0) * 8ms);
  }

  .ho__dot {
    flex: none;
    width: 6px;
    height: 6px;
    background: var(--cl-navy-500);
    translate: 0 -1px;
  }

  /* Gone: carried across and let go. The row leaves to the right because that is where the
     twin is, on this page and on every other screen of it.
   *
   * The lists opt out of the split layer (`.solid`) precisely because of this rule. That
   * layer paints type by clipping a fixed-position gradient to the letters, and `translate`
   * or `filter` on the element re-anchors the gradient to the element itself - at which
   * point the paint lands as a filled rectangle behind the words instead of inside them.
   * A dark blue block slid across the text on every scroll, and only during the sweep. */
  .ho__item[data-gone] {
    opacity: 0;
    translate: 2.5rem 0;
    filter: blur(3px);
  }

  .ho__item--kept {
    color: #4a5f92;
    font-weight: 700;
  }

  .ho__item--kept .ho__dot {
    background: var(--cl-accent);
    box-shadow: 0 0 0 0 rgba(56, 128, 246, 0.5);
  }

  /* Only once everything else has gone: the three that were never routine stop being three
     more lines in a list and become the point. */
  .ho[data-clear] .ho__item--kept .ho__dot {
    animation: ping 2.2s ease-out infinite;
  }

  @keyframes ping {
    70% {
      box-shadow: 0 0 0 10px rgba(56, 128, 246, 0);
    }
    100% {
      box-shadow: 0 0 0 0 rgba(56, 128, 246, 0);
    }
  }

  .ho__twin {
    padding: clamp(1rem, 2vw, 1.4rem);
    border-radius: var(--cl-r-lg);
    /* Opaque, for the same reason the panels are: the boundary can be dragged, and a
       translucent dark card over the day half goes grey with pale type inside it. */
    background: #26314f;
    border: 1px solid rgba(178, 197, 239, 0.14);
  }

  .ho__tally {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
  }

  .ho__tally b {
    font-size: clamp(2.25rem, 5vw, 3.25rem);
    font-weight: 900;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--cl-accent);
    font-variant-numeric: tabular-nums;
  }

  .ho__tally span {
    font-size: 0.8125rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--cl-navy-400);
  }

  .ho__note {
    margin-top: 0.75rem;
    font-size: 0.9375rem;
    line-height: 1.45;
    color: var(--cl-navy-300);
  }

  /* The handle. A range input, dressed as the boundary it drags. */
  .ho__grip {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    margin-top: clamp(1.5rem, 3vw, 2.25rem);
    background: transparent;
    cursor: col-resize;
  }

  .ho__grip::-webkit-slider-runnable-track {
    height: 2px;
    background: linear-gradient(
      90deg,
      var(--cl-accent) 0 calc(var(--p) * 1%),
      rgba(178, 197, 239, 0.2) calc(var(--p) * 1%) 100%
    );
  }

  .ho__grip::-moz-range-track {
    height: 2px;
    background: rgba(178, 197, 239, 0.2);
  }

  .ho__grip::-moz-range-progress {
    height: 2px;
    background: var(--cl-accent);
  }

  .ho__grip::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 34px;
    margin-top: -16px;
    border-radius: 2px;
    background: var(--cl-blue-400);
    box-shadow: 0 0 22px rgba(56, 128, 246, 0.65);
    cursor: col-resize;
  }

  .ho__grip::-moz-range-thumb {
    width: 14px;
    height: 34px;
    border: 0;
    border-radius: 2px;
    background: var(--cl-blue-400);
    box-shadow: 0 0 22px rgba(56, 128, 246, 0.65);
    cursor: col-resize;
  }

  .ho__grip:focus-visible {
    outline: 3px solid var(--cl-accent);
    outline-offset: 6px;
    border-radius: var(--cl-r-sm);
  }

  /* Same reason as the rows above: outside the split layer, so one tone for both sides. */
  .ho__hint {
    margin-top: 0.6rem;
    font-size: 0.6875rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: #6b7ca6;
    transition: opacity 300ms ease;
  }

  .ho[data-touched] .ho__hint {
    opacity: 0;
  }

  @media (max-width: 760px) {
    .ho__cols {
      grid-template-columns: minmax(0, 1fr);
    }
  }

  /* On a phone the week is one column, and it is not pinned.
   *
   * Two columns at 390 points cut every line in half - "Read 41 unread", "Move what slipped
   * to" - because the rows are a fixed height and the text had nowhere to wrap. And a pin a
   * hundred vh tall holding twenty two rows, a card and a slider cannot fit the screen it is
   * pinned to, so here the section scrolls like any other and the slider is the control. */
  @media (max-width: 620px) {
    .handover {
      height: auto;
    }

    .ho__pin {
      position: static;
      min-height: 0;
    }

    .ho__list {
      grid-template-columns: minmax(0, 1fr);
      /* Rows keep their height so the emptiness left behind still reads, and grow when a
         line needs two. */
      grid-auto-rows: minmax(1.85rem, auto);
    }

    .ho__item {
      padding-block: 0.3rem;
    }

    /* It leaves by fading, not by sliding. The slide is two and a half rem to the right, and
       on a phone that is past the edge of the screen. */
    .ho__item[data-gone] {
      translate: 0 0;
    }

    /* The control comes first.
     *
     * Read down the page, the slider sat under twenty two rows and a card: you reach the
     * handle having already scrolled the thing it moves off the screen, drag it, and see
     * nothing happen. The counter and the handle go to the top, and the list they empty
     * follows them - so the number changes under your thumb and the rows go with it. */
    .ho {
      display: flex;
      flex-direction: column;
    }

    .ho__cols {
      display: contents;
    }

    .ho__twin {
      order: 1;
    }

    .ho__grip {
      order: 2;
      margin-top: 1.25rem;
    }

    .ho__hint {
      order: 3;
    }

    .ho__list {
      order: 4;
      margin-top: 1.75rem;
    }

    .ho__kept {
      order: 5;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .ho__item {
      transition: opacity 1ms;
      filter: none;
    }

    .ho__item[data-gone] {
      translate: 0 0;
      filter: none;
    }

    .ho[data-clear] .ho__item--kept .ho__dot {
      animation: none;
    }
  }

/* --- components/Errand.astro --- */
.asks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  /* On a phone the four asks are a menu, not a cloud.
   *
   * Pills sized by their own text wrapped into a ragged two-and-two with a lonely one
   * underneath - the shape a tag list takes when nobody chose a shape. A single column of
   * equal rows reads as four options and gives a thumb something to hit. */
  @media (max-width: 700px) {
    .asks {
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      gap: 0.5rem;
    }

    .ask {
      width: 100%;
      padding-block: 0.75rem;
      text-align: start;
    }
  }

  .ask {
    padding: 0.5rem 0.95rem;
    border-radius: var(--cl-r-full);
    /* Mid-tones, because the boundary can be dragged and these buttons then stand on white.
       A border and a label picked for the night go to nearly nothing over the day half. */
    border: 1px solid rgba(90, 112, 160, 0.38);
    background: none;
    color: #5f739f;
    font: inherit;
    font-size: 0.875rem;
    cursor: pointer;
    transition:
      border-color 200ms ease,
      color 200ms ease,
      background 200ms ease;
  }

  .ask:hover {
    color: var(--cl-navy-100);
    border-color: rgba(102, 160, 255, 0.5);
  }

  .ask.is-on {
    background: rgba(56, 128, 246, 0.16);
    border-color: rgba(102, 160, 255, 0.55);
    color: var(--cl-blue-300, #8cbaff);
  }

  .run {
    overflow: hidden;
    font-family: var(--cl-mono);
    font-size: clamp(0.8125rem, 1.05vw, 0.9375rem);
    line-height: 1.5;
  }

  .run__bar {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.85rem 1.15rem;
    border-bottom: 1px solid var(--cl-line);
    color: var(--cl-text3);
    font-size: 0.8125rem;
  }

  .run__dot {
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background: var(--cl-accent);
    box-shadow: 0 0 0 0 rgba(56, 128, 246, 0.6);
  }

  /* Only while it is running: a pulse that never stops says the machine is idling, which is
     the opposite of what this section is about. */
  [data-running="true"] .run__dot {
    animation: pulse 1.6s ease-out infinite;
  }

  @keyframes pulse {
    70% {
      box-shadow: 0 0 0 0.55rem rgba(56, 128, 246, 0);
    }
    100% {
      box-shadow: 0 0 0 0 rgba(56, 128, 246, 0);
    }
  }

  .run__name {
    color: var(--cl-text);
    font-weight: 700;
  }

  .run__meta {
    margin-inline-start: auto;
  }

  .run__meta b {
    color: var(--cl-text2);
    font-variant-numeric: tabular-nums;
  }

  .run__body {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 1.35rem clamp(1.15rem, 2.2vw, 1.75rem) 1.6rem;
    /* Enough room for the longest of the four, so switching between them does not make the
       section jump under the reader's cursor. */
    min-height: 19rem;
  }

  .ln {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    color: var(--cl-text2);
    opacity: 0;
    transform: translateY(0.35rem);
  }

  [data-running] .ln {
    transition:
      opacity 280ms ease,
      transform 280ms ease;
    transition-delay: calc(var(--i, 0) * 420ms);
  }

  [data-played="true"] .ln {
    opacity: 1;
    transform: none;
  }

  .ln__t {
    min-width: 0;
  }

  .ln__n {
    margin-inline-start: auto;
    padding-inline-start: 1rem;
    color: var(--cl-text3);
    white-space: nowrap;
  }

  .ln--prompt {
    margin-bottom: 0.75rem;
    color: var(--cl-text);
    font-weight: 700;
  }

  .ln__caret {
    color: var(--cl-accent);
    font-weight: 900;
  }

  /* Workers run at the same time, each finishing on its own clock - so the group takes as
     long as the slowest, not as long as the sum. That is the whole reason a swarm exists,
     and a list that ticked in order would say the opposite. */
  .wk {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    opacity: 0;
    transition: opacity 280ms ease;
    transition-delay: calc(var(--i, 0) * 420ms);
  }

  [data-played="true"] .wk {
    opacity: 1;
  }

  .ln--wk {
    padding-inline-start: 1.5rem;
    color: var(--cl-text3);
    opacity: 1;
    transform: none;
    transition: none;
  }

  .ln__tick {
    color: var(--cl-accent);
    opacity: 0;
    transition: opacity 160ms ease;
    transition-delay: var(--ms);
  }

  .ln__bar {
    flex: 1;
    min-width: 3rem;
    height: 2px;
    margin-inline: 0.4rem;
    background: rgba(248, 250, 255, 0.1);
    overflow: hidden;
  }

  .ln__bar i {
    display: block;
    height: 100%;
    width: 100%;
    background: var(--cl-accent);
    transform-origin: left;
    scale: 0 1;
  }

  .ln--wk .ln__n {
    opacity: 0;
    transition: opacity 160ms ease;
    transition-delay: var(--ms);
  }

  [data-played="true"] .ln__bar i {
    scale: 1 1;
    transition: scale var(--ms) cubic-bezier(0.35, 0.6, 0.3, 1);
    /* The swarm starts when the step above it says so, not when the panel appears. */
    transition-delay: calc(var(--wk-start, 1260ms));
  }

  [data-played="true"] .ln__tick,
  [data-played="true"] .ln--wk .ln__n {
    opacity: 1;
    transition-delay: calc(var(--ms) + var(--wk-start, 1260ms));
  }

  .ln--result {
    margin-top: 0.9rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--cl-line);
    color: var(--cl-text);
  }

  /* The one coloured line in the panel, because it is the one the page is arguing about.
     Blue when the twin stopped and handed back; green when it finished on its own - the
     reader should be able to tell the two apart without reading a word. */
  .ln--gate {
    color: var(--cl-accent-br);
    font-weight: 700;
  }

  .ln--gate-done {
    color: #4fbf8a;
  }

  .errand__foot {
    --day: var(--cl-navy-700);
    --night: var(--cl-navy-400, #7e93c4);
    max-width: 56ch;
    font-size: 0.9375rem;
  }

  /* On a phone a right-aligned note in the same line as wrapping text pushes the text into a
     second line and leaves one word stranded under it. The note goes underneath instead, the
     way a terminal wraps. */
  @media (max-width: 620px) {
    /* Steps only. The prompt keeps its caret on the same line as the words, and a worker's
       one-word verdict stays beside its bar - stacking those made the panel twice as tall
       and pulled the verdict away from the thing it belongs to. */
    .ln:not(.ln--prompt):not(.ln--wk) {
      flex-wrap: wrap;
    }

    .ln:not(.ln--prompt):not(.ln--wk) .ln__n {
      width: 100%;
      margin-inline-start: 0;
      padding-inline-start: 0;
      white-space: normal;
      opacity: 0.85;
    }

    .ln__bar {
      min-width: 1.5rem;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .ln,
    .wk,
    .ln__tick,
    .ln--wk .ln__n {
      opacity: 1;
      transform: none;
      transition: none;
    }

    .ln__bar i {
      scale: 1 1;
      transition: none;
    }
  }

/* --- components/Trust.astro --- */
/* Three cards, two, or one - and never two plus an orphan.
   *
   * `auto-fit` alone gave exactly that on a laptop: the column had room for two cards of
   * 19rem and dropped the third underneath at a third of the width, which reads as a
   * mistake rather than a row. In the band where two fit, the third takes the full width on
   * purpose; below that they stack. */
  .tgrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: clamp(1rem, 2vw, 1.5rem);
    align-items: start;
  }

  @media (min-width: 700px) and (max-width: 1439px) {
    .tgrid {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .tgrid > :last-child {
      grid-column: 1 / -1;
    }
  }

  @media (min-width: 1440px) {
    .tgrid {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  .tcard {
    padding: clamp(1.25rem, 2vw, 1.6rem);
  }

  .tcard__h {
    margin-bottom: 0.5rem;
    font-size: 1.1875rem;
    font-weight: 900;
    line-height: 1.25;
    letter-spacing: -0.015em;
    color: var(--cl-navy-100);
  }

  /* The heading is a button so it can open the card on a phone. On a wide screen it is only
     a heading: no pointer, no chevron, nothing to press. */
  .tcard__btn {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    /* Vertically centred against the chevron, with the same air above and below. The button
       used to take its height from the text alone, so a heading that wrapped to two lines
       sat high in the card and one that did not sat low. */
    min-height: 2.75rem;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: start;
    cursor: default;
  }

  .tcard__chev {
    display: none;
  }

  .tcard__s {
    margin-bottom: 1rem;
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--cl-navy-300);
  }

  .tcard__s--mid {
    margin-top: 1.1rem;
  }

  .tcard__f {
    margin-top: 1rem;
    padding-top: 0.9rem;
    border-top: 1px solid rgba(178, 197, 239, 0.14);
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--cl-navy-400, #7e93c4);
  }

  /* --- 1: the gate ---------------------------------------------------------- */

  .gate {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    counter-reset: none;
  }

  .gate li {
    display: flex;
    gap: 0.6rem;
    font-size: 0.875rem;
    line-height: 1.45;
    color: var(--cl-navy-200, #d8e4fb);
  }

  .gate__n {
    flex: none;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.1rem;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: rgba(56, 128, 246, 0.18);
    color: var(--cl-blue-300, #8cbaff);
    font-family: var(--cl-mono);
    font-size: 0.6875rem;
  }

  .errands {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }

  .er {
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    width: 100%;
    padding: 0.4rem 0.5rem;
    border: 0;
    border-radius: var(--cl-r-sm);
    background: none;
    font: inherit;
    font-size: 0.875rem;
    text-align: start;
    color: var(--cl-navy-200, #d8e4fb);
  }

  .er--done {
    color: var(--cl-navy-400, #7e93c4);
  }

  .er--stop {
    cursor: pointer;
  }

  .er--stop:hover,
  .er--stop:focus-visible {
    background: rgba(56, 128, 246, 0.14);
  }

  .er__ic {
    color: #4fbf8a;
  }

  .er--stop .er__ic {
    color: var(--cl-accent-br);
  }

  .er__t {
    min-width: 0;
  }

  .er__n {
    margin-inline-start: auto;
    padding-inline-start: 0.75rem;
    font-family: var(--cl-mono);
    font-size: 0.6875rem;
    white-space: nowrap;
    color: var(--cl-navy-400, #7e93c4);
  }

  .er--stop[aria-pressed="true"] {
    background: rgba(56, 128, 246, 0.16);
  }

  .tcard__out {
    margin-top: 0.85rem;
    padding: 0.7rem 0.85rem;
    border-inline-start: 2px solid var(--cl-accent);
    border-radius: 0 var(--cl-r-sm) var(--cl-r-sm) 0;
    background: rgba(56, 128, 246, 0.1);
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--cl-navy-100);
  }

  /* --- 2: whose instruction --------------------------------------------------- */

  .hands {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
  }

  .hand {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    width: 100%;
    padding: 0.5rem;
    border: 1px solid transparent;
    border-radius: var(--cl-r-sm);
    background: none;
    font: inherit;
    text-align: start;
    color: inherit;
    cursor: pointer;
  }

  .hand:hover {
    background: rgba(56, 128, 246, 0.1);
  }

  .hand[aria-pressed="true"] {
    background: rgba(56, 128, 246, 0.14);
    border-color: rgba(102, 160, 255, 0.35);
  }

  .hand__v {
    flex: none;
    width: 4.4rem;
    padding: 0.15rem 0;
    border-radius: 3px;
    font-family: var(--cl-mono);
    font-size: 0.625rem;
    letter-spacing: 0.06em;
    text-align: center;
    text-transform: uppercase;
  }

  .hand__v--done {
    background: rgba(79, 191, 138, 0.16);
    color: #6fd0a4;
  }

  .hand__v--held {
    background: rgba(214, 168, 78, 0.16);
    color: #e0bb72;
  }

  .hand__v--refused {
    background: rgba(232, 104, 109, 0.16);
    color: #f0989b;
  }

  .hand__b {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
  }

  .hand__from {
    font-size: 0.8125rem;
    color: var(--cl-navy-200, #d8e4fb);
  }

  .hand__ask {
    font-family: var(--cl-mono);
    font-size: 0.6875rem;
    line-height: 1.45;
    color: var(--cl-navy-400, #7e93c4);
  }

  /* --- 3: the seal ----------------------------------------------------------- */

  .seal {
    padding: 0.35rem 0.8rem;
    border-radius: var(--cl-r-full);
    border: 1px solid rgba(102, 160, 255, 0.45);
    background: none;
    font: inherit;
    font-size: 0.8125rem;
    color: var(--cl-blue-300, #8cbaff);
    cursor: pointer;
  }

  .seal[aria-pressed="true"] {
    background: rgba(56, 128, 246, 0.16);
  }

  .files {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    font-family: var(--cl-mono);
    font-size: 0.75rem;
  }

  .fl {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.4rem 0.55rem;
    border-radius: var(--cl-r-sm);
    background: rgba(8, 14, 30, 0.28);
  }

  .fl__p {
    color: var(--cl-blue-300, #8cbaff);
  }

  .fl__b {
    color: var(--cl-navy-300);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* The custody contract, set as a spec rather than as prose: these are the five facts a
     reader has to be able to check us against later, and a paragraph hides them. */
  .spec {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    margin-top: 1.1rem;
  }

  .spec dt {
    font-family: var(--cl-mono);
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--cl-blue-300, #8cbaff);
    margin-bottom: 0.15rem;
  }

  .spec dd {
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--cl-navy-200, #d8e4fb);
  }

  .fl--sealed .fl__b {
    color: var(--cl-navy-400, #7e93c4);
  }

  [data-server="true"] .fl--open .fl__b::after {
    content: " · stored as written";
    color: #d6a84e;
  }

  [data-server="true"] .fl--sealed .fl__p::after {
    content: " · sealed";
    color: #4fbf8a;
  }

  /* Same wrap as the errand panel: a right-aligned note beside wrapping text strands a word
     under it. The note takes its own line here. */
  /* One card open at a time.
   *
   * Stacked in full, the three run to some three thousand points on a phone - the reader
   * scrolls past two of them to reach the third and reads none. Each is a self-contained
   * article, which is exactly what an accordion is for: the titles are the summary of the
   * section, and the one you press is the one you get. */
  @media (max-width: 900px) {
    .tcard__btn {
      align-items: flex-start;
      cursor: pointer;
      min-height: 44px;
    }

    .tcard__chev {
      display: block;
      flex: none;
      width: 0.55rem;
      height: 0.55rem;
      margin-top: 0.35rem;
      margin-inline-start: auto;
      border-right: 2px solid var(--cl-blue-300, #8cbaff);
      border-bottom: 2px solid var(--cl-blue-300, #8cbaff);
      rotate: 45deg;
      transition: rotate 220ms ease;
    }

    [aria-expanded="true"] .tcard__chev {
      rotate: -135deg;
    }

    .tcard__body[hidden] {
      display: none;
    }

    .tcard__h {
      margin-bottom: 0;
    }

    /* Not a sibling selector off the button: the button lives inside the heading, so the
       body is the heading's sibling and not the button's. */
    .tcard__body {
      margin-top: 0.85rem;
    }
  }

  @media (max-width: 620px) {
    .er {
      flex-wrap: wrap;
    }

    .er__n {
      width: 100%;
      margin-inline-start: 0;
      padding-inline-start: 1.1rem;
    }
  }

/* --- components/PixelNum.astro --- */
.num {
    --unit: clamp(0.5rem, 1.05vw, 0.8rem);
    position: relative;
    display: block;
    width: calc(var(--cols) * var(--unit));
    height: calc(var(--rows) * var(--unit));
  }

  .num i {
    position: absolute;
    left: calc(var(--c) * var(--unit));
    top: calc(var(--r) * var(--unit));
    width: calc(var(--unit) - 1px);
    height: calc(var(--unit) - 1px);
    background: var(--cl-accent);
    opacity: 0;
    translate: calc(var(--unit) * -1.5) 0;
  }

  /* Triggered on itself, not on the row. Astro scopes component styles, so a selector
     hanging off an ancestor that lives in another component never matches - the numerals
     stayed at zero opacity with the row already marked as shown. Self-triggering also
     reads better: the numeral assembles when the numeral arrives.
     The stagger runs along columns, so it is material arriving, not a list fading in. */
  .num[data-shown="true"] i {
    opacity: 1;
    translate: 0 0;
    transition:
      opacity 420ms ease,
      translate 520ms cubic-bezier(0.2, 0.8, 0.3, 1);
    transition-delay: calc(var(--i) * 11ms);
  }

  .num i:nth-child(3n) {
    background: var(--cl-blue-400);
  }

  .num i:nth-child(5n) {
    opacity: 0;
    background: var(--cl-navy-500);
  }

  .num[data-shown="true"] i:nth-child(5n) {
    opacity: 0.55;
  }

  /* Assembled is not finished. A numeral that snaps into place and then holds perfectly
     still is a picture of the material, not the material - everything else on this page
     keeps moving. Three rhythms so no row ever pulses together: most cells breathe, a
     seventh of them drop out and come back, and a few drift a pixel off the grid. */
  .num[data-shown="true"] i {
    animation: breathe 4.2s ease-in-out infinite;
    animation-delay: calc(var(--i) * 47ms);
  }

  .num[data-shown="true"] i:nth-child(7n) {
    animation: blink 5.6s steps(1, end) infinite;
    animation-delay: calc(var(--i) * 83ms);
  }

  .num[data-shown="true"] i:nth-child(4n + 1) {
    animation: breathe 4.2s ease-in-out infinite, sway 6.8s ease-in-out infinite;
    animation-delay: calc(var(--i) * 47ms), calc(var(--i) * 61ms);
  }

  @keyframes breathe {
    0%,
    100% {
      opacity: 1;
    }
    45% {
      opacity: 0.42;
    }
  }

  @keyframes blink {
    0%,
    88% {
      opacity: 0.55;
    }
    90%,
    94% {
      opacity: 0.06;
    }
    96%,
    100% {
      opacity: 0.55;
    }
  }

  @keyframes sway {
    0%,
    100% {
      translate: 0 0;
    }
    50% {
      translate: 1px -1px;
    }
  }

  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  @media (prefers-reduced-motion: reduce) {
    .num i {
      opacity: 1;
      translate: 0 0;
      transition: none;
    }

    .num i:nth-child(5n) {
      opacity: 0.55;
    }
  }

/* --- components/Footer.astro --- */
/* Opaque like the closing section above it: both belong to the owner's side, and the
     night backdrop is fixed, so anything transparent down here would show it through. */
  .footer {
    position: relative;
    background: transparent;
    color: var(--cl-navy-800);
    padding-block: clamp(3rem, 6vw, 4.5rem);
    border-top: 1px solid var(--cl-navy-200);
  }

  /* No border where the footer is itself the fold. On a page with a closing block the day
     starts above the footer and a hairline separates two light surfaces; on a page without
     one the day starts here, the canvas draws the dissolve across this exact edge, and the
     border cut a white line straight through it. */
  main:not(:has(.closing)) + .footer {
    border-top: 0;
  }

  .footer__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.5rem 2.5rem;
  }

  /* On a phone the row becomes a stack, and a stack of three unequal blocks pinned to the
     left edge under a centred closing looks like leftovers. Centred, it reads as the end of
     the page. */
  @media (max-width: 760px) {
    .footer__inner {
      flex-direction: column;
      align-items: center;
      gap: 1.4rem;
      text-align: center;
    }

    .footer__links {
      margin-inline-start: 0;
      justify-content: center;
      gap: 1.1rem 1.5rem;
    }

    .footer__legal {
      margin-inline-start: 0;
    }
  }

  .footer__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-weight: 700;
    font-size: 1.0625rem;
    letter-spacing: -0.01em;
    color: var(--cl-navy-950);
    text-decoration: none;
  }

  .footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-inline-start: auto;
  }

  .footer__links a {
    color: var(--cl-text2);
    text-decoration: none;
    font-weight: 700;
  }

  .footer__links a:hover {
    color: var(--cl-accent-br);
  }

  .footer__legal {
    flex-basis: 100%;
    margin: 0;
    color: var(--cl-text3);
    font-size: 0.9375rem;
  }

/* --- pages/index.astro --- */
.band {
    max-width: 22ch;
  }

  .band em {
    font-style: normal;
    color: var(--cl-accent);
  }

  .band__strike {
    position: relative;
    color: var(--cl-text3);
  }

  .band__strike::after {
    content: "";
    position: absolute;
    left: -0.05em;
    right: -0.05em;
    top: 55%;
    height: 0.07em;
    background: currentColor;
  }

  /* Numbered rows rather than a grid of equal cards. Four identical boxes is the shape a
     page takes when nobody decided anything; a stepped list says these are in order. */
  .steps {
    display: flex;
    flex-direction: column;
    gap: clamp(1.75rem, 3.5vw, 2.75rem);
  }

  /* Stacked, not columned. The numeral used to share a grid cell boundary with the
     heading and outgrew it; a block of its own has nothing to climb into. */
  .steps__item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: clamp(0.9rem, 1.8vw, 1.35rem);
    padding-top: 1.5rem;
    border-top: 1px solid var(--cl-line);
  }

  /* Every other row steps in: the ragged edge is what stops four rows from reading as a
     table. */
  .steps__item:nth-child(even) {
    margin-inline-start: clamp(0rem, 3vw, 2.5rem);
  }

  .steps__text {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    color: var(--cl-text2);
  }

  .steps__text .h3 {
    color: var(--cl-text);
  }

  .proof__inner {
    display: grid;
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    align-items: center;
    gap: clamp(2rem, 5vw, 4rem);
  }

  .proof__figure {
    position: relative;
    perspective: 900px;
    /* The tilt is written by the script as two angles; zero until a pointer arrives, so a
       touch screen and a keyboard get a portrait that simply sits there. */
    --tx: 0deg;
    --ty: 0deg;
  }

  /* The whole card turns in space: rotation plus a small lift toward the viewer, and a
     shadow that leans the opposite way. Those three together are what makes it read as an
     object being turned rather than an image being skewed - and none of them touch what is
     inside the frame. */
  .proof__inner-frame {
    position: relative;
    display: block;
    border-radius: var(--cl-r-xl);
    overflow: hidden;
    background: var(--cl-navy-950);
    box-shadow:
      calc(var(--ty) / 1deg * -1.6px) calc(24px + var(--tx) / 1deg * 1.6px) 60px
      rgba(0, 0, 0, 0.5);
    transform: rotateX(var(--tx)) rotateY(var(--ty)) translateZ(0);
    transform-style: preserve-3d;
    transition:
      transform 420ms cubic-bezier(0.2, 0.7, 0.3, 1),
      box-shadow 420ms ease;
    will-change: transform;
  }

  .proof__figure img {
    display: block;
    width: 100%;
    /* The picture does not move inside its frame. The parallax that used to slide it was
       read as the background crawling - which is exactly what it was, since the portrait
       carries its own dark backdrop. The frame tilts; the contents sit still, the way a
       photograph behaves when you turn it in your hand. */
  }

  /* Aurora. Two blurred lobes in the brand's own blue and magenta, drifting at different
     speeds so the pattern never repeats visibly. Behind the frame, never over the face. */
  .proof__aura {
    position: absolute;
    inset: -12% -14%;
    z-index: -1;
    border-radius: 50%;
    background:
      radial-gradient(38% 42% at 30% 30%, rgba(56, 128, 246, 0.55), transparent 70%),
      radial-gradient(42% 40% at 72% 62%, rgba(214, 74, 190, 0.42), transparent 72%);
    filter: blur(38px);
    opacity: 0.7;
    /* Still. The glow moved for a while and it was the wrong thing to animate: the eye
       reads a drifting background as something crawling behind the picture, and the
       picture is the subject. What moves here is what the reader causes - the lean - and
       what belongs to the dissolve itself. */
  }

  /* A slow pass of light across the glass, the way a screen catches a window. Six seconds
     of nothing between passes: an effect you notice twice is charm, one you notice every
     second is a nightclub. */
  .proof__sheen {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      104deg,
      transparent 38%,
      rgba(255, 255, 255, 0.16) 48%,
      rgba(255, 255, 255, 0.04) 55%,
      transparent 62%
    );
    translate: -120% 0;
    animation: sheen 9s ease-in-out infinite;
    pointer-events: none;
  }

  @keyframes sheen {
    0%,
    62% {
      translate: -120% 0;
    }
    82%,
    100% {
      translate: 120% 0;
    }
  }

  /* The dissolve sheds. The portrait's own right edge is pixels coming apart, so a few of
     them leave the frame and fade - the mark that says virtual, moving. */
  .proof__motes {
    position: absolute;
    inset: 0;
    pointer-events: none;
  }

  /* The dust. Thirty squares seeded across the dissolve zone, each with its own size,
     colour, speed and drift, so the edge looks like it is coming apart continuously rather
     than blinking on a timer. The maths is deliberately coprime-ish (71, 37, 13) - a plain
     modulo lands them in visible rows. */
  .proof__motes i {
    position: absolute;
    right: calc(3% + (var(--m) * 37 % 27) * 1.1%);
    top: calc(8% + (var(--m) * 71 % 82) * 1%);
    width: calc(2px + (var(--m) * 13 % 3) * 1.5px);
    height: calc(2px + (var(--m) * 13 % 3) * 1.5px);
    border-radius: 1px;
    background: rgba(146, 187, 255, 0.92);
    box-shadow: 0 0 7px rgba(56, 128, 246, 0.85);
    opacity: 0;
    animation: mote calc(4.2s + (var(--m) % 7) * 0.55s) linear infinite;
    animation-delay: calc(var(--m) * -0.31s);
  }

  /* Every third one carries the magenta of the dissolve rather than the blue. */
  .proof__motes i:nth-child(3n) {
    background: rgba(233, 140, 219, 0.9);
    box-shadow: 0 0 7px rgba(214, 74, 190, 0.75);
  }

  .proof__motes i:nth-child(5n) {
    animation-name: mote-far;
  }

  @keyframes mote {
    0% {
      opacity: 0;
      translate: 0 0;
      scale: 0.6;
    }
    14% {
      opacity: 0.95;
      scale: 1;
    }
    100% {
      opacity: 0;
      translate: 38px -30px;
      scale: 0.7;
    }
  }

  @keyframes mote-far {
    0% {
      opacity: 0;
      translate: 0 0;
    }
    18% {
      opacity: 0.7;
    }
    100% {
      opacity: 0;
      translate: 64px -14px;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .proof__sheen,
    .proof__motes i {
      animation: none;
    }

    .proof__sheen {
      opacity: 0;
    }

    .proof__inner-frame {
      transition: none;
    }
  }

  /* Day returns here, as a block. Opaque, so it covers the fixed night backdrop for its
     own height and nothing else. The pixel row along its top edge is the same dissolve as
     the seam, turned on its side: the boundary between the worlds, crossed one last time. */
  /* No background of its own: the day ground below the fold is painted by the field, so
     the canvas can lay its horizontal fringe on top of the crossing. */
  .closing {
    padding-block: clamp(7rem, 14vw, 11rem);
    color: var(--cl-navy-950);
  }

  /* Centred at every width, like the hero. This block is the same shape as the opening -
     one claim and one button - and a left-aligned closing under a centred opening reads as
     two different pages. It used to be centred only on a phone, which meant the desktop
     version had a wide empty right half and the button adrift in the corner. */
  .closing .side {
    align-items: center;
    text-align: center;
    /* Centred on the page, not merely inside its own column. Centring the text while the
       column itself stayed pinned to the left gutter put the whole block in the left third
       of a wide screen and called it centred. */
    margin-inline: auto;
  }

  .closing .prose {
    margin-inline: auto;
  }


  .closing .prose {
    color: var(--cl-navy-800);
  }

  @media (max-width: 860px) {
    .proof__inner {
      grid-template-columns: minmax(0, 1fr);
      justify-items: center;
      text-align: center;
    }

    /* Centred on a phone. A portrait pinned to the left gutter under a centred hero reads
       as a stray element rather than the subject of the section. */
    .proof__figure {
      max-width: 18rem;
      margin-inline: auto;
    }

    .proof .prose,
    .proof .label {
      margin-inline: auto;
    }

    .steps__item:nth-child(even) {
      margin-inline-start: 0;
    }
  }

/* --- pages/download.astro --- */
/* The first screen of a page opens in the day, and the boundary frays inward from the
     right edge there. Only this section needs the clearance; below it the seam is off the
     other side of the screen entirely. */
  @media (max-width: 620px) {
    .dl .head .wrap{
      padding-inline-end: 3rem;
    }
  }

  .dl .head{
    padding-block: clamp(6rem, 12vh, 9rem) clamp(3rem, 6vw, 4.5rem);
  }

  .dl .grid{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: 1.25rem;
    margin: 0;
    padding: 0;
  }

  /* Night panels, like every other card on the site.
   *
   * These were built when this page sat in the day: a white card with a navy border. The
   * page now opens with the boundary already parked to the left, so the whole thing stands
   * in the night, and a light card there is a hole cut in the page. */
  .dl .plat{
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: clamp(1.5rem, 2.5vw, 2rem);
    border-radius: var(--cl-r-lg);
    background: #26314f;
    border: 1px solid rgba(178, 197, 239, 0.16);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
    transition: border-color 160ms ease;
  }

  .dl .plat[data-current="true"]{
    border-color: var(--cl-accent);
    box-shadow: 0 1px 2px rgba(28, 39, 64, 0.06), 0 6px 22px rgba(28, 39, 64, 0.07);
  }

  .dl .plat__head{
    display: flex;
    align-items: center;
    gap: 0.75rem;
  }

  .dl .plat__badge{
    padding: 0.2rem 0.6rem;
    border-radius: var(--cl-r-full);
    background: var(--cl-accent-soft);
    color: var(--cl-accent-br);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.06em;
  }

  .dl .plat__req{
    margin: 0;
    color: var(--cl-navy-300);
    font-size: 0.9375rem;
  }

  .dl .plat__builds{
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-top: auto;
    padding-top: 0.6rem;
  }

  .dl .plat__soon{
    display: inline-flex;
    align-items: center;
    padding: 0.7rem 1.2rem;
    border-radius: var(--cl-r-full);
    border: 2px dashed rgba(178, 197, 239, 0.28);
    color: var(--cl-navy-300);
    font-weight: 700;
    font-size: 0.9375rem;
  }

  .dl .notice{
    margin-top: 2rem;
    padding: clamp(1.5rem, 3vw, 2rem);
    border-radius: var(--cl-r-lg);
    background: rgba(56, 128, 246, 0.12);
    border: 1px solid rgba(56, 128, 246, 0.35);
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
  }

  .dl .notice p{
    margin: 0;
    color: var(--cl-navy-200);
    max-width: 60ch;
  }

  .dl .notice a{
    color: var(--cl-accent-br);
    font-weight: 700;
  }

  .dl .steps{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .dl .steps li{
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-top: 1rem;
    border-top: 2px solid var(--cl-line2);
  }

  .dl .steps span{
    font-size: 0.8125rem;
    font-weight: 900;
    letter-spacing: 0.16em;
    color: var(--cl-accent);
  }

  .dl .steps p{
    margin: 0;
    color: var(--cl-text2);
  }
