/* ============================================================================
   RCS Care — public site skin
   ----------------------------------------------------------------------------
   Implements assets/PALETTE.md v2, "colour as light". Every token below the
   line marked SHARED is copied verbatim out of that file; app.css carries the
   same names at the same values, so a visitor moving from this page into the
   portal sees one product. Neither file may change a shared value without
   PALETTE.md changing first.

   What replaced what: the site used to wear the flat navy skin taken from the
   2022 profile deck — #050D1C grounds, #1C5FA8 accent, everything inside one
   low-chroma family. On screen it read as dull, which is what the client said.
   The direction they chose instead is a gradient mesh ground with glass
   surfaces on it: deep, saturated, and never a flat fill.

   --moss still holds a blue rather than a green. The name is kept because
   app.css re-points the same token; renaming would fork the two files, which
   is the failure this arrangement exists to prevent.

   Structure of the colour layer, in the order it paints:

     1. --ink, the page ground.
     2. body.site::before — the mesh. One fixed, full-viewport layer of four
        big radials off --mesh-1/2/3. Fixed, so it is composited once and
        never repaints while the page scrolls.
     3. a band's own background, where it has one (.sec-tint, .plate).
     4. .band-mesh — a second, band-local mesh whose stops are chosen by the
        band's data-hue. This is what makes scrolling move through the light:
        each chapter sits at a different point in the gradient.
     5. content, on opaque or glass surfaces.
   ========================================================================= */

body.site {
  /* ── SHARED with app.css — PALETTE.md, implement verbatim ──────────────── */

  /* The three stops everything else is derived from. Constant across both
     themes: grounds change, the light does not. */
  --mesh-1:     #1B3BFF;   /* blue   — the brand's centre of gravity */
  --mesh-2:     #7C3AED;   /* violet — the shift */
  --mesh-3:     #06B6D4;   /* cyan   — the lift */

  --ink:        #05071A;
  --surface:    #0B0F2A;
  --surface-2:  #131A3D;
  --surface-3:  #1C2550;
  --line:       #2A3468;
  --line-soft:  #1A2148;

  --moss:       #3B6BFF;   /* brand accent — fills, bars */
  --moss-lift:  #7FA5FF;   /* accent as type on dark */
  --moss-dim:   #1B3BFF;   /* deep fill, gradient anchor */

  --signal:     #FFC53D;   /* licence / inspection tag */
  --alert:      #FF6B85;   /* sighting, overdue, destructive */
  --info:       #22D3EE;   /* informational — must not read as brand */

  --text:       #EEF1FF;
  --muted:      #A9B1DC;
  --faint:      #7A83B8;

  --navy-flat:  #1B3BFF;

  --glass:      rgba(255,255,255,.055);
  --glass-line: rgba(255,255,255,.11);
  --glass-blur: 14px;

  --display: 'Newsreader', Georgia, 'Times New Roman', serif;
  --body:    'Public Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ── landing-only, below the shared line ───────────────────────────────
     Three chapter hues plus a lifted gold. PALETTE.md names the base three;
     the -lift variants are this file's, and they are the only ones allowed to
     carry type. The portals never reference these names, so adding them
     cannot desynchronise anything.

     They are used as an *index*, not as decoration: each band of the page
     carries one in its rule, its mesh stops, its card edges and its numerals,
     while every eyebrow on the site stays gold. One attribute, data-hue,
     recolours a whole chapter. */
  --aqua:       #22D3EE;   /* measurement, coverage, scale */
  --aqua-lift:  #67E8F9;
  --iris:       #9A93F2;   /* method, sequence, the things we are judged on */
  --iris-lift:  #C0BBFF;
  --ember:      #FF8A5B;   /* the warm counterpoint — people, clients, trade */
  --ember-lift: #FFB392;
  --gold-lift:  #FFD470;   /* --signal, lifted far enough to be type */

  /* The per-band accent. Every band sets these two on itself; components read
     them and never name a hue directly, which is why one attribute recolours
     a whole section and why a new band is a two-line change. */
  --hue:      var(--moss);
  --hue-lift: var(--moss-lift);

  /* Mesh strength. Four numbers rather than alphas written into forty
     gradients — this is the knob the whole page turns on, and it is the one
     that has to be defended: see "the legibility budget" below.
       --g*  the fixed ground mesh          --b*  a band's own mesh  */
  --g1: 29%;  --g2: 23%;  --g3: 16%;
  --b1: 20%;  --b2: 15%;  --b3: 17%;
  --bA: var(--b1);         /* the near stop, scaled down for the two hot hues */
  --hot: .65;              /* how far gold and ember are pulled back — see below */
  --tint-veil: 86%;        /* how opaque .sec-tint is over the ground mesh */
  --hue-tint:   6%;        /* the hue wash at the top edge of a card */
  --glass-veil: 55%;       /* --surface behind the glass on the two panels */
  --nav-veil:   86%;       /* the header crosses everything — see "glass" */

  --grain-o:  .05;         /* film over the hero and the record plate */
  --plate:    #070A20;     /* the record band, which stays dark in both themes */
  /* The default glow, for anything outside a chapter. It is re-declared on
     [data-hue] below and that is not tidiness: a custom property's var()
     references are substituted at computed-value time on the element that
     *declares* it, and the resolved value is what descendants inherit. Left
     only here, --glow would freeze to the brand blue on <body> and every
     chapter's glow — aqua, iris, ember, gold — would come out the same colour
     as the hero's. It did, until the rule below existed. */
  --glow:     color-mix(in srgb, var(--hue) 42%, transparent);
}

/* ══ the legibility budget ══════════════════════════════════════════════════
   The failure mode of every mesh design is text measured against the average
   of the gradient rather than its extreme. The numbers above were chosen
   against the extreme, and the reasoning is recorded here so the next person
   to turn the mesh up knows what they are spending.

   Worst pixel = one ground stop at full strength + a 20% tail from its
   nearest neighbour + one band stop at full + a 20% tail. On the dark theme
   that composites to #073B5E; on light, to #BFD8F9.

     dark  --text  #EEF1FF on #073B5E  10.4:1     light  12.8:1 on #BFD8F9
     dark  --muted #A9B1DC on #073B5E   5.6:1     light   5.4:1
     dark  --moss-lift as --hue-lift    4.9:1     light   4.6:1
     the four -lift chapter hues     6.6–8.3:1    light 5.0–7.5:1

   --faint is the token with no headroom: #7A83B8 is 4.6:1 on --surface-2 and
   nothing above that. So --faint never sits on a mesh. It appears only inside
   an opaque card, on the record plate, or on a white photo plate where the
   light column is re-pointed locally. Everything that used to be --faint out
   in the open — the chapter index, the licence strip, the footer, the hero
   note, a page-head byline — is --muted now, and reads better for it.

   The same rule is why cards are opaque rather than translucent. A card at
   90% --surface over the mesh lands --faint at 4.3:1. Translucency, and the
   backdrop-filter that goes with it, is therefore spent on four surfaces that
   carry no --faint text at all. See "glass" below.
   ══════════════════════════════════════════════════════════════════════════ */

/* A serif at display size wants tighter tracking and a shorter leading than
   the grotesque this system was built around. */
body.site h1, body.site h2, body.site h3 {
  font-family: var(--display); letter-spacing: -.015em; line-height: 1.12;
  font-weight: 600; text-wrap: balance;
}
body.site h1 { font-weight: 500; }
body.site p { line-height: 1.72; }

/* ── light ───────────────────────────────────────────────────────────────
   The mesh stops do not change; the ground does, and so does everything that
   has to carry type on it. Two things about the light theme are worth
   spelling out, because a mesh design that only works dark is half a design.

   First, the direction of failure inverts. On dark, colour *lightens* the
   ground and the risk is pale type washing out; the lightest pixel is the one
   to measure. On light, colour *darkens* the ground and the risk is dark type
   sinking into it, so the darkest pixel is the one to measure. That is why
   --g1/--b1 are so much lower here than in dark: #1B3BFF is the stop that
   darkens white fastest.

   Second, cyan is the opposite — #06B6D4 over white barely moves the
   luminance, so it can be the strongest stop of the three here where it is
   the weakest in dark. The light theme is therefore built around the cyan
   lift with blue and violet as accents, which is a different picture from the
   dark one rather than a washed-out copy of it.

   The chapter hues are re-derived, not reused. #67E8F9 on white is 1.4:1 —
   fine as a 2px rule, nowhere near enough for the numerals and links that
   also take --hue-lift. Same hue, dark enough to carry type over the mesh at
   its darkest. */
@media (prefers-color-scheme: light) {
  body.site {
    --ink:       #F4F6FF;
    --surface:   #FFFFFF;
    --surface-2: #EDF1FF;
    --surface-3: #E0E7FF;
    --line:      #CFD8F5;
    --line-soft: #E4E9FB;
    --moss:      #2647D8;
    --moss-lift: #1B3BFF;
    --moss-dim:  #C9D6FF;
    --signal:    #8A5A06;
    --alert:     #C0304C;
    --info:      #0E7490;
    --text:      #0A1030;
    --muted:     #454E80;
    --faint:     #5C6598;
    --navy-flat: #2647D8;

    --glass:      rgba(255,255,255,.72);
    --glass-line: rgba(16,24,64,.10);

    --aqua:       #0E7490;  --aqua-lift:  #155E75;
    --iris:       #6D28D9;  --iris-lift:  #4C1D95;
    --ember:      #B4470F;  --ember-lift: #7C2D12;
    --gold-lift:  #6E4705;

    --g1: 10%;  --g2: 11%;  --g3: 17%;
    --b1:  6%;  --b2:  7%;  --b3: 13%;
    --hot: 1;
    --tint-veil: 88%;
    --hue-tint:   6%;
    --glass-veil: 55%;
    --nav-veil:   86%;
    --grain-o:   .03;
  }
}
/* app.js writes data-theme on <html>, not on <body> — these were written as
   `body.site[data-theme=…]`, which requires the attribute to sit on the body
   itself and so never matched. The toggle therefore swapped the logo variant
   (those rules below are descendant selectors and did match) while leaving the
   palette on whatever the OS said: pick dark on a light system and you got the
   dark logo on a light ground. Descendant form, to match how it is set. */
[data-theme="dark"] body.site {
  --ink: #05071A; --surface: #0B0F2A; --surface-2: #131A3D; --surface-3: #1C2550;
  --line: #2A3468; --line-soft: #1A2148;
  --text: #EEF1FF; --muted: #A9B1DC; --faint: #7A83B8;
  --moss: #3B6BFF; --moss-lift: #7FA5FF; --moss-dim: #1B3BFF;
  --signal: #FFC53D; --alert: #FF6B85; --info: #22D3EE; --navy-flat: #1B3BFF;
  --glass: rgba(255,255,255,.055); --glass-line: rgba(255,255,255,.11);
  --aqua: #22D3EE; --aqua-lift: #67E8F9;
  --iris: #9A93F2; --iris-lift: #C0BBFF;
  --ember: #FF8A5B; --ember-lift: #FFB392;
  --gold-lift: #FFD470;
  --g1: 29%; --g2: 23%; --g3: 16%;
  --b1: 20%; --b2: 15%; --b3: 17%;
  --hot: .65; --tint-veil: 86%; --hue-tint: 6%; --glass-veil: 55%; --nav-veil: 86%; --grain-o: .05;
}
[data-theme="light"] body.site {
  --ink: #F4F6FF; --surface: #FFFFFF; --surface-2: #EDF1FF; --surface-3: #E0E7FF;
  --line: #CFD8F5; --line-soft: #E4E9FB;
  --text: #0A1030; --muted: #454E80; --faint: #5C6598;
  --moss: #2647D8; --moss-lift: #1B3BFF; --moss-dim: #C9D6FF;
  --signal: #8A5A06; --alert: #C0304C; --info: #0E7490; --navy-flat: #2647D8;
  --glass: rgba(255,255,255,.72); --glass-line: rgba(16,24,64,.10);
  --aqua: #0E7490; --aqua-lift: #155E75;
  --iris: #6D28D9; --iris-lift: #4C1D95;
  --ember: #B4470F; --ember-lift: #7C2D12;
  --gold-lift: #6E4705;
  --g1: 10%; --g2: 11%; --g3: 17%;
  --b1:  6%; --b2:  7%; --b3: 13%;
  --hot: 1; --tint-veil: 88%; --hue-tint: 6%; --glass-veil: 55%; --nav-veil: 86%; --grain-o: .03;
}

/* ══ the mesh ════════════════════════════════════════════════════════════
   One fixed, full-viewport layer of four radials off the three stops. Fixed
   rather than scrolling, which is the whole performance argument: the layer
   is rasterised once and the compositor reuses it for the life of the page,
   so scrolling costs nothing to repaint. A `background-attachment: fixed` on
   the body would look identical and repaint the canvas on every scroll frame,
   which is exactly what a mid-range phone cannot afford.

   Four gradients, not fourteen. Their centres are spread far enough apart
   relative to their radii that at most one of them is near full strength at
   any given pixel — that separation is what the legibility budget above is
   counting on, so moving a centre means re-measuring.

     A  blue    top left, centre off-canvas above
     B  violet  top right, on canvas
     C  cyan    bottom right, on canvas
     D  violet  bottom left, centre off-canvas below

   Painting: body's own background is --ink; a z-index:-1 fixed ::before
   paints *after* that background and before all content, so the mesh lands on
   the ground and under everything else without touching a single element's
   stacking. */
body.site { background: var(--ink); }
body.site::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(46% 40% at  8% -4%,
      color-mix(in srgb, var(--mesh-1) var(--g1), transparent), transparent 66%),
    radial-gradient(44% 38% at 96%  8%,
      color-mix(in srgb, var(--mesh-2) var(--g2), transparent), transparent 64%),
    radial-gradient(42% 38% at 88% 92%,
      color-mix(in srgb, var(--mesh-3) var(--g3), transparent), transparent 66%),
    radial-gradient(52% 42% at 18% 106%,
      color-mix(in srgb, var(--mesh-2) var(--g2), transparent), transparent 70%);
}

