/* =========================================================================
   Webfonts, self-hosted.

   These used to come from fonts.googleapis.com. Serving them from our own
   origin removes a render-blocking stylesheet on a third-party host plus two
   DNS+TLS handshakes from the critical path - the browser already has a warm
   connection to us.

   Only the weights this stylesheet actually uses are here: Hedvig Letters Serif
   400 (every heading), Inter 400-600 (all reading text and every control), Lora
   500 (the wordmark, its only use), and DM Mono 500 (the .overline, its only
   use). Adding a new weight means adding the file too - it will silently fall
   back otherwise.

   Inter is variable, so one file per subset covers the whole weight range. That is
   why its filenames read 400_600 rather than a single number, and why swapping in a
   third weight costs nothing. Hedvig has one weight and no wght axis at all.

   latin-ext is included so European names in future copy (Zurich, Societe
   Generale) do not drop to a fallback face mid-word. unicode-range means the
   browser downloads a subset only if the page needs a character from it, so
   this costs nothing on an all-English page.

   Refresh with scripts/fetch-fonts.py.
   ========================================================================= */

/* >>> webfonts: generated by scripts/fetch-fonts.py, do not hand-edit <<< */

/* DM Mono 500 - latin-ext */
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/dm-mono-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* DM Mono 500 - latin */
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/dm-mono-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Hedvig Letters Serif 400 - latin-ext */
@font-face {
  font-family: 'Hedvig Letters Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/fonts/hedvig-letters-serif-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Hedvig Letters Serif 400 - latin */
@font-face {
  font-family: 'Hedvig Letters Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/fonts/hedvig-letters-serif-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Inter 400 600 - latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/assets/fonts/inter-400_600-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Inter 400 600 - latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/assets/fonts/inter-400_600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Lora 500 - latin-ext */
@font-face {
  font-family: 'Lora';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/lora-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Lora 500 - latin */
@font-face {
  font-family: 'Lora';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/lora-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* <<< end webfonts >>> */


/* =========================================================================
   Greywick Digital — site stylesheet.

   One file for all pages. It was three near-identical <style> blocks until
   2026-08-20; they had already drifted apart in four places, so they were
   merged rather than kept in sync by hand.

   Built on the TDC site's design system (page rails, dashed rules, aurora
   hero, dot-field bands, duo-tone headlines), recolored monochrome.
   Structure follows page-architecture.md in the branding workspace.
   ========================================================================= */

  /* =========================================================================
     Greywick Digital draft landing page, built on the TDC site's design system
     (page rails, dashed rules, aurora hero, dot-field bands, duo-tone
     headlines) but recolored monochrome — black, white and neutral graphite
     in place of the violet-on-indigo palette of the earlier draft. Same structure,
     same architecture in page-architecture.md; copy drawn from narrative.md
     and positioning.md.
     ========================================================================= */
  /* ONE light ground, and the dark one is placed by hand [Matias, 2026-09-10].

     Four near-whites were in play before this: --bg #faf9f6 on most pages, #ffffff on
     the home mechanism and products bands, --surface-alt #f2f1ed on the alternating
     bands of cLTC, about and privacy, and a stray #fafafa on one card hover. Nothing
     ever chose between them, and the home page ended up the only page whose sections
     were painted at all.

     So --bg is #ffffff and it is the ground everywhere: body, footer, every section,
     every card, every form field. --surface-alt survives with a narrowed job - a tint
     INSIDE something, never a section ground: card thumbnails, icon plates, table
     stripes, menu-row hover.

     This reverses the 2026-08-25 note that used to sit here, which called pure #ffffff
     the single biggest cause of the page feeling harsh and warmed the palette off it.
     That call was then quietly contradicted on 8 and 9 September, when the home bands
     and the product cards went pure white and stayed. Both were in the file at once.
     This picks the white.

     OPEN, deliberately not fixed here: every grey below still carries the 3-4 point
     warm bias it was given for a warm ground, so on white it will read faintly yellow.
     Retune after the dark bands are placed, not before - half these greys only ever
     appear on dark, and moving them now means moving them twice.

     --accent-bg #eceae5 came out at the same time. Declared 2026-08-25, never used. */
  :root {
    --accent:        #1f1f1c;
    --accent-strong: #000000;
    --accent-line:   #dedcd6;

    --bg:           #ffffff;
    --surface-alt:  #f2f1ed;
    /* The mechanism band's line field is white lines over a wash of this
       [Matias, 2026-09-10], and it is the only colour on the site outside photography.
       In CHANNELS, not a hex, because the wash needs it twice - at full strength and at
       zero alpha - and a separate rgba() spelling of the same colour is exactly the
       thing that stays blue when someone changes the hex. #D2DCFF. */
    --tint-blue:    210 220 255;
    /* 2026-09-10 [Matias]: #121212 to #101010. The dark ground is no longer one band
       in the middle of a light page, it is the whole bottom of every page - the
       sign-off band and the footer under it read as one block - and at that size
       #121212 sat a shade short of black without reading as a deliberate grey. */
    --surface-dark: #101010;

    --ink-h1:       #1a1a18;
    --ink-h-faded:  #7a7873;
    --ink:          #1c1c1a;
    --ink-2:        #575652;
    --ink-3:        #6b6a66;
    --on-dark:      #ffffff;
    --on-dark-muted: rgba(255,255,255,0.72);

    /* Three hairline weights, deliberately. --hairline is the quiet one: cards, the
       art-plate divider, FAQ and news rows, where the line only has to suggest an edge.
       --border stays heavier for structure that must stay findable at a glance - page
       rails, nav, footer. --border-input is heavier again because interactive boundaries
       want 3:1 under WCAG 1.4.11, which neither of the others meets. Collapsing these
       back into one token makes either the cards too loud or the rails invisible; no
       single value serves all three. */
    --hairline:      #eae8e3;
    --border:        #e2e0da;
    --border-input:  #8f8d87;
    --border-hover:  #cbc9c3;
    --border-strong: #aba9a3;

    /* 2026-09-09 [Matias]: Inter and Hedvig Letters Serif out, Outfit and Newsreader
       in, after looking at the arrangements side by side in the font lab rather than
       reasoning about them. The shape of the decision, which matters more than the two
       names: SANS SETS EVERY HEADING, SERIF SETS EVERYTHING READ AT LENGTH. It is the
       inverse of what the site did before, and it is the arrangement Ondo uses.

       --font stayed the name of THE SANS through that swap, deliberately. Around
       thirty rules already said font-family: var(--font) meaning "the UI face" - every
       button, tag, label, sub-head and form control - and they are all still correct
       with Outfit behind it. Redefining --font as the serif would have quietly turned
       every one of them serif, which is the kind of change that looks fine on the page
       you are testing and wrong on the four you are not.

       So the serif arrives as a NEW token used by prose only. See the reading-text
       block further down for which selectors take it. */

    /* 2026-09-10 [Matias]: back to Inter, and Outfit is gone.

       The short version of a day that went in a circle. Outfit was replaced by Gellix
       (Displaay Type Foundry) on trial files, which looked right and could not ship:
       the trial licence forbids storing the files on a publicly accessible server,
       which is what self-hosting a webfont is. Rather than buy the licence, Inter.

       Inter is OFL, so there is no licensing question at all, and it is the face this
       site ran on until 2026-09-09. That makes this a reversal of the note directly
       above, which is worth being plain about rather than quietly overwriting: the
       swap TO Outfit was made in a font lab against Inter, and Outfit lost on second
       look. The ARRANGEMENT that note describes is untouched and is the part that
       mattered - sans sets every heading, serif sets everything read at length.

       Both tokens, not just --font-display. Around thirty rules take var(--font)
       meaning "the UI face" and six of those are HEADINGS: the mechanism step
       headings, the product card headings, the FAQ summaries, the technology rule
       keys, the spec table headers, the footer column titles. Splitting the two tokens
       across two sans faces is how you get a site that looks subtly broken.

       The hero headline needs no special case: .hero__title takes --font-body, the
       Newsreader serif, not the sans.

       Inter sets a MUCH larger image than Outfit did: x-height 0.546 of the em against
       Outfit's 0.460, so text looks about 19 percent bigger at the same font-size.
       Nothing below has been rescaled for that yet, and the -0.018em heading tracking
       further down was chosen for Outfit's round counters. Look before adjusting. */
    /* 2026-09-10 [Matias], later the same day: the ARRANGEMENT is reverted too. Back
       to SERIF SETS EVERY HEADING, SANS SETS EVERYTHING ELSE - Hedvig Letters Serif
       on the headings, Inter on all reading text and every control, which is what the
       site ran on up to 2026-09-09 and is the inverse of the two notes above.

       Only the arrangement went back. The rest of the 2026-09-09 pass is untouched and
       deliberately so: --fs-body and --fs-prose stay split, and the ten-odd selectors
       that were moved onto --font-body stay on it. That token now resolves to the SANS,
       which is why prose is sans again without touching those selectors one by one.

       So --font-body no longer means "the serif". It means "the face running text is
       set in", which is the sans today. If a reading serif comes back, point this token
       at it and the prose follows; do not repoint --font, which around thirty rules
       take to mean the UI face.

       The hero headline lost its exception in the process. It was the one heading
       deliberately left serif while the rest went sans; now every heading is serif and
       it simply inherits, exactly as it did before 2026-09-09. */
    --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-display: "Hedvig Letters Serif", Georgia, serif;
    --font-body:    "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-logo:    "Lora", Georgia, serif;
    --font-mono:    "DM Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* ---------- THE TYPE SCALE ----------
       Eight steps, each with ONE job. Rewritten 2026-09-10 [Matias] after an audit
       found nine tokens competing with 44 hardcoded font-sizes, which is not a scale.

       Read this before adding a size ANYWHERE:

         --fs-display  hero headline, home page only              serif
         --fs-h1       page title, one per page                   serif
         --fs-h2       section heading                            serif
         --fs-h3       card and block heading, h3 and h4          sans 500
         --fs-prose    ALL running text, intro paragraphs included sans
         --fs-small    card descriptions, UI text, controls       sans
         --fs-caption  notes, legal, timestamps, form messages    sans
         --fs-micro    overlines, tags, table headers             sans

       TWO RULES, and the audit exists because both were broken.

       1. SERIF STOPS AT H2. Display, h1 and h2 are the serif; h3 and everything
          below it is the sans. The site used to assign the two faces per component,
          which is how it ended up with two h3s on the home page in different faces.
          Do not set font-family on a heading. The level decides it.

       2. NO COMPONENT INVENTS A SIZE. If a card heading wants to be smaller than
          --fs-h3, the answer is that it does not. This is what went wrong before:
          .card--product h3 at 21px/600 sat next to .how-steps h4 at 20px/400 and a
          21px lead paragraph, so a paragraph outranked a heading and two headings at
          the same level disagreed. Every one of those was a local decision that
          looked fine in its own component.

       SIX font-size values in this file are still literal, and all six are sanctioned:
       four are the wordmark (a brand lockup, sized to its own drawing at each
       breakpoint), one is the mobile nav row, and one is the form input at 1rem, which
       is a hard floor - iOS zooms the page on focus below 16px. Everything else on the
       site takes a token. If you are about to add the 45th literal size, the answer is
       one of the eight above.

       THERE IS NO LEAD SIZE, and that is the point. A --fs-lead of 19px sat here until
       2026-09-10 and was cut on sight [Matias]: a paragraph one step larger than body
       text, sitting directly under every heading on the site, stops reading as a
       paragraph and starts reading as a second headline. It was the "giant subheadline"
       problem, and shrinking it from 21px to 19px only made it a smaller version of the
       same thing.

       An intro paragraph is a PARAGRAPH. It is set at --fs-prose like every other one,
       and it is distinguished by what it says, plus colour and measure if it needs help.
       Do not reintroduce a size between --fs-prose and --fs-h3. */
    --fs-micro:    0.6875rem;
    --fs-caption:  0.8125rem;
    --fs-small:    0.9375rem;
    --fs-prose:    1.0625rem;
    /* 17px, the same number as --fs-prose, on purpose [Matias 2026-09-11]. A card
       heading is one step above the 15px card paragraph under it and one weight above
       it (500 vs 400), and no more. It was 20-22px/600, which read as oversized in a
       card. Tried on the home page first, then applied everywhere. */
    --fs-h3:       1.0625rem;
    --fs-h2:       clamp(1.75rem, 1.05rem + 1.30vw, 2.375rem);
    --fs-h1:       clamp(2.25rem, 1.35rem + 1.65vw, 3.0rem);
    --fs-display:  clamp(2.75rem, 1.50rem + 2.30vw, 4.0rem);

    /* The news article column reads at length at a 680px measure, which is the one
       place a step above --fs-prose is earned. It is NOT a lead.
       18px, down from 19px on 2026-10-01 [Matias]: "a little bit large". The 19px was
       set on 2026-09-09 when the article ran in Newsreader; it now runs in Inter, whose
       taller x-height makes the same number read noticeably bigger. 18px Inter sits
       where long-form sans reading usually lands (roughly 17 to 18px). */
    --fs-article:  1.125rem;

    --section-y:   clamp(72px, 10vw, 124px);

    --rail-width:    1280px;
    --rail-color:    var(--border);
    /* The same rail, on the dark ground. OPAQUE on purpose: .page-rails is fixed at
       z-index 40 and runs the height of the viewport, so anything painted over the
       dark block has to COVER that pale line rather than composite with it. This is
       the solid equivalent of rgba(255,255,255,0.13) over --surface-dark, which is
       the weight the dashes on .how-band already use. If --surface-dark moves, this
       has to be recomputed: 16 + 0.13 * (255 - 16) = 47. */
    --rail-on-dark:  #2f2f2f;
    --nav-h:         64px;
    --frame-edge:    24px;
    --content-inset: 15px;
    --frame-w:       min(var(--rail-width), 100% - (var(--frame-edge) * 2));
    --content-w:       1060px;
    --content-frame-w: min(var(--content-w), 100% - (var(--frame-edge) * 2));

    --radius: 4px;
  }

  *, *::before, *::after { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
  @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *{ animation:none !important; transition:none !important; } }
  body {
    margin: 0; position: relative; overflow-x: hidden; background: var(--bg); color: var(--ink-2);
    font-family: var(--font); font-size: var(--fs-small); line-height: 1.55; font-weight: 400;
    font-feature-settings: "ss01" on, "tnum" on;
    -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  }
  /* Weight 400 and tracking -0.022em are HEDVIG's numbers, restored with it later on
     2026-09-10. They replace the 500 / -0.018em chosen for Inter that morning, and the
     reasoning for those is worth keeping because it does not transfer: Inter's 400 is
     drawn for UI text at 15px and reads thin blown up, so every heading on the site went
     up a step together; and tight tracking flatters a serif's modelled stems while it
     closes up the round counters of a geometric sans.

     Both of those are arguments about a SANS. Hedvig has one weight and no wght axis at
     all, so a 500 here is not a heavier face, it is the browser smearing the 400 into a
     synthetic bold. If a heading looks light to you, the fix is size or colour. */
  h1, h2, h3, h4 { margin: 0; color: var(--ink); }

  /* SERIF, weight 400, tight tracking. Hedvig ships one weight and no wght axis, so
     any weight above 400 here is the browser faking a bold - if a heading reads light,
     change size or colour, not weight. */
  h1, h2 { font-family: var(--font-display); font-weight: 400; line-height: 1.08; letter-spacing: -0.022em; }

  /* SANS, weight 600. h3 and h4 are the same thing at the same size: a block heading.
     h4 exists for document outline, not for a smaller step - the scale has no room
     below h3 that is not body text. If you want smaller, you want --fs-small. */
  h3, h4 { font-family: var(--font); font-weight: 500; font-size: var(--fs-h3);
    line-height: 1.3; letter-spacing: -0.01em; }

  /* ---------- reading text ----------
     Everything here is Inter today: --font-body and --font resolve to the same face
     since the 2026-09-10 revert, so the split below currently changes nothing visible.

     It is kept anyway, and deliberately. The whole cost of reintroducing a reading
     serif is repointing --font-body; delete this and that becomes an audit of every
     paragraph on the site again. Which is exactly the work the block was written to
     avoid, on 2026-09-09, when the site had two faces:

     This is written as one broad rule over <p> plus a list of exceptions, rather than
     as a list of the prose classes that should get it. There are 36 bare <p> elements
     across the pages and more classed ones, so an allowlist would have missed some and
     left the site set in two faces at random. The exception list is short, closed, and
     visible; the allowlist would have been long, open and invisible.

     The exceptions are all paragraphs that are FURNITURE rather than prose: meta lines,
     status stamps, form messages, the link paragraph under the mechanism diagram. A
     class beats a bare element selector, so each of these wins wherever it appears and
     the order of these two rules does not matter.

     dt and td stay on the sans: the hero spec rail's label/value pairs and the status
     tables are tabular data, and Ondo sets that kind of thing in its sans too.

     dd is split, because the site uses <dl> for two unrelated jobs. In the hero rail a
     dd is a VALUE ("CIP-56 on Canton"), tabular, sans. On the privacy page a .defs dd
     is a paragraph of running prose that happens to answer the dt above it, so it takes
     the serif like any other prose. Checked by walking every page and reading back the
     computed family, which is how the privacy page turned up set almost entirely in the
     sans on the first pass. */
  p, blockquote, .defs dd { font-family: var(--font-body); font-size: var(--fs-prose); }
  .overline, .status, .as-at, .mech__more, .fig__n, .form-title, .form-note,
  .form-error, .footer__tag, .article__meta, .article__back, .article__sign,
  .news-item__date, .news-item__tag, .footer__legal p { font-family: var(--font); }
  /* 400, not the 500 this carried while the headings were Inter: see the note on the
     h1-h4 rule above. Hedvig ships one weight, so 500 here buys a faked bold. */
  h1 { font-size: var(--fs-h1); letter-spacing: -0.03em; color: var(--ink-h1); }
  h2 { font-size: var(--fs-h2); letter-spacing: -0.025em; }

  /* DEMOTED HEADINGS. An <h2> in a list of items is an h2 for the document outline and
     a card heading to the eye. Rather than weaken the markup or invent a size, these
     take the h3 treatment whole. This is the ONLY sanctioned way for a heading to
     leave its level - add a selector here, never a font-size in the component.

     .news-entry--lead is the exception's exception: the featured post at the top of the
     newsroom is a page-level headline and keeps the serif h2. */
  .news-row h2,
  .news-entry__title { font-family: var(--font); font-weight: 500; font-size: var(--fs-h3);
    line-height: 1.3; letter-spacing: -0.01em; }
  .news-entry--lead .news-entry__title { font-family: var(--font-display); font-weight: 400;
    font-size: var(--fs-h2); line-height: 1.08; letter-spacing: -0.022em; }

  /* INLINE HEADINGS. A FAQ question, a toggle row, a definition term: each labels the
     block under it but sits IN a run of text, so a jump to --fs-h3 would break the
     column. They stay at prose size and mark themselves with weight instead, which is
     what weight is for. Not a size of their own - --fs-prose at 500, the same as a card heading. */
  .faq summary,
  .rules-toggle summary,
  .defs dt { font-family: var(--font); font-weight: 500; font-size: var(--fs-prose);
    letter-spacing: -0.005em; color: var(--ink); }
  p { margin: 0; }
  a { color: var(--accent); text-decoration: none; }
  a:hover { text-decoration: underline; text-underline-offset: 3px; }
  b { color: var(--ink); font-weight: 600; }
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

  .muted { color: var(--ink-h-faded); font-weight: 400; }

  /* Mono, not the body sans. A monospaced label reads as instrumentation and record
     rather than marketing, which is most of the institutional register these carry.
     Set a notch smaller than --fs-overline and with more tracking, because DM Mono's
     advance width is wider than the UI sans at the same size and matching the old size
     made the eyebrows visibly heavier than the headlines they sit above. The comparison
     face was Inter, then Outfit from 2026-09-09, then Inter again from 2026-09-10;
     the relationship is unchanged, which is why the numbers here did not move. */
  .overline {
    font-family: var(--font-mono); font-size: var(--fs-micro); letter-spacing: 0.16em;
    text-transform: uppercase; color: var(--ink-3); font-weight: 500;
  }

  /* ---------- layout ---------- */
  .container { width: 100%; max-width: var(--content-frame-w); margin-inline: auto; padding-inline: var(--content-inset); }

  .page-rails {
    position: fixed; top: var(--nav-h); bottom: 0; left: 50%; transform: translateX(-50%);
    width: var(--frame-w); border-inline: 1px solid var(--rail-color); pointer-events: none; z-index: 40;
  }

  /* The home page's hero is full-bleed, and the rails must not cross it: they begin
     at its bottom edge instead. .page-rails is position:fixed, so it cannot simply
     "start below the hero" in flow - the script at the foot of index.html moves its
     top edge instead, tracking the hero while it is on screen and pinning it under
     the nav once the hero has gone. 100vh is only the value before that script runs,
     picked so the rails cannot flash across the hero on load. */
  .home .page-rails { top: 100vh; }

  .divider { border: 0; border-top: 1px solid var(--rail-color); height: 0; margin: 0; pointer-events: none; }
  .divider--rails { width: var(--frame-w); margin-inline: auto; }
  /* Edge to edge, crossing the vertical rails rather than stopping inside them. The
     footer's top border is the same figure; this is that line made reusable.

     Where it goes, as of 2026-09-10 [Matias]: closing a full-bleed block, which today
     means the home hero and nothing else.

     It used to sit above every sign-off band as well, fencing the band top and bottom
     with the footer's border. That came out when the band went dark: the top edge of
     the black IS the line now, and a grey hairline drawn on the white directly above
     it read as something left behind rather than as structure.

     --rails is the narrower one, for a divider separating two sections that both sit
     inside the frame. */
  .divider--full { width: 100vw; margin-inline: calc(50% - 50vw); }

  .section { padding-block: var(--section-y); position: relative; }

  /* .section--alt and .section--white both came out on 2026-09-10. --alt painted a
     section --surface-alt; --white painted one #fff through a full-bleed pseudo-element
     so the paint would cross the vertical rails. On a white page the first is a second
     shade nobody chose and the second is a no-op. Sections are separated by .divider
     now, and the block below is the only thing that paints one. */

  /* ---------- the dark ground ----------
     The site is white everywhere and dark is PLACED [Matias, 2026-09-10]. Two ways in:

       - .section--dark, put on any <section> by hand.
       - .contact, the sign-off band, which is dark WITHOUT the modifier as of
         2026-09-10 [Matias]. It is always the last thing before the footer, the
         footer is dark too, and the two are meant to read as one block closing the
         page, so a band that could be light is a band that could break the pair.
         A new sign-off band needs no modifier: <section class="section contact">.

     :is() rather than three repeated selector lists so the arms cannot drift.
     Specificity is what it was, since every arm is a single class.

     REVERSED here, deliberately. Both bands used to paint an INSET plate, --frame-w
     wide, so the black stopped short of the screen edge. It is full bleed now,
     because the dark footer under it is full bleed and an inset band above it left a
     white gutter down both sides of the seam. What marks the band's box is now its
     rails and its line field, not the edge of its paint. */
  .section--dark, .contact, .news-cta { background: var(--surface-dark); color: var(--on-dark); }
  :is(.section--dark, .contact, .news-cta) :is(h1, h2, h3, h4) { color: var(--on-dark); }
  :is(.section--dark, .contact, .news-cta) p { color: var(--on-dark-muted); }
  /* --accent is near-black, the same as this ground, so the primary button inverts. */
  :is(.section--dark, .contact, .news-cta) .btn--primary { background: #fff; color: var(--surface-dark); }
  :is(.section--dark, .contact, .news-cta) .btn--primary:hover { background: #e4e4e4; }
  :is(.section--dark, .contact, .news-cta) .btn--ghost { color: var(--on-dark); border-color: rgba(255,255,255,0.34); }
  :is(.section--dark, .contact, .news-cta) .btn--ghost:hover { background: var(--on-dark); color: var(--ink); border-color: var(--on-dark); }

  /* ---------- the dark plate ----------
     Added 2026-09-11 [Matias], first on the cLTC endorsement section. The third way
     dark gets placed: not a band crossing the page but a rounded card sitting INSIDE
     the rails, stopping short of them and of the dividers above and below by
     --plate-inset. Because it stops short, the pale fixed rails never cross the black
     and there is nothing for .rails-dark to cover.

     Inverted by redefining the ink tokens at section scope, exactly as .footer does,
     so .head, .quote and anything else dropped in here resolve against the dark set
     without a dark copy of their rules.

     The container padding is the one piece of arithmetic. On wide screens .container
     is narrower than the frame and the copy already sits well inside the plate; once
     the two widths meet, the copy would land on the plate's edge, so the padding grows
     to the plate's inset plus a gutter. max() picks whichever is larger at any width. */
  .section--plate {
    --ink:           var(--on-dark);
    --ink-2:         var(--on-dark-muted);
    --ink-3:         rgba(255,255,255,0.52);
    --border:        rgba(255,255,255,0.12);
    --border-strong: rgba(255,255,255,0.30);
    --surface-alt:   rgba(255,255,255,0.06);
    --plate-inset:   clamp(6px, 0.6vw, 8px);
    --plate-gutter:  clamp(24px, 4vw, 48px);
    isolation: isolate;
    padding-block: calc(var(--plate-inset) + clamp(56px, 7vw, 96px));
  }
  .section--plate::before {
    content: ""; position: absolute; z-index: -1;
    top: var(--plate-inset); bottom: var(--plate-inset); left: 50%; transform: translateX(-50%);
    width: calc(var(--frame-w) - var(--plate-inset) * 2);
    /* Navy, not --surface-dark: #141E4C is the darkest stop of the cLTC hero gradient
       (.page-hero), so the plate reads as the same blue as the top of the page
       [Matias 2026-09-11]. */
    background: #141E4C; border-radius: 16px;
  }
  .section--plate .container {
    padding-inline: max(var(--content-inset),
      calc(var(--plate-inset) + var(--plate-gutter) - (var(--frame-w) - var(--content-frame-w)) / 2));
  }
  /* The rails are gone under 660px and .container runs full width, so the plate
     follows the screen edge instead of the frame. */
  @media (max-width: 660px) {
    .section--plate::before { width: calc(100% - var(--plate-inset) * 2); }
    .section--plate .container { padding-inline: calc(var(--plate-inset) + var(--plate-gutter)); }
  }

  /* ---------- the page rails, over the dark ground ----------
     .page-rails is FIXED at z-index 40 and runs from the nav to the bottom of the
     viewport, so it does not stop where a dark block starts: its pale line carries
     straight on over the black and glares. Every dark block therefore carries its own
     rails, painted opaque at a higher z-index so they cover the pale pair underneath
     rather than sitting beside them.

     .how-band had this first and still keeps its own copy, because that band is light
     and only the geometry is shared. This is the one for the dark block at the foot of
     every page, used twice: once by the sign-off band, once by the footer under it. */
  .rails-dark {
    position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
    width: var(--frame-w); border-inline: 1px solid var(--rail-on-dark);
    pointer-events: none; z-index: 45;
  }
  /* .page-rails is display:none under 660px, so there is nothing left to cover. */
  @media (max-width: 660px) { .rails-dark { display: none; } }

  /* ---------- the ruled field ----------
     Hairlines every 8px, running the full height of a band and stopping at the rails,
     so the box the rails draw reads as ruled rather than as flat. The texture is the
     point: it gives the eye something to measure the band against, which a plain
     ground does not.

     It is masked, not faded, and the mask is an ELLIPSE WITH A HOLE IN IT: transparent
     out to 35 percent of the way to its edge, fully opaque by 75. The lines therefore
     clear the copy in the middle and only appear in the corners and along the top and
     bottom edges. Running them under the copy is what makes a field like this look
     like a background someone forgot to turn off.

     Two grounds, one rule. --line-ink is the only thing that differs between them, so
     the geometry and the mask cannot drift apart: --light is the same field in dark
     ink for a white band. The light alpha is higher because dark ink on white at 0.05
     is invisible, and lower than a plain multiplication would suggest because white
     bands on this site already carry rails and card borders at similar weights.

     Three numbers to turn if this wants adjusting: --line-ink, which is how loud; the
     8px, which is how close together; and the mask geometry, which is the one to move
     when a band's content is not a compact block in the middle - the ellipse is sized
     for copy that sits centred, and a band whose content fills it wants a different
     hole. Both spacings are near the low end on purpose: at the width of a page this
     is texture, not pattern, and one step louder reads as a graph. */
  .line-field {
    --line-ink: rgba(255,255,255,0.05);
    position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
    width: var(--frame-w); pointer-events: none;
    background-image: repeating-linear-gradient(to right,
      var(--line-ink) 0, var(--line-ink) 1px,
      transparent 1px, transparent 8px);
    -webkit-mask-image: radial-gradient(ellipse 70% 50% at 50% 50%, transparent 35%, #000 75%);
            mask-image: radial-gradient(ellipse 70% 50% at 50% 50%, transparent 35%, #000 75%);
  }
  /* Not a straight inversion of the dark alpha. Dark ink on white at 0.05 disappears,
     and much above 0.07 the band starts reading as graph paper rather than as a ground
     with a grain. 0.07 was picked by looking at the mechanism band at three values,
     not by computing an equivalent. */
  .line-field--light { --line-ink: rgba(28,28,26,0.07); }

  /* ---------- the tinted variant ----------
     UNUSED since 2026-09-13 [Matias]: the mechanism band went back to --light. Kept
     because switching again is one class in index.astro.

     White lines over a blue wash: the inverse of --light rather than a recolour of it
     [Matias, 2026-09-10].

     WHAT THIS COSTS, because it is not obvious and it is the reason --light is still
     declared above rather than deleted. In the monochrome variants the ruling is
     TEXTURE: ink lines, no ground, and --line-ink alone says how loud it is. Here the
     lines and the tint are one object, since white lines are invisible without
     something behind them. You cannot turn the texture down without turning the colour
     down with it, and the arch stops being something a reader half-notices and becomes
     the loudest thing on the page after the hero. Switching back is one class in
     index.astro, which is why --light stays.

     The wash is full strength at the band's TOP EDGE and gone by the foot of the strip,
     so the colour meets the hero's dark edge head on rather than easing in under it.
     That is the chosen version of three [Matias]; the softer one starts the gradient at
     transparent and peaks around 42 percent. If the hero above this band is ever
     relit, that meeting point is the thing to look at first.

     The 8px spacing is repeated here rather than shared with the base rule. Both are
     the same field and they have to stay in step: change one, change the other. */
  .line-field--tint {
    --line-ink: #ffffff;
    background-image:
      repeating-linear-gradient(to right,
        var(--line-ink) 0, var(--line-ink) 1px,
        transparent 1px, transparent 8px),
      linear-gradient(to bottom,
        rgb(var(--tint-blue)) 0%, rgb(var(--tint-blue) / 0) 100%);
  }

  @media (max-width: 660px) {
    .page-rails, .divider--rails { width: 100%; }
    .page-rails { display: none; }
    .container { max-width: none; padding-inline: 20px; }
  }

  /* ---------- buttons ---------- */
  .btn {
    display: inline-flex; align-items: center; gap: 0.5em; font-family: var(--font);
    font-weight: 400; font-size: var(--fs-small); padding: 0.85em 1.35em; border-radius: var(--radius);
    border: 1px solid transparent; cursor: pointer; transition: background .18s, border-color .18s, color .18s; text-decoration: none;
  }
  .btn--sm { font-size: var(--fs-caption); padding: 0.5em 1em; }
  .btn--md { font-size: var(--fs-small); padding: 0.65em 1.25em; }
  .btn--primary { background: var(--accent); color: var(--on-dark); }
  .btn--primary:hover { background: var(--accent-strong); text-decoration: none; }
  .btn--ghost { background: transparent; color: var(--ink); border-color: var(--ink); }
  .btn--ghost:hover { background: var(--ink); color: var(--on-dark); text-decoration: none; }
  .btn .arr { transition: transform .18s; }
  .btn:hover .arr { transform: translateX(3px); }

  /* ---------- nav ---------- */
  .nav { position: sticky; top: 0; z-index: 50; background: rgba(255,255,255,0.82); backdrop-filter: saturate(140%) blur(8px); border-bottom: 1px solid var(--border); }
  /* Columns are assigned by hand rather than by source order: the menu button sits
     between the wordmark and the links in the markup, where the keyboard needs it,
     and is display:none above 640px. */
  .nav__inner { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; height: var(--nav-h); gap: 16px; }
  .nav__logo  { grid-column: 1; }
  .nav__links { grid-column: 2; }
  .nav__cta   { grid-column: 3; }
  .nav__logo { display: inline-flex; align-items: center; gap: 10px; justify-self: start; }
  .nav__logo:hover { text-decoration: none; }
  .nav__logo .word { font-family: var(--font-logo); font-weight: 500; word-spacing: -0.06em; font-size: 1.25rem; letter-spacing: -0.005em; color: var(--ink); white-space: nowrap; }
  /* The symbol is a mask, not an img: it paints in the same colour as the word, so it follows the word from white over the dark hero to ink once the bar goes solid, with one PNG. */
  .nav__logo .mark { flex: none; width: 1.3em; height: 1.3em; font-size: 1.25rem; background-color: var(--ink); -webkit-mask: url("/assets/img/logo-symbol.png") center / contain no-repeat; mask: url("/assets/img/logo-symbol.png") center / contain no-repeat; }
  .nav__links { display: flex; align-items: center; gap: 24px; justify-self: center; white-space: nowrap; }
  .nav__links > a,
  .nav-drop summary {
    color: var(--ink-2); font-size: var(--fs-small); font-weight: 400;
  }
  .nav__links > a:hover,
  .nav-drop summary:hover { color: var(--ink); text-decoration: none; }
  .nav__links > a.is-current,
  .nav-drop.is-current > summary,
  .nav__links > a[aria-current="page"] { color: var(--ink); }
  .nav__cta { justify-self: end; }

  .nav-drop { position: relative; }
  .nav-drop summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: inherit;
  }
  .nav-drop summary::-webkit-details-marker { display: none; }
  .nav-drop summary::after {
    content: "";
    width: 0;
    height: 0;
    border-left: 3.5px solid transparent;
    border-right: 3.5px solid transparent;
    border-top: 4px solid currentColor;
    opacity: 0.55;
    translate: 0 1px;
  }
  .nav-drop[open] summary::after { rotate: 180deg; }
  /* The panel is a surface, not floating text. Until 2026-09-05 it was declared
     transparent with no border and no shadow, so an open menu read as two words
     hovering over whatever happened to be underneath, and over the home page's hero
     video that was unreadable as well as odd.

     It is a light card in every state now, including over the dark hero, which is why
     the .dark-hero overrides further down set only the summary and no longer repaint these
     links white. A menu that changes colour with the page behind it is a menu the
     reader has to re-learn at every scroll position.

     Left-aligned and left-anchored rather than centred: a dropped panel is a list to
     scan down, and centred rows give it a ragged left edge to fight. The 2px offset
     pulls the row text back into line with the summary above it, since the rows carry
     14px of their own padding. */
  .nav-drop__panel {
    position: absolute;
    top: calc(100% + 10px);
    left: -2px;
    min-width: 190px;
    padding: 6px;
    /* #fff rather than --bg, which is the page's own off-white: a panel painted the
       same colour as the page behind it separates only by its hairline. White reads
       as lifted off the page, and gives --surface-alt enough contrast to work as the
       row hover. */
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px rgba(20,20,20,0.04), 0 12px 28px rgba(20,20,20,0.12);
    z-index: 60;
  }
  /* The gap between summary and panel is dead space the pointer crosses on its way
     down. Without this the menu is fine (it opens on click, not hover) but the row
     hover state flickers as the cursor leaves and re-enters. */
  .nav-drop__panel::before {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 100%;
    height: 10px;
  }
  .nav-drop__panel a,
  .nav-drop__soon {
    display: block;
    padding: 8px 14px;
    border-radius: 3px;
    font-size: var(--fs-small);
    white-space: nowrap;
    text-align: left;
  }
  .nav-drop__panel a { color: var(--ink-2); }
  .nav-drop__panel a:hover,
  .nav-drop__panel a:focus-visible {
    color: var(--ink);
    text-decoration: none;
    background: var(--surface-alt);
  }
  .nav-drop__panel a.is-current { color: var(--ink); font-weight: 500; }
  /* A separated caption, not a menu row: it is not clickable and must not look it. */
  .nav-drop__soon {
    color: var(--ink-3);
    cursor: default;
    font-size: var(--fs-caption);
    margin-top: 4px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    border-radius: 0;
  }

  /* On phones the whole links row is itself a panel hanging under the bar, so this
     stops being a dropdown and becomes an accordion group inside it: in flow, no
     card of its own, no shadow, indented so the one product reads as belonging to
     the Products row above it. The script opens it with the menu.

     It used to be centred under the summary here, because on the old two-row phone
     layout "Products" sat close to the right rail and a 190px card hanging off its
     left edge was clipped at 320px. There is no floating card left to clip. */
  @media (max-width: 640px) {
    .nav-drop__panel {
      position: static;
      min-width: 0;
      padding: 0 0 0 14px;
      margin: 2px 0 6px;
      background: none;
      border: 0;
      border-left: 1px solid var(--border);
      border-radius: 0;
      box-shadow: none;
      translate: none;
    }
    .nav-drop__panel::before { content: none; }
    .nav-drop__panel a { padding: 10px 8px; }
    /* The rule above the caption is what separates it from the link in the floating
       card. Inside a full width panel the same rule runs the width of the screen and
       reads as a divider across the menu; the panel's own left rule already groups
       the two. */
    .nav-drop__soon { padding: 4px 8px 8px; margin-top: 0; border-top: 0; }
  }

  @media (prefers-reduced-motion: no-preference) {
    .nav-drop[open] .nav-drop__panel { animation: navDropIn 0.14s ease-out; }
    /* transform, not translate. The mobile rule above centres the panel with
       translate: -50% 0, and a keyframe on the same property would overwrite that
       centring for the length of the animation and snap the panel sideways.
       transform and translate are separate properties that compose, so the lift
       rides on top of the centring instead of replacing it. */
    @keyframes navDropIn {
      from { opacity: 0; transform: translateY(-4px); }
      to   { opacity: 1; transform: translateY(0); }
    }
  }

  /* ---------- the phone menu ----------
     Four links, a wordmark and a call to action do not fit on one 375px row: measured
     at 390px the row wants ~397px inside a 350px container. Until 2026-09-09 the bar
     answered that by becoming TWO ROWS on phones, 104px tall, with the four links
     centred underneath. Nothing was hidden, but a nav that eats 104px of a 812px
     screen is eating the top eighth of every page, and the links were 20px tall,
     which is half the 44px a thumb needs.

     So: wordmark and the call to action on one 64px row, the four links behind a
     button. The call to action stays IN THE BAR at every width, which was the whole
     objection to a hamburger here and is the part worth keeping. --nav-h goes back to
     64px on phones with it, and the hero, the rails, the anchor offsets and the home
     page script all read that number rather than assuming one.

     Everything below 640px, which is where the one-row bar actually runs out of room.
     The button is display:none above it and the panel rules never apply. */
  .nav__toggle {
    display: none;
    justify-self: end;
    width: 44px; height: 44px;
    margin-right: -10px;   /* optical: the bars line up with the rail, the box overhangs it */
    padding: 0;
    background: none; border: 0; cursor: pointer;
    align-items: center; justify-content: center;
    color: var(--ink);
  }
  /* Three bars drawn from one element: the middle is the box, the outer two are its
     pseudo-elements. On open the outer two travel to the middle and cross, and the
     middle fades, so the icon becomes a close without a second icon to swap in. */
  .nav__burger,
  .nav__burger::before,
  .nav__burger::after {
    content: "";
    display: block;
    width: 20px; height: 1.5px;
    background: currentColor;
    border-radius: 2px;
    transition: transform .2s ease, opacity .12s ease;
  }
  .nav__burger { position: relative; }
  .nav__burger::before { position: absolute; top: -6px; left: 0; }
  .nav__burger::after  { position: absolute; top:  6px; left: 0; }
  .nav.is-open .nav__burger { background: transparent; }
  .nav.is-open .nav__burger::before { transform: translateY(6px) rotate(45deg); }
  .nav.is-open .nav__burger::after  { transform: translateY(-6px) rotate(-45deg); }

  @media (max-width: 640px) {
    .nav__inner { grid-template-columns: 1fr auto auto; gap: 12px; }
    /* Rows stated as well as columns. The button sits before the call to action in
       the markup and after it on the screen, and auto-placement will not run
       backwards across a row: left to itself it drops the call to action onto a
       second row and the bar grows a phantom 30px. */
    .nav__logo   { grid-row: 1; }
    .nav__cta    { grid-row: 1; grid-column: 2; }
    .nav__toggle { display: flex; grid-row: 1; grid-column: 3; }
    .nav__logo :is(.word, .mark) { font-size: 1.0625rem; }

    /* The panel is anchored to .nav, which is sticky on every page and fixed on the
       home page: both are positioned, so both are the containing block for this.
       It is a light card in every state, like the products panel it contains, so a
       reader meets the same menu over the dark hero and over a white page. */
    .nav__links {
      display: none;
      position: absolute;
      top: 100%; left: 0; right: 0;
      flex-direction: column;
      align-items: stretch;
      justify-self: stretch;
      gap: 0;
      padding: 8px 0 14px;
      background: #fff;
      border-bottom: 1px solid var(--border);
      box-shadow: 0 14px 28px rgba(20,20,20,0.10);
      /* Taller than any phone in portrait would need, but a landscape phone is
         ~375px tall and the panel has to be able to scroll inside it. */
      max-height: calc(100svh - var(--nav-h));
      overflow-y: auto;
      z-index: 55;
    }
    .nav.is-open .nav__links { display: flex; }

    /* 44px rows. This is the reason the menu exists: the links were 20px tall on the
       old two-row bar, tightly spaced, on the one layout where the pointer is a
       thumb. The inset matches .container's own 20px so the rows line up with the
       wordmark above them. */
    .nav__links > a,
    .nav-drop summary {
      font-size: 1rem;
      padding: 12px 20px;
      border-radius: 0;
    }
    .nav__links > a:hover,
    .nav-drop summary:hover { background: var(--surface-alt); text-decoration: none; }
    .nav-drop summary { justify-content: space-between; }
    .nav-drop summary::after { margin-left: auto; }
    .nav-drop { padding-bottom: 2px; }

    /* Over the hero the bar's own links are painted white, and these are not on the
       bar any more. Repainting them for the page behind the panel would put white
       text on a white card. */
    .dark-hero .nav__links > a,
    .dark-hero .nav-drop summary,
    .dark-hero .nav:where(.is-solid, .is-open) .nav__links > a,
    .dark-hero .nav:where(.is-solid, .is-open) .nav-drop summary { color: var(--ink-2); }
    .dark-hero .nav__links > a.is-current,
    .dark-hero .nav-drop.is-current > summary { color: var(--ink); }
  }

  /* 360px Androids and the 375pt iPhone SE. The row layout no longer depends on the
     links fitting, so this is only the wordmark keeping clear of the button. */
  @media (max-width: 389px) {
    .nav__logo :is(.word, .mark) { font-size: 1rem; }
  }

  /* ---------- nav over a dark hero ----------
     On the home page the nav has no bar of its own: the hero video runs behind it and
     the links sit straight on the footage. sticky -> fixed is what buys that - it takes
     the nav out of flow so the hero starts at y=0 rather than 64px down. .hero__main
     pays those 64px back as padding, so the copy still centres in the space the reader
     actually sees.

     Past the hero the nav is over white page and would be white on white, so the script
     at the foot of index.html adds .is-solid and the bar comes back. Every rule here is
     therefore in a pair: the state over the video, then the .is-solid state that puts
     back what styles.css does on every other page.

     Scoped to .dark-hero throughout, which BaseLayout puts on the home page and on any
     page passing darkHero (/cltc, from 2026-09-11 [Matias]). Pages without a dark hero
     keep the sticky white bar unchanged. */
  .dark-hero .nav {
    position: fixed; left: 0; right: 0; top: 0;
    background: transparent; backdrop-filter: none; border-bottom-color: transparent;
    transition: background .25s, border-color .25s, backdrop-filter .25s;
  }
  .dark-hero .nav__logo .word { color: var(--on-dark); }
  .dark-hero .nav__logo .mark { background-color: var(--on-dark); }
  .dark-hero .nav__toggle { color: var(--on-dark); }
  .dark-hero .nav__links > a,
  .dark-hero .nav-drop summary { color: rgba(255,255,255,0.72); }
  .dark-hero .nav__links > a:hover,
  .dark-hero .nav__links > a.is-current,
  .dark-hero .nav-drop.is-current > summary,
  .dark-hero .nav-drop summary:hover { color: var(--on-dark); }
  /* The global focus ring is --accent, which is near-black and vanishes against the
     hero footage, so a keyboard user tabbing across the transparent bar could not see
     where they were. White over the video, back to --accent once the bar is solid.
     Scoped to the bar itself: the panel is a light card and keeps the normal ring. */
  .dark-hero .nav__links > a:focus-visible,
  .dark-hero .nav-drop > summary:focus-visible,
  .dark-hero .nav__logo:focus-visible,
  .dark-hero .nav__toggle:focus-visible,
  .dark-hero .nav__cta a:focus-visible { outline-color: #fff; }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__links > a:focus-visible,
  .dark-hero .nav:where(.is-solid, .is-open) .nav-drop > summary:focus-visible,
  .dark-hero .nav:where(.is-solid, .is-open) .nav__logo:focus-visible,
  .dark-hero .nav:where(.is-solid, .is-open) .nav__toggle:focus-visible,
  .dark-hero .nav:where(.is-solid, .is-open) .nav__cta a:focus-visible { outline-color: var(--accent); }

  /* No .dark-hero overrides for .nav-drop__panel. The panel is a light card in every
     state now (see its rule above), so repainting its rows for the dark hero would
     put white text on an off-white surface. Only the summary, which really does sit
     on the footage, is restyled here. */
  /* --accent is near-black and would sink into the footage - the same inversion the
     hero's own buttons make a few rules below. */
  .dark-hero .nav__cta .btn--primary { background: #fff; color: var(--surface-dark); }
  .dark-hero .nav__cta .btn--primary:hover { background: #e4e4e4; }

  /* :where() so the two states share every rule at the specificity .is-solid alone
     used to carry. .is-solid is the scroll state, set by the hero script once the
     hero has gone; .is-open is the phone menu, which hangs a white card off the
     bottom of the bar and cannot do that while the bar itself is transparent film
     over the hero. */
  .dark-hero .nav:where(.is-solid, .is-open) {
    background: rgba(255,255,255,0.82); backdrop-filter: saturate(140%) blur(8px);
    border-bottom-color: var(--border);
  }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__logo .word { color: var(--ink); }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__logo .mark { background-color: var(--ink); }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__toggle { color: var(--ink); }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__links > a,
  .dark-hero .nav:where(.is-solid, .is-open) .nav-drop summary { color: var(--ink-2); }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__links > a:hover,
  .dark-hero .nav:where(.is-solid, .is-open) .nav__links > a.is-current,
  .dark-hero .nav:where(.is-solid, .is-open) .nav-drop.is-current > summary,
  .dark-hero .nav:where(.is-solid, .is-open) .nav-drop summary:hover { color: var(--ink); }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__cta .btn--primary { background: var(--accent); color: var(--on-dark); }
  .dark-hero .nav:where(.is-solid, .is-open) .nav__cta .btn--primary:hover { background: var(--accent-strong); }
  /* A fixed nav covers the top of whatever an in-page link jumps to. */
  .dark-hero [id] { scroll-margin-top: calc(var(--nav-h) + 8px); }

  /* ---------- hero ----------
     Sized from the VIEWPORT, not from its own contents. It used to be the other way
     round: .hero__main asked for min(660px, 82vh). The 660px cap is what made that
     wrong - past a certain screen height the hero stopped growing, so the taller the
     display, the more of the white section below showed under it. Now the hero is a
     column at least one screen tall and the copy centres in it.

     min-height, not height, so the hero can still grow past the screen rather than clip
     if the copy ever gets longer or a reader has large text set.

     svh = SMALL viewport height: mobile browsers measure it with their address bar
     SHOWING, so the hero fills the screen on load and nothing jumps when the bar hides
     on scroll. The 100vh line above it is the fallback for browsers without svh, which
     is the behaviour we would have had anyway. */
  .hero { position: relative; min-height: 100vh; min-height: 100svh; display: flex; flex-direction: column; }
  /* inset:0, not the --frame-w box the rest of the page sits in: the footage runs the
     full width of the screen, which is the whole point of the treatment. */
  .hero__bg { position: absolute; inset: 0; overflow: hidden; z-index: 0;
    background: radial-gradient(ellipse 120% 95% at 50% 28%, #3c3c3c 0%, #242424 28%, #181818 58%, #121212 100%); }
  /* .hero sets the height and this takes the whole thing - .hero__main is now the
     only child of .hero, so flex:1 1 auto gives it 100% and the copy centres in it. */
  .hero__main { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center;
    flex: 1 1 auto; padding-top: var(--nav-h); }
  .hero__copy { max-width: 720px; text-align: center; }
  .hero__overline { margin-bottom: 32px; display: inline-block; }
  .hero__overline.overline { color: var(--on-dark-muted); padding: 0.35em 1em; border: 1px solid rgba(255,255,255,0.2); border-radius: 999px; }
  /* No font-family here: it inherits --font-display like every other heading, which
     is the serif again as of 2026-09-10.

     Between 2026-09-09 and that revert this rule carried an explicit override keeping
     the hero serif while the rest of the headings were sans, plus a long note defending
     it. Both are gone because there is no longer anything to defend - the arrangement
     came back and the exception dissolved into the rule. Worth knowing if you are
     reading the history: the hero being serif was asked for directly and held up
     through a day of swaps, so it is the part of this that has never been in doubt.

     font-weight 400 is still stated rather than inherited, because it was 400 here even
     while the headings were briefly at 500. It matches h1 today; leave it stated. */
  .hero__title { font-size: var(--fs-display); line-height: 1.06; letter-spacing: -0.03em; color: var(--on-dark); text-wrap: balance; }
  .hero__title .muted { color: rgba(255,255,255,0.62); }
  .hero__ctas { display: flex; gap: 14px; margin-top: 34px; flex-wrap: wrap; justify-content: center; }
  .hero .btn--ghost { color: var(--on-dark); border-color: rgba(255,255,255,0.45); }
  .hero .btn--ghost:hover { background: var(--on-dark); color: var(--ink); border-color: var(--on-dark); }
  /* The contact band used to share this rule. It is light now, so its button is the
     ordinary near-black one again; .section--dark re-inverts it if the band goes dark. */
  .hero .btn--primary { background: #fff; color: var(--surface-dark); }
  .hero .btn--primary:hover { background: #e4e4e4; }

  /* Hero plate. Footage and treatment chosen 2026-09-09 [Matias], picked against the
     alternatives side by side rather than tuned by guess.

     The clip is a daylight timelapse over midtown, crossfaded 1.5s from its own tail
     back into its head so the loop point does not cut. That is why hero-bg.mp4 is 6.5
     seconds rather than the 8 it was shot at: the overlap is the last 1.5s spent twice.
     Re-cutting it from the source without that crossfade puts a visible jump back in.

     blur(2px) is doing real work here, not decoration. Sharp footage competes with the
     headline for the eye, and a skyline is all vertical edges pointing at the type. Two
     pixels is enough to push it behind the copy while leaving the shape of the city
     readable. saturate(0.98) is very nearly a no-op and is kept only because it was in
     the chosen setting; it costs nothing, since blur has already promoted this to its
     own layer.

     scale(1.020) is NOT a crop preference. A blur samples past the element's edges and
     would otherwise show a soft transparent margin down all four sides, so the plate is
     grown just past what the 2px radius eats. Change the blur and this has to move with
     it, roughly 1 + blur/100. */
  .hero__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0;
    filter: saturate(0.98) blur(2px); transform: scale(1.020); }
  /* 0.8 is load bearing. Below about 0.6 the subline and the spec rail stop being
     comfortably readable against the brighter parts of the sky. */
  .hero__scrim { position: absolute; inset: 0; z-index: 0; pointer-events: none; background: rgba(18,18,18,0.8); }

  @media (max-width: 880px) {
    .hero__main { padding-top: calc(var(--nav-h) + 16px); padding-bottom: 16px; }
  }

  /* ---------- section heads ---------- */
  /* 2026-09-07 [Matias]: .head__overline is gone, and so is every pre-headline on the
     site. The rule that replaced them: a headline has to work with nothing above it,
     because a reader will not read a label and a headline as one sentence. Do not
     reintroduce the class. A section that needs a label above the title needs a better
     title. .head__tag below is the one survivor in that slot, and it is a status
     value, not a label. */
  .head { max-width: 760px; padding-bottom: clamp(30px, 4vw, 52px); }
  /* An "In build" style status pill, in the slot the pre-headlines used to occupy.
     18px to the title, which is the gap that slot has always had. */
  .head__tag { margin-bottom: 18px; }
  /* For a head whose only follower is a single control, usually a button. The
     full clamp is measured for a head that runs into a card grid or a band; over
     one button it reads as a hole. */
  .head--tight { padding-bottom: clamp(20px, 2.2vw, 28px); }
  .head__title { max-width: 30ch; }
  /* Long-form headline device: solid clause carries the point, faded clause
     elaborates. 34ch is the tightest measure that lands this at 3 lines rather
     than 4 (the default 30ch gives 4; anything past ~46ch flattens it to 2 long
     lines and it stops reading as a headline). Use sparingly, or the fade
     becomes decoration rather than structure. */
  .head__title--long { max-width: 34ch; }

  /* Forced line break inside a headline, desktop only. On narrow screens the
     headline already wraps on its own, so the manual break is suppressed to
     avoid an awkward two-word orphan line. */
  .br-desktop { display: none; }
  @media (min-width: 761px) { .br-desktop { display: inline; } }

  /* Same device, one breakpoint lower: on from 641px, which is the phone boundary the
     nav already uses. For headlines that orphan their last word well above 760px. */
  .br-tablet { display: none; }
  @media (min-width: 641px) { .br-tablet { display: inline; } }
  .head__intro { font-size: var(--fs-prose); line-height: 1.6; color: var(--ink-2); margin-top: 18px; max-width: 62ch; }
  .head__intro b { color: var(--ink); font-weight: 600; }

  /* Centred section head, for a section whose body is a centred column rather than a
     full-width grid. The inner max-widths have to be re-centred individually, not
     dropped: .head__title and .head__intro carry their own measure and would sit hard
     left inside a centred block otherwise.
     Generalised from a pair of #faq rules, which were an ID hook on a section the
     2026-08-26 rewrite deleted. */
  .head--center { text-align: center; margin-inline: auto; }
  .head--center .head__title,
  .head--center .head__intro { margin-inline: auto; }
  /* 780px matches .faq exactly, so a head and the toggle list under it share one
     measure. 26ch on the title because a centred headline wants to break earlier than
     the 30ch default, which runs it nearly the full width of the column. */
  .head--narrow { max-width: 780px; }
  .head--narrow .head__title { max-width: 26ch; }

  /* ---------- the loop (2-step device) ----------
     The connecting arrow was removed 2026-08-20: it implied a strict left-to-right
     sequence, but the section's whole argument is that the two halves run at the
     same time and feed each other. Numbers went with it for the same reason.
     .loop__ic is the same bordered-square idiom as .card__ic in section 02, one
     size up because these two cards carry more weight than the four below. */
  .loop { margin-top: clamp(12px, 2vw, 20px); display: flex; align-items: stretch; gap: clamp(16px, 2.4vw, 28px); }
  .loop__step { flex: 1; display: flex; flex-direction: column; background: var(--bg); border: 1px solid var(--hairline); border-radius: 12px; overflow: hidden; }
  /* The art PLATE is what gives these two cards their weight — a tinted, dot-fielded
     panel with a hard edge against the text below, rather than a small icon floating
     in the padding. The card itself goes white so the plate is the only tinted area. */
  .loop__art { position: relative; height: clamp(148px, 14.5vw, 182px); background: var(--surface-alt); border-bottom: 1px solid var(--hairline); display: flex; align-items: center; justify-content: center; }
  .loop__dots { position: absolute; inset: 0; background-image: radial-gradient(circle, rgba(0,0,0,0.13) 0.8px, transparent 1px); background-size: 9px 9px; }
  /* Stroke widths are in viewBox units, so rendered thickness is stroke-width x (width/120).
     At the 118px cap that puts the solid line at ~1.18px and the dashed at ~0.88px — a hair
     over the 1px hairline the rest of the site uses for borders, which is the intended read.
     If the svg width is ever changed, re-check those two numbers rather than the raw values. */
  .loop__art svg { position: relative; width: clamp(96px, 9.5vw, 118px); height: auto; fill: none; stroke: var(--accent); stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; }
  /* Dashed = draftsman's construction line: present, but clearly subordinate to the
     solid geometry it guides. Solid fill = a settled node. */
  .loop__art .dash { stroke: var(--border-strong); stroke-width: 0.9; stroke-dasharray: 3 4; }
  .loop__art .fill { fill: var(--accent); stroke: none; }
  .loop__body { padding: clamp(24px, 2.6vw, 30px); }
  .loop__step h3 { margin-bottom: 12px; }
  .loop__step p { font-size: var(--fs-small); line-height: 1.62; color: var(--ink-3); }
  @media (max-width: 760px) { .loop { flex-direction: column; } }

  /* ---------- pillar / use-case / trust cards ---------- */
  /* These four share section 01's card shell — same border, radius and body type — but
     deliberately get NO art plate. That is the hierarchy: section 01 is the argument and
     earns the drawings, section 02 is the supporting list. Before 2026-08-20 they had no
     container at all, which read as unfinished once section 01 gained real cards. */
  .cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(16px, 2.4vw, 28px); }
  .card { background: var(--bg); border: 1px solid var(--hairline); border-radius: 12px; padding: clamp(24px, 2.6vw, 30px); }
  /* 2026-09-12 [Matias]: blue plate and blue mark, in place of the warm grey square on
     --accent-line the icons shipped in the day before. Both blues are the page's existing
     ones rather than new colour: #3a3a9e is a stop of the cLTC hero gradient, and the plate
     is --tint-blue washed over white, the same mix the mechanism band's ground uses at a
     different strength. The card border above is untouched, so the plate is the only thing
     that changed colour.

     Used only by the five Why Canton cards on /technology. Both tints are mixed over #fff
     deliberately: on a dark band this plate would need redoing, not inheriting. */
  .card__ic { width: 44px; height: 44px; border-radius: var(--radius); border: 1px solid color-mix(in srgb, rgb(var(--tint-blue)) 70%, #fff); background: color-mix(in srgb, rgb(var(--tint-blue)) 40%, #fff); display: flex; align-items: center; justify-content: center; margin-bottom: 20px; }
  .card__ic svg { width: 21px; height: 21px; stroke: #3a3a9e; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
  /* 2026-09-08 [Matias]: the sans, not the display serif every other heading uses.
     A card heading is a label on a block of content, not a headline, and at card sizes
     the serif was competing with the h2 above it instead of sitting under it. This one
     rule carries every card on the site - the home page's three, the five Why Canton
     cards, the About screen, and the product cards.

     WEIGHT 600 since 2026-09-10, and it was 500 before that. What matters is not the
     number but the GAP: this is one step above whatever h1-h4 carry, and both moved by
     100 together when the base went 400 to 500. Close that gap and card headings go
     back to reading as slightly-larger body copy, which is the failure described next.

     The old 500 reasoning, still the reasoning, with the numbers shifted up one. The
     card heading and the paragraph under it are the same family at the same weight, so
     size was the only thing separating them, and size alone had to be pushed to 1.6x
     the body before the heading looked like a heading - which is section-headline
     proportion and read as oversized in every card row on the site. At 500 the
     distinction is carried by weight, the size can sit at a normal 1.27x, and the
     headings stopped looking large. The weight is free either way: Inter is a variable
     font and the file already shipped covers 400 to 600, so asking for anything inside
     that range costs no extra download. 600 is the TOP of that range, so this rule
     cannot absorb another +100 without adding a file. */

  /* (This paragraph used to justify the weight by saying Inter 500 was already loaded
     for .overline. That was wrong twice over even then, since .overline is DM Mono, and
     it stopped applying entirely when Inter left the site on 2026-09-09.)

     NOT to be confused with the bold card title rejected on 2026-08-20: that was an
     18px SYNTHETIC bold on the serif, which the browser fakes by smearing the
     letterforms. This is a real drawn weight.

     17px to 19px, one responsive size for every card heading on the site. The clamp is
     .card--numbered's, lifted to the shell so a plain .card and a numbered one cannot
     disagree at laptop widths - which they did for one build, the numbered cards
     tracking the viewport down to 17px while the product cards sat flat at 19px. */
  .card h3 { margin-bottom: 12px; }
  /* 15px (var(--fs-small)), not the 1rem/16px it shipped at originally. Landed here after a
     round-trip: #uses cards tried 16.8px/bold titles with 14px body, then an 18px synthetic-
     bold serif title with 14px body - [Matias] rejected the bold title outright ("I don't
     like it") and asked for the plain title size back everywhere, with the body settling at
     15px on ALL cards (#uses and #institutions alike), not just the ones that were trialled.
     So .card h3 is back to its untouched original and this is the only card rule that moved
     from where it started. See decisions.md, 2026-08-20, for the full trial history. */
  .card p { font-size: var(--fs-small); line-height: 1.62; color: var(--ink-3); }
  /* 24px against the component's 21px, scoped to the home page's opening section
     [Matias, 2026-09-06]: the three cards there carry the argument the page is built
     on, and at the shared card size the headings sat too close to their own body copy
     to lead it. The base .card h3 above is deliberately untouched - it has been
     round-tripped once already (see its note) and every other card row on the site
     still wants it. The five Why Canton cards on the technology page use the same
     component and were left at 21px; if they ever want this, give them the same
     scoped rule rather than moving the base. */
  /* The #why size override is GONE as of 2026-09-08. It existed to push these three
     headings clear of their own body copy and went 24px, 22px, 20px over three passes
     without ever looking right, because size was the wrong lever - see the weight note
     on .card h3 above. These now take the shared card size like every other card on
     the site, and the home page is no longer a special case. Do not reintroduce it. */
  @media (max-width: 760px) { .cards { grid-template-columns: 1fr; } }
  /* #uses dropped from four cards to three on 2026-08-20 (the redundant "Bring a new asset
     onto Canton" card was cut - see index.html). Three cards read as an unbalanced 2-over-1
     in the shared two-column grid, so #uses runs its own three-across row on desktop only;
     the shared mobile media query above still stacks it to one column same as #institutions. */
  /* Was #uses .cards until 2026-08-26. An ID hook could not survive the rewrite that
     replaced that section, and three-up is wanted on more than one page now. */
  @media (min-width: 761px) { .cards--3 { grid-template-columns: repeat(3, 1fr); } }
  /* Five cards as 3 over 2, both rows full width. A plain 3-column grid would leave
     the second row as two cards and a hole, which reads as a missing sixth card.
     Six tracks instead: the first three cards take two each, the last two take three
     each, so both rows fill the row exactly.

     ONLY CORRECT FOR EXACTLY FIVE CARDS. nth-last-child is counting from the end, so
     adding a sixth would give a 3 + 1 + 2 mess. Change the rule, do not add a card. */
  @media (min-width: 761px) {
    .cards--3-2 { grid-template-columns: repeat(6, 1fr); }
    .cards--3-2 > * { grid-column: span 2; }
    .cards--3-2 > *:nth-last-child(-n+2) { grid-column: span 3; }
  }

  /* ---------- how it works: dark band, two step-columns ---------- */
  /* Deliberately taller than a normal section (2026-08-20 [Matias]). This is the page's one
     dark band, so giving the content air on both sides makes it read as a pause in the scroll
     rather than another stripe. On a desktop viewport it fills the screen below the sticky nav
     with the content centred; narrower than that it falls back to padding alone, because 100vh
     on a phone is measured against a browser chrome that moves as you scroll.
     min-height, never height: if the content is taller than the viewport it simply wins, so
     this can stretch the band but can never crop it. */
  /* 2026-09-08 [Matias]: the full-viewport-height rule that used to sit here is gone.
     Filling the screen below the nav was holding the three steps in the middle of a
     band far taller than they are, so the section read as padding with content in it.
     Sized by its own padding now, a little above the --section-y rhythm so it still
     carries more air than an ordinary section without stretching to the viewport. */
  .how-band { position: relative; color: var(--on-dark); padding-block: clamp(88px, 11vw, 148px);
    display: flex; flex-direction: column; justify-content: center; }
  .how-band__bg { position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
    width: 100vw; overflow: hidden; background: var(--surface-dark); z-index: 0; }
  /* Opaque, not rgba(): this must COVER the pale .page-rails line running underneath at
     z-index 40, and a translucent border would just composite over it and stay pale.

     Kept as its own rule rather than folded into .rails-dark, which is the same figure
     for the two blocks at the foot of the page. This band is LIGHT (see
     .how-band--light below, which overrides the colour here), so it is only the
     GEOMETRY the three have in common and the colour they disagree on. Sharing the
     class would mean a light band asking for a class called dark and then undoing it. */
  .how-band__rails { position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
    width: var(--frame-w); border-inline: 1px solid var(--rail-on-dark); pointer-events: none; z-index: 45; }
  @media (max-width: 660px) { .how-band__rails { display: none; } }
  /* 2026-09-10 [Matias]: .how-band__dots is gone, and .line-field replaced it. The dot
     field was masked to a top fade, opaque at the top of the band and gone by 38 percent,
     so it read as atmosphere at the section's opening. Continuous lines do not work that
     way: a texture that appears at the top and stops in mid air looks unfinished, where
     dots just thin out. Both were built and compared on this band before choosing.

     So the field runs the whole band here and the HOLE does the work instead. It is
     wider and taller than the sign-off band's, and pushed slightly above centre, which
     is what this band's content needs: a headline near the top and a three card grid
     under it, filling the box rather than sitting as one compact block in the middle of
     it. The result is ruling down both margins beside the grid and across the corners,
     with the headline and the cards on clean white.

     What to watch: the hole is sized against the CONTENT, not the band, so adding a
     lead paragraph back under the headline or a fourth step to the grid means looking
     at this again. The failure it prevents is lines running behind the headline, which
     is the version that looked wrong. */
  /* THE BEND, AND ONLY AT THE TOP [Matias, 2026-09-10]. The ruling is a strip across
     the head of the band, not a treatment of the whole of it, and the lines in it do
     not stop on a level line: they stop on a CURVE, short in the middle and running
     deeper the further out they sit. It is the difference between a texture that has
     been faded and one that has been shaped, and it is what makes the headline look
     like it is sitting in a clearing rather than on top of a wash.

     TWO PARTS, and it matters which does what, because the obvious reading of the CSS
     gets it backwards.

     The HEIGHT is what stops the lines, not the mask. A radial gradient is transparent
     inside its ellipse and opaque everywhere outside it, so a mask alone would clear
     the middle and then bring the ruling straight back below the ellipse, at the foot
     of the band. Shortening the element removes that region rather than masking it, so
     there is nothing under the card grid to paint. The alternative was a second mask
     layer intersected with the first, which needs mask-composite and its two
     incompatible syntaxes for one line of effect.

     The MASK is only the curve. The ellipse sits at the BOTTOM edge of the strip, so
     what the lines meet is its top edge: highest at the centre, falling away toward the
     sides. 66% and 82% are its radii; 78% is where the fade starts, and moving it
     toward 100 makes the lines end more abruptly.

     The horizontal radius and the fade stop move TOGETHER, which is not obvious. A
     lower stop is a longer, softer fade, but it also pulls the transparent boundary
     inward, and if it pulls in past the field's own half width then the lines at the
     extreme edges never reach zero before the element ends and they cut off on a hard
     horizontal. Widening the ellipse is what buys that back. Lower the one, widen the
     other, and check the outermost lines.

     THE 82% IS THE ONE THAT MATTERS and it was 64% for an hour. It sets how far the
     arch lifts in the middle, and the constraint on it is not taste, it is the headline
     at NARROW widths. The clear zone is an arch, so it is at its narrowest just under
     the crown; at 1440 the headline is about a third of the field and sits comfortably
     inside, but by 640 it is nearly two thirds and the first line was catching the
     ruling. Lifting the crown clears it at every width and needs no breakpoint. Check
     this at 640 and not only at 1440 if it is ever retuned: setting --line-ink to a
     solid red for one render is the fastest way to see the actual shape.

     The height clamp tracks the two things that actually set where the headline ends,
     which is where the lines should stop: the band's own top padding, clamp(88px, 11vw,
     148px), and the headline's size, clamp(2.05rem, 4vw, 2.9rem) over two lines. Both
     scale with viewport width and both cap, so this does too. It lands about 12px under
     the headline at 640, 1024 and 1440. If either of those clamps is retuned, this one
     has to move with it, and the symptom of getting it wrong is lines crossing the
     headline rather than stopping above it.

     Four depths were built and compared on the band before this one. Shallower reads as
     a flat fade; softer loses the curve entirely; running the ruling down the margins
     past the card grid was the version before this note and was too much. */
  .how-band .line-field {
    bottom: auto;
    height: clamp(210px, 16vw + 115px, 350px);
    -webkit-mask-image: radial-gradient(ellipse 66% 82% at 50% 100%, transparent 78%, #000 100%);
            mask-image: radial-gradient(ellipse 66% 82% at 50% 100%, transparent 78%, #000 100%);
  }
  .how-band > .container { position: relative; z-index: 1; }
  .how-band__title { color: var(--on-dark); text-align: center; max-width: 26ch; margin-inline: auto; }
  /* Two-part head: a solid first line and a faded second one, set as blocks rather than
     left to wrap. The 26ch cap comes off because the cap is no longer what makes the
     break, and a measure narrow enough to force two lines out of THIS copy would force
     three out of the next edit.

     NO LONGER A STEP UP IN SIZE. This carried 2.9rem against the base head's 2.6rem
     from 2026-09-09, on the reasoning that the two-line device needs both lines to read
     as one headline and that at the base size the stack looked like a heading with a
     caption under it. Back to the base on 2026-09-10 [Matias]: under the ruled field the
     head was carrying too much weight for the second thing on the page.

     That reasoning was not wrong, it was load-bearing at the time. What changed is that
     the field now draws an arch around the head, and a clearing does the work of setting
     something apart that the extra 4.8px was doing before.

     The floor is 2.125rem, --fs-h2, which is what every other section head on the site
     takes. Built and looked at: at that size the two lines go back to reading as a
     heading with a caption, so 2.6rem is the bottom of the useful range for THIS device,
     not merely a preference. It stays under the hero's 3.15rem either way. */

  /* .muted supplies the fade, from --ink-h-faded. On the dark variant of this band it
     would be invisible, so if the band ever goes back to dark the span needs the hero's
     treatment (rgba white) rather than this one.

     No font-size here any more: the size comes from .how-band__title above, so the two
     cannot drift. */
  .how-band__title--split { max-width: none; line-height: 1.08; }
  .how-band__title--split .muted { display: block; text-wrap: balance; }
  /* The lead's own 20px was measured under a single 41.6px line. Under a two-line
     46.4px stack it reads as part of the headline block rather than as the line after
     it, so the split variant opens the gap. */
  .how-band__title--split + .how-band__lead { margin-top: 28px; }
  .how-band__note { text-align: center; color: var(--on-dark-muted); font-size: var(--fs-caption); margin-top: 16px; }

  .how-band__canvas { position: relative; z-index: 1; margin-top: clamp(36px, 4.5vw, 56px);
    width: 100%; max-width: var(--frame-w); margin-inline: auto; display: flex; align-items: stretch; }
  .how-band__grid { flex: 0 0 var(--content-frame-w); max-width: var(--content-frame-w);
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px;
    background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.07); }
  .how-band__side { flex: 1 1 0; min-width: 0;
    background-image:
      repeating-linear-gradient(to right, rgba(255,255,255,0.13) 0, rgba(255,255,255,0.13) 5px, transparent 5px, transparent 12px),
      repeating-linear-gradient(to right, rgba(255,255,255,0.13) 0, rgba(255,255,255,0.13) 5px, transparent 5px, transparent 12px);
    background-repeat: no-repeat, no-repeat; background-position: left top, left bottom; background-size: 100% 1px, 100% 1px; }

  .how-col { background: var(--surface-dark); padding: clamp(26px, 2.8vw, 36px); }
  .how-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; }
  .how-steps li { display: flex; gap: 14px; align-items: flex-start; }
  .how-steps__n { flex: 0 0 auto; width: 24px; height: 24px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.28);
    display: flex; align-items: center; justify-content: center; font-size: var(--fs-micro); color: #fff; font-family: var(--font); margin-top: 1px; }
  .how-steps h4 { color: var(--on-dark); margin-bottom: 8px; }
  .how-steps p { font-size: var(--fs-small); color: #a8a8a8; line-height: 1.55; }

  @media (max-width: 760px) { .how-band__grid { grid-template-columns: 1fr; } }

  /* ---------- how-band, light variant ----------
     2026-09-08 [Matias]: the same band painted white, so the two can be compared
     stacked on the home page. Structure, spacing and copy are untouched: this
     only repaints. If the light version is not the one that survives, delete this
     whole block along with the section in index.astro.

     The band is #fff rather than --bg for the same reason the nav panel is: a band
     painted the page's own off-white does not read as a band at all. The step cards
     are #fff too, exactly as the dark version paints its cards --surface-dark, so
     the only thing separating them is the 1px gap - here --border rather than the
     dark band's rgba(255,255,255,0.07). */
  .how-band--light { color: var(--ink-2); }
  .how-band--light .how-band__bg { background: #fff; }
  .how-band--light .how-band__rails { border-inline-color: var(--border); }
  .how-band--light .how-band__title { color: var(--ink); }
  .how-band--light .how-band__lead { color: var(--ink-2); }
  .how-band--light .how-band__note { color: var(--ink-3); }

  /* 2026-09-13 [Matias]: separate cards, picked in the variant lab over the joined
     grid, no boxes, big numerals, the cLTC art plate and the framed grid. The shell
     is the site's own card (1px --hairline, 12px radius), and the gap is the product
     row's gap on purpose: the two rows sit one above the other and their columns
     must line up. Change one, change both. */
  .how-band--light .mech__steps { background: none; border: 0; gap: clamp(16px, 2.4vw, 28px); }
  .how-band--light .mech__step { background: #fff; border: 1px solid var(--hairline); border-radius: 12px; }
  .how-band--light .mech__n { color: var(--ink-3); }
  /* Same alphas as the dark band's marks, inverted onto --ink, so the drawings keep
     their weight relative to the text rather than going hard black. */
  .how-band--light .mech__art svg { stroke: rgba(28,28,26,0.72); }
  .how-band--light .mech__art svg .dash { stroke: rgba(28,28,26,0.4); }
  .how-band--light .mech__art svg .fill { fill: rgba(28,28,26,0.72); }
  .how-band--light .mech__step h3 { color: var(--ink); }
  .how-band--light .mech__step p { color: var(--ink-2); }
  .how-band--light .mech__link { color: var(--ink); border-bottom-color: var(--border-strong); }
  .how-band--light .mech__link:hover { border-bottom-color: var(--ink); }

  /* ---------- news ---------- */
  /* Rule-topped columns rather than filled cards: the FAQ directly below already uses
     horizontal rules, so this keeps the two neighbours in the same language while the
     3-up grid (vs FAQ's single stack) keeps them from reading as one long list. */
  .news__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
  .news__head .head { padding-bottom: clamp(24px, 3vw, 36px); }
  .news__all { font-size: var(--fs-small); color: var(--ink); display: inline-flex; align-items: center; gap: 0.45em;
    padding-bottom: clamp(30px, 3.4vw, 44px); white-space: nowrap; }
  .news__all .arr { transition: transform .18s; }
  .news__all:hover { text-decoration: none; }
  .news__all:hover .arr { transform: translateX(3px); }

  .news-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(24px, 3vw, 40px); }
  .news-item { display: flex; flex-direction: column; border-top: 1px solid var(--border-strong); padding-top: 20px; }
  .news-item:hover h3 { color: var(--ink-2); }
  .news-item h3 { transition: color .18s; }
  .news-item__meta { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
  .news-item__tag { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.1em; text-transform: uppercase;
    font-weight: 500; color: var(--ink-3); border: 1px solid var(--border); border-radius: 999px; padding: 0.32em 0.8em; }
  .news-item__date { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.04em; color: var(--ink-3); }
  .news-item h3 { margin-bottom: 12px; }
  .news-item p { font-size: var(--fs-small); line-height: 1.6; color: var(--ink-2); }
  @media (max-width: 860px) { .news-grid { grid-template-columns: 1fr; gap: 28px; } }

  /* ---------- newsroom index ----------
     ADDED 2026-09-08, with the newsroom itself. .news-grid above it is the 3-up
     teaser that used to sit on the home page; it is unused today and kept because
     the home page will want a "latest" row back once there is more than one post to
     put in it.

     This is a LIST, not that grid. One announcement in a 3-up grid is a third of a
     row of content and two thirds of empty page, which reads as a site waiting for
     news rather than a site that has some. The list gives the first entry the full
     measure and degrades gracefully: the second and third arrive underneath it at
     the same weight, and nothing has to be redesigned when they do.

     The date column is fixed and the body takes the rest, so the headlines line up
     down the page regardless of how long the dates are. */
  .news-list { list-style: none; margin: 0; padding: 0; }
  .news-list > li + li { margin-top: clamp(28px, 3.4vw, 44px); }
  /* Padding is symmetric on purpose: the row is fenced top and bottom by a rule
     (the last one is .news-list's border-bottom), and an entry with air above the
     tag but none under the Read link reads as if it has been cut off. */
  .news-entry { display: grid; grid-template-columns: 200px 1fr; gap: clamp(20px, 3vw, 44px);
    border-top: 1px solid var(--border-strong); padding-block: 24px; color: inherit; }
  .news-entry:hover { text-decoration: none; }
  .news-entry__meta { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
  /* 2026-09-08 [Matias]: the 22ch title cap and the 56ch desc cap came off, for the
     same reason as the article header. They were set for a short headline and left
     the card's two lines of text at roughly half the width of the column holding
     them. Both now run to the entry body. */
  .news-entry__title { color: var(--ink); transition: color .18s; }
  .news-entry:hover .news-entry__title { color: var(--ink-2); }
  .news-entry__desc { font-size: var(--fs-prose); line-height: 1.6; color: var(--ink-2); margin-top: 14px; }
  .news-entry__more { display: inline-flex; align-items: center; gap: 0.45em; margin-top: 20px;
    font-size: var(--fs-small); color: var(--ink); }
  .news-entry__more .arr { transition: transform .18s; }
  .news-entry:hover .news-entry__more .arr { transform: translateX(3px); }
  @media (max-width: 760px) {
    .news-entry { grid-template-columns: 1fr; gap: 16px; }
    .news-entry__meta { flex-direction: row; align-items: center; }
  }

  /* ---------- newsroom article ----------
     The only long-form reading on the site. Everything else is a page of sections
     with a measure set per block; this is one column of continuous prose, so it gets
     the one thing the rest of the site does not need: a comfortable reading size.

     18px (--fs-article) over the site's 17px prose, and 1.65 line-height over 1.6.
     (19px and 1.7 until 2026-10-01; see the token's note.)
     Both are a step up because the reader is here for six hundred words rather than
     a caption, and because the measure below is wider than any paragraph elsewhere
     on the site. If this ever stops being the only article, these three rules are
     the ones that make it feel like a publication rather than a page.

     2026-09-09 [Matias]: the size moved from 17px to 19px, asked for directly. The
     measure came with it, 720px to 680px, because 19px Newsreader across 720px sets
     about 78 characters and the whole point of the larger size is a line a reader can
     return from without losing it. 680px puts it back near 74. The standfirst tracks
     the body on the same token, deliberately: see its own note below. */
  .article { max-width: 680px; margin-inline: auto; padding-block: clamp(56px, 7vw, 96px) clamp(48px, 6vw, 80px); }
  .article__head { padding-bottom: clamp(28px, 3.4vw, 44px); }
  .article__meta { display: flex; align-items: center; gap: 14px; margin-bottom: 22px; flex-wrap: wrap; }
  .article__date { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.04em; color: var(--ink-3); }
  /* 2026-09-08 [Matias]: the 20ch cap came off the h1 and the 44ch cap came off
     the standfirst. Both were set for a short, clever headline and held the two
     lines a reader sees first at roughly half the measure of the body below
     them, so the header read as indented rather than as a header. They now run
     to the article measure, like everything else in the column. */
  /* The deck line. Set as a lead rather than as the first paragraph so the body can
     open on the asset without repeating it.

     2026-09-09 [Matias], and this is the THIRD setting in one day, so the short
     version first: it is now the body paragraph exactly - same face, same size, same
     colour - and the only thing separating it is weight 600. Nothing else.

     That is deliberate. The two earlier attempts both tried to make the deck stand out
     by making it BIGGER, first 21px then 26px, and each time the fix created the next
     problem: at 26px sans over 17px serif the deck stopped reading as part of the
     article and started reading as a second headline. Weight is the quieter instrument
     and it is enough at this size.

     Weight 600 is a REAL weight here, not a synthetic one. --font-body is Inter as of
     2026-09-10 and it is fetched as a variable file declaring 400 to 600, so the browser
     draws an actual semibold rather than smearing the 400. 600 is the TOP of that range:
     if this line ever wants 700, the range in scripts/fetch-fonts.py has to move first
     or it silently becomes a faked bold, the artefact rejected on 2026-08-20.

     Superseded reasoning, kept because it keeps getting re-derived: the deck used to be
     the LIGHTER of the two at --ink-3 over --ink-2 body, which inverts the hierarchy a
     deck exists to set. It is not lighter than the body any more; it is the same. Do
     not take this colour below the body's. */
  .article__standfirst { font-size: var(--fs-article); line-height: 1.65; color: var(--ink);
    margin-top: 20px; font-family: var(--font-body); font-weight: 600; }
  /* --ink, not the --ink-2 the rest of the site's prose uses. An article is the one
     place someone reads several hundred words in a row, and the mid grey that looks
     considered in a short section paragraph turns into work over that distance. */
  .article__body p { font-size: var(--fs-article); line-height: 1.65; color: var(--ink); }
  .article__body p + p { margin-top: 1.15em; }
  /* --accent is near ink on this site, so a link inside running text is invisible
     until hovered. The press release boilerplate is the first article copy with
     inline links (2026-10-01), hence the underline here and nowhere else. */
  .article__body p a { text-decoration: underline; text-decoration-color: var(--ink-3);
    text-decoration-thickness: 1px; text-underline-offset: 3px; }
  .article__body p a:hover { text-decoration-color: currentColor; }
  /* Its own size rather than --fs-h3, as of 2026-09-09. --fs-h3 bottoms out at 21px,
     which was a clear step over a 17px body and is a 2px difference over a 19px one:
     the subheads stopped reading as subheads the moment the prose grew. Sized off the
     body instead, at roughly 1.3 to 1.45 times it across the range, which is the step
     the rest of the site's scale keeps. Move this if --fs-article moves. */
  .article__body h2 { margin-top: clamp(40px, 4.6vw, 62px); margin-bottom: 20px; max-width: 24ch; }
  /* The sign off, which is a name standing alone rather than a sentence. */
  .article__sign { margin-top: clamp(36px, 4vw, 52px); font-family: var(--font);
    font-weight: 600; color: var(--ink); font-size: var(--fs-prose); }
  .article__foot { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(40px, 4.6vw, 60px);
    padding-top: clamp(28px, 3vw, 40px); border-top: 1px solid var(--border); }
  .article__back { padding-block: clamp(28px, 3.4vw, 44px); font-size: var(--fs-small); }
  .article__back a { color: var(--ink-2); display: inline-flex; align-items: center; gap: 0.5em; }
  .article__back a:hover { color: var(--ink); text-decoration: none; }
  .article__back .arr { transition: transform .18s; }
  .article__back a:hover .arr { transform: translateX(-3px); }

  /* ---------- FAQ ---------- */
  .faq { max-width: 780px; margin-inline: auto; }
  .faq details { border-top: 1px solid var(--hairline); padding-block: 22px; }
  .faq details:last-of-type { border-bottom: 1px solid var(--hairline); }
  .faq summary { cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 16px; }
  .faq summary::-webkit-details-marker { display: none; }
  .faq summary .ic { flex: 0 0 auto; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; color: var(--accent); transition: transform .18s; }
  .faq summary .ic svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; }
  .faq details[open] summary .ic { transform: rotate(45deg); }
  .faq__a { font-size: var(--fs-prose); color: var(--ink-2); line-height: 1.65; margin-top: 14px; max-width: 64ch; }

  /* ---------- contact band ----------
     Dark, always, and painted by the dark-ground block far above rather than here.
     What is left in this rule is position:relative, which is what .lines-dark and
     .rails-dark hang off.

     The dot field this band used to carry is gone, replaced by .lines-dark. Dots and
     ruled lines are two textures doing one job and running both made the band busy;
     the dots survive on .how-band, which is the only place they are now. */
  .contact { position: relative; }
  .contact .container { position: relative; z-index: 2; text-align: center; }
  .contact h2 { max-width: 20ch; margin-inline: auto; }
  /* .contact__sub came out on 2026-09-10 [Matias]. The band is a headline and a
     button now, nothing else, on every page that carries one. .news-cta__sub is still
     declared a few rules down because that band is on no page and has not been through
     the same edit; if it ever comes back, this is the shape it should come back in.

     36px rather than the 32 this carried under a sub-line. The button used to answer a
     paragraph and now answers a headline directly, and at 32 the two read as one
     stacked block. It is the gap Attio sets between the same two elements. */
  .contact__cta { margin-top: 36px; display: flex; justify-content: center; }
  /* ---------- the two dark sign-off bands ----------
     The paint is full bleed as of 2026-09-10, so the old failure this rule was
     written for - copy printing outside the black - cannot happen any more. The inset
     stays anyway, because what it now holds the copy clear of is the RAILS: they run
     at --frame-w, .container is capped at the narrower --content-w, and below ~700px
     the two collapse onto the same width and the text would touch the line.

     The number is the frame's inset plus a gutter, written as --frame-edge plus
     rather than as a flat value so that moving the frame moves this with it.

     .news-cta is the same band, styled a few rules down and currently on no page.
     It is here so the two cannot drift if it comes back. */
  .contact .container,
  .news-cta .container { padding-inline: calc(var(--frame-edge) + 24px); }
  @media (max-width: 480px) {
    .contact .container,
    .news-cta .container { padding-inline: calc(var(--frame-edge) + 16px); }
  }

  /* ---------- footer ----------
     Dark since 2026-09-10 [Matias], and it is the second half of one block: the
     sign-off band above it carries the same ground, and the border-top below is the
     only thing between them. Every page ends this way, including the four that carry
     no sign-off band, where the footer is the whole of the dark block.

     The inversion is done by REDEFINING THE INK TOKENS at .footer scope rather than by
     rewriting the dozen rules under it. Custom properties inherit, so every
     .footer__* rule below still says var(--ink-2) and still means "the second text
     colour", and it resolves against the dark set in here. The alternative was a
     dark-mode copy of each of those rules, kept in sync by hand, which is how a
     footer ends up with a link colour that only two of its four lists use.

     The values are white at four opacities rather than four picked greys, so they
     hold their relationship to each other if --surface-dark ever moves again. */
  .footer {
    --ink:           var(--on-dark);
    --ink-2:         var(--on-dark-muted);
    --ink-3:         rgba(255,255,255,0.52);
    --border:        rgba(255,255,255,0.12);
    --border-strong: rgba(255,255,255,0.30);
    --surface-alt:   rgba(255,255,255,0.06);
    position: relative;
    background: var(--surface-dark); border-top: 1px solid var(--border);
    padding-block: clamp(48px, 6vw, 72px) 28px;
  }
  .footer__top { display: flex; justify-content: space-between; gap: 40px; flex-wrap: wrap; align-items: flex-start; }
  .footer__logo { display: inline-flex; align-items: center; gap: 10px; }
  .footer__logo .word { font-family: var(--font-logo); font-weight: 500; word-spacing: -0.06em; font-size: 1.08rem; letter-spacing: -0.005em; color: var(--ink); }
  .footer__logo .mark { flex: none; width: 1.3em; height: 1.3em; font-size: 1.08rem; background-color: var(--ink); -webkit-mask: url("/assets/img/logo-symbol.png") center / contain no-repeat; mask: url("/assets/img/logo-symbol.png") center / contain no-repeat; }
  .footer__logo:hover { text-decoration: none; }
  .footer__tag { color: var(--ink-3); font-size: var(--fs-small); line-height: 1.55; margin-top: 16px; max-width: 42ch; }
  /* Two labelled columns rather than the flat row this was until 2026-09-05, so the
     grouping matches the nav's. Columns wrap onto their own row on narrow screens
     because .footer__top already wraps. */
  .footer__nav { display: flex; gap: clamp(40px, 6vw, 72px); }
  .footer__col { display: flex; flex-direction: column; gap: 10px; }
  .footer__coltitle { font-family: var(--font); font-size: var(--fs-micro); font-weight: 500;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 4px; }
  .footer__col a { color: var(--ink-2); font-size: var(--fs-small); }
  .footer__col a:hover { color: var(--ink); text-decoration: none; }
  /* Deliberately not a link, matching .nav-drop__soon in the bar. */
  .footer__soon { color: var(--ink-3); font-size: var(--fs-small); }
  .footer__bottom { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px; margin-top: 44px; padding-top: 22px; border-top: 1px solid var(--border); }
  .footer__privacy { color: var(--ink-3); font-size: var(--fs-micro); letter-spacing: 0.04em; }
  .footer__privacy:hover { color: var(--ink-2); text-decoration: none; }
  .footer__social { margin-left: auto; }
  @media (max-width: 520px) { .footer__social { margin-left: 0; } }
  .footer__copy { color: var(--ink-3); font-size: var(--fs-micro); letter-spacing: 0.04em; }

  /* ---------- social links (footer on every page + contact page) ---------- */
  /* Brand marks are filled, not stroked like the rest of the site's icons: LinkedIn and X
     are recognised by their solid silhouettes, and redrawing them as outlines to match the
     house style makes them harder to spot for no gain. The bordered circle around each one
     is what ties them back to the system. */
  .social { display: flex; align-items: center; gap: 10px; }
  .social a { display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 999px; border: 1px solid var(--border);
    color: var(--ink-2); transition: color .18s, border-color .18s, background-color .18s; }
  .social a:hover { color: var(--ink); border-color: var(--border-strong); background: var(--surface-alt); text-decoration: none; }
  .social svg { width: 15px; height: 15px; fill: currentColor; display: block; }
  
  /* ---------- page head ---------- */
  /* Roughly half again as much air as this carried before 2026-08-27 [Matias]: it was
     56/96 over 36/56, which set the four inner-page heads at about the height of an
     ordinary section and read as cramped. A hero earns more room than the sections
     under it, and that difference is most of what makes it read as a hero at all.
     Still weighted to the top, because the sticky nav sits directly above it. */
  .page-head { padding-block: clamp(80px, 9.5vw, 140px) clamp(64px, 7.5vw, 108px); }
  .page-head__label { display: block; margin-bottom: 18px; }
  .page-head h1 { max-width: 18ch; }
  .page-head__desc { font-size: var(--fs-prose); line-height: 1.6; color: var(--ink-2); margin-top: 20px; max-width: 56ch; }
  /* Centred page head. Every page head uses this as of 2026-08-27 [Matias]; the
     modifier is kept rather than folded into .page-head so an internal or legal page
     can still run left-aligned without fighting it.

     The max-widths above are set for a left-aligned head and have to be re-centred
     rather than removed: dropping them lets the h1 run the full frame, which at this
     size is far past a comfortable measure. 24ch, not the 18ch above, because a
     centred h1 that breaks to four lines reads as a stack rather than a sentence. */
  .page-head--center { text-align: center; }
  .page-head--center h1 { max-width: 24ch; margin-inline: auto; }
  .page-head--center .page-head__desc { margin-inline: auto; }
  /* .status is an inline-flex pill, so text-align on the parent does not move it.
     It needs the block-level centring of its own margin box. */
  .page-head--center .status { margin-inline: auto; }

  /* ---------- page hero ----------
     2026-09-11 [Matias]. The PageHero component: a wrapper around the ordinary
     .page-head rather than a new head, so taking it off leaves a plain head. Two
     versions, blue on /cltc and dark on /about. The rules below are blue by default
     and .page-hero--dark overrides them.

     Blue: navy at the edges rising to lavender at the foot. The two ends, #141E4C and
     #E0E0F9, are Matias's, from a reference; the stops between them are picked by eye,
     because a straight blend of the two passes through a dull grey-blue rather than the
     saturated indigo the reference has. The ellipse is sized so the copy stays in the
     navy half.

     Inverted by redefining the ink tokens at wrapper scope, as .section--plate and
     .footer do. The arc is an SVG in PageHero.astro, so its gradient can run across the
     curve. */
  .page-hero {
    --ink:           var(--on-dark);
    --ink-h1:        var(--on-dark);
    --ink-2:         var(--on-dark-muted);
    --ink-3:         rgba(255,255,255,0.52);
    --border:        rgba(255,255,255,0.16);
    /* Translucent, which the site's --rail-on-dark cannot be, so the rails sit on the
       gradient at any point of it. Safe because of the z-index below. */
    --rail-on-dark:  rgba(255,255,255,0.1);
    --hero-offset:   0px;
    /* The arc's gradient stops, read by the SVG in PageHero.astro. */
    --arc-1: #8e96ea; --arc-2: #dfe0fb; --arc-3: #ffffff; --arc-4: #c9c5f7; --arc-5: #a29cf2;
    /* z-index 41 lifts the hero over the fixed page rails (40) and under the nav (50),
       so the pale rails never show through it. */
    position: relative; z-index: 41; isolation: isolate; overflow: hidden; padding-top: var(--hero-offset);
    background: radial-gradient(ellipse 90% 100% at 50% 112%,
      #E0E0F9 0%, #a5a3f2 16%, #6a64dc 32%, #3a3a9e 52%, #1d2466 74%, #141E4C 100%);
  }
  /* The nav is transparent and fixed over the hero on .dark-hero pages, so the hero
     starts under it and pays its height back. */
  .dark-hero .page-hero { --hero-offset: var(--nav-h); }
  .page-hero .container { position: relative; z-index: 2; }
  .page-hero .page-head { padding-block: clamp(150px, 15vw, 232px) clamp(112px, 16vw, 260px); }
  /* Wider than the centred head's 24ch, so the line after the forced break fits on one line. */
  .page-hero h1 { max-width: 32ch; }
  .page-hero h1 .muted { color: rgba(255,255,255,0.5); }
  /* Above the h1 here, where the reference sets its overline. */
  .page-hero .status { margin: 0 auto 28px; }
  /* Trial, 2026-09-11 [Matias]: /cltc's token lockup, a mark beside a ticker with a
     short name under it, after a reference. It takes the In build pill's place above
     the h1. The ticker is in the display serif, as the h1 is. */
  .token-lockup { display: inline-flex; align-items: center; gap: 14px; margin: 0 auto 32px; text-align: left; }
  .token-lockup__mark { width: 28px; height: 36px; flex: none; fill: var(--ink); }  .token-lockup__text { display: flex; flex-direction: column; gap: 2px; margin: 0; }
  .token-lockup__ticker { font-family: var(--font-display); font-size: 25px; line-height: 1.05; font-weight: 400; letter-spacing: -0.01em; color: var(--ink); }
  .token-lockup__name { font-size: 14px; line-height: 1.3; color: var(--ink-2); }

  .page-hero__arc {
    position: absolute; z-index: 0; left: 50%; top: calc(var(--hero-offset) + clamp(40px, 4.5vw, 64px)); transform: translateX(-50%);
    width: clamp(600px, 90vw, 1180px); aspect-ratio: 1200 / 640; pointer-events: none;
  }
  .page-hero__arc svg { display: block; width: 100%; height: 100%; overflow: visible; }
  .page-hero__arc path { fill: none; stroke-width: 1.5; }

  /* Dark: the first version of this hero, on /about since 2026-09-11 [Matias], where it
     replaced a violet version. A near-black ground, a warm arc (orange on the left, pale
     at the crown, violet on the right), a warm glow rising from the foot and a G mark
     at the crown. The rails are an opaque #222, which on a flat ground reads the same
     as a translucent line. */
  .page-hero--dark {
    --rail-on-dark: #222222;
    --arc-1: #d9733a; --arc-2: #f4e2d8; --arc-3: #e6dcf7; --arc-4: #9f7cf0; --arc-5: #7446e6;
    background: var(--surface-dark);
  }
  .page-hero__glow {
    position: absolute; z-index: 0; inset: 0; pointer-events: none;
    background: radial-gradient(ellipse 60% 62% at 50% 118%,
      rgb(148 62 24 / 0.55) 0%, rgb(148 62 24 / 0.2) 45%, transparent 78%);
  }
  /* The crown mark: the logo symbol on a dark tile, as in the favicon. The ring of page
     ground round the tile makes the arc run into it rather than under it.
     top: 6.25% is the crown of the arc, y=40 in a 640 tall viewBox. */
  .page-hero__node {
    position: absolute; left: 50%; top: 6.25%; transform: translate(-50%, -50%);
    display: grid; place-items: center; width: 36px; height: 36px; border-radius: 9px;
    background: #181818; border: 1px solid rgba(255,255,255,0.22);
    box-shadow: 0 0 0 6px var(--surface-dark), 0 0 30px 6px rgb(236 122 99 / 0.3);
  }
  .page-hero__node .mark { width: 20px; height: 20px; background-color: var(--on-dark);
    -webkit-mask: url("/assets/img/logo-symbol.png") center / contain no-repeat; mask: url("/assets/img/logo-symbol.png") center / contain no-repeat; }

  /* ---------- news list ---------- */
  /* Full-width rows rather than the homepage's 3-up grid: an archive is read down a
     column, and the row gives each item room for a longer standfirst. Same rule-topped
     treatment, so the two views read as the same component at different densities. */
  .news-list { border-bottom: 1px solid var(--hairline); }
  .news-row { display: grid; grid-template-columns: 190px 1fr; gap: clamp(20px, 4vw, 56px);
    border-top: 1px solid var(--hairline); padding-block: clamp(26px, 3vw, 38px); }
  .news-row__meta { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
  .news-row__tag { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.1em; text-transform: uppercase;
    font-weight: 500; color: var(--ink-3); border: 1px solid var(--border); border-radius: 999px; padding: 0.32em 0.8em; }
  .news-row__date { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.04em; color: var(--ink-3); }
  .news-row h2 { max-width: 30ch; }
  .news-row p { font-size: var(--fs-small); line-height: 1.65; color: var(--ink-2); margin-top: 12px; max-width: 62ch; }
  @media (max-width: 760px) { .news-row { grid-template-columns: 1fr; gap: 14px; }
    .news-row__meta { flex-direction: row; align-items: center; } }

  /* ---------- sign-off band ---------- */
  .news-cta { position: relative; margin-top: clamp(64px, 8vw, 104px); padding-block: clamp(56px, 7vw, 88px); }
  .news-cta .container { position: relative; z-index: 2; text-align: center; }
  .news-cta h2 { max-width: 20ch; margin-inline: auto; margin-top: 14px; }
  .news-cta__sub { color: var(--ink-2); font-size: var(--fs-prose); line-height: 1.6; max-width: 56ch; margin: 20px auto 0; }
  .news-cta__btn { margin-top: 32px; display: flex; justify-content: center; }


  /* ---------- contact page ---------- */
  .contact-page { padding-block: clamp(64px, 9vw, 112px); }

  .contact-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(48px, 7vw, 96px);
    align-items: start;
  }

  @media (max-width: 760px) {
    .contact-grid { grid-template-columns: 1fr; gap: 48px; }
  }

  .contact-info__label { display: block; margin-bottom: 20px; }
  .contact-info__title { font-size: var(--fs-h1); color: var(--ink-h1); }
  .contact-info__desc { font-size: var(--fs-prose); line-height: 1.65; color: var(--ink-2); margin-top: 20px; max-width: 40ch; }

  /* Booking link, directly under the headline. Sits above the email and socials blocks,
     which carry their own larger clamp, so it stays visually attached to the h1. */
  .contact-info__cta { margin-top: 24px; }

  /* Email and socials, stacked under the intro. Both use the page's existing .overline as
     their label so they read as one pair of small headings, not two new type styles. */
  .contact-info__block { margin-top: clamp(30px, 3.4vw, 42px); }
  .contact-info__subhead { display: block; margin: 0 0 12px; }
  .contact-info__email { font-family: var(--font); font-size: var(--fs-prose); color: var(--ink);
    border-bottom: 1px solid var(--border-strong); padding-bottom: 2px; transition: border-color .18s; }
  .contact-info__email:hover { border-bottom-color: var(--ink); text-decoration: none; }

  .contact-card {
    background: var(--bg);
    border: 1px solid var(--hairline);
    border-radius: 8px;
    padding: clamp(28px, 3vw, 40px);
  }

  .form-title {
    font-family: var(--font);
    font-size: var(--fs-prose);
    font-weight: 600;
    color: var(--ink);
    letter-spacing: -0.01em;
    line-height: 1.3;
    margin: 0 0 28px;
  }

  .form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
  .form-group:last-of-type { margin-bottom: 24px; }

  .form-group label {
    font-size: var(--fs-caption);
    font-weight: 500;
    color: var(--ink);
  }

  /* --border-input, not --border: an input's outline is the affordance that says
     "type here", so WCAG 1.4.11 asks for 3:1 against the page. The decorative
     hairlines elsewhere are nowhere near that and must not be reused here. */
  .form-group input,
  .form-group select,
  .form-group textarea {
    font-family: var(--font);
    font-size: var(--fs-small);
    color: var(--ink);
    background: var(--bg);
    border: 1px solid var(--border-input);
    border-radius: var(--radius);
    padding: 0.6em 0.85em;
    width: 100%;
    transition: border-color .15s;
    outline: none;
  }

  .form-group input::placeholder,
  .form-group textarea::placeholder { color: var(--ink-3); }

  .form-group input:focus,
  .form-group select:focus,
  .form-group textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(20,20,20,0.10); }

  .form-group textarea { resize: vertical; min-height: 120px; line-height: 1.5; }

  /* Safari on iOS zooms the whole page in on any field it focuses whose text is under
     16px, and does not zoom back out afterwards: the reader is left on a page they
     have to pinch. 15px is the site's body size and is under that line, so the fields
     go to 16px on phones. The label above each one stays where it is. Padding grows
     with it so a field clears 44px rather than the 38px it measured at. */
  @media (max-width: 640px) {
    .form-group input,
    .form-group select,
    .form-group textarea { font-size: 1rem; padding: 0.7em 0.85em; }
  }

  .btn-submit {
    display: block; width: 100%; text-align: center;
    font-family: var(--font); font-size: var(--fs-small); font-weight: 500;
    background: var(--accent); color: var(--on-dark);
    border: none; border-radius: var(--radius);
    padding: 0.85em 1.35em;
    cursor: pointer; transition: background .18s;
  }
  .btn-submit:hover { background: var(--accent-strong); }
  .btn-submit:disabled { background: var(--border); color: var(--ink-3); cursor: not-allowed; }
  .btn-submit:disabled:hover { background: var(--border); }

  .form-error { font-size: var(--fs-caption); line-height: 1.55; color: var(--ink); font-weight: 500; margin: 0 0 14px; }

  .form-success p { font-size: var(--fs-prose); line-height: 1.6; color: var(--ink-2); margin: 0; }



  /* ---------- product page ---------- */
  /* Status pill, sitting between the page-head lead and its CTA. cLTC is not live -
     Slava was still building the user application on 2026-08-24 - so the first
     screen has to say so plainly rather than let confident product copy imply
     something you can use today. Written as a <p>, not a decorative <span>, because
     it is a factual statement about the product and belongs in the reading order.
     Same pill shape as .news-row__tag; deliberately not that class, because this one
     will outlive the placeholder news and should not disappear with it. */
  .status { display: inline-flex; align-items: center; gap: 8px; margin-top: 26px;
    font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.1em;
    text-transform: uppercase; font-weight: 500; color: var(--ink-3);
    border: 1px solid var(--border); border-radius: 999px; padding: 0.42em 0.95em; }
  .status__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); flex: none; }


  /* =======================================================================
     2026-08-26 rewrite. Components added for the six-page structure in the
     content brief. Two visual rules from that brief constrain everything here:

     - No vaults, safes, padlocks or shields. They are the visual form of the
       word "custody", which the copy is deliberately not using. Every mark
       below draws a MECHANISM instead: signatures converging, an equals sign,
       a credentialled transfer.
     - No mock dashboards and no invented numbers used as decoration. .fig
       exists to display a real, sourced figure with its as-at date attached,
       so a number can never appear on this site without one.
     ======================================================================= */

  /* ---------- numbered cards ----------
     A card carrying a small numeral, a heading and a paragraph. Used by the home
     page's three moves and by the reserve page's three verification steps.

     Both were numbered LISTS until 2026-08-27 [Matias]. The objection recorded at
     the time was that a card grid can be read in any order while the order is what
     carries the meaning. That answer is kept rather than dropped: the markup stays
     an <ol> and every card still leads with its numeral, so the sequence survives
     the change of container.

     Reuses the house .card shell. Only the type is retuned, because these carry a
     heading that can wrap to three lines and a full paragraph under it, which is
     more than the shell's 21px title was drawn for. */
  /* The grid is often an <ol>, so strip the list chrome wherever that is the case
     rather than making every such list carry an extra class. */
  ol.cards { list-style: none; margin: 0; padding: 0; }
  /* Column, so the paragraph sits under the heading rather than beside it, and so
     a short card still fills the row height the grid gives it. */
  .card--numbered { display: flex; flex-direction: column; }
  .card__n { font-family: var(--font); font-size: var(--fs-micro); line-height: 1;
    letter-spacing: 0.1em; color: var(--ink-3); margin-bottom: 16px; }
  .card--numbered h3 { margin-bottom: 10px; }
  /* --ink-2, not the shell's --ink-3: these paragraphs are three to five times the
     length of the card copy the shell was built for, and want the darker ink. */
  .card--numbered p { color: var(--ink-2); line-height: 1.65; }

  /* ---------- art card ----------
     Added 2026-09-11 [Matias], first on the cLTC #why cards. The house .card shell with
     a drawing plate across its top: a light tint, a dot field, and a mechanism mark in
     the middle. It is the .loop__art plate from the old home page loop, brought back
     onto the card shell, and it draws in the same hand as .mech__art on the home page:
     ink at 0.72 for settled geometry, 0.4 dashed for construction lines.

     No border between plate and body. The tint against the white is edge enough, and
     a hairline there made the plate read as a separate box stacked on the card.

     2026-09-11 [Matias]: the plate went from the warm --surface-alt to a trace of the
     site's --tint-blue, a quarter strength over white, and the dots went fainter and
     took a little of the same blue so they sit in the ground rather than on it. The
     warm version was the reference site's exact treatment; this is ours.

     The card's own padding moves to .card__body so the plate can run to the card's
     edges; overflow:hidden is what rounds the plate's top corners. */
  .card--art { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
  .card__art { display: grid; place-items: center; height: clamp(168px, 15vw, 204px);
    background-color: color-mix(in srgb, rgb(var(--tint-blue)) 25%, #fff);
    background-image: radial-gradient(circle, rgba(30,45,90,0.10) 0.8px, transparent 1.2px);
    background-size: 10px 10px; background-position: 5px 5px; }
  /* viewBox is 120, so stroke widths are roughly rendered px at the 120px cap. */
  .card__art svg { width: clamp(104px, 9vw, 120px); height: auto; display: block; overflow: visible;
    fill: none; stroke: rgba(28,28,26,0.72); stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round; }
  .card__art .dash { stroke: rgba(28,28,26,0.4); stroke-width: 0.9; stroke-dasharray: 3 3.5; }
  /* A dotted ring: near-zero dashes with round caps draw as dots. */
  .card__art .dot { stroke: rgba(28,28,26,0.4); stroke-width: 1.2; stroke-dasharray: 0.01 3.4; }
  .card__art .fill { fill: rgba(28,28,26,0.72); stroke: none; }
  .card__art .hatch { stroke: rgba(28,28,26,0.32); stroke-width: 0.6; }
  .card__body { flex: 1; padding: clamp(24px, 2.6vw, 30px); }
  .card--art p { color: var(--ink-2); line-height: 1.65; }

  /* ---------- callout: REMOVED 2026-08-27 ----------
     The rule-on-the-edge pull quote that used to close five sections. All five are
     gone [Matias] and the component with them.

     Worth knowing why, because the pattern will want to come back: a line set apart
     as a quote is not read as more important, it is read as a caption, and a caption
     under a section that has already made its point is skipped. Every one of the five
     was either a restatement of something said better nearby or a claim that deserved
     a heading. What survived moved UP, into an h2 or a section intro. If a sentence
     seems to need this treatment again, that is usually a sign it should be the
     section's headline instead. */
  /* ---------- term list (cLTC in detail) ----------
     Added 2026-09-11 [Matias], replacing the horizontal spec strip, after USDY's
     "In Detail" block and cETH's "Overview": headline and one sentence on the left,
     a list of terms on the right, each row a label and a value. It reads like a term
     sheet, which is the point. This page's job is to make cLTC look like an
     instrument with defined terms.

     THE TEST FOR ADDING A ROW: would someone sizing up the asset look for it, and is
     there a sourced value we are allowed to publish? The most official looking rows
     in both references are ones we cannot have: redemption, liquidity and unwrap
     (the counsel hold); legal structure, regulatory status and domicile (no
     regulatory language, and the entity details do not exist yet); "Ratio: 1:1"
     (the vocabulary rule, which the Supply row covers in approved words). None of
     those goes in without the matching sign off. A missing redemption row is more
     visible in this format than in prose, and that was accepted knowingly.

     No Status row. [Matias] took Status off the old strip on 2026-09-10 and the In
     build pill off the hero on 2026-09-11. If a status marker is wanted before
     launch, it is an ordinary row here and needs no special styling.

     Labels muted, values in ink, both in the sans, like cETH. The two columns stack
     below 980px, where a half width list gets too narrow for the longer values, and
     each row stacks its label over its value below 640px. */
  .terms { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    gap: clamp(32px, 5vw, 88px); align-items: start; }
  .terms .head { padding-bottom: 0; }
  .terms__list { margin: 0; border-top: 1px solid var(--border-hover); }
  .terms__row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    gap: clamp(16px, 2.4vw, 32px); padding-block: clamp(10px, 1.1vw, 13px);
    border-bottom: 1px solid var(--border-hover); }
  .terms__row dt { display: flex; align-items: center; gap: 10px; font-family: var(--font);
    font-size: var(--fs-small); line-height: 1.45; color: var(--ink-3); }
  /* One line icon per label, after USDY's detail table. Drawn inline at 24 units in
     the house icon style (1.5 stroke, round caps and joins, no fill, same as
     .card__ic), a shade darker than the label so the column still reads as muted
     beside the values. Decorative: every svg is aria-hidden, the label carries the
     meaning. A new row needs an icon in the same style or none of them should have
     one. */
  .terms__ic { flex: 0 0 auto; width: 18px; height: 18px; fill: none; stroke: var(--ink-2);
    stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
  .terms__row dd { margin: 0; font-family: var(--font); font-size: var(--fs-small);
    line-height: 1.45; color: var(--ink); font-weight: 500; }
  @media (max-width: 980px) {
    .terms { grid-template-columns: 1fr; gap: clamp(28px, 4vw, 40px); }
  }
  @media (max-width: 640px) {
    .terms__row { grid-template-columns: 1fr; gap: 4px; }
  }

  /* ---------- attributed quote (cLTC, newsroom) ----------
     Added 2026-09-10, when the Litecoin Foundation supplied a quote. Read the
     removed-callout note directly above before assuming this is that pattern
     coming back, because it is not, and the difference is the whole reason this
     exists.

     The callout was OUR OWN sentence set apart for emphasis, and it failed for the
     reason recorded above: a line in quote treatment is read as a caption, and a
     caption restating the section it sits in gets skipped. Nothing was gained by
     the frame because nothing new was in it.

     This is a named third party saying something we cannot say about ourselves.
     The frame is not emphasis, it is attribution: the portrait and the name are
     the content, and without them the sentence is just more site copy. That is
     also why the portrait is not decorative and must not be dropped to save
     space on small screens. It stacks instead.

     One instance per page, maximum. The moment there are two, this is a
     testimonial wall, which is a different and much weaker thing. */
  /* margin: 0 because there is no global figure reset in this sheet and the UA
     default (0 40px) would indent it out of alignment with the copy around it.
     Vertical spacing is set per placement below, since a section and an article
     body space their blocks differently. */
  .quote { display: grid; grid-template-columns: clamp(104px, 12vw, 148px) 1fr;
    gap: clamp(24px, 3.2vw, 44px); align-items: start;
    max-width: 74ch; margin: 0; }
  /* On cltc.astro it follows .head, whose padding-bottom is the whole gap. It used to
     add a margin-top of its own on top of that, which stacked to nearly 100px and left
     the quote adrift from its heading [Matias, 2026-09-11]. */
  /* In the newsroom body it interrupts running prose, so it needs air on both
     sides and slightly more of it than a paragraph break. */
  .article__body .quote { margin-block: clamp(34px, 3.8vw, 48px); }

  /* Square rather than the 4/3 of .card__thumb. A face wants a square or a portrait
     crop; 4/3 gives a landscape box that a head sits in the middle of with air on
     both sides. */
  /* 12px corners, the same as .card and .loop__step [Matias 2026-09-11]. */
  .quote__portrait { aspect-ratio: 1; overflow: hidden; background: var(--surface-alt);
    border: 1px solid var(--border); border-radius: 12px; display: grid; place-items: center; }
  .quote__portrait img { display: block; width: 100%; height: 100%; object-fit: cover; }

  /* Cutout portraits. The Schwartz photograph is a PNG with the background keyed out,
     so the box has to supply its own ground or the figure floats on whatever is behind
     it: it would half vanish into the navy plate on /cltc and wash out completely on the
     warm newsroom page. The ground is a radial lifted from the cLTC hero's own ramp
     (#3a3a9e, #1d2466, #141E4C are its last three stops), which puts the same blue
     behind the face on both pages.

     Lightest at the top, darkest at the foot, because that is the way round the subject
     needs: his hair is dark and reads against the lifted top, his shirt is near white
     and reads against the dark foot. Inverted, both ends of him would sink.

     On /cltc the box bottoms out at the plate's own #141E4C, so the frame melts into the
     plate and only the halo and the border ring mark it. On the newsroom page the same
     box reads as a deliberate navy tile on the warm ground. */
  .quote__portrait--cutout {
    background: radial-gradient(ellipse 88% 80% at 50% 14%, #3a3a9e 0%, #1d2466 46%, #141E4C 100%);
    border-color: rgba(255,255,255,0.14);
  }

  /* Placeholder state. Deliberately plain and deliberately legible as a placeholder:
     an initials block that looked like a design choice would ship. scripts/go-live.sh
     blocks launch while any page still references a placeholder portrait. */
  .quote__initials { font-family: var(--font-display); font-size: clamp(1.5rem, 2.4vw, 2rem);
    letter-spacing: 0.02em; color: var(--ink-3); line-height: 1; }
  .quote__ph { display: block; margin-top: 8px; font-family: var(--font-mono);
    font-size: var(--fs-micro); letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--border-strong); }

  /* Set in the reading serif at lead size. Not italic: the marks and the attribution
     already say it is speech, and italic at this length is harder to read for no gain.
     No hanging punctuation and no oversized decorative quote glyph, both of which
     read as marketing on an institutional page. */
  .quote__text { margin: 0; font-family: var(--font-body); font-size: var(--fs-prose);
    line-height: 1.5; color: var(--ink); letter-spacing: -0.005em; }
  .quote__text p { font-size: inherit; line-height: inherit; color: inherit; margin: 0; }
  .quote__text p + p { margin-top: 0.7em; }

  .quote__by { margin-top: clamp(20px, 2.2vw, 28px); }
  .quote__name { font-family: var(--font); font-size: var(--fs-small); color: var(--ink); font-weight: 500; }
  /* Same treatment as .person__role on the about page: one register for "this is a
     human being's job title", wherever it appears. */
  .quote__role { display: block; margin-top: 7px; font-family: var(--font); font-size: var(--fs-micro);
    letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3); }

  /* On the cLTC page the quote IS the section rather than an interruption in running
     text, so it runs larger than the newsroom one: a bigger face, and the quote set
     above prose size [Matias 2026-09-11]. This is a deliberate exception to "no size
     between --fs-prose and --fs-h3", kept here rather than as a token because nothing
     else should use it. max-width is in rem, not ch, so the text column does not
     shrink to thirty characters a line once the type grows. */
  .quote--feature { grid-template-columns: clamp(140px, 18vw, 220px) 1fr; max-width: 60rem; }
  .quote--feature .quote__text { font-size: clamp(1.125rem, 0.98rem + 0.4vw, 1.25rem); line-height: 1.45; }

  /* The portrait keeps its own width when it stacks rather than growing to full
     bleed, because a 500px face above two lines of type is a portrait with a caption,
     not a quote. */
  @media (max-width: 620px) {
    .quote { grid-template-columns: 1fr; gap: 24px; }
    .quote__portrait { width: 112px; }
    .quote--feature .quote__portrait { width: 140px; }
  }

  /* ---------- mechanism diagram (home, dark band) ----------
     The brief's design note, followed literally: "Draw the mechanism, not a
     generic flow. The two details that carry the whole argument visually are
     the reserve requiring multiple independent signatures, and the attestation
     feed sitting underneath every stage rather than at the end."

     Hence the rail. It is not decoration: it spans all three columns and drops
     a tick into each one, because attestation is continuous and under every
     stage, not a fourth step at the end.

     There is NO arrow between the steps and NO arrow returning to the start.
     Also from the brief: "an exit arrow will be read as a redemption promise
     even without a caption", and redemption is on hold until counsel signs off.
     The 01/02/03 numerals carry the sequence instead. Do not add arrows. */
  /* No max-width of its own [2026-09-13]. It used to take --content-frame-w, but that
     token's "100% - 48px" resolves against .mech's parent, which is already the inset
     .container, so the row came out 48px narrower than the product row below it and
     no column edge lined up. The container is the width; the three steps and the
     three product columns share its box and the same gap, so their edges coincide. */
  .mech { position: relative; z-index: 1; width: 100%; margin: clamp(38px, 4.5vw, 58px) auto 0; }
  .mech__steps { list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
    background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.07); }
  .mech__step { background: var(--surface-dark); padding: clamp(24px, 2.6vw, 34px); }
  .mech__n { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.14em; color: rgba(255,255,255,0.44); }
  .mech__art { margin: 18px 0 20px; }
  .mech__art svg { width: 74px; height: 74px; display: block; fill: none;
    stroke: rgba(255,255,255,0.72); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
  .mech__art svg .dash { stroke-dasharray: 3 5; stroke: rgba(255,255,255,0.4); }
  .mech__art svg .fill { fill: rgba(255,255,255,0.72); stroke: none; }
  .mech__step h3 { color: var(--on-dark); }
  .mech__step p { font-size: var(--fs-small); line-height: 1.6; color: var(--on-dark-muted); margin-top: 10px; }
  /* 14px, one pixel under the 15px paragraph above the rule [Matias, 2026-09-06].
     The two paragraphs do different jobs - the first states the step, the second the
     property that makes it hold - and the divider alone was not enough to say so.

     REMOVED 2026-09-08 [Matias]: p.mech__diff, the second paragraph under a hairline
     rule in each step. The rule set a border-top, 14px of padding and 14px type. It
     went with the copy - see the note in index.astro for why - and is not worth
     keeping as dead style. */

  /* The section's only exit. Before this the mechanism ran into the product cards with
     no route to the page that explains it. */
  .mech__more { text-align: center; margin-top: clamp(26px, 3vw, 38px); }
  .mech__link { color: var(--on-dark); font-size: var(--fs-small); font-weight: 500;
    text-decoration: none; border-bottom: 1px solid rgba(255,255,255,0.28); padding-bottom: 3px; }
  .mech__link:hover { border-bottom-color: var(--on-dark); }
  .mech__link .arr { display: inline-block; transition: transform .18s; }
  .mech__link:hover .arr { transform: translateX(3px); }

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

  /* ---------- the five rules, as toggles (technology) ----------
     Was .pillars, five stacked open blocks, until 2026-08-27. Now the .faq
     accordion with a numeral in front of each summary. It was lettered A to E
     until 2026-09-05, on the reasoning that five rules holding at once are not
     five steps in an order; the letters read as an oddity before they read as an
     argument, so they are numerals now.

     The summary is one flex row: [numeral + rule] on the left, [+] on the right.
     The numeral is inside the left span so a summary that wraps to two lines
     keeps its text aligned under itself rather than under the numeral. */
  .rules-toggle summary { align-items: flex-start; gap: 20px; }
  .rules-toggle summary > span:first-child { display: grid; grid-template-columns: 28px 1fr;
    gap: 4px; align-items: baseline; }
  /* The page font, not --font-display [Matias, 2026-09-09]. The serif was set when these
     were letters and it read as a drop cap; on numerals it reads as a different font
     rather than as a marker, which is what it is. */
  .rules-toggle__key { font-family: var(--font); font-size: var(--fs-prose); line-height: inherit; color: var(--ink-3); }
  /* Bumped from the .faq summary's 1rem: these are the layer most readers take,
     and at 1rem they sat below the body copy they introduce. line-height is set
     because a two-line summary at the default was too tight against the letter. */
  .rules-toggle summary { line-height: 1.45; }
  /* The [+] is centred on the FIRST line of the summary, not on the block, so it
     does not drift down the row when the rule wraps. */
  .rules-toggle summary .ic { margin-top: 2px; }
  .rules-toggle .faq__a { padding-left: 32px; }
  @media (max-width: 560px) {
    .rules-toggle summary > span:first-child { grid-template-columns: 22px 1fr; }
    .rules-toggle .faq__a { padding-left: 22px; }
    /* Nothing here about the open rule's [x] appearing to sit 4px past the right
       rail: that is the 45 degree rotation's bounding box, 20px turned is 28px wide,
       and it does the same on a desktop. It is the arm of a glyph crossing a
       hairline, not a layout that has overflowed. */
  }

  /* ---------- spec table ----------
     Two- and three-column fact tables (design decisions, roadmap status). Wrapped
     in .table-scroll so a narrow viewport scrolls the TABLE and never the page. */
  .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .spec { width: 100%; border-collapse: collapse; min-width: 520px; text-align: left; }
  .spec th { font-family: var(--font); font-size: var(--fs-micro); letter-spacing: 0.1em; text-transform: uppercase;
    font-weight: 500; color: var(--on-dark); background: var(--surface-dark); padding: 14px clamp(14px, 1.6vw, 20px); }
  .spec td { border-bottom: 1px solid var(--hairline); padding: clamp(16px, 1.8vw, 22px) clamp(14px, 1.6vw, 20px);
    font-size: var(--fs-small); line-height: 1.6; color: var(--ink-2); vertical-align: top; }
  .spec td:first-child { color: var(--ink); font-weight: 500; }
  .spec tr:nth-child(even) td { background: var(--surface-alt); }

  /* ---------- status tags ----------
     From the brief: a roadmap is "labelled by status, never by date". These are the
     only labels the site may use, and the wording is fixed. Do not invent a fourth. */
  .tag { display: inline-block; white-space: nowrap; font-family: var(--font); font-size: var(--fs-micro);
    letter-spacing: 0.1em; text-transform: uppercase; font-weight: 500;
    border: 1px solid var(--border-strong); border-radius: 999px; padding: 0.36em 0.8em; color: var(--ink); }
  /* --proven and --planned came off on 2026-09-09 with the technology page's status
     table, the only place either was used. --build is still on the cLTC card. */
  .tag--build   { border-color: var(--border-strong); color: var(--ink-2); }

  /* ---------- product cards ----------
     The home page's product list. Reuses the house .card shell; what is added is a
     status tag at the top, a heading one step up from the base card, and a bottom
     aligned link on the cards that have somewhere to go.

     The whole card is the link, not a button inside it, so the click target is the
     card a reader is already looking at. .card__link is therefore a span and not an
     <a>: an anchor inside an anchor is invalid, and it only ever needs to LOOK like
     the link the card already is.

     One card is a link and one is not, deliberately. That asymmetry is the hierarchy:
     there is one product to read about, and the other card is honest about being a
     statement of intent rather than a page that exists. */
  /* ---------- products row ----------
     2026-09-08 [Matias]: the head sits in the grid as the first column rather than as a
     band above it. Three equal columns, so the head column is the same width as a card
     and the row reads as one object instead of a title with a gap beside it.

     The head starts at the cards' top edge [Matias, 2026-09-09]. A first pass centred it
     vertically against them, on the theory that a short head at the top of a tall column
     leaves a hole under it. It reads as a misalignment instead: the row has one top edge
     and the head is part of the row.

     It collapses to the cards' own breakpoints: at 980px the head goes full width above
     a two card row, which is the old layout and the right one once the columns are too
     narrow to set a headline in. */
  .products { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2.4vw, 28px); }
  .products__intro { display: flex; flex-direction: column;
    padding-right: clamp(8px, 2vw, 32px); }
  /* Pulls the h1's half-leading off the top so the CAP of "Our products" lands on the
     cards' top edge rather than its line box, which is the alignment the eye reads. */
  .products__intro .head__title { margin-top: -0.16em; }
  .products__lead { font-size: var(--fs-prose); line-height: 1.6; color: var(--ink-2);
    margin-top: 16px; max-width: 32ch; }
  @media (max-width: 980px) {
    .products { grid-template-columns: repeat(2, 1fr); }
    .products__intro { grid-column: 1 / -1; padding-right: 0;
      padding-bottom: clamp(12px, 2vw, 24px); }
    .products__lead { max-width: 52ch; }
  }
  @media (max-width: 760px) { .products { grid-template-columns: 1fr; } }

  /* #fff, not the page's warm --bg [Matias, 2026-09-09]: on the white plane this section
     now sits on, an off-white card reads as a tint rather than a card. Same call as
     .how-band--light, which paints its step cards #fff on the same ground. The border
     is what defines the card; the fill only has to not compete with the plate. */
  .card--product { display: flex; flex-direction: column; background: #fff; }
  /* Link and status share the card's bottom row, link left and tag right
     [Matias, 2026-09-11]. The tag used to sit above the heading. */
  .card__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin-top: auto; padding-top: 20px; }
  /* One step above the shared card size [Matias, 2026-09-09]. The history matters here,
     because this number has moved twice: it was 1.5rem until 2026-09-08 and came down to
     the shared 19px so the three card rows on the home page would read as one size. The
     products row is no longer one of three - it is two cards across two thirds of the
     frame, with the paragraphs gone - so the name is now the only thing on the card and
     can carry the extra step. Do NOT propagate this to .card h3: the other rows still
     want the shared size.

     Flat 21px, not a clamp. A first attempt wrote clamp(1.1875rem, 1.5vw, 1.3125rem),
     which needs a 1400px viewport before the middle term clears the floor - so it was
     19px on every screen anyone actually uses, and the size did not change at all. */
  .card--product h3 { margin-bottom: 10px; }
  /* The bottom edge push (margin-top:auto, padding-top floor) lives on .card__foot now;
     the link's base 24px top margin would only double it. */
  .card--product .card__link { margin-top: 0; }

  /* The image plate. Empty it is a tinted 16:9 block, which is what card 02 is meant to
     be: a slot with nothing in it yet. Put an <img> inside and it covers the plate with
     no other change, so artwork can be dropped in from the page and never from here.

     Negative margins pull it out to the card's edges - the plate is a full bleed
     element inside a padded card, and the padding is a clamp, so the pull has to be the
     same clamp rather than a fixed number. */
  /* 4:3, and this is the FLOOR for these two images, not a preference. Measured
     2026-09-08: the type in both is centred at 52.2% of the image height, so a centred
     crop keeps it centred at any ratio. What sets the limit is the Empire State
     antenna in the cLTC image, whose tip is at 13.5%. A centred 4:3 crop of a square
     starts at 12.5% and clears it by 1%; a centred 3:2 crop starts at 16.7% and cuts
     the spire off, which reads as a mistake rather than a crop. Going shorter than 4:3
     needs new artwork with the skyline smaller in the frame, not a tighter crop. */
  .card__thumb { aspect-ratio: 4 / 3; overflow: hidden; background: var(--surface-alt);
    border-bottom: 1px solid var(--hairline);
    margin: calc(clamp(24px, 2.6vw, 30px) * -1) calc(clamp(24px, 2.6vw, 30px) * -1) clamp(22px, 2.2vw, 26px);
    border-radius: 11px 11px 0 0; }
  /* Centred, so the crop takes the same amount off the top and the bottom and the type
     baked into each image stays where it was composed: in the middle. An earlier pass
     biased this to 28% to save the spire in the cLTC image from a tighter crop, and the
     cost was the type sitting low in the frame, which is worse than a clipped antenna
     [Matias, 2026-09-08]. If a future crop has to save something at the top of an
     image, change the RATIO, not this. */
  .card__thumb img { display: block; width: 100%; height: 100%; object-fit: cover;
    object-position: center; }

  /* Card 02. No status tag and no link, because it is not a product yet and either one
     would imply otherwise. Otherwise it is built exactly like card 01, which is what
     stops the row reading as one real card next to an unfinished one.

     Its heading and line centre as a PAIR in the space card 01 fills with a tag, two
     lines and a link. auto margins rather than justify-content:center on the card,
     because the plate is pulled to the card's top edge by negative margins and centring
     the whole column would drag it back down; auto margins only eat the free space
     below the plate. The pair has to be split across the two elements - margin-block
     on the h3 alone would centre the h3 and shove the line away from it. */
  /* One element to centre now rather than a heading and line as a pair, so auto on both
     sides of the h3 does what margin-block auto cannot do inside a flex column. */
  .card--empty h3 { margin-top: auto; margin-bottom: auto; }
  a.card--product { text-decoration: none; color: inherit;
    transition: border-color .18s, background .18s; }
  /* A neutral grey on hover, not --surface-alt: that is the warm tint the card fill just
     came off, and bringing it back on hover would put the warmth back a pointer at a time. */
  /* Was #fafafa, the fourth near-white on the site and a token nobody declared. On a
     white page it was invisible anyway. --surface-alt is the one tint left. */
  a.card--product:hover { border-color: var(--border-strong); background: var(--surface-alt); }
  /* 400, not 500 [Matias, 2026-09-09]: the name above it is the weight that should carry
     the card, and a semibold link under a heading one step larger was competing with it. */
  .card__link { display: block; margin-top: 24px; font-size: var(--fs-small); font-weight: 400; color: var(--ink); }
  .card__link .arr { display: inline-block; transition: transform .18s; }
  a.card--product:hover .card__link .arr { transform: translateX(3px); }

  /* ---------- sourced figures ----------
     Every number carries its as-at date in the same component, so a figure
     physically cannot be placed on the site without one. If you find yourself
     wanting to drop the caption, the number does not belong on the page. */
  .figs { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--border);
    border: 1px solid var(--border); }
  .fig { background: var(--bg); padding: clamp(24px, 2.8vw, 34px); }
  .fig__n { font-family: var(--font-display); font-size: clamp(1.75rem, 3.2vw, 2.5rem); line-height: 1;
    letter-spacing: -0.02em; color: var(--ink-h1); }
  .fig__desc { font-size: var(--fs-small); line-height: 1.6; color: var(--ink-2); margin-top: 14px; max-width: 40ch; }
  .fig__asat { display: block; margin-top: 12px; font-family: var(--font); font-size: var(--fs-micro);
    letter-spacing: 0.06em; color: var(--ink-3); }
  @media (max-width: 640px) { .figs { grid-template-columns: 1fr; } }

  /* ---------- notice ----------
     The honest empty state. Used where the site has nothing to show yet and the
     brief forbids showing something anyway ("never label a static figure as live").
     Loud on purpose: an absence stated plainly reads as caution, an absence left
     to the reader to notice reads as evasion. */
  .notice { border: 1px solid var(--border-strong); padding: clamp(24px, 3vw, 38px); }
  .notice p { font-size: var(--fs-prose); line-height: 1.65; color: var(--ink-2); margin-top: 12px; max-width: 64ch; }
  .notice .tag { margin-bottom: 16px; }

  /* ---------- numbered method: REMOVED 2026-08-27 ----------
     The reserve page's "how you check it" steps used to be a narrow numbered list at
     68ch, on the reasoning that each step is a sentence rather than a card. They are
     the .cards--3 / .card--numbered row now [Matias]: three short steps set as running
     text in a single column read as an essay about verification, where three cards
     read as a procedure you could actually carry out. */

  /* ---------- monospace data ----------
     The brief allows monospace for "addresses, hashes and attestation data, never
     as a display face". This is the only class that may use it. */
  .mono { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: var(--fs-caption); letter-spacing: 0; color: var(--ink-2); word-break: break-all; }

  /* ---------- people (about) ----------
     2026-09-12 [Matias]: photographs, after a reference he supplied. Portrait tiles
     with the image bled to the edges, a dark scrim at the foot, and the role over the
     name on top of it. The biographies that this section was built around on
     2026-09-11 are gone: he chose pictures instead, so there is no .person__bio any
     more and nothing here is waiting on four paragraphs.

     Real gaps rather than the 1px hairline grid this used to be. A hairline grid works
     for flat cards sharing a ground; separate images want air between them, and the
     reference has it.

     Two columns at the small end, not one. A single column of 3:4 photographs is a
     very long page for four faces, and at 375px a two-up tile is still 165px wide. */
  .people { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(12px, 1.6vw, 20px); }
  .person { position: relative; border-radius: 12px; overflow: hidden; aspect-ratio: 3 / 4;
    background: var(--surface-alt); }
  .person__photo { display: block; width: 100%; height: 100%; object-fit: cover; }
  /* The scrim is what makes the caption readable over an unknown photograph. Three
     stops rather than two: a straight linear fade leaves a visible band edge across
     the middle of the picture. */
  .person__caption { position: absolute; inset-inline: 0; bottom: 0; padding: clamp(14px, 1.6vw, 20px);
    background: linear-gradient(to top, rgb(0 0 0 / 0.82) 0%, rgb(0 0 0 / 0.5) 46%, transparent 100%); }
  .person__role { display: block; margin-bottom: 6px; font-family: var(--font); font-size: var(--fs-micro);
    letter-spacing: 0.09em; text-transform: uppercase; color: rgb(255 255 255 / 0.66); }
  .person h3 { font-size: clamp(0.9375rem, 1.15vw, 1.125rem); line-height: 1.2; letter-spacing: -0.01em;
    color: #fff; font-weight: 500; }
  /* No photograph yet. A dark tile rather than a pale one, so the caption keeps the
     same treatment as the tiles beside it and the row still reads as one row.
     go-live.sh blocks launch while any of these remain. */
  .person--placeholder { background: var(--surface-dark); display: grid; place-items: center; }
  .person__ph { font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.14em;
    text-transform: uppercase; color: rgb(255 255 255 / 0.35); }
  @media (max-width: 980px) { .people { grid-template-columns: repeat(2, 1fr); } }

  /* ---------- contact routes ----------
     The brief splits contact by intent rather than offering one form, because the
     three audiences arrive with different questions and one of them (holders) is
     asking the question the site deliberately does not answer. */
  .routes { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(20px, 2.4vw, 30px); }
  .route { border-top: 1px solid var(--ink); padding-top: clamp(18px, 2vw, 24px); }
  .route p { font-size: var(--fs-prose); line-height: 1.62; color: var(--ink-2); margin-top: 10px; }
  .route__to { display: block; margin-top: 14px; font-size: var(--fs-small); color: var(--ink);
    border-bottom: 1px solid var(--border-strong); padding-bottom: 2px; }
  @media (max-width: 760px) { .routes { grid-template-columns: 1fr; gap: 30px; } }

  /* ---------- footer legal ----------
     Registered entity details and the jurisdictional note. The brief calls entity
     details in the footer "a credibility signal in itself for this audience". */
  .footer__legal { border-top: 1px solid var(--border); margin-top: clamp(28px, 3vw, 40px); padding-top: 22px; }
  /* The one piece of genuine prose kept on the sans, listed with the furniture in the
     reading-text block above. At 12px a serif gives up real legibility, and this is
     the smallest type on the site: the disclaimer has to stay readable for the people
     who actually go looking for it. Everything else in the footer is sans anyway. */
  .footer__legal p { font-size: var(--fs-caption); line-height: 1.6; color: var(--ink-3); max-width: 88ch; }
  .footer__legal p + p { margin-top: 8px; }


  /* ---------- definition rows ----------
     Term plus one sentence, hairline separated. Used where a list is neither a
     sequence (which would be .problem or .method) nor a set of equal weight cards
     (.cards), but a glossary: "Why Canton", "What gets published here". */
  .defs { border-top: 1px solid var(--hairline); }
  .defs__row { display: grid; grid-template-columns: 268px 1fr; gap: clamp(16px, 3vw, 44px);
    border-bottom: 1px solid var(--hairline); padding-block: clamp(18px, 2.1vw, 27px); }
  .defs dd { font-size: var(--fs-prose); line-height: 1.62; color: var(--ink-2); max-width: 62ch; }
  @media (max-width: 640px) { .defs__row { grid-template-columns: 1fr; gap: 8px; } }

  /* ---------- how-band lead ----------
     One sentence under the band's headline, centred with it. Bigger than
     .how-band__note, which is a footnote; this is the argument. */
  .how-band__lead { text-align: center; max-width: 62ch; margin: 20px auto 0;
    font-size: var(--fs-prose); line-height: 1.6; color: var(--on-dark-muted); }

  .skip-link { position: absolute; left: 12px; top: -60px; z-index: 200;
    background: var(--ink); color: var(--on-dark); padding: 12px 18px;
    border-radius: var(--radius); font-size: var(--fs-small); }
  .skip-link:focus { top: 12px; text-decoration: none; }

  .form-note { margin-top: 16px; font-size: var(--fs-caption); line-height: 1.6; color: var(--ink-3); }
  .form-note a { color: var(--ink-2); border-bottom: 1px solid var(--border-strong); }
  .form-note a:hover { color: var(--ink); text-decoration: none; }

  .as-at { margin-top: clamp(22px, 2.6vw, 30px); font-size: var(--fs-caption); line-height: 1.6;
    color: var(--ink-3); max-width: 70ch; }