/* The deck rules every slide with a navy bar. Borrowed as a section marker so
   the site and the printed profile feel like one company. */
.rule-top { border-top: 3px solid var(--moss); padding-top: 2.6rem; }

/* ── shared section furniture ────────────────────────────────────────────── */
.sec-pad  { padding: 5.5rem 0; }
.sec-tint { background: var(--surface-2); border-block: 1px solid var(--line); }
.grid-cards { display: grid; gap: 1.1rem;
              grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }

/* ── service cards ───────────────────────────────────────────────────────── */
.svc { display: flex; flex-direction: column; gap: .7rem; height: 100%; }
.svc-ic { width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center;
          background: color-mix(in srgb, var(--moss) 16%, transparent);
          color: var(--moss-lift); flex: none; }
.svc-ic svg { width: 21px; height: 21px; }
.svc h3 { font-size: 1.05rem; }
.svc p  { color: var(--muted); font-size: .88rem; line-height: 1.65; }
.svc ul { list-style: none; display: grid; gap: .3rem; margin-top: auto; padding-top: .5rem; }
.svc li { font-size: .8rem; color: var(--faint); display: flex; gap: .5rem; }
.svc li::before { content: '·'; color: var(--moss-lift); font-weight: 700; }

/* ── chip lists (property types, states, standards) ──────────────────────── */
.chips { display: flex; flex-wrap: wrap; gap: .45rem; }
.chip {
  font-size: .8rem; padding: .4rem .8rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); white-space: nowrap;
}
.chip.on { border-color: var(--moss); color: var(--moss-lift);
           background: color-mix(in srgb, var(--moss) 10%, transparent); }

/* ── numbered pillars ────────────────────────────────────────────────────── */
.pillars { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.pillar { border-left: 2px solid var(--moss); padding-left: 1rem; }
.pillar .n { font-family: var(--mono); font-size: .68rem; letter-spacing: .12em;
             color: var(--moss-lift); text-transform: uppercase; }
.pillar h3 { font-size: 1rem; margin: .4rem 0 .35rem; }
.pillar p  { font-size: .85rem; color: var(--muted); line-height: 1.6; }

/* ── client names ────────────────────────────────────────────────────────── */
.clients { display: flex; flex-wrap: wrap; gap: .5rem .55rem; }
.clients span {
  font-size: .875rem; padding: .5rem .9rem; border-radius: 8px;
  background: var(--surface); border: 1px solid var(--line); color: var(--text);
}

/* ── credentials ─────────────────────────────────────────────────────────── */
/* Two states, and they must not be confusable at a glance or on a phone held
   at arm's length. In force: the accent blue, a solid rule down the edge.
   Lapsed: the amber --signal reserved for licence marks, a dashed edge and a
   card that sits back rather than forward. The status word is spelled out on
   every card — colour alone would fail anyone who cannot see the difference. */
.creds { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.cred { border: 1px solid var(--line); border-left: 3px solid var(--moss);
        border-radius: var(--radius); padding: 1.1rem 1.2rem;
        background: var(--surface); position: relative; }
.cred .t { font-size: .93rem; font-weight: 600; }
.cred .n { font-family: var(--mono); font-size: .78rem; color: var(--moss-lift); margin-top: .3rem; }
.cred .b { font-size: .78rem; color: var(--faint); margin-top: .7rem; line-height: 1.55; }

.cred-head { display: flex; align-items: baseline; justify-content: space-between;
             gap: .7rem; }
.cred-status {
  flex: none; font-family: var(--mono); font-size: .6rem; letter-spacing: .12em;
  text-transform: uppercase; padding: .22rem .5rem; border-radius: 999px;
  border: 1px solid var(--moss); color: var(--moss-lift);
  background: color-mix(in srgb, var(--moss) 14%, transparent);
}
.cred-dates { margin: .7rem 0 0; display: grid; gap: .25rem; }
.cred-dates div { display: flex; gap: .6rem; align-items: baseline; }
.cred-dates dt { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
                 text-transform: uppercase; color: var(--faint); width: 64px; flex: none; }
.cred-dates dd { margin: 0; font-size: .78rem; color: var(--muted); }
.cred-issuer { margin-top: .7rem; padding-top: .6rem; border-top: 1px solid var(--line-soft);
               font-size: .72rem; color: var(--faint); }

/* --signal is a mark colour: it carries a border or a filled badge fine, and
   at .78rem on the light theme's white it lands near 2:1, which is under the
   bar for the one line on this page nobody may miss. So a darker amber for
   amber *text*, --signal itself for rules and fills. One token, defined where
   it is used rather than in the palette block, which the platform retheme
   reads as its reference. */
.cred.is-lapsed { --signal-ink: #FFD470; }
@media (prefers-color-scheme: light) {
  .cred.is-lapsed { --signal-ink: #6E4705; }
}
[data-theme="dark"]  .cred.is-lapsed { --signal-ink: #FFD470; }
[data-theme="light"] .cred.is-lapsed { --signal-ink: #6E4705; }
/* A certificate sheet is a white plate in both themes, so the amber under it
   is always the dark one — the same value the light column uses. */
.cert-shot.is-lapsed { --signal-ink: #6E4705; }

.cred.is-lapsed { border-left-color: var(--signal); border-style: dashed;
                  border-left-style: solid; background: transparent; }
.cred.is-lapsed .n { color: var(--muted); }
.cred.is-lapsed .cred-status { border-color: var(--signal); color: var(--signal-ink);
                               background: color-mix(in srgb, var(--signal) 14%, transparent); }
.cred.is-lapsed .cred-dates dd { color: var(--signal-ink); }
.cred.is-lapsed .cred-dates div:first-child dd { color: var(--muted); }

/* ── the chemical schedule off the paper receipt ─────────────────────────── */
.schedule-wrap { overflow-x: auto; border: 1px solid var(--line);
                 border-radius: var(--radius); background: var(--surface); }
.schedule { width: 100%; border-collapse: collapse; font-size: .82rem;
            min-width: 42rem; }
.schedule th, .schedule td { text-align: left; padding: .62rem .9rem;
                             border-bottom: 1px solid var(--line-soft); }
.schedule thead th { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
                     text-transform: uppercase; color: var(--faint);
                     border-bottom: 1px solid var(--line); white-space: nowrap; }
.schedule tbody th { font-weight: 600; color: var(--text); }
.schedule tbody td { color: var(--muted); }
.schedule tbody tr:last-child th, .schedule tbody tr:last-child td { border-bottom: 0; }
.schedule tbody tr:hover th, .schedule tbody tr:hover td {
  background: color-mix(in srgb, var(--moss) 7%, transparent); }
.schedule .mono { font-family: var(--mono); font-size: .74rem; }
/* Four columns will not fit a phone, and the one that gets pushed off is the
   approved area — the column the section is about. Say so rather than hope. */
.schedule-hint { display: none; font-size: .82rem; margin-bottom: .7rem; }
@media (max-width: 780px) { .schedule-hint { display: block; } }

/* ── support list ────────────────────────────────────────────────────────── */
.support { display: grid; gap: .8rem; }
.support li { display: flex; gap: .8rem; align-items: flex-start;
              font-size: .9rem; line-height: 1.6; }
.support li svg { width: 17px; height: 17px; flex: none; margin-top: .22rem; color: var(--moss-lift); }
.support li span { color: var(--muted); }
.support li b { color: var(--text); font-weight: 600; }

/* ── the schematic-map legend ────────────────────────────────────────────── */
.legend { display: flex; gap: 1.2rem; flex-wrap: wrap; margin-top: 1rem; }
.legend i { width: 13px; height: 13px; border-radius: 3px; display: inline-block;
            margin-right: .45rem; vertical-align: -1px; }
.legend span { font-size: .78rem; color: var(--muted); font-family: var(--mono); }

/* ── contact ─────────────────────────────────────────────────────────────── */
.contact-grid { display: grid; gap: 2rem; grid-template-columns: 1.1fr .9fr; align-items: start; }
@media (max-width: 820px) { .contact-grid { grid-template-columns: 1fr; } }
.cline { display: flex; gap: .9rem; align-items: baseline; padding: .7rem 0;
         border-bottom: 1px solid var(--line-soft); font-size: .93rem; }
.cline:last-child { border-bottom: 0; }
.cline .k { font-family: var(--mono); font-size: .68rem; letter-spacing: .1em;
            text-transform: uppercase; color: var(--faint); width: 88px; flex: none; }


/* ══════════════════════════════════════════════════════════════════════
   Page furniture, previously inline in index.html. Shared now that the
   site is more than one page — nav, hero, strip, steps, coverage,
   standards, FAQ and footer are identical everywhere.
   ══════════════════════════════════════════════════════════════════════ */

/* ── nav ───────────────────────────────────────────────────────────────── */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 100; padding: 1rem 0;
  transition: background .3s, border-color .3s, padding .3s;
  border-bottom: 1px solid transparent;
}
.nav.stuck { background: color-mix(in srgb, var(--ink) 82%, transparent);
             backdrop-filter: blur(16px) saturate(1.4); border-bottom-color: var(--line);
             padding: .6rem 0; }
.nav-in { display: flex; align-items: center; justify-content: space-between; gap: 2rem; }
.nav-links { display: flex; gap: 1.75rem; font-size: .875rem; color: var(--muted); }
.nav-links a { position: relative; transition: color .18s; }
.nav-links a:hover { color: var(--text); }
.nav-links a::after {
  content: ''; position: absolute; left: 0; right: 100%; bottom: -5px; height: 1.5px;
  background: var(--signal); transition: right .28s var(--ease);
}
.nav-links a:hover::after { right: 0; }
.nav-cta { display: flex; align-items: center; gap: .6rem; }
.icon-btn { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 9px;
            border: 1px solid var(--line); background: transparent; cursor: pointer;
            color: var(--muted); transition: all .18s; }
.icon-btn:hover { color: var(--text); border-color: var(--moss); }

/* ── hero ──────────────────────────────────────────────────────────────── */
.hero { position: relative; padding: 9.5rem 0 5rem; overflow: hidden; }
.hero-grid {
  position: absolute; inset: 0; z-index: 0; opacity: .5;
  background-image:
    linear-gradient(var(--line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(120% 80% at 70% 0%, #000 20%, transparent 72%);
}
.hero-glow {
  position: absolute; z-index: 0; width: 780px; height: 780px; border-radius: 50%;
  top: -400px; right: -180px; pointer-events: none;
  background: radial-gradient(circle, color-mix(in srgb, var(--moss) 26%, transparent), transparent 62%);
  animation: drift 22s ease-in-out infinite alternate;
}
@keyframes drift { to { transform: translate(-70px, 60px) scale(1.12); } }

.hero-in { position: relative; z-index: 1; display: grid;
           grid-template-columns: minmax(0, 1.35fr) minmax(300px, .65fr);
           gap: 3.5rem; align-items: center; }
/* The serif sets wider than the grotesque this was scaled for; 4.35rem
   pushed the second line past its column and under the hero card. */
.hero h1 { font-size: clamp(2.4rem, 4.6vw, 3.6rem); margin: 1.1rem 0 1.25rem; }
/* No nowrap: it forced a line longer than the grid column, which is how the
   headline ended up printed underneath the credentials card. */
.hero h1 .hl { color: var(--moss-lift); position: relative; }
/* The swipe underline is gone. It was absolutely positioned against an inline
   span, which is fine while the span holds one line and nonsense once it wraps
   — it drew a rule under a fragment of the last line only. The colour shift
   already carries the emphasis. */
.hero p.lede { font-size: 1.075rem; color: var(--muted); max-width: 33rem; line-height: 1.65; }
.hero-actions { display: flex; gap: .7rem; margin-top: 2rem; flex-wrap: wrap; }
.hero-note { margin-top: 1.4rem; font-size: .8rem; color: var(--faint); display: flex;
             align-items: center; gap: .5rem; }

/* The hero visual is the document the client actually receives. A live-looking
   feed of invented records would say more about the software than about the
   service, and a prospect cannot verify a ticker — they can read a receipt.

   What used to sit here was the sample-receipt block, and when it was deleted
   its selectors went with it but eleven declaration lines did not. CSS error
   recovery does not stop at a stray `}` — the parser reads an orphaned
   declaration as the start of a selector and keeps swallowing until it finds a
   block, which meant the whole run was consuming `.strip { … }` below as its
   own body. The licence strip has therefore had no top rule, no bottom rule and
   no vertical padding on every page of the site. Removed; .strip is a rule
   again. */

/* ── strip ─────────────────────────────────────────────────────────────── */
.strip { border-block: 1px solid var(--line); padding: 1.4rem 0; }
.strip-in { display: flex; align-items: center; gap: 2.4rem; flex-wrap: wrap; justify-content: center; }
.strip .lab { font-family: var(--mono); font-size: .66rem; letter-spacing: .16em;
              text-transform: uppercase; color: var(--faint); }
.strip .co { font-family: var(--display); font-weight: 600; font-size: 1.02rem;
             color: var(--muted); letter-spacing: -.02em; transition: color .2s; }
.strip .co:hover { color: var(--text); }

/* ── sections ──────────────────────────────────────────────────────────── */
section { padding: 6rem 0; }
.sec-head { max-width: 46rem; margin-bottom: 3rem; }
.sec-head h2 { font-size: clamp(1.9rem, 3.6vw, 2.7rem); margin: .8rem 0 1rem; }
.sec-head p { color: var(--muted); font-size: 1.02rem; }

/* steps — numbered because this genuinely is a sequence */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; }
.step { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
        padding: 1.6rem 1.4rem; position: relative; overflow: hidden;
        transition: transform .3s var(--ease), border-color .3s; }
.step:hover { transform: translateY(-4px); border-color: var(--moss); }
.step-n { font-family: var(--mono); font-size: .7rem; color: var(--signal); letter-spacing: .1em; }
.step h3 { font-size: 1.2rem; margin: .7rem 0 .55rem; }
.step p { font-size: .885rem; color: var(--muted); }
.step-art { margin-top: 1.2rem; height: 92px; border-radius: var(--radius-sm);
            background: var(--surface-2); border: 1px solid var(--line-soft);
            display: grid; place-items: center; overflow: hidden; position: relative; }

/* portals */
.portals { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; }
.portal { border-radius: var(--radius-lg); border: 1px solid var(--line); overflow: hidden;
          background: var(--surface); transition: transform .35s var(--ease), box-shadow .35s; }
.portal:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); }
.portal-top { padding: 1.5rem 1.4rem 1.2rem; }
.portal-top h3 { font-size: 1.25rem; margin-bottom: .5rem; }
.portal-top p { font-size: .875rem; color: var(--muted); min-height: 3.2em; }
.portal ul { list-style: none; padding: 0 1.4rem 1.2rem; margin: 0; display: grid; gap: .45rem; }
.portal li { font-size: .82rem; color: var(--muted); display: flex; gap: .5rem; align-items: flex-start; }
.portal li::before { content: '—'; color: var(--moss-lift); flex: none; }
.portal-foot { padding: 1rem 1.4rem; border-top: 1px solid var(--line); background: var(--surface-2); }

/* coverage */
.cover { display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; align-items: center; }
/* With no state list the section is one short column, and full section padding
   leaves it floating in dead space above the CTA. */
#coverage.slim { padding-block: 4.5rem; }
#coverage.slim .cover { grid-template-columns: 1fr; max-width: 46rem; }
.states { display: flex; flex-wrap: wrap; gap: .45rem; }
.state { font-family: var(--mono); font-size: .74rem; padding: .32rem .7rem; border-radius: 999px;
         border: 1px solid var(--line); color: var(--muted); background: var(--surface);
         transition: all .2s var(--ease); }
.state:hover, .state.hot { border-color: var(--moss); color: var(--text);
         background: color-mix(in srgb, var(--moss) 14%, var(--surface)); }

/* standards — four clauses, four cards; the tag is the clause reference
   because that is the thing a QA head actually searches for */
.std-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.1rem; }
.std { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
       padding: 1.5rem 1.4rem; transition: border-color .3s, transform .3s var(--ease); }
.std:hover { border-color: var(--moss); transform: translateY(-3px); }
.std-tag { font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--signal);
           padding-bottom: .7rem; margin-bottom: .8rem; border-bottom: 1px solid var(--line-soft); }
.std h3 { font-size: 1.08rem; margin-bottom: .5rem; }
.std p { font-size: .865rem; color: var(--muted); }

/* the two documents — the whole commercial argument, side by side */
.deliverables { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; }
.deliv { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
         padding: 1.9rem 1.8rem; transition: border-color .3s, transform .3s var(--ease); }
.deliv:hover { border-color: var(--moss); transform: translateY(-4px); }
.deliv h3 { font-size: 1.35rem; margin: .8rem 0 .7rem; }
.deliv > p { font-size: .93rem; color: var(--muted); line-height: 1.62; }
.deliv ul { list-style: none; padding: 0; margin: 1.2rem 0 0; display: grid; gap: .5rem;
            border-top: 1px solid var(--line-soft); padding-top: 1.1rem; }
.deliv li { font-size: .84rem; color: var(--muted); display: flex; gap: .55rem;
            align-items: flex-start; }
.deliv li::before { content: '✓'; color: var(--moss-lift); flex: none; font-weight: 700; }
.deliv-foot { margin-top: 1.1rem; padding-top: 1rem; border-top: 1px solid var(--line-soft);
              font-size: .82rem; color: var(--faint); line-height: 1.62; }
.deliv-note { margin-top: 1.4rem; font-size: .88rem; color: var(--faint); text-align: center;
              max-width: 46rem; margin-inline: auto; }

/* contact lines render only for details that have actually been filled in */
.contact-lines { display: flex; gap: 1.6rem; justify-content: center; flex-wrap: wrap;
                 font-size: .95rem; }
.contact-lines a { color: var(--text); text-decoration: none; display: flex; gap: .45rem;
                   align-items: center; border-bottom: 1px solid var(--line); padding-bottom: 2px; }
.contact-lines a:hover { border-color: var(--moss-lift); color: var(--moss-lift); }
.contact-lines .k { font-family: var(--mono); font-size: .66rem; letter-spacing: .12em;
                    text-transform: uppercase; color: var(--muted); }

.platform { font-size: .82rem; color: var(--muted); }
.platform a { color: var(--muted); text-decoration: none;
              border-bottom: 1px solid var(--line); }
.platform a:hover { color: var(--moss-lift); border-color: var(--moss-lift); }

.proof { margin-top: 2.5rem; display: grid; grid-template-columns: 1.5fr 1fr; gap: 2.5rem;
         align-items: center; background: var(--surface); border: 1px solid var(--line);
         border-radius: var(--radius-lg); padding: 2rem 2.2rem; }
.proof blockquote { margin: .9rem 0 1.4rem; font-family: var(--display); font-weight: 400;
                    font-size: 1.3rem; line-height: 1.45; letter-spacing: -.02em; }
.proof-who { display: flex; align-items: center; gap: .8rem; font-size: .84rem; }
.proof-who .mono { font-size: .72rem; color: var(--faint); }
.avatar-lg { width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;
             background: var(--surface-3); color: var(--muted); font-family: var(--mono);
             font-size: .8rem; font-weight: 600; flex: none; }
.proof-stat { text-align: center; padding-left: 2rem; border-left: 1px solid var(--line); }
.proof-stat .lab { font-size: .64rem; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); }
.proof-stat .big { font-family: var(--display); font-size: 2.4rem; font-weight: 800;
                   letter-spacing: -.04em; line-height: 1.1; }
.proof-stat .big .u { font-size: .8rem; font-family: var(--body); font-weight: 500;
                      color: var(--muted); margin-left: .35rem; letter-spacing: 0; }
.proof-stat .big.hi { color: var(--moss-lift); }
.proof-stat .arrow { color: var(--signal); font-size: 1.1rem; margin: .1rem 0; }

/* faq — accordion; only one open, because scanning beats reading here */
.qa { border-bottom: 1px solid var(--line); }
.qa summary {
  list-style: none; cursor: pointer; padding: 1.15rem 0; display: flex;
  justify-content: space-between; gap: 1.5rem; align-items: center;
  font-family: var(--display); font-weight: 600; font-size: 1.05rem; letter-spacing: -.02em;
}
.qa summary::-webkit-details-marker { display: none; }
.qa summary::after { content: '+'; color: var(--signal); font-size: 1.4rem; font-weight: 400;
                     transition: transform .3s var(--ease); flex: none; line-height: 1; }
.qa[open] summary::after { transform: rotate(45deg); }
/* The reset sets `a { color: inherit }`, which is right for a nav item or a
   card whose whole surface is the target. A link inside running prose has to
   look like one, or it is furniture. */
.qa .ans a, .prose a, .deliv li a, .docfacts dd a, p.muted a {
  color: var(--moss-lift);
  border-bottom: 1px solid color-mix(in srgb, var(--moss-lift) 40%, transparent);
}
.qa .ans a:hover, .prose a:hover, .deliv li a:hover,
.docfacts dd a:hover, p.muted a:hover { border-bottom-color: currentColor; }

.qa .ans { padding: 0 0 1.3rem; color: var(--muted); font-size: .92rem; max-width: 44rem;
           animation: fadeDown .32s var(--ease); }
@keyframes fadeDown { from { opacity: 0; transform: translateY(-6px); } }

/* cta */
.cta { text-align: center; padding: 6.5rem 0; position: relative; overflow: hidden; }
.cta::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(60% 100% at 50% 100%, color-mix(in srgb, var(--moss) 22%, transparent), transparent 70%);
}
.cta-in { position: relative; z-index: 1; }
.cta h2 { font-size: clamp(2rem, 4.4vw, 3.2rem); margin-bottom: 1rem; }

/* The footer closes the document, so it gets a ground of its own rather than
   trailing off into the page mesh — a thin glass base and a lit rule across
   the top, the same pair the licence strip uses at the other end. */
footer {
  position: relative;
  border-top: 1px solid var(--glass-line);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  box-shadow: inset 0 1px 0 var(--glass-line);
  padding: 2.5rem 0; font-size: .82rem; color: var(--muted);
}
footer::before {
  content: ''; position: absolute; inset: -1px 0 auto 0; height: 2px;
  background: linear-gradient(90deg, var(--mesh-1), var(--mesh-2) 45%,
              var(--mesh-3) 72%, transparent);
  opacity: .55;
}
.foot-in { display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; align-items: center; }

@media (max-width: 980px) {
  .hero-in, .cover { grid-template-columns: 1fr; }
  .steps, .portals, .std-grid { grid-template-columns: 1fr; }
  .proof { grid-template-columns: 1fr; gap: 1.6rem; padding: 1.5rem; }
  .proof-stat { padding-left: 0; border-left: none; border-top: 1px solid var(--line); padding-top: 1.4rem; }
  .deliverables { grid-template-columns: 1fr; }
  .nav-links { display: none; }
  .hero { padding-top: 7rem; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Multi-page site: brand lockup, page heads, guides, prose, mobile nav
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the logo ────────────────────────────────────────────────────────────
   Two files rather than one: the wordmark's "CARE" and "PEST CONTROL" are a
   mid grey that disappears into a navy ground, so the dark variant remaps
   just those neutrals and leaves the blue shield alone. Both are keyed off
   the company's own artwork, so the honeycomb texture survives.
   Sized by height — the aspect ratio comes from the width/height attributes,
   which also stops the header reflowing while the image loads. */
.brand-logo { display: inline-flex; align-items: center; flex: none; }
.brand-logo img { height: 38px; width: auto; display: block; }
.foot-logo img { height: 30px; }
/* display:block — as an inline image it sat on the text baseline and the
   line beneath it rode up into the wordmark. */
.hero-logo { display: block; height: 48px !important; width: auto; margin-bottom: 1.2rem; }

/* `.brand-logo img` is (0,1,1) and beat a bare `.logo-dark` at (0,1,0), so
   both variants rendered at once and the wordmark sat on top of itself.
   Matching element+class on both sides settles it. */
img.logo-dark  { display: none; }
@media (prefers-color-scheme: dark) {
  img.logo-light { display: none; }
  img.logo-dark  { display: block; }
}
[data-theme="dark"]  img.logo-light { display: none; }
[data-theme="dark"]  img.logo-dark  { display: block; }
[data-theme="light"] img.logo-light { display: block; }
[data-theme="light"] img.logo-dark  { display: none; }

/* ── nav ─────────────────────────────────────────────────────────────────── */
.nav-links a.on { color: var(--text); }
.nav-links a.on::after { right: 0; }
.nav-burger { display: none; }

@media (max-width: 940px) {
  .nav-burger { display: grid; }
  .nav-links {
    position: absolute; inset: 100% 0 auto; flex-direction: column; gap: 0;
    background: var(--surface); border-block: 1px solid var(--line);
    padding: .5rem 0; display: none;
  }
  .nav.open .nav-links { display: flex; }
  .nav-links a { padding: .85rem 1.4rem; min-height: 44px; display: flex; align-items: center; }
  .nav-links a::after { display: none; }
  .nav-cta .btn-ghost { display: none; }
}
@media (max-width: 520px) {
  .brand-logo img { height: 30px; }
  .nav-cta .btn-primary { display: none; }
}

/* ── page head (every page except home) ──────────────────────────────────── */
.page-head { padding: 8.5rem 0 3.2rem; border-bottom: 1px solid var(--line);
             background: var(--surface-2); }
.page-head h1 { font-size: clamp(2rem, 4.6vw, 3.1rem); margin: .8rem 0 1rem; max-width: 20ch; }
.page-head .lede { color: var(--muted); font-size: 1.06rem; max-width: 58ch; }
.page-head .byline { font-size: .74rem; color: var(--muted); margin-top: 1.2rem;
                     letter-spacing: .04em; }

/* ── hero credentials card, in place of the sample receipt ───────────────── */
.hero-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.7rem 1.6rem; box-shadow: var(--shadow-lg);
}
.hero-card-line { font-size: .82rem; color: var(--muted); margin-bottom: 1.1rem; }
.hero-card-creds { display: grid; gap: .55rem; }
.hero-card-creds div { display: flex; justify-content: space-between; gap: 1rem;
                       font-size: .8rem; padding-bottom: .55rem;
                       border-bottom: 1px solid var(--line-soft); }
.hero-card-creds div:last-child { border-bottom: 0; }
.hero-card-creds .n { font-family: var(--mono); color: var(--moss-lift); flex: none; }
.hero-card-foot { margin-top: 1.3rem; padding-top: 1.1rem; border-top: 1px solid var(--line);
                  display: flex; flex-direction: column; gap: .3rem; }
.hero-card-foot .big { font-size: 1.12rem; font-weight: 600; }
.hero-card-foot a { color: var(--text); }

/* ── two-column blocks ───────────────────────────────────────────────────── */
.two-col { display: grid; grid-template-columns: 1.05fr .95fr; gap: 2.4rem; align-items: start; }
@media (max-width: 860px) { .two-col { grid-template-columns: 1fr; gap: 1.6rem; } }

.motto { font-size: 1.1rem; line-height: 1.6; margin: .9rem 0 0; font-family: var(--display);
         font-style: italic; color: var(--text); border: 0; padding: 0; }

/* ── clients, grouped ────────────────────────────────────────────────────── */
.sector { margin-bottom: 2.2rem; }
.sector h2 { font-size: 1.05rem; margin-bottom: .8rem; }
.sector .count { font-family: var(--mono); font-size: .72rem; color: var(--muted); }

/* ── guides ──────────────────────────────────────────────────────────────── */
.guide-grid { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.guide-card {
  display: flex; flex-direction: column; gap: .55rem; height: 100%;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.5rem 1.4rem; transition: border-color .25s, transform .25s var(--ease);
}
.guide-card:hover { border-color: var(--moss); transform: translateY(-3px); }
.guide-card h3 { font-size: 1.08rem; }
.guide-card p  { font-size: .88rem; color: var(--muted); }
.guide-card .more { margin-top: auto; padding-top: .7rem; font-size: .82rem;
                    color: var(--moss-lift); font-weight: 500; }

/* ── long-form ───────────────────────────────────────────────────────────── */
.wrap.narrow { max-width: 44rem; }
.prose { padding: 3.4rem 0 4rem; font-size: 1.03rem; }
.prose h2 { font-size: 1.42rem; margin: 2.6rem 0 .8rem; }
.prose h3 { font-size: 1.08rem; margin: 1.8rem 0 .5rem; }
.prose p  { margin-bottom: 1.05rem; color: var(--muted); }
.prose p  > em { color: var(--text); font-style: italic; }
.prose ul { margin: 0 0 1.3rem 1.1rem; display: grid; gap: .45rem; }
.prose li { color: var(--muted); line-height: 1.68; }
.prose li::marker { color: var(--moss-lift); }
.prose b  { color: var(--text); font-weight: 600; }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.6rem 0 1.4rem; }

/* ── closing band ────────────────────────────────────────────────────────── */
.cta-band { background: var(--surface-2); border-top: 1px solid var(--line); }

/* ── hero card sizing ───────────────────────────────────────────────────── */
/* The card carries a licence number and a phone number, both of which are
   worse for breaking. Keep them on one line and let the grid give the column
   the room rather than wrapping "1800 120 009090" in half. */
.hero-card-creds div { align-items: baseline; }
.hero-card-creds span:first-child { min-width: 0; }
.hero-card-foot .big, .hero-card-creds .n { white-space: nowrap; }

@media (max-width: 980px) {
  .hero-in { grid-template-columns: 1fr; gap: 2.4rem; }
  .hero-card { max-width: 30rem; }
}

/* ── the licence strip ──────────────────────────────────────────────────── */
/* One credential centred in a wide band reads as an orphan. Left-align it
   against the same gutter as everything else and let it grow as licences are
   renewed and reappear. */
.strip-in { justify-content: flex-start; gap: 1.4rem; flex-wrap: wrap; }
.strip .co { font-family: var(--mono); font-size: .84rem; font-weight: 500;
             letter-spacing: .01em; }
/* The strip runs straight across the page mesh with no band of its own, so it
   gets a thin glass rail to stand on — enough of a ground for the mono type,
   and a lit hairline top and bottom that carries the chapter change. Its
   label was --faint; on a lit ground that is under the bar, so --muted. */
.strip {
  background: color-mix(in srgb, var(--surface) 62%, transparent);
  border-block: 1px solid var(--glass-line);
  box-shadow: inset 0 1px 0 var(--glass-line);
}
.strip .lab { color: var(--muted); }
/* The strip only ever names what is in force. The way to the lapsed marks —
   and to the scans of all of them — is this link, not a longer strip. */
.strip-link { margin-left: auto; font-size: .78rem; color: var(--moss-lift);
              white-space: nowrap; border-bottom: 1px solid transparent; }
.strip-link:hover { border-bottom-color: currentColor; }
@media (max-width: 720px) { .strip-link { margin-left: 0; } }

/* ── client marks ─────────────────────────────────────────────────────────
   The artwork came off the company profile and sits on white, so every tile
   carries its own light plate in both themes rather than being keyed out —
   a dozen brands' logos each need their own background treatment to key
   cleanly, and getting one wrong looks worse than a consistent plate.
   Muted at rest so a wall of thirty-six reads as texture, full colour on
   hover for the one somebody is actually looking at. */
.logo-wall, .clients {
  display: grid; gap: .7rem;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
}
/* ── the white photo plates ───────────────────────────────────────────────
   A client mark, a certificate scan and a photographed document are all
   artwork on white, so their plate is #fff in both themes. Which means the
   type in and under them is on white in both themes too, and the dark
   column's greys do not hold there: --faint #7A83B8 on white is 3.6:1.

   So a white plate re-points the three type tokens to PALETTE's *light*
   column for the length of the element. Same move the record plate makes in
   the other direction, same reason: one override rather than forty, and no
   chance of a caption inside a white box reading a dark-theme value. */
.logo-tile, .cert-shot, .doc-shot {
  --text: #0A1030; --muted: #454E80; --faint: #5C6598; --line-soft: #E4E9FB;
  --moss: #2647D8; --moss-lift: #1B3BFF; --signal: #8A5A06;
  color: var(--text);
}
.logo-tile {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm);
  height: 84px; display: grid; place-items: center; padding: .8rem 1rem;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .5);
  transition: border-color .2s var(--ease), transform .2s var(--ease),
              box-shadow .2s var(--ease);
}
.logo-tile img {
  max-height: 48px; max-width: 100%; width: auto; object-fit: contain;
  /* Was saturate(.15) opacity(.72), the fashionable treatment, and it made
     half of them unreadable — a client wall nobody can read is decoration
     where the names were the point. */
  filter: saturate(.92); opacity: .94; transition: opacity .2s var(--ease);
}
.logo-tile:hover { border-color: var(--hue, var(--moss)); transform: translateY(-2px);
                   box-shadow: 0 14px 32px -22px var(--glow); }
.logo-tile:hover img { filter: none; opacity: 1; }
@media (prefers-reduced-motion: reduce) { .logo-tile { transition: none; } }

/* ── certificates ─────────────────────────────────────────────────────── */
/* A scan of a certificate is a stronger claim than a sentence about one, so a
   lapsed document is flagged on the sheet itself. The flag sits on the plate
   over the top edge of the artwork rather than in the caption, so a reader
   skimming three thumbnails, or screenshotting one, takes the status with
   them. Held to the corner: every one of these sheets has printing near the
   top and there is no free corner common to all three. */
.cert-shots { display: grid; gap: 1rem;
              grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.cert-shot { background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
             padding: .7rem; text-align: center; position: relative; }
.cert-shot img { width: 100%; height: auto; border-radius: 4px; display: block; }
.cert-shot figcaption { font-family: var(--mono); font-size: .68rem; letter-spacing: .06em;
                        text-transform: uppercase; color: var(--faint); margin-top: .6rem; }
.cert-valid { display: block; margin-top: .3rem; letter-spacing: .04em;
              text-transform: none; font-size: .7rem; color: var(--muted); }

.cert-flag {
  position: absolute; top: .35rem; left: .35rem; z-index: 1;
  font-family: var(--mono); font-size: .58rem; letter-spacing: .14em;
  text-transform: uppercase; padding: .26rem .55rem; border-radius: 4px;
  background: var(--moss); color: #fff; box-shadow: 0 1px 6px rgb(0 0 0 / .3);
}
.cert-shot.is-lapsed { border-color: var(--signal); border-style: dashed; }
.cert-shot.is-lapsed .cert-flag { background: var(--signal); color: #fff; }
/* Desaturated so a lapsed sheet reads as an archive document even before the
   flag is parsed. Not so far that the certificate becomes unreadable — the
   point is to publish it, not to hide it in plain sight. */
.cert-shot.is-lapsed img { filter: grayscale(.65) contrast(.96); opacity: .9; }
.cert-shot.is-lapsed:hover img { filter: none; opacity: 1; }
.cert-shot.is-lapsed .cert-valid { color: var(--signal-ink); }

/* ── document detail, field by field ──────────────────────────────────── */
/* The certificates carry more than a number, and an auditor asking "who
   issued it and under what rule" should not have to squint at a JPEG. */
.docfacts { display: grid; gap: 0; margin: .9rem 0 0; }
.docfacts div { display: grid; grid-template-columns: 11rem 1fr; gap: 1rem;
                padding: .6rem 0; border-bottom: 1px solid var(--line-soft); }
.docfacts div:last-child { border-bottom: 0; }
.docfacts dt { font-family: var(--mono); font-size: .64rem; letter-spacing: .1em;
               text-transform: uppercase; color: var(--faint); padding-top: .14rem; }
.docfacts dd { margin: 0; font-size: .88rem; color: var(--muted); line-height: 1.6; }
.docfacts dd b { color: var(--text); font-weight: 600; }
@media (max-width: 640px) {
  .docfacts div { grid-template-columns: 1fr; gap: .2rem; }
}

/* ── a photographed document shown for what it is ─────────────────────── */
.doc-shot { background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
            padding: .7rem; margin: 0; }
.doc-shot img { width: 100%; height: auto; border-radius: 4px; display: block; }
.doc-shot figcaption { font-size: .78rem; color: var(--faint); margin-top: .7rem;
                       line-height: 1.55; }

/* Six nav links plus two buttons is tight before the burger takes over. */
@media (max-width: 1120px) { .nav-links { gap: 1.15rem; } }


/* ══════════════════════════════════════════════════════════════════════════
   THE SCROLL LAYER
   --------------------------------------------------------------------------
   The brief from the account holder was "more colours, more animation, not a
   classic template". The counter-pressure is that this company sells
   evidentiary rigour — licences, batch numbers, a code read back by your own
   site contact — and motion that reads as ornament argues against the
   product. So the rule applied throughout below is that an effect has to look
   *engineered*: an instrument panel or a technical document, never a carousel.

   Three ideas carry it.

     1. The page is an indexed document. Each band is a chapter and carries one
        hue, shown in its tab rule, its wash and its numerals. The gold stays
        constant — every eyebrow on the site is still --signal — so the hues
        read as an index rather than as confetti. One attribute, data-hue,
        recolours a whole band.

     2. The rhythm is deliberately uneven. Five identical padded bands was the
        actual complaint. There are now short data bands, a tall asymmetric
        one, a full-bleed marquee and a plate that stays dark in both themes.

     3. Motion is scroll-linked where the platform allows it and merely
        entrance-based where it does not. Every scroll-driven rule lives inside
        @supports (animation-timeline: …) and the base CSS is authored as the
        *finished* state, so Safari and Firefox get a correct static page and
        Chromium gets the parallax on top. Nothing is ever revealed only by a
        scroll-driven animation.

   The invariant that outranks all of it: no content is ever hidden by
   something that can fail. Reveal opacity is gated on .js (see app.css), the
   observer has a sweep behind it, and every rule here has a print and a
   reduced-motion override that lands on "visible, static, correct".
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the chapter hues ─────────────────────────────────────────────────────
   A band names its chapter once. Everything inside reads --hue / --hue-lift
   and never a colour, which is why a new section is two lines and why nothing
   here can drift away from the palette block at the top of the file.

   With the mesh in play a chapter now moves the *ground* as well as the
   accent: --mesh-a and --mesh-b are the two stops its own mesh is drawn from,
   so scrolling from the figures band into services is a shift from cyan-over-
   blue to blue-over-violet rather than the same wash in a different tint.
   Ember and gold, which have no stop of their own in the contract, borrow
   their accent as the near stop and take a contract stop as the far one — so
   even those two bands are anchored in the same three colours. */
/* Re-declared per chapter so it resolves against that chapter's --hue rather
   than the one frozen on <body>. See the note on --glow above. */
[data-hue] { --glow: color-mix(in srgb, var(--hue) 42%, transparent); }

[data-hue="brand"] { --hue: var(--moss);   --hue-lift: var(--moss-lift);
                     --mesh-a: var(--mesh-1); --mesh-b: var(--mesh-2); }
[data-hue="aqua"]  { --hue: var(--aqua);   --hue-lift: var(--aqua-lift);
                     --mesh-a: var(--mesh-3); --mesh-b: var(--mesh-1); }
[data-hue="iris"]  { --hue: var(--iris);   --hue-lift: var(--iris-lift);
                     --mesh-a: var(--mesh-2); --mesh-b: var(--mesh-1); }
/* Gold and ember are the two chapters with no stop of their own in the
   contract, and both are far brighter than the three blues — #FFC53D is
   L .61 against #1B3BFF at .11. Left at full --b1 they wash their band to a
   warm grey. The near stop is therefore scaled rather than given a second set
   of per-theme alphas, so there is still one knob.

   Only on dark. On light the same two are the *darkest* stops available, not
   the brightest — #8A5A06 and #B4470F — so pulling them back would leave the
   licences page and the client wall as the two colourless chapters on the
   site. --hot is 1 there. */
[data-hue="ember"] { --hue: var(--ember);  --hue-lift: var(--ember-lift);
                     --mesh-a: var(--ember);  --mesh-b: var(--mesh-2);
                     --bA: calc(var(--b1) * var(--hot)); }
[data-hue="gold"]  { --hue: var(--signal); --hue-lift: var(--gold-lift);
                     --mesh-a: var(--signal); --mesh-b: var(--mesh-1);
                     --bA: calc(var(--b1) * var(--hot)); }

/* --signal is a mark colour, not a type colour. On dark it happens to work as
   both, but the eyebrow also has to read on the light theme's mesh at its
   darkest, where #8A5A06 lands at 3.5:1. --signal keeps its value for rules
   and fills — app.css depends on it — and the type takes --gold-lift, which
   is the same amber pushed until it clears 4.5:1 on either ground. */
body.site .eyebrow { color: var(--gold-lift); }

/* ── band furniture ──────────────────────────────────────────────────────── */
.band { position: relative; isolation: isolate; }

/* The hairline that opens a chapter. Drawn as a gradient rather than a border
   so it can fade out rather than stopping dead at the gutter, and it starts on
   --hue-lift rather than --hue: at 1px on a lit ground the darker step of a
   pair is 2.6:1 and simply disappears. A one-pixel glow under it does the rest
   — this is the "accent edge" the whole skin is built out of. */
.band > .band-rule {
  position: absolute; inset: 0 0 auto; height: 1px; z-index: 2;
  background: linear-gradient(90deg,
    var(--hue-lift) 0%, color-mix(in srgb, var(--hue) 55%, transparent) 34%, transparent 68%);
  box-shadow: 0 0 12px -2px var(--glow);
  transform-origin: left;
}

/* The band's own mesh. Three radials off the chapter's two stops plus the
   constant cyan lift, sitting over the band's background and under its
   content. Deliberately positioned so no two peaks land on the same pixel:
   near stop at the top-left shoulder, far stop at the bottom-right, lift high
   and right of centre.

   Static. The only drift on the site is in the hero and the closing band,
   where there is no small type to sit still against. */
.band > .band-mesh {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    /* The two edge washes. A band's vertical padding is never less than
       3.8rem, so a gradient that has faded out by 64px lives entirely in the
       gutter above the first line of type — which is the point: it is colour
       put exactly where no contrast budget is being spent, and it is what
       makes the join between two chapters read as a change of light rather
       than a change of tint. */
    linear-gradient(180deg,
      color-mix(in srgb, var(--mesh-a, var(--mesh-1)) var(--b2), transparent), transparent 64px),
    linear-gradient(0deg,
      color-mix(in srgb, var(--mesh-b, var(--mesh-2)) var(--b2), transparent), transparent 48px),
    radial-gradient(66% 78% at 2% -6%,
      color-mix(in srgb, var(--mesh-a, var(--mesh-1)) var(--bA), transparent), transparent 64%),
    radial-gradient(56% 68% at 100% 104%,
      color-mix(in srgb, var(--mesh-b, var(--mesh-2)) var(--b2), transparent), transparent 66%),
    radial-gradient(34% 44% at 72% 4%,
      color-mix(in srgb, var(--mesh-3) var(--b3), transparent), transparent 70%);
}
/* Alternate bands mirror their mesh, so a run of five sections does not read
   as the same wash stamped five times. A transform on an empty absolutely
   positioned div — no layout, and it is rasterised once. */
.band:nth-of-type(even) > .band-mesh { transform: scaleX(-1); }

/* A tinted band is a plane raised off the ground, not an opaque lid: at 86%
   the page mesh still reads through it, which is what keeps the join between
   two bands from looking like a seam. */
.sec-tint { background: color-mix(in srgb, var(--surface-2) var(--tint-veil), transparent); }

/* The chapter tab — a mono label behind a short hue rule, the way a section
   divider is tabbed in a printed audit file. */
.tab {
  display: flex; align-items: center; gap: .75rem; margin-bottom: 1.5rem;
  font-family: var(--mono); font-size: .62rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--hue-lift);
}
.tab::before {
  content: ''; width: 2.4rem; height: 2px; flex: none; border-radius: 2px;
  background: linear-gradient(90deg, var(--hue-lift), var(--hue));
  box-shadow: 0 0 10px -1px var(--glow);
  transform-origin: left;
}
/* --faint has no headroom left once there is colour under it — see the
   legibility budget. The chapter index sits straight on the band mesh, so it
   takes --muted. Same for every other label that floats free of a card. */
.tab .ix { color: var(--muted); letter-spacing: .14em; }

/* Grain. A single 140px noise tile, no blend mode and no animation, scoped to
   the two surfaces that want atmosphere rather than laid over the whole page
   — a fixed full-viewport blended layer is the kind of thing that costs a
   mid-range phone its frame budget for no argument in return. */
.grain::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: var(--grain-o);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ══ hero ════════════════════════════════════════════════════════════════ */
.hero { padding: 10.5rem 0 6rem; --hue: var(--moss); --hue-lift: var(--moss-lift); }
/* The hero is where the mesh is allowed to be loudest: nothing here is smaller
   than the lede, and the credentials card carries its own ground. All three
   contract stops appear, the blue and violet crossing behind the headline and
   the cyan lifting the far shoulder. */
.hero-mesh {
  position: absolute; inset: -20% -10% 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(44% 50% at 78%  8%,
      color-mix(in srgb, var(--mesh-1) calc(var(--b1) + 10%), transparent), transparent 68%),
    radial-gradient(38% 44% at 12%  0%,
      color-mix(in srgb, var(--mesh-2) calc(var(--b2) + 8%), transparent), transparent 66%),
    radial-gradient(46% 52% at 58% 98%,
      color-mix(in srgb, var(--mesh-3) var(--b3), transparent), transparent 70%);
}
/* Two blobs at different speeds rather than one, so the hero never settles
   into a shape you can point at. Transform only — no layout, no paint — and
   site.js parks both the moment the hero leaves the viewport, so the drift
   costs a compositor tick for the first screen and nothing after that. */
.hero-glow { animation: drift 26s ease-in-out infinite alternate; }
.hero-glow.two {
  top: auto; bottom: -520px; right: auto; left: -260px;
  width: 640px; height: 640px;
  animation: drift2 34s ease-in-out infinite alternate;
}
@keyframes drift2 { to { transform: translate(90px, -70px) scale(1.16); } }

/* The measured ground line. 56px squares was a grid; this adds a heavier rule
   every fourth one, which is what makes it read as drawn rather than as a
   texture pack. */
.hero-grid {
  background-image:
    linear-gradient(var(--glass-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--glass-line) 1px, transparent 1px),
    linear-gradient(color-mix(in srgb, var(--mesh-3) 30%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--mesh-3) 30%, transparent) 1px, transparent 1px);
  background-size: 56px 56px, 56px 56px, 224px 224px, 224px 224px;
}

/* Entrance. Gated on .js, fill-mode both, and the keyframe *ends* at the
   resting state — so a browser that refuses the animation, a print, or the
   reduced-motion clamp in app.css all land on "visible and in place". */
@keyframes rise { from { opacity: 0; transform: translateY(16px); } }
.js .hero [data-rise] { animation: rise .85s var(--ease) both;
                        animation-delay: calc(var(--i, 0) * 95ms); }

/* The headline arrives as a wipe rather than a fade — a clip-path on the
   block, not on each line, because the number of lines depends on the
   viewport and anything per-line would have to be measured in JS. */
@keyframes wipe {
  from { opacity: 0; clip-path: inset(0 0 102% 0); transform: translateY(10px); }
  60%  { opacity: 1; }
  to   { opacity: 1; clip-path: inset(0 0 -20% 0); transform: none; }
}
.js .hero h1 { animation: wipe 1.15s var(--ease) both; animation-delay: 120ms; }

/* The underline the previous pass had to delete. It was an absolutely
   positioned bar against an inline span, which draws under a fragment of the
   last line once the span wraps. As a background image with box-decoration-
   break it follows every line box it is on, and it can still animate: the
   width is background-size, which is not a layout property. */
/* The drawn underline is gone too — client call, 17 Aug: the highlight is
   the colour alone now. Keep .hl painting nothing here so no later block
   resurrects a rule under a wrapped line. */

.hero p.lede { max-width: 34rem; }
.hero-note { color: var(--muted); }
.hero-note::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--moss-lift);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--moss-lift) 60%, transparent);
  animation: ping 2.8s var(--ease) infinite;
}
@keyframes ping {
  0%      { box-shadow: 0 0 0 0 color-mix(in srgb, var(--moss-lift) 55%, transparent); }
  70%,to  { box-shadow: 0 0 0 9px transparent; }
}


/* ══════════════════════════════════════════════════════════════════════════
   GLASS
   --------------------------------------------------------------------------
   Two treatments, and the difference between them is the whole performance
   story of this file.

   `backdrop-filter` is the expensive one. It forces the element onto its own
   composited layer, and it forces the browser to read back and blur whatever
   is painted behind it — on every frame in which either moves. On a mid-range
   Android that is the single most costly thing in this palette, and it does
   not degrade gracefully: four of them on a scrolling list is a visibly
   dropped frame rate, not a slightly slower page.

   So it is spent, deliberately, on three surfaces and no others:

     .nav.stuck   content scrolls underneath it. This is the case the effect
                  exists for, it is one element, and it is already promoted
                  because the header is fixed.
     .hero-card   the one object floating in the brightest part of the mesh,
                  above the fold, on the page's most important screen. One
                  element, one page.
     .fig-grid    the figures band, blurred once as a *single* element with
                  four tiles inside it rather than four times over. Off-screen
                  after the first scroll and never composited again.

   Everything the brief warns about is excluded by name: the 36-tile marquee,
   the client wall, the chemical schedule, the credential list, every card in
   every grid. Those get the second treatment.

   The second treatment is glass without the blur — an opaque --surface, a
   hue wash falling from the top edge, a bright hairline along that edge and a
   soft hue bleed outside the box. It costs one extra background layer and one
   box-shadow, and at rest it is indistinguishable from the expensive version
   unless you go looking for the blur.

   Opacity is the other half of the reason. --faint (#7A83B8) is 4.6:1 on
   --surface-2 and has nothing left; a card at 90% --surface over the mesh
   drops it to 4.3:1. Cards are therefore opaque and the three blurred panels
   are the ones that carry no --faint text. See the legibility budget.
   ══════════════════════════════════════════════════════════════════════════ */

/* The bulk treatment. Applied to every card-like surface on the site through
   one list, so a new card cannot quietly miss it. */
.card, .svc-row, .cred, .step, .std, .deliv, .portal, .guide-card,
.proof, .schedule-wrap, .hero-card, .fig-grid {
  position: relative;
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--hue) var(--hue-tint), transparent), transparent 38%),
    var(--surface);
  border-color: var(--line);
  box-shadow: inset 0 1px 0 var(--glass-line);
}
/* The hue bleed. Sits outside the box rather than under the text, so it can be
   as strong as it likes without spending any of the contrast budget. */
.grid-cards > .card:hover, .svc-row:hover, .cred:hover, .step:hover, .std:hover,
.deliv:hover, .portal:hover, .guide-card:hover {
  box-shadow: inset 0 1px 0 var(--glass-line),
              0 18px 44px -28px var(--glow),
              0 0 0 1px color-mix(in srgb, var(--hue) 30%, transparent);
}

/* ── the three that earn the blur ────────────────────────────────────────
   Each is translucent over --surface at --glass-veil, so the mesh reads
   through it as a hue shift while the ground under the type stays bounded.
   The @supports block below restores an opaque ground where backdrop-filter
   is unavailable — Firefox before 103, and anything with the effect switched
   off for battery. */
.nav.stuck, .hero-card, .fig-grid {
  background:
    linear-gradient(180deg, var(--glass), transparent 46%),
    color-mix(in srgb, var(--surface) var(--glass-veil), transparent);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.35);
          backdrop-filter: blur(var(--glass-blur)) saturate(1.35);
  border: 1px solid var(--glass-line);
  box-shadow: inset 0 1px 0 var(--glass-line);
}
@supports not (backdrop-filter: blur(1px)) {
  .nav.stuck, .hero-card, .fig-grid {
    background:
      linear-gradient(180deg, var(--glass), transparent 46%),
      var(--surface);
  }
}
/* The nav is the one glass surface whose backdrop is not known in advance.
   The hero card sits on the hero mesh and the figures panel sits on its own
   band, both bounded; the header crosses the whole document — the mesh, the
   tinted bands, the record plate that stays dark in a light page, and a wall
   of thirty-six white logo tiles.

   At the --glass-veil the other two use, that is not a header, it is a
   contrast bug in two directions at once: on dark, 55% of #0B0F2A over a
   white tile is #797B8A and the nav links land at 2.0:1; on light, 55% of
   white over the record plate is #9397B1 and they land at 2.75:1. Measured,
   not guessed — both were on screen before this rule existed.

   So the header takes its own veil, high enough that the worst backdrop it
   can ever cross still clears AA: 86% puts the white tile at 6.1:1 and the
   plate at 6.0:1. The remaining 14% is still a real blurred backdrop — a
   header that has to stay readable over arbitrary content cannot be more
   transparent than that, and pretending otherwise is how glass navs end up
   unreadable exactly once per site. */
.nav.stuck {
  background:
    linear-gradient(180deg, var(--glass), transparent 46%),
    color-mix(in srgb, var(--surface) var(--nav-veil), transparent);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
          backdrop-filter: blur(12px) saturate(1.4);
  border: 0; border-bottom: 1px solid var(--glass-line);
  box-shadow: 0 10px 30px -22px rgb(0 0 0 / .6);
}
@supports not (backdrop-filter: blur(1px)) {
  .nav.stuck {
    background: linear-gradient(180deg, var(--glass), transparent 46%), var(--surface);
  }
}

/* The credentials card sits forward of the mesh, with a lit edge on top so it
   reads as the one solid object in a soft field. */
.hero-card {
  overflow: hidden;
  box-shadow: inset 0 1px 0 var(--glass-line),
              0 34px 80px -44px color-mix(in srgb, var(--mesh-2) 80%, transparent),
              0 2px 30px -14px color-mix(in srgb, var(--mesh-1) 45%, transparent);
}
.hero-card::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 2px; z-index: 1;
  background: linear-gradient(90deg, var(--mesh-1), var(--mesh-2), var(--mesh-3), transparent);
}
.js .hero-card { animation: rise 1s var(--ease) both; animation-delay: 320ms; }

/* Luminous focus. The outline in app.css is a hard gold ring; on a lit ground
   it wants a halo behind it or it reads as a sticker. Colour is unchanged —
   gold is the focus colour on both sides of the sign-in — only the glow is
   added, and it is a box-shadow so it costs nothing to paint. */
body.site :focus-visible {
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--signal) 26%, transparent),
              0 0 18px -2px color-mix(in srgb, var(--signal) 55%, transparent);
}

/* ══ the figures band ════════════════════════════════════════════════════
   Four numbers, all of them derived from data that already exists in site.js
   — the state list, the client list, the standing chemical schedule. They are
   written into the markup so they survive no-JS and print, and site.js
   re-derives them on load and complains in the console if the page and the
   data have drifted. Nothing here is a number somebody liked the look of. */
.figures { padding: 3.4rem 0; border-block: 1px solid var(--line);
           background: color-mix(in srgb, var(--surface-2) var(--tint-veil), transparent); }
/* The grid is one glass panel, blurred once, with the four tiles drawn on it
   as transparent cells divided by hairlines. Doing it the other way round —
   four glass tiles — would be four composited layers and four backdrop reads
   for the same picture. */
.fig-grid { display: grid; gap: 0; grid-template-columns: repeat(4, 1fr);
            border-radius: var(--radius); overflow: hidden; }
.fig { background: transparent; padding: 1.5rem 1.4rem; position: relative;
       border-left: 1px solid var(--glass-line); }
.fig:first-child { border-left: 0; }
/* The key is a --faint-weight label sitting on glass, so it takes --muted. */
.fig .k { font-family: var(--mono); font-size: .6rem; letter-spacing: .16em;
          text-transform: uppercase; color: var(--muted); }
.fig .v { font-family: var(--display); font-size: clamp(2.1rem, 3.4vw, 2.9rem);
          font-weight: 700; letter-spacing: -.035em; line-height: 1.05;
          margin: .5rem 0 .1rem; color: var(--hue-lift);
          font-variant-numeric: tabular-nums; }
.fig .v .u { font-family: var(--body); font-size: .8rem; font-weight: 500;
             letter-spacing: 0; color: var(--muted); margin-left: .35rem; }
.fig .d { font-size: .78rem; color: var(--muted); line-height: 1.55; margin-top: .5rem; }

/* The meter is the count drawn rather than restated: exactly --n ticks, so
   twenty states is twenty marks you could stop and count. Ticks come from a
   repeating background rather than twenty spans — same reading, no DOM. */
.meter { height: 10px; margin-top: .9rem; overflow: hidden; }
.meter i {
  display: block; height: 100%; transform-origin: left;
  background-image: linear-gradient(90deg, var(--hue-lift) 0 45%, transparent 45%);
  background-size: calc(100% / var(--n)) 100%;
}
.js .meter i { transform: scaleX(0); transition: transform 1.1s var(--ease) .12s; }
.js .in .meter i, .js .reveal.in .meter i { transform: none; }

/* The tile dividers are hairlines rather than gaps in a background, so they
   have to be re-cut whenever the column count changes. */
@media (max-width: 900px) {
  .fig-grid { grid-template-columns: repeat(2, 1fr); }
  .fig { border-top: 1px solid var(--glass-line); }
  .fig:nth-child(-n+2)  { border-top: 0; }
  .fig:nth-child(odd)   { border-left: 0; }
  .fig:nth-child(even)  { border-left: 1px solid var(--glass-line); }
}
@media (max-width: 460px) {
  .fig-grid { grid-template-columns: 1fr; }
  .fig { border-left: 0; border-top: 1px solid var(--glass-line); }
  .fig:first-child { border-top: 0; }
}

/* ══ services — the asymmetric band ══════════════════════════════════════
   Was a four-up card grid, which is the shape every one of these sites uses.
   Now a narrow heading column that stays put while four wide cards pass it.
   Note: no overflow:hidden on the section — an overflow scroll container is
   the classic way to silently break a sticky descendant. overflow:clip if it
   is ever needed here, which does not create one. */
.svc-band { --hue: var(--moss); --hue-lift: var(--moss-lift); }
.svc-split { display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
             gap: 3.5rem; align-items: start; }
.svc-aside { position: sticky; top: 7rem; }
.svc-list { display: grid; gap: 1rem; }

.svc-row {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: 3.2rem minmax(0, 1fr); gap: 1.3rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 1.7rem 1.7rem 1.6rem;
  transition: border-color .3s var(--ease), transform .3s var(--ease),
              box-shadow .3s var(--ease);
}
/* The hue edge. scaleX rather than width so the draw is a compositor job. */
.svc-row::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 2px;
  background: linear-gradient(90deg, var(--hue), transparent);
  transform: scaleX(1); transform-origin: left;
}
.js .svc-row::before { transform: scaleX(0); transition: transform .8s var(--ease) .1s; }
.js .svc-row.in::before, .js .in .svc-row::before { transform: scaleX(1); }
.svc-row:hover { transform: translateY(-3px); border-color: var(--hue);
                 box-shadow: 0 18px 40px -28px color-mix(in srgb, var(--hue) 90%, transparent); }
.svc-row .svc-ic { width: 46px; height: 46px; border-radius: 12px;
                   background: color-mix(in srgb, var(--hue) 16%, transparent);
                   color: var(--hue-lift); }
.svc-row .svc-ic svg { width: 23px; height: 23px; }
.svc-row h3 { font-size: 1.16rem; margin-bottom: .45rem; }
.svc-row > div:last-child > p { color: var(--muted); font-size: .89rem; line-height: 1.68; }
.svc-row ul { list-style: none; margin: .95rem 0 0; padding: .85rem 0 0;
              border-top: 1px solid var(--line-soft);
              display: grid; gap: .3rem;
              grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.svc-row li { font-size: .79rem; color: var(--faint); display: flex; gap: .5rem; }
.svc-row li::before { content: '·'; color: var(--hue-lift); font-weight: 700; }
/* The ghost numeral. Data-ink at the scale of ornament — it is the card's
   index in the list, which is the one number a card like this can honestly
   carry. */
.svc-row .ghost {
  position: absolute; right: 1.1rem; top: .2rem; z-index: 0;
  font-family: var(--display); font-size: 4.6rem; font-weight: 700;
  letter-spacing: -.05em; line-height: 1; pointer-events: none;
  color: color-mix(in srgb, var(--hue) 13%, transparent);
  transition: color .3s var(--ease), transform .4s var(--ease);
}
.svc-row:hover .ghost { color: color-mix(in srgb, var(--hue) 22%, transparent);
                        transform: translateY(-2px); }
.svc-row > * { position: relative; z-index: 1; }

@media (max-width: 1040px) {
  .svc-split { grid-template-columns: 1fr; gap: 2.2rem; }
  .svc-aside { position: static; }
}
@media (max-width: 560px) {
  .svc-row { grid-template-columns: 1fr; gap: .9rem; padding: 1.4rem 1.3rem; }
  .svc-row .ghost { font-size: 3.4rem; right: .8rem; }
}

/* ══ pillars as a rail ═══════════════════════════════════════════════════
   Three items that genuinely are a set rather than a sequence, so they run
   along one line instead of stacking as cards. The connecting rule is the
   point: it is what says "these three, together". */
.rail-band { --hue: var(--iris); --hue-lift: var(--iris-lift); }
.rail3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem;
         position: relative; padding-top: 2.6rem; }
.rail3::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 2px;
  background: linear-gradient(90deg, var(--hue-lift), var(--hue) 24%,
              color-mix(in srgb, var(--hue) 30%, transparent) 70%, transparent);
  box-shadow: 0 0 14px -3px var(--glow);
  transform-origin: left;
}
.js .rail3::before { transform: scaleX(0); transition: transform 1.2s var(--ease); }
.js .rail3.in::before { transform: scaleX(1); }
.rail3 > div { position: relative; }
.rail3 > div::before {
  content: ''; position: absolute; top: -2.6rem; left: 0;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--ink); border: 2px solid var(--hue);
  transform: translateY(-4px); transition: background .3s, box-shadow .3s;
}
.rail3 > div.hot::before { background: var(--hue);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--hue) 18%, transparent); }
.rail3 .n { font-family: var(--mono); font-size: .66rem; letter-spacing: .16em;
            color: var(--hue-lift); text-transform: uppercase; }
.rail3 h3 { font-size: 1.22rem; margin: .5rem 0 .5rem; }
.rail3 p  { font-size: .89rem; color: var(--muted); line-height: 1.68; }
@media (max-width: 860px) {
  .rail3 { grid-template-columns: 1fr; gap: 2.2rem; padding-top: 0; padding-left: 1.6rem; }
  .rail3::before { inset: 0 auto 0 0; width: 2px; height: auto;
                   background: linear-gradient(180deg, var(--hue-lift), var(--hue) 24%,
                     color-mix(in srgb, var(--hue) 30%, transparent) 70%, transparent);
                   transform-origin: top; }
  .js .rail3::before { transform: scaleY(0); }
  .js .rail3.in::before { transform: scaleY(1); }
  .rail3 > div::before { top: .35rem; left: -1.6rem; transform: none; }
}

/* ══ the record plate ════════════════════════════════════════════════════
   The one real risk in this design: a band that stays dark whichever theme
   the page is in. The argument is that this section *is* the document — the
   receipt and the audit — and a document plate reads as an object rather than
   as a stretch of page. It also gives the light theme somewhere to land.

   The core tokens are re-pointed for the length of this element only. That is
   the same move site.css already makes for --moss at body level: one override
   instead of forty, and no risk of a component inside the plate reading a
   light-theme value against a dark ground. Nothing outside .plate sees it,
   and app.css never sees it at all. */
.plate {
  --glow: color-mix(in srgb, var(--signal) 42%, transparent);
  /* The dark column of PALETTE.md, verbatim. Copying it rather than inventing
     a third set is what stops the plate drifting when the contract moves. */
  --ink: #05071A; --surface: #0B0F2A; --surface-2: #131A3D; --surface-3: #1C2550;
  --line: #2A3468; --line-soft: #1A2148;
  --text: #EEF1FF; --muted: #A9B1DC; --faint: #7A83B8;
  --moss: #3B6BFF; --moss-lift: #7FA5FF; --moss-dim: #1B3BFF;
  --signal: #FFC53D; --alert: #FF6B85; --info: #22D3EE;
  --glass: rgba(255,255,255,.055); --glass-line: rgba(255,255,255,.11);
  --hue: var(--signal); --hue-lift: #FFD470;
  --gold-lift: #FFD470;
  --b1: 22%; --b2: 16%; --b3: 9%;
  --grain-o: .05;
  background: var(--plate); color: var(--text);
  border-block: 1px solid #1E2652;
}
/* The plate carries its own mesh rather than the chapter's, because it is the
   one band that does not sit on the page ground: blue and violet under a low
   gold, the colours of the document it stands in for. */
.plate .band-mesh {
  background:
    radial-gradient(60% 72% at 4% -4%,
      color-mix(in srgb, var(--mesh-1) var(--b1), transparent), transparent 64%),
    radial-gradient(52% 64% at 100% 104%,
      color-mix(in srgb, var(--mesh-2) var(--b2), transparent), transparent 66%),
    radial-gradient(36% 46% at 66% 6%,
      color-mix(in srgb, var(--signal) var(--b3), transparent), transparent 70%);
}
.plate .sec-head p, .plate .muted { color: var(--muted); }
/* The two document panels are the one place on the site where a card is
   genuinely translucent without a blur behind it: the plate ground is fixed
   and dark, so the mesh showing through cannot lift the type off its budget.
   88% --surface, verified at --faint 4.5:1 on the plate's brightest pixel. */
.plate .deliv {
  background:
    linear-gradient(180deg, var(--glass), transparent 44%),
    color-mix(in srgb, var(--surface) 88%, transparent);
  border-color: var(--line);
  box-shadow: inset 0 1px 0 var(--glass-line);
}
.plate .deliv:hover { border-color: var(--signal);
  box-shadow: inset 0 1px 0 var(--glass-line),
              0 20px 48px -30px color-mix(in srgb, var(--signal) 55%, transparent); }
.plate .deliv li::before { color: var(--gold-lift); }
.plate .deliv li a, .plate .qa .ans a { color: var(--gold-lift); }
.plate .deliv-foot, .plate .deliv-note { color: var(--muted); }
/* Ruled like the paper it stands in for — a hairline every 34px behind the
   two cards, at the strength of a watermark. */
.plate .deliverables { position: relative; }
.plate .deliv { position: relative; overflow: hidden; }
.plate .deliv::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: repeating-linear-gradient(180deg,
    transparent 0 33px, color-mix(in srgb, #fff 4%, transparent) 33px 34px);
  mask-image: linear-gradient(180deg, transparent, #000 40%);
}
.plate .deliv > * { position: relative; z-index: 1; }

/* ══ the client marquee ══════════════════════════════════════════════════
   Thirty-six marks in a static grid says "here is a list". Three rows moving
   at three speeds says "there are more of these than fit", which is the
   actual claim and the one a grid cannot make.

   Mechanics: each row holds the same set twice and translates by exactly one
   set plus one gap — calc(-50% - gap/2), because -50% of a two-set track is
   half a gap short of a seam. Rows pause on hover and on keyboard focus, and
   site.js pauses them outright when the band is off screen so three infinite
   animations are not being composited behind the footer. */
.mq-band { --hue: var(--ember); --hue-lift: var(--ember-lift); }
.mq-rows { display: grid; gap: .7rem; }
.mq {
  --gap: .7rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.mq-track { display: flex; gap: var(--gap); width: max-content;
            animation: mq var(--dur, 60s) linear infinite;
            will-change: transform; }
.mq-set   { display: flex; gap: var(--gap); }
.mq.rev .mq-track { animation-direction: reverse; }
@keyframes mq { to { transform: translateX(calc(-50% - var(--gap) / 2)); } }
.mq:hover .mq-track, .mq:focus-within .mq-track,
.mq-band.paused .mq-track { animation-play-state: paused; }

.mq .logo-tile { width: 156px; flex: none; height: 78px; }
.mq .logo-tile:hover { border-color: var(--hue); }
/* Every tile is focusable-adjacent content in a moving strip, so give the
   hover treatment to :focus-within too — a keyboard user gets the same stop. */
.logo-tile { position: relative; overflow: hidden; }
.logo-tile::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 40%,
              color-mix(in srgb, var(--hue, var(--moss)) 22%, transparent) 50%, transparent 60%);
  transform: translateX(-120%);
  transition: transform .6s var(--ease);
}
.logo-tile:hover::after { transform: translateX(120%); }
.logo-tile:hover img { transform: scale(1.045); }
.logo-tile img { transition: opacity .2s var(--ease), transform .3s var(--ease); }

.mq-foot { display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
           margin-top: 1.5rem; font-size: .84rem; color: var(--muted); }
.mq-foot .c { font-family: var(--mono); color: var(--hue-lift); font-weight: 600; }

@media (max-width: 700px) { .mq .logo-tile { width: 128px; height: 68px; } }

/* The section spine (left-gutter tick rail) is gone — client call, 17 Aug.
   The nav hairline carries the chapter hue on every width now. */

/* ══ the nav hairline ════════════════════════════════════════════════════
   Scroll position and current chapter in one 2px rule. --nav-hue is written
   by site.js from whichever band is crossing the header, so the line changes
   colour as the document changes chapter. Pure decoration: if the scroll
   timeline is unavailable it simply never appears. */
.nav::after {
  content: ''; position: absolute; inset: auto 0 -1px 0; height: 2px;
  transform: scaleX(0); transform-origin: left;
  background: linear-gradient(90deg,
    var(--nav-hue, var(--moss)), color-mix(in srgb, var(--nav-hue, var(--moss-lift)) 55%, var(--gold-lift)));
  transition: background .5s linear;
}

/* ══ CTA ═════════════════════════════════════════════════════════════════
   The closing band is the second place the mesh is allowed to move, and for
   the same reason as the hero: the only type here is a heading and two short
   lines of --muted, both of which have room to spare. The light rises from
   below the fold, which is where the page is asking you to go. */
.cta-band { position: relative; overflow: hidden;
            background: color-mix(in srgb, var(--surface-2) var(--tint-veil), transparent); }
.cta-band .band-mesh {
  background:
    radial-gradient(58% 106% at 50% 122%,
      color-mix(in srgb, var(--mesh-1) calc(var(--b1) + 12%), transparent), transparent 66%),
    radial-gradient(34% 66% at 8% -6%,
      color-mix(in srgb, var(--mesh-3) var(--b3), transparent), transparent 70%),
    radial-gradient(34% 66% at 92% -6%,
      color-mix(in srgb, var(--mesh-2) var(--b2), transparent), transparent 70%);
}
/* Two of the three stops drift, slowly and out of phase, so the light under
   the closing line never settles. Transform only, one composited layer each,
   and site.js parks them the moment the band leaves the viewport — which for
   the last section of a page is most of the visit. */
/* The drift animates `translate` and `scale`, not `transform`. Both are
   composited exactly like a transform, and keeping them separate means the
   mirror on alternate bands (a plain `transform: scaleX(-1)`) survives — and
   that reduced motion can stop the drift without also un-mirroring the band. */
.cta-band .band-mesh { animation: meshDrift 38s ease-in-out infinite alternate; }
@keyframes meshDrift { to { translate: -3% -2%; scale: 1.1; } }
.cta-band.offscreen .band-mesh { animation-play-state: paused; }

/* ── buttons, links, cards: the small stuff ─────────────────────────────── */
/* The primary button is a gradient rather than a flat --moss fill, which is
   the palette's own direction and also settles a contrast problem: white 14px
   semibold on dark --moss #3B6BFF is 4.44:1, six hundredths short of AA.
   mesh-1 → mesh-2 carries white at 6.7:1 and 5.7:1 and is the same two stops
   the page ground is built from. Flagged in the report; the fix belongs in
   PALETTE.md, not here, so this does not change a token. */
.btn-primary {
  position: relative; overflow: hidden;
  background: linear-gradient(120deg, var(--mesh-1), var(--mesh-2));
  color: #fff;
  box-shadow: 0 8px 22px -12px color-mix(in srgb, var(--mesh-1) 90%, transparent),
              inset 0 1px 0 rgb(255 255 255 / .22);
}
.btn-primary:hover {
  background: linear-gradient(120deg, var(--mesh-2), var(--mesh-1));
  box-shadow: 0 14px 34px -14px color-mix(in srgb, var(--mesh-2) 90%, transparent),
              inset 0 1px 0 rgb(255 255 255 / .28);
}
.btn-primary::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 42%, rgba(255,255,255,.28) 50%, transparent 58%);
  transform: translateX(-130%);
  transition: transform .65s var(--ease);
}
.btn-primary:hover::after { transform: translateX(130%); }
/* The ghost button picks up the chapter it is standing in. */
.btn-ghost { border-color: var(--line); }
.btn-ghost:hover { border-color: var(--hue, var(--moss)); color: var(--text);
  background: color-mix(in srgb, var(--hue, var(--moss)) 12%, transparent);
  box-shadow: 0 10px 26px -20px var(--glow); }

.card { transition: border-color .3s var(--ease), transform .3s var(--ease); }
.guide-card:hover, .cred:hover { border-color: var(--hue, var(--moss)); }
.cred { transition: border-color .25s var(--ease), transform .25s var(--ease); }
.cred:hover { transform: translateY(-2px); }
.chip { transition: border-color .2s var(--ease), color .2s var(--ease),
                    background .2s var(--ease), transform .2s var(--ease); }
.chip:hover { border-color: var(--hue, var(--moss)); color: var(--hue-lift, var(--moss-lift));
              transform: translateY(-1px); }
.state { cursor: default; }
.state:hover { transform: translateY(-1px); }
.strip .co { position: relative; }

/* The licence strip earns a live mark: this is the one line on the page that
   asserts present standing, and site.js only ever puts in-force credentials
   in it. Shape and the word both carry it — the dot is not doing the work. */
.strip { position: relative; }
.strip .lab::before {
  content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--signal); margin-right: .5rem; vertical-align: 1px;
  animation: blip 2.4s ease-in-out infinite;
}

/* Page heads on the inner pages get the same chapter treatment as a band, so
   the nine other pages read as the same document rather than as leftovers. */
.page-head { position: relative; overflow: hidden; isolation: isolate;
             background: color-mix(in srgb, var(--surface-2) var(--tint-veil), transparent); }
.page-head::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 2px; z-index: 2;
  background: linear-gradient(90deg, var(--hue-lift), var(--hue) 22%,
              color-mix(in srgb, var(--hue) 40%, transparent) 46%, transparent 78%);
  box-shadow: 0 0 14px -3px var(--glow);
}
.js .page-head h1 { animation: wipe 1s var(--ease) both; animation-delay: 80ms; }
.js .page-head .lede { animation: rise .8s var(--ease) both; animation-delay: 260ms; }
.js .page-head .eyebrow { animation: rise .7s var(--ease) both; }

/* Long-form pages keep their hue as a quiet spine down the left of every
   heading. A guide is a document to read, not a place to put effects. */
.prose h2 { position: relative; padding-left: 1rem; }
.prose h2::before {
  content: ''; position: absolute; left: 0; top: .18em; bottom: .18em; width: 3px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--hue-lift, var(--moss-lift)), var(--hue, var(--moss)));
  box-shadow: 0 0 12px -2px var(--glow);
}
.prose li::marker { color: var(--hue-lift, var(--moss-lift)); }

/* ══ scroll-driven layer ═════════════════════════════════════════════════
   Chromium only today. Everything in here is *additive depth* on elements
   that are already in their finished state — never the thing that makes
   content appear. A browser without animation-timeline renders the base
   rules above and is correct; it simply does not get the parallax. */
@supports (animation-timeline: view()) {
  .hero-mesh, .hero-grid {
    animation: heroPar linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 90vh;
  }
  @keyframes heroPar { to { transform: translateY(-72px); opacity: .25; } }

  .hero-in {
    animation: heroLift linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 80vh;
  }
  @keyframes heroLift { to { transform: translateY(28px); opacity: .55; } }

  .nav::after {
    animation: navProg linear both;
    animation-timeline: scroll(root block);
  }
  @keyframes navProg { to { transform: scaleX(1); } }

  /* Chapter rules and tab ticks draw themselves as the band enters, which is
     the same gesture the .in class produces elsewhere — just measured against
     scroll position rather than fired on a threshold. */
  .band > .band-rule, .tab::before {
    animation: drawX linear both;
    animation-timeline: view();
    animation-range: entry 8% entry 62%;
  }
  @keyframes drawX { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  /* The plate settles the last few pixels as it arrives — a document being
     laid down rather than a card sliding in. */
  .plate .deliverables {
    animation: settle linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 78%;
  }
  @keyframes settle { from { transform: scale(.975); opacity: .55; } to { transform: none; opacity: 1; } }

  .fig-grid {
    animation: settle linear both;
    animation-timeline: view();
    animation-range: entry 4% entry 72%;
  }
}

/* ══ reduced motion ══════════════════════════════════════════════════════
   app.css already clamps every duration to .01ms, which handles fades but is
   the wrong tool for two cases: an infinite marquee clamped to one .01ms
   iteration ends its keyframe and parks the track a full set to the left, and
   a scroll-driven animation is not measured in time at all. Both are named
   here and switched off properly, and every end state is the resting one. */
@media (prefers-reduced-motion: reduce) {
  .js .hero [data-rise], .js .hero h1, .js .hero h1 .hl, .js .hero-card,
  .js .page-head h1, .js .page-head .lede, .js .page-head .eyebrow {
    animation: none !important; opacity: 1 !important;
    transform: none !important; clip-path: none !important;
  }
  .hero-glow, .hero-glow.two, .strip .lab::before, .hero-note::before {
    animation: none !important; }
  .mq-track { animation: none !important; transform: none !important; }
  /* Static, the strip becomes a row nobody can reach the end of, so it
     becomes a scrollable one instead. */
  .mq { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
  .js .meter i, .js .svc-row::before, .js .rail3::before {
    transform: none !important; transition: none !important; }
  .logo-tile::after { display: none; }
  .btn-primary::after { display: none; }
  .nav::after { display: none; }
  .band > .band-rule, .tab::before, .plate .deliverables, .fig-grid,
  .hero-mesh, .hero-grid, .hero-in, .cta-band .band-mesh {
    animation: none !important; animation-timeline: none !important;
    opacity: 1 !important;
  }
  /* transform is reset separately: .band-mesh uses one statically to mirror
     alternate bands, and clearing it here would un-mirror every other section
     for exactly the readers who asked for less change, not more. */
  .band > .band-rule, .tab::before, .plate .deliverables, .fig-grid,
  .hero-mesh, .hero-grid, .hero-in { transform: none !important; }
  .cta-band .band-mesh { translate: none !important; scale: none !important; }
  .svc-row:hover, .step:hover, .std:hover, .deliv:hover, .portal:hover,
  .guide-card:hover, .cred:hover, .logo-tile:hover, .chip:hover, .state:hover {
    transform: none !important; }
}

/* ══ print ═══════════════════════════════════════════════════════════════
   webcheck asserts the reveal override in app.css; these are the ones this
   file adds. A printed page has no scroll, no hover and no viewport, so every
   effect that depends on one has to resolve to its finished state. */
@media print {
  .band-mesh, .hero-mesh, .hero-glow, .hero-grid, .grain::after,
  .nav::after, .svc-row .ghost, .plate .deliv::after,
  body.site::before { display: none !important; }
  /* Paper is white and ink is black. Every ground on the site is either a
     mesh, a tint or a piece of glass, so each one is named and flattened —
     including the page ground itself, which is now a fixed layer rather than
     a background colour, and including the three backdrop-filters, which
     would otherwise print as a grey slab. */
  html, body.site { background: #fff !important; color: #000 !important; }
  .hero, .page-head, .band, .plate, .strip, .figures, .sec-tint, .cta-band,
  .fig, .fig-grid, .nav, .nav.stuck, .hero-card, .card, .svc-row, .cred,
  .step, .std, .deliv, .portal, .guide-card, .proof, .schedule-wrap, footer {
    background: #fff !important; color: #000 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
  }
  /* !important, and not for tidiness. The theme overrides are written as
     `[data-theme="dark"] body.site` — (0,2,1) — so a plain `body.site` block
     in here loses the cascade and every var(--muted) on the page prints as
     #A9B1DC on white, which is 1.9:1. It printed exactly like that until this
     line said so. Custom properties take !important; this is the one place
     that is the right tool rather than a shortcut. */
  body.site, .plate, .logo-tile, .cert-shot, .doc-shot {
    --text: #000 !important; --muted: #333 !important; --faint: #555 !important;
    --surface: #fff !important; --line: #bbb !important; --line-soft: #ddd !important;
    --glass-line: #ddd !important; --glass: transparent !important;
    --glow: transparent !important; --signal-ink: #000 !important;
    --hue: #999 !important; --hue-lift: #555 !important;
    --moss: #444 !important; --moss-lift: #333 !important;
    --mesh-1: #999 !important; --mesh-2: #999 !important; --mesh-3: #999 !important;
    --gold-lift: #444 !important; --signal: #777 !important;
  }
  /* Chapter accents are colour, and paper has none. The rules stay — they are
     structure, and a section divider is worth ink — but they print grey. */
  .band > .band-rule, .tab::before, .rail3::before, .page-head::before,
  .prose h2::before, .hero-card::before, .svc-row::before, footer::before,
  .band .sec-head .eyebrow::before, .meter i {
    box-shadow: none !important; background: #999 !important;
  }
  .btn-primary { background: #fff !important; color: #000 !important;
                 border: 1px solid #666 !important; }
  .cert-flag { background: #444 !important; color: #fff !important; }
  .js .hero [data-rise], .js .hero h1, .js .hero-card, .js .page-head h1,
  .js .page-head .lede, .js .page-head .eyebrow, .fig-grid,
  .plate .deliverables, .hero-in {
    animation: none !important; opacity: 1 !important; transform: none !important;
    clip-path: none !important;
  }
  .js .meter i, .js .svc-row::before, .js .rail3::before { transform: none !important; }
  /* A moving strip cannot print. It becomes the grid it replaced. */
  .mq { overflow: visible !important; -webkit-mask-image: none !important; mask-image: none !important; }
  .mq-track { animation: none !important; transform: none !important; display: block; }
  .mq-set { display: flex; flex-wrap: wrap; }
  .mq-rows { gap: .3rem; }
  .svc-split, .fig-grid, .rail3 { grid-template-columns: 1fr !important; }
  .svc-aside { position: static !important; }
}

/* ── stacking, and a specificity bug the reveal was quietly causing ─────── */
/* Band content sits above the wash and the grain, both of which are absolutely
   positioned siblings inside the band. */
.band > .wrap { position: relative; z-index: 1; }
.band > .mq-rows { position: relative; z-index: 1; }
.svc-aside .sec-head { margin-bottom: 1.6rem; }
.svc-aside .sec-head h2 { font-size: clamp(1.8rem, 3vw, 2.4rem); }

/* `.js .reveal.in { transform: none }` in app.css is three classes; every hover
   lift on this site was written as one class and a pseudo-class, so the moment
   an element was both revealed and hoverable the lift silently lost the
   cascade. .deliv, .guide-card, .cred and .step have all been dead on hover.
   Restated at a weight that wins, rather than touching app.css — the portals
   read that file too. */
.js .reveal.in.svc-row:hover   { transform: translateY(-3px); }
.js .reveal.in.deliv:hover     { transform: translateY(-4px); }
.js .reveal.in.guide-card:hover{ transform: translateY(-3px); }
.js .reveal.in.cred:hover      { transform: translateY(-2px); }
.js .reveal.in.step:hover      { transform: translateY(-4px); }
.js .reveal.in.std:hover       { transform: translateY(-3px); }
.js .reveal.in.portal:hover    { transform: translateY(-6px); }

/* ── the nav hairline's chapter colour ──────────────────────────────────
   site.js copies the current band's data-hue onto the header. Registering the
   property is what lets the colour cross-fade rather than jump; where
   @property is unsupported the transition is skipped and the colour snaps,
   which is a perfectly good hairline. */
@property --nav-hue {
  syntax: '<color>'; inherits: true; initial-value: #7FA5FF;
}
.nav { transition: background .3s, border-color .3s, padding .3s, --nav-hue .45s linear; }
.nav                  { --nav-hue: var(--moss-lift); }
.nav[data-hue="brand"] { --nav-hue: var(--moss-lift); }
.nav[data-hue="gold"]  { --nav-hue: var(--signal); }
.nav[data-hue="aqua"]  { --nav-hue: var(--aqua-lift); }
.nav[data-hue="iris"]  { --nav-hue: var(--iris-lift); }
.nav[data-hue="ember"] { --nav-hue: var(--ember-lift); }
.nav::after {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--nav-hue) 55%, transparent), var(--nav-hue));
}

/* ── hero, small screens ────────────────────────────────────────────────
   Restated after the block above, which sets a taller desktop hero than the
   earlier @media rule was written against. */
@media (max-width: 980px) {
  .hero { padding: 7.5rem 0 3.5rem; }
  .hero-glow.two { width: 420px; height: 420px; bottom: -320px; left: -180px; }
}
@media (max-width: 620px) {
  .sec-pad { padding: 3.8rem 0; }
  .figures { padding: 2.4rem 0; }
  .tab { margin-bottom: 1.1rem; }
  .hero-mesh { inset: -10% -20% 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   The two drifting blobs, and the paper rule on the plate
   --------------------------------------------------------------------------
   What used to live here was a second, later definition of every mesh on the
   site — a "wash calibration" pass that re-mixed each gradient from --hue-lift
   at a --wash-1/--wash-2 strength. It is gone: the mesh is now defined once,
   off --mesh-1/2/3, in the block near the top of this file, and having two
   places that both claim to own the same gradient is how the previous version
   ended up with a knob nobody could find. --wash-* and --mesh-o no longer
   exist; --g1..--g3 and --b1..--b3 replace them, and they are declared with
   the palette rather than eight hundred lines away from it.
   ══════════════════════════════════════════════════════════════════════════ */

/* The two hero blobs. Mixed from the contract stops so they are the same light
   as the ground rather than a second, unrelated colour, and parked by site.js
   the moment the hero leaves the screen. */
.hero-glow {
  background: radial-gradient(circle,
    color-mix(in srgb, var(--mesh-1) 30%, transparent), transparent 62%);
}
.hero-glow.two {
  background: radial-gradient(circle,
    color-mix(in srgb, var(--mesh-3) 24%, transparent), transparent 62%);
}

/* Ruled like paper, at the strength of a watermark and no more — at 4% the
   rules were reading as strikethrough where they crossed a line of type. */
.plate .deliv::after {
  background: repeating-linear-gradient(180deg,
    transparent 0 33px, color-mix(in srgb, #fff 3%, transparent) 33px 34px);
}

/* ── the headline rule ──────────────────────────────────────────────────
   At 3px in full strength across two wrapped lines it read as a hyperlink,
   which is the one thing a headline must not look like. Thinner, softer and
   sitting further from the baseline: an emphasis mark, not a link. Now drawn
   through all three stops, so the one piece of colour in the headline is the
   page's own light rather than a fourth blue. */
/* (headline underline removed — see the note beside the retired ruleDraw) */

/* ── the chapter rail, out of the text column's way ─────────────────────
   Pinned to a fixed distance from the content edge rather than the viewport
   edge, so the gap between a tick and the first character of a heading is the
   same at 1440px as it is at 2560px. */

/* ══════════════════════════════════════════════════════════════════════════
   The nine inner pages
   --------------------------------------------------------------------------
   Same system, quieter. Each page head and each band carries its chapter hue,
   and that hue reaches the components through --hue rather than through a
   colour written into a rule — so the four service cards on services.html are
   the same four colours as the four rows on the home page without either file
   knowing about the other.

   The guides deliberately get least of it. They are documents to read; a rule
   beside each heading and a coloured list marker is the whole intervention.
   ══════════════════════════════════════════════════════════════════════════ */

/* The chapter rule belongs at the foot of a page head, not the top — the top
   edge is underneath a fixed header and nobody has ever seen it there. */
.page-head::before { inset: auto 0 0; height: 2px; }

/* The section eyebrow gets the same leading tick the home page's .tab has, so
   a section head reads as a chapter marker on all ten pages. */
.band .sec-head .eyebrow::before {
  content: ''; display: inline-block; width: 1.7rem; height: 2px;
  background: var(--hue); margin-right: .65rem; vertical-align: .22em;
  border-radius: 2px;
}

/* Components take the band's hue rather than naming the brand blue. Where a
   card sets its own data-hue — the four services — it wins over the band's,
   which is how one section can hold four colours without a second class. */
.svc-ic { background: color-mix(in srgb, var(--hue) 16%, transparent);
          color: var(--hue-lift); }
.svc li::before, .support li svg, .deliv li::before,
.guide-card .more, .pillar .n { color: var(--hue-lift); }
.pillar { border-left-color: var(--hue); }
.grid-cards > .card { transition: border-color .3s var(--ease), transform .3s var(--ease),
                                  box-shadow .3s var(--ease); }
/* Scoped to .grid-cards: a card in a grid of choices is something you point
   at, a card holding the registered office is not, and giving both a hover
   state tells the reader the address is clickable. */
.grid-cards > .card:hover {
  border-color: var(--hue);
  box-shadow: 0 16px 36px -26px color-mix(in srgb, var(--hue) 90%, transparent);
}
.js .grid-cards > .reveal.in.card:hover { transform: translateY(-3px); }
.chip.on { border-color: var(--hue); color: var(--hue-lift);
           background: color-mix(in srgb, var(--hue) 10%, transparent); }
.state:hover, .state.hot { border-color: var(--hue);
           background: color-mix(in srgb, var(--hue) 14%, var(--surface)); }
.strip-link, .contact-lines a:hover, .platform a:hover { color: var(--hue-lift); }
/* Credential standing is deliberately NOT wired to the chapter hue, and this
   is the one exception worth spelling out. The licences page is chapter gold;
   wiring --hue through .cred-status painted the "In force" badge in the same
   amber as "Lapsed", and two states that must never be confusable at a glance
   became one colour with two words in it. Status is meaning. The index does
   not get a vote: in force is the brand blue, lapsed is --signal, always. */
.cred { border-left-color: var(--moss); }
.cred .n { color: var(--moss-lift); }
.cred-status { border-color: var(--moss); color: var(--moss-lift);
               background: color-mix(in srgb, var(--moss) 14%, transparent); }
.cred.is-lapsed { border-left-color: var(--signal); }
.cred.is-lapsed .n { color: var(--muted); }
.cred.is-lapsed .cred-status { border-color: var(--signal); color: var(--signal-ink);
               background: color-mix(in srgb, var(--signal) 14%, transparent); }
.cred:hover { border-color: var(--hue); border-left-color: var(--moss); }
.cred.is-lapsed:hover { border-left-color: var(--signal); }
/* Both flags sit on a white sheet in both themes, so both take the light
   column's fills — dark blue for in force, dark amber for lapsed — and white
   type on each: 7.1:1 and 5.9:1. The dark column's #FFC53D on white is 1.6:1
   and the flag would simply not be there. */
.cert-flag { background: var(--moss); color: #fff; }
.cert-shot.is-lapsed .cert-flag { background: var(--signal); color: #fff; }
.schedule tbody tr:hover th, .schedule tbody tr:hover td {
  background: color-mix(in srgb, var(--hue) 8%, transparent); }
.qa summary::after { color: var(--hue-lift); }
.qa .ans a, .prose a, .deliv li a, .docfacts dd a, p.muted a { color: var(--hue-lift); }

/* Guides: the reading measure is the design. A hue bar beside each heading,
   a hue list marker, and nothing else moves. */
.prose-page .page-head .lede { max-width: 46ch; }
.prose h2::before { background: var(--hue); }

/* A long document deserves to know how far through it you are. Same hairline
   as everywhere else — Chromium draws it, everyone else gets a header that
   simply has no rule under it. */

/* ── the marquee when it is not a marquee ────────────────────────────────
   Found by actually printing it. Each row carries its set twice so the loop
   has no seam, and the track is width:max-content so the two sets sit on one
   line. Stop the animation without undoing either of those and you get every
   client logo printed twice, on a strip two thousand pixels wide running off
   the right edge of the paper.

   So both static states — print and reduced motion — drop the duplicate set
   (it is the aria-hidden one, so nothing is lost) and let the remaining tiles
   wrap inside the page. */
.mq-track { width: max-content; }
@media print {
  .mq-rows { width: min(1240px, 100% - 3rem); margin-inline: auto; }
  .mq-track { width: auto !important; display: block !important; }
  .mq-set { display: flex !important; flex-wrap: wrap; gap: .4rem; }
  .mq-set[aria-hidden="true"] { display: none !important; }
  .mq .logo-tile { width: 118px; height: 58px; }
}
@media (prefers-reduced-motion: reduce) {
  .mq-set[aria-hidden="true"] { display: none; }
}

/* ── the marquee stop control ───────────────────────────────────────────── */
.mq-band.stopped .mq-track { animation-play-state: paused; }
.mq-pause {
  display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  font-family: var(--mono); font-size: .68rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
  background: transparent; border: 1px solid var(--line);
  border-radius: 999px; padding: .34rem .7rem .34rem .6rem;
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.mq-pause:hover { border-color: var(--hue); color: var(--text); }
/* Two bars for pause, a triangle once it is paused — the control shows what
   pressing it will do. Two bars as a gradient rather than a clip-path: an
   eight-point polygon that draws two rectangles has to cross itself to get
   from one to the other, and the crossing shows. */
.mq-pause .gl {
  width: 9px; height: 9px; flex: none;
  background: linear-gradient(90deg,
    var(--hue-lift) 0 34%, transparent 34% 66%, var(--hue-lift) 66% 100%);
}
.mq-band.stopped .mq-pause .gl {
  background: var(--hue-lift);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
@media (prefers-reduced-motion: reduce) { .mq-pause { display: none; } }
@media print { .mq-pause { display: none !important; } }

/* The three tracks were promoted with will-change whether they were running
   or not. A running animation promotes its own layer; standing three 2000px
   textures up permanently so they can sit paused behind the footer is exactly
   the sort of thing that costs a mid-range phone nothing useful. */
.mq-track { will-change: auto; }

/* Decoration stops when it leaves the screen. site.js sets .offscreen. */
.hero.offscreen .hero-glow,
.hero.offscreen .hero-note::before,
.strip.offscreen .lab::before { animation-play-state: paused; }

/* The phone type floor for the marketing micro-labels. It lives here rather
   than in app.css because these classes are defined here, and site.css is
   linked after app.css — a rule of equal specificity over there loses.
   .fig .k lands at 9.6px and .tab at 9.9px, which is under what anyone reads
   at arm's length on a phone; the desktop sizes are deliberate and untouched. */
@media (max-width: 500px) {
  .fig .k, .cline .k, .contact-lines .k, .tab { font-size: .7rem; }
}
