/* ============================================================================
   Baseer POC — design system
   ----------------------------------------------------------------------------
   Colour is BDS v1, LOCKED (baseer/brand/docs/COLOR_SYSTEM.md, 2026-08-03) and
   WCAG-validated upstream — the hex values below are copied, not chosen. Both
   of BDS's two lights ship here: THE RANGE (dark, the proving ground) is the
   default, THE REPORT (light) is selectable. The shell canon originally
   dropped the toggle as "dark-only by design"; that was amended 2026-08-09.

   Everything else here — space, type, elevation, motion, the component
   vocabulary — is this file's job. BDS v1 §2 (typography), §3 (registers) and
   §5 (motion) are still unlocked upstream, so this is the working system until
   they land, built to the register the product actually is: a task surface, not
   a brand surface. Fixed rem type (not fluid), structural responsiveness (the
   sidebar collapses, type does not), one component vocabulary reused across
   every page.
   ========================================================================= */

/* ---- colour: BDS v1 "one material, two lights" ---------------------------
   THE RANGE (dark) is the default per COLOR_SYSTEM.md; THE REPORT (light) is
   the client-artifact palette.

   ** AMENDMENT 2026-08-10 — THE RANGE's neutrals are now NEUTRAL. **
   Upstream usage law #1 says "neutrals + brand sit at 68–90° (warm gold);
   outside that band is not Baseer", and the Range's greys were warm-tinted
   accordingly. Client decision after seeing the landing page: the brand reads
   strongest as black + Revelation Gold, and the warm greys read as brown next
   to the gold rather than as one hue family. The gold, the ink, the LOOP
   semantics and the whole light palette are untouched — only the dark
   neutrals lost their tint. COLOR_SYSTEM.md needs this reconciled upstream.

   Each replacement is luminance-matched to the value it replaced, so the
   upstream WCAG audit still holds. THE REPORT (light) keeps its warm paper —
   it is the printed artifact, and cream is the point there.

   The four tokens the upstream spec doesn't enumerate for light (--text-hi,
   --surface-3, --line-3, --accent-strong) are derived below and checked
   against the WCAG floors.

   `color-scheme` is set per theme so native scrollbars and form controls
   follow along. -------------------------------------------------------- */
:root, :root[data-theme="dark"] {
  color-scheme: dark;
  /* NEUTRAL RANGE — amended 2026-08-10, see the note above. Each value is the
     grey with the SAME relative luminance as the warm one it replaced, so every
     contrast pair upstream validated is preserved exactly (max ΔL 0.0013).
     Only the hue is gone; the tonal architecture is untouched. */
  --bg: #0e0e0e;            /* was #100e0a */
  --surface: #161616;       /* was #191510 */
  --surface-2: #1e1e1e;     /* was #221d15 */
  --surface-3: #252525;     /* pressed/selected                              */
  --line: #2c2c2c;          /* hairline: grouping, not a boundary you act on  */
  --line-2: #404040;        /* raised hairline: hover, dividers under weight  */
  --line-3: #616161;        /* 3:1 vs --bg — anything focusable               */
  --text: #ececec;
  --text-2: #9f9f9f;        /* 7.27:1 on --bg, unchanged                      */
  --text-hi: #f7f7f7;       /* display-only (usage law #3)                    */
  --accent: #ffcf7a;        /* voice: text, links, active numbers             */
  --accent-fill: #f5b23c;   /* fill only: mark, primary button, bars          */
  --accent-fill-hover: #f8c25e;
  --accent-strong: #f5b23c; /* derived: small status indicators (dot, bar)    */
  --accent-ink: #14110b;
  --accent-wash: #241a0b;
  --mark: var(--accent-fill);
  /* the LOOP semantic axis — Suspected → Proven → Fixed → Verified.
     Never reuse these for anything that is not a trust-state. */
  --crit: #f9736b;          /* Proven      */
  --warn: #e8873a;          /* Suspected   */
  --ok: #57cc8a;            /* Verified    */
  --info: #5ab0f2;          /* Fixed       */
  /* 3-layer shadows: contact + form + ambient. On the Range they read as
     depth against near-black; on the Report they must be far lighter. */
  --e1: 0 1px 2px rgba(0,0,0,.30);
  --e2: 0 1px 2px rgba(0,0,0,.24), 0 4px 10px rgba(0,0,0,.26);
  --e3: 0 1px 2px rgba(0,0,0,.22), 0 8px 20px rgba(0,0,0,.30), 0 20px 44px rgba(0,0,0,.26);
}

:root[data-theme="light"] {
  color-scheme: light;
  /* De-tinted 2026-08-10 alongside the Range, same luminance-matched method
     (max ΔL 0.0013). The Report was cream because it is "the paper the founder
     shows the gatekeeper"; the client's call is that the brand reads strongest
     as one warm thing — the gold — on a neutral ground, in both lights. */
  --bg: #efefef;            /* was #f2efe6 */
  --surface: #f8f8f8;       /* was #faf8f2 */
  --surface-2: #fefefe;     /* was #fffefb */
  --surface-3: #e5e5e5;     /* light "raised" recedes — it can't go lighter
                               than --surface-2, so a pressed chip tints down */
  --line: #d7d7d7;
  --line-2: #bbbbbb;
  --line-3: #818181;        /* 3.39:1 vs --bg — anything focusable            */
  --text: #111111;
  --text-2: #636363;        /* 5.22:1 on --bg                                 */
  --text-hi: #090909;       /* display-only, one step deeper                  */
  --accent: #8a5e08;        /* Deep Gold — the voice on light                 */
  --accent-fill: #f5b23c;   /* fill is the SAME gold in both schemes (law #4) */
  --accent-fill-hover: #e2a231;
  --accent-strong: #a86f10; /* derived: 3.68:1 vs --bg — the gold fill alone
                               is ~1.9:1 here and would fail as an indicator  */
  --accent-ink: #14110b;
  --accent-wash: #f7ecd5;
  --mark: var(--accent);    /* the fill gold disappears on cream; the mark
                               carries in Deep Gold instead                   */
  --crit: #b12e2a;
  /* #9a5a12 -> #8d520f, the third hue to hit the same rule as --ok and --info:
     4.18:1 against its own 10% wash. Today nothing put warn text on a warn tint,
     so it passed the audit while being one component away from failing it. Now
     4.76:1 in that case. */
  --warn: #8d520f;
  /* --ok and --info are each one step darker than they were (#19764a, #1f6fb0).
     Both failed AA against their OWN wash — the `held` pill was green text on a
     green tint of itself at 4.29:1, the `accepted` pill blue on blue at 4.16:1.
     A semantic hue is *always* read on a tint of itself somewhere in this system,
     so that, not the page ground, is the condition it has to clear. A hue that
     can't survive its own wash is a trap for every future use of it, which is why
     the tokens moved rather than the two components. Now 4.93:1 and 5.14:1 in
     those worst cases, and every other use of both gained with them. */
  --ok: #156b43;
  --info: #196099;
  --e1: 0 1px 2px rgba(60,48,20,.08);
  --e2: 0 1px 2px rgba(60,48,20,.07), 0 4px 10px rgba(60,48,20,.08);
  --e3: 0 1px 2px rgba(60,48,20,.06), 0 8px 20px rgba(60,48,20,.09), 0 20px 44px rgba(60,48,20,.08);
}

:root {
  /* ---- space: 4pt scale. Every margin/padding/gap comes from here ------- */
  --s1: 4px;   --s2: 8px;   --s3: 12px;  --s4: 16px;
  --s5: 20px;  --s6: 24px;  --s7: 32px;  --s8: 40px;
  --s9: 48px;  --s10: 64px; --s11: 80px; --s12: 96px;
  /* semantic: the three distances that carry the rhythm */
  --tight: var(--s2);                              /* within a group        */
  --group: var(--s4);                              /* between groups        */
  --stack: var(--s9);                              /* between sections      */
  --gutter: clamp(var(--s4), 3.2vw, var(--s7));    /* page edge             */

  /* ---- type: fixed rem, ~1.2 ratio, product register -------------------- */
  --t-2xs: 0.6875rem;  /* 11   mono eyebrows, pills                         */
  --t-xs:  0.75rem;    /* 12   timestamps, meta                             */
  --t-sm:  0.8125rem;  /* 13   secondary body                               */
  --t-base:0.875rem;   /* 14   body                                         */
  --t-md:  1rem;       /* 16   row titles                                   */
  --t-lg:  1.25rem;    /* 20   section / page heads                         */
  --t-xl:  1.75rem;    /* 28   page hero                                    */
  --t-2xl: 2.75rem;    /* 44   the verdict number, the grade — 3.1:1 vs body */

  --sans: Inter, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  /* ---- radius (elevation is themed, see the colour blocks above) --------- */
  --r-sm: 5px; --r: 8px; --r-lg: 12px; --r-full: 999px;

  /* ---- z: semantic, never arbitrary ------------------------------------- */
  --z-sticky: 100; --z-drawer-backdrop: 200; --z-drawer: 210;
  --z-popover: 300; --z-toast: 400;
  /* the state of the page itself — above every layer drawn *on* the page */
  --z-top: 500;

  /* ---- motion ----------------------------------------------------------- */
  --dur-1: 120ms; --dur-2: 180ms; --dur-3: 260ms;
  --ease: cubic-bezier(.22,.61,.36,1);            /* ease-out-quart          */
  --spring: cubic-bezier(.34,1.26,.64,1);         /* micro-interactions      */
}

/* ============================ base ====================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { margin: 0; font-weight: 650; line-height: 1.25; color: var(--text-hi); }
p { margin: 0; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
input { font: inherit; color: inherit; background: none; border: 0; outline: 0; min-width: 0; }
/* `ol` too: the reset covered `ul` only, so the first ordered list in the app
   (the loop bar) inherited a 40px inline padding and sat off the content grid
   on every screen. A reset with a hole in it is worse than none — it hides the
   default until something lands on it. */
ul, ol { list-style: none; margin: 0; padding: 0; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }
/* `hidden` must win over any component display — .row is flex, [hidden] alone
   would lose to it. */
[hidden] { display: none !important; }
.mono { font-family: var(--mono); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.i { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none;
     stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.i.sm { width: 13px; height: 13px; }
.i.lg { width: 20px; height: 20px; }

/* Detail that earns its place on a wide screen and costs someone the page title
   on a narrow one. `display:none` drops it from the accessible name too, which
   is fine here and only here: the label reads "Full evidence" without it, which
   is complete on its own. Never wrap the load-bearing half of a label in this. */
@media (max-width: 559px) { .wide-only { display: none; } }


/* ============================ shell ===================================== */
.shell { display: grid; grid-template-columns: 248px minmax(0, 1fr); min-height: 100dvh; }

/* ---- sidebar ---- */
.sidebar {
  display: flex; flex-direction: column; gap: var(--s5);
  background: var(--surface); border-right: 1px solid var(--line);
  padding: var(--s5) var(--s3) var(--s4);
  position: sticky; top: 0; height: 100dvh; overflow: hidden;
}
.brand { display: flex; align-items: center; gap: var(--s3); padding: 0 var(--s2); flex: none; }
.brand .mark { width: 26px; height: 26px; color: var(--mark); flex: none; }
.brand .word { font-weight: 800; font-size: var(--t-md); letter-spacing: .14em; color: var(--text-hi); }

.nav { display: flex; flex-direction: column; gap: 2px; flex: none; }
.navlink {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 40px; padding: 0 var(--s3); border-radius: var(--r-sm);
  color: var(--text-2); font-size: var(--t-base); font-weight: 500;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.navlink:hover { background: var(--surface-2); color: var(--text); }
.navlink[aria-current="page"] { background: var(--surface-2); color: var(--text-hi); font-weight: 600; }
.navlink[aria-current="page"] .i { color: var(--accent); }
.navlink .live-dot { margin-left: auto; }

/* the one animated affordance in the shell — a scan is genuinely running */
.live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-strong); flex: none; }
@media (prefers-reduced-motion: no-preference) {
  .live-dot { animation: pulse 1.8s var(--ease) infinite; }
}
@keyframes pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-strong) 45%, transparent); }
  50%      { opacity: .5; box-shadow: 0 0 0 5px transparent; }
}

.sb-foot { margin-top: auto; position: relative; flex: none; }

/* ---- account popover ---- */
.popover {
  position: absolute; bottom: calc(100% + var(--s2)); left: 0; right: 0;
  background: var(--surface-2); border: 1px solid var(--line-2);
  border-radius: var(--r); padding: var(--s3); box-shadow: var(--e3);
  z-index: var(--z-popover);
}
.popover[hidden] { display: none; }
.pop-head { padding: var(--s1) var(--s2) var(--s3); border-bottom: 1px solid var(--line); margin-bottom: var(--s2); }
.pop-head b { display: block; font-size: var(--t-sm); color: var(--text-hi); }
.pop-head small { font-size: var(--t-xs); color: var(--text-2); }
.pop-list a { display: block; min-height: 36px; line-height: 36px; padding: 0 var(--s2);
              border-radius: var(--r-sm); font-size: var(--t-sm); color: var(--text-2); }
.pop-list a:hover { background: var(--surface); color: var(--text); }
/* ---- theme switcher: a 3-way segmented control, the standard shape for
   System / Light / Dark. Lives in the account popover rather than the topbar
   so the topbar keeps exactly one action. ---- */
.theme-switch { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }
.theme-switch .lbl { display: block; padding: 0 var(--s2) var(--s2);
                     font-family: var(--mono); font-size: var(--t-2xs);
                     letter-spacing: .1em; text-transform: uppercase; color: var(--text-2); }
.seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 2px;
       padding: 2px; background: var(--bg); border: 1px solid var(--line-2); border-radius: var(--r-sm); }
.seg button {
  display: flex; align-items: center; justify-content: center; gap: var(--s1);
  min-height: 30px; padding: 0 var(--s1); border-radius: 3px;
  font-size: var(--t-xs); font-weight: 600; color: var(--text-2);
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.seg button .i { width: 14px; height: 14px; }
.seg button:hover { color: var(--text); }
.seg button[aria-checked="true"] { background: var(--surface-3); color: var(--text-hi); }
/* .seg also hosts real tabs (checkout's M-Pesa/Card switch is role="tablist",
   correctly aria-selected not aria-checked — a different widget, same pill
   look) — checkout.html previously used a bare, undefined "field-group"
   class here and rendered as plain unstyled text with no active state. */
.seg button[aria-selected="true"] { background: var(--surface-3); color: var(--text-hi); }

.pop-status { display: flex; align-items: center; gap: var(--s2); padding: var(--s3) var(--s2) var(--s1);
              margin-top: var(--s2); border-top: 1px solid var(--line);
              font-family: var(--mono); font-size: var(--t-2xs); color: var(--text-2); }
.pop-status .dot { width: 6px; height: 6px; border-radius: 50%; flex: none;
                   background: var(--text-2); }
.pop-status .dot.ok  { background: var(--ok);   box-shadow: 0 0 6px var(--ok); }
.pop-status .dot.deg { background: var(--warn); box-shadow: 0 0 6px var(--warn); }
.pop-status .dot.off { background: var(--crit); }

/* ---- system status bar: conditions that qualify the whole page -----------
   Sticky directly under the topbar. Inline in the content it would scroll away
   from the numbers it is qualifying, and a caveat you can scroll past is a
   caveat nobody read. */
.sysbar { position: sticky; top: 56px; z-index: var(--z-sticky);
          display: flex; align-items: center; gap: var(--s3);
          padding: var(--s3) var(--gutter); font-size: var(--t-sm);
          border-bottom: 1px solid; }
.sysbar[hidden] { display: none; }
.sysbar .i { flex: none; }
/* 8%, not 12%: mixed into the near-black ground a warm hue turns brown fast,
   and brown is the one thing this palette is not allowed to be. Same weight as
   .banner.warn, so the two read as one system rather than two warnings. */
.sysbar.deg { color: var(--text); background: color-mix(in srgb, var(--warn) 8%, var(--bg));
              border-bottom-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.sysbar.deg .i { stroke: var(--warn); }
.sysbar.off { color: var(--text); background: color-mix(in srgb, var(--crit) 8%, var(--bg));
              border-bottom-color: color-mix(in srgb, var(--crit) 40%, transparent); }
.sysbar.off .i { stroke: var(--crit); }

.acct {
  display: flex; align-items: center; gap: var(--s3); width: 100%;
  padding: var(--s2); border-radius: var(--r-sm); text-align: left;
  transition: background var(--dur-1) var(--ease);
}
.acct:hover { background: var(--surface-2); }
.avatar { width: 28px; height: 28px; border-radius: 50%; background: var(--accent-fill);
          color: var(--accent-ink); display: grid; place-items: center;
          font-weight: 800; font-size: var(--t-xs); flex: none; }
.acct-meta { flex: 1; min-width: 0; }
.acct-meta b { display: block; font-size: var(--t-sm); font-weight: 600; color: var(--text-hi); }
.acct-meta small { display: block; font-size: var(--t-xs); color: var(--text-2);
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct .i { transition: transform var(--dur-2) var(--ease); }
.acct[aria-expanded="true"] .i { transform: rotate(180deg); }

/* ---- main / topbar ---- */
.main { display: flex; flex-direction: column; min-width: 0; }
.topbar {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 56px; padding: 0 var(--gutter); flex: none;
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: var(--z-sticky);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
}
.crumbs { display: flex; align-items: center; gap: var(--s2); min-width: 0;
          font-size: var(--t-base); color: var(--text-2); }
.crumbs a { color: var(--text-2); border-radius: var(--r-sm); }
.crumbs a:hover { color: var(--text); }
.crumbs .sep { color: var(--line-3); flex: none; }
.crumbs .here { color: var(--text-hi); font-weight: 600;
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar-end { margin-left: auto; display: flex; align-items: center; gap: var(--s2); flex: none; }

.nav-toggle { display: none; width: 36px; height: 36px; place-items: center;
              border: 1px solid var(--line-2); border-radius: var(--r-sm); color: var(--text-2); }
.nav-toggle:hover { border-color: var(--line-3); color: var(--text); }

.content { flex: 1; padding: var(--s7) var(--gutter) var(--s12); }
.content-inner { max-width: 1180px; margin: 0 auto; }

/* ============================ responsive shell ==========================
   Structural, not fluid: the sidebar changes what it is at each step.
   ≥1080  full rail with labels
   720–1079  icon rail (labels become tooltips via title/aria-label)
   <720   off-canvas drawer behind a topbar toggle
   ====================================================================== */
@media (max-width: 1079px) {
  .shell { grid-template-columns: 64px minmax(0, 1fr); }
  .sidebar { padding: var(--s5) var(--s2) var(--s4); align-items: center; }
  .brand { padding: 0; }
  .brand .word { display: none; }
  .nav { width: 100%; }
  .navlink { justify-content: center; padding: 0; }
  .navlink span.label { display: none; }
  .navlink .live-dot { position: absolute; margin: 0; transform: translate(11px, -11px); }
  .navlink { position: relative; }
  .acct { justify-content: center; padding: var(--s2) 0; }
  .acct-meta, .acct > .i { display: none; }
  .popover { left: calc(100% + var(--s2)); right: auto; bottom: 0; width: 232px; }
}

@media (max-width: 719px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .nav-toggle { display: grid; }
  .sidebar {
    position: fixed; inset: 0 auto 0 0; width: 248px; height: 100dvh;
    padding: var(--s5) var(--s3) var(--s4); align-items: stretch;
    z-index: var(--z-drawer); box-shadow: var(--e3);
    transform: translateX(-100%);
    /* visibility, not just transform: a drawer that is merely translated
       off-screen keeps its links in the tab order and the a11y tree, so a
       keyboard user tabs into an invisible menu. visibility animates
       discretely — it flips instantly on open, waits for the slide on close. */
    visibility: hidden;
    transition: transform var(--dur-3) var(--ease), visibility var(--dur-3);
  }
  .sidebar[data-open="true"] { transform: translateX(0); visibility: visible; }
  .brand .word, .navlink span.label, .acct-meta, .acct > .i { display: revert; }
  .navlink { justify-content: flex-start; padding: 0 var(--s3); }
  .navlink .live-dot { position: static; margin-left: auto; transform: none; }
  .acct { justify-content: flex-start; padding: var(--s2); }
  .popover { left: 0; right: 0; bottom: calc(100% + var(--s2)); width: auto; }
  .backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.6);
              z-index: var(--z-drawer-backdrop); border: 0; width: 100%; }
  .backdrop[hidden] { display: none; }
  .content { padding: var(--s6) var(--gutter) var(--s10); }
}
@media (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
}
@media (min-width: 720px) { .backdrop { display: none; } }

/* ============================ primitives ================================
   ONE bounded surface (.panel) and ONE list vocabulary (.rows/.row), reused on
   every page. Previously four near-identical box styles (card, target-card,
   placeholder-card, finding-row) drifted apart; that was the incoherence.
   ====================================================================== */

.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); }
.panel-head {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line);
}
.panel-head h2, .panel-head h3 { font-size: var(--t-md); font-weight: 650; }
.panel-head .end { margin-left: auto; display: flex; align-items: center; gap: var(--s3); }
.panel-body { padding: var(--s5); }
.panel-foot { padding: var(--s3) var(--s5); border-top: 1px solid var(--line);
              font-size: var(--t-xs); color: var(--text-2); }

/* section heading — space and weight carry the hierarchy, no eyebrow chrome */
.section { margin-top: var(--stack); }
.section:first-child { margin-top: 0; }
.section-head { display: flex; align-items: baseline; gap: var(--s3); margin-bottom: var(--s4); }
.section-head h2 { font-size: var(--t-lg); letter-spacing: -.015em; }
.section-head .count { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-2); }
.section-head .end { margin-left: auto; }

/* ---- rows: dense, hairline-divided, whole row is the target ---- */
.rows { display: flex; flex-direction: column; }
.row {
  display: flex; align-items: center; gap: var(--s4);
  padding: var(--s4) var(--s5); min-height: 68px;
  border-top: 1px solid var(--line);
  transition: background var(--dur-1) var(--ease);
}
.row:first-child { border-top: 0; }
a.row:hover { background: var(--surface-2); }
a.row:focus-visible { outline-offset: -2px; }
.row-main { flex: 1; min-width: 0; }
.row-title { font-family: var(--mono); font-size: var(--t-md); color: var(--text-hi);
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-meta { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
            margin-top: var(--s1); font-size: var(--t-sm); color: var(--text-2); }
.row-meta b { color: var(--text); font-weight: 650; }
.row-end { display: flex; align-items: center; gap: var(--s3); flex: none; color: var(--text-2); }
a.row .chevron { transition: transform var(--dur-2) var(--spring); }
a.row:hover .chevron { transform: translateX(3px); color: var(--accent); }

/* On a phone the pill competes with the title for the same line and forces the
   meta to wrap mid-phrase. Drop it: the glyph (a distinct shape per state) and
   the meta sentence already carry the state in non-colour form, so nothing is
   lost — this is redundancy being shed, not information. */
@media (max-width: 559px) {
  .row { gap: var(--s3); padding: var(--s4); }
  .row .pill { display: none; }
}

/* ---- state glyph: the LOOP axis, never colour alone (icon + colour + text) -- */
.glyph { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
         flex: none; border: 1px solid var(--line-2); color: var(--text-2); }
.glyph.ok      { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 38%, transparent); }
.glyph.crit    { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 40%, transparent); }
.glyph.running,
.glyph.wait,
.glyph.report  { color: var(--accent-strong); border-color: color-mix(in srgb, var(--accent-strong) 42%, transparent); }
.glyph.pending { color: var(--text-2); border-style: dashed; }
/* a regression carries the crit hue AND its own icon AND its own word — the
   only finding state that gets all three, because it is the only one that is
   also an argument (see lifecycle.FINDING) */
.glyph.regress { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 55%, transparent);
                 box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit) 9%, transparent); }
.glyph .pulse-ring { width: 8px; height: 8px; border-radius: 50%; background: var(--accent-strong); }
@media (prefers-reduced-motion: no-preference) {
  .glyph.running .pulse-ring { animation: pulse 1.6s var(--ease) infinite; }
}

/* ---- buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 36px; padding: 0 var(--s4); border-radius: var(--r-sm);
  font-size: var(--t-sm); font-weight: 600; white-space: nowrap;
  border: 1px solid transparent;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease), transform var(--dur-1) var(--spring);
}
.btn:active { transform: translateY(1px); }
/* exactly one primary per surface — Von Restorff only works if it is isolated */
.btn-primary { background: var(--accent-fill); color: var(--accent-ink); font-weight: 700; }
.btn-primary:hover { background: var(--accent-fill-hover); }
.btn-primary .i { stroke: var(--accent-ink); }
.btn-ghost { border-color: var(--line-3); color: var(--text); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-wash); }
.btn-quiet { color: var(--text-2); }
.btn-quiet:hover { background: var(--surface-2); color: var(--text); }
.btn-block { width: 100%; }
.btn-lg { min-height: 42px; padding: 0 var(--s5); font-size: var(--t-base); }

/* ---- form field ---- */
.field {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 38px; padding: 0 var(--s3);
  background: var(--bg); border: 1px solid var(--line-2); border-radius: var(--r-sm);
  color: var(--text-2);
  transition: border-color var(--dur-1) var(--ease);
}
.field:focus-within { border-color: var(--accent); }
.field input { flex: 1; min-width: 0; font-size: var(--t-sm); color: var(--text); }
.field input::placeholder { color: var(--text-2); }
.field kbd { flex: none; font-family: var(--mono); font-size: var(--t-2xs); color: var(--text-2);
             border: 1px solid var(--line-2); border-radius: 3px; padding: 1px 5px; }
.field-lg { min-height: 42px; }
.field-lg input { font-size: var(--t-base); }

/* A field's label. Was only ever defined scoped to .auth-form, so it rendered
   unstyled anywhere else — the app-side forms are the "anywhere else". */
.lbl { display: block; font-size: var(--t-sm); font-weight: 600; color: var(--text);
       margin-bottom: var(--s2); }

/* A small dimmed line under a field or button — explaining what happens
   next, not restating the label. checkout.html previously reused home.css's
   "page__sub" here (hero-scaled, and a class this stylesheet never loads at
   all) — rendered with zero styling, browser-default <p> margin and full
   body-text brightness. */
.hint { margin: var(--s2) 0 0; font-size: var(--t-sm); line-height: 1.5; color: var(--text-2); }

/* ---- checkbox row ----
   Used where a choice needs a sentence next to it rather than a word — the API
   token scopes, where "what does this one actually let a token do" is the whole
   decision. The label wraps the input so the explanation is part of the hit
   target, not just decoration beside it. */
.chk {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s2) 0; cursor: pointer;
  font-size: var(--t-sm); color: var(--text-2);
}
.chk input { flex: none; margin-top: 2px; accent-color: var(--accent); }
/* The bold lead-in is the scope name; it carries the contrast, the sentence
   after it sits back on --text-2. Colour, not opacity — a washed label fails AA
   against its own background. */
.chk b { color: var(--text); font-weight: 600; font-family: var(--mono);
         font-size: var(--t-xs); }
.chk em { color: var(--text); font-style: normal; font-weight: 600; }

/* ---- pills / tags: state always carries a word, not just a hue ---- */
.pill {
  display: inline-flex; align-items: center; gap: var(--s1);
  font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .07em;
  text-transform: uppercase; padding: 3px var(--s2); border-radius: var(--r-full);
  border: 1px solid transparent; white-space: nowrap;
}
/* Pills are keyed on MEANING, not on the database enum — `new` and `active` are
   two names for "open", and the interface has no business making the customer
   learn our column values. The map lives in app/lifecycle.py (FINDING.tone). */
.pill.open {
  color: var(--crit); background: color-mix(in srgb, var(--crit) 10%, transparent);
  border-color: color-mix(in srgb, var(--crit) 26%, transparent);
}
/* regress: open, and worse than open. Louder than `.open` by exactly one step —
   a heavier border and a mark — never by a second hue, which would break the
   "one hue per meaning" rule the whole state axis rests on. */
.pill.regress {
  color: var(--crit); background: color-mix(in srgb, var(--crit) 13%, transparent);
  border-color: color-mix(in srgb, var(--crit) 52%, transparent); font-weight: 600;
}
.pill.regress .i { stroke: currentColor; width: 13px; height: 13px; }
.pill.closed {
  color: var(--ok); background: color-mix(in srgb, var(--ok) 10%, transparent);
  border-color: color-mix(in srgb, var(--ok) 26%, transparent);
}
/* decided: still exploitable, but by the customer's choice rather than by our
   failure to fix it. Dashed reads as "an agreement, not a demonstration" — and
   that meaning is consistent everywhere dashes appear in this interface. */
.pill.decided {
  color: var(--info); background: color-mix(in srgb, var(--info) 8%, transparent);
  border-color: color-mix(in srgb, var(--info) 42%, transparent); border-style: dashed;
}
/* in flight: the instrument is engaged, so it wears the brand's own hue */
.pill.flight {
  color: var(--accent); background: color-mix(in srgb, var(--accent-fill) 10%, transparent);
  border-color: color-mix(in srgb, var(--accent-fill) 32%, transparent);
}
.pill.neutral { color: var(--text-2); border-color: var(--line-2); }
/* replay: a playback of a stored hunt. Deliberately NOT the "flight" gold —
   a replay is not a live pass, and the interface must never imply it is. It is
   a re-telling, so it stays monochrome with one gold clock icon. */
.pill.replay {
  color: var(--text-hi); background: var(--surface-2);
  border-color: color-mix(in srgb, var(--accent-fill) 38%, transparent);
}
.pill.replay .i { width: 12px; height: 12px; stroke: var(--accent-strong); }
.sev { font-family: var(--mono); font-size: var(--t-2xs); text-transform: uppercase;
       letter-spacing: .07em; color: var(--text-2); }

/* ---- empty states: teach the interface, never "nothing here" ---- */
.empty { display: flex; flex-direction: column; align-items: center; text-align: center;
         gap: var(--s3); padding: var(--s10) var(--s6); }
.empty .glyph { width: 44px; height: 44px; }
.empty h3 { font-size: var(--t-md); }
.empty p { max-width: 46ch; font-size: var(--t-sm); color: var(--text-2); }
.empty .btn { margin-top: var(--s2); }

/* ============================ verdict block =============================
   The dashboard's overview tier (Shneiderman: overview first). Deliberately a
   sentence with one large real number, not a stat-tile grid — the tile grid is
   the SaaS reflex, and the brand voice is "we demonstrated," which is prose.
   ====================================================================== */
/* Padding was --s7 top and bottom, and the section below it added another --s9.
   Eighty pixels of air between the verdict and the fleet it describes, on a
   surface whose whole job is the fleet: at 1146x660 that left two rows of six
   visible. The band pays its own rhythm now and the section pays the gap. */
.verdict { display: flex; align-items: flex-start; gap: var(--s6); flex-wrap: wrap;
           padding: 0 var(--s8) 0 0; }
.verdict-figure { font-size: var(--t-2xl); font-weight: 700; line-height: .95;
                  letter-spacing: -.04em; font-variant-numeric: tabular-nums; flex: none; }
.verdict-figure.crit { color: var(--crit); }
.verdict-figure.ok { color: var(--ok); }
.verdict-copy { flex: 1; min-width: 260px; padding-top: var(--s1); }
.verdict-copy p { font-size: var(--t-lg); line-height: 1.35; color: var(--text);
                  letter-spacing: -.01em; text-wrap: balance; }
.verdict-copy p b { color: var(--text-hi); font-weight: 650; }
.verdict-meta { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
                margin-top: var(--s4); font-family: var(--mono); font-size: var(--t-xs);
                color: var(--text-2); }
.verdict-meta .sep { color: var(--line-3); }

/* The facts in the band that are not neutral. They keep a hue and an icon so
   they cannot be skimmed past, and they cost one line instead of the ninety
   vertical pixels a full-width banner was charging for the same sentence. */
.verdict-flags { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
                 margin-top: var(--s4); font-family: var(--mono); font-size: var(--t-xs); }
.verdict-flags:empty { display: none; }
.verdict-flags + .verdict-meta { margin-top: var(--s3); }
.vflag { display: inline-flex; align-items: center; gap: var(--s2);
         padding: 3px var(--s3) 3px var(--s2); border-radius: var(--r-full);
         border: 1px solid transparent; }
.vflag .i { width: 13px; height: 13px; }
.vflag.regress { color: var(--crit); background: color-mix(in srgb, var(--crit) 10%, transparent);
                 border-color: color-mix(in srgb, var(--crit) 34%, transparent); }
.vflag.warn    { color: var(--warn); background: color-mix(in srgb, var(--warn) 10%, transparent);
                 border-color: color-mix(in srgb, var(--warn) 34%, transparent); }
.vflag.ok      { color: var(--ok);   background: color-mix(in srgb, var(--ok) 10%, transparent);
                 border-color: color-mix(in srgb, var(--ok) 34%, transparent); }

/* Inline "·" separators are their own flex items, so a wrap can strand one at
   the end of a line. Render them as a prefix on the item that follows instead:
   a wrapped line then opens with "· last verified 09 Aug", which reads. */
.verdict-meta .sep, .scan-sub .sep, .row-meta .sep { display: none; }
.verdict-meta > span + span::before,
.scan-sub > span + span::before,
.row-meta > span + span::before {
  content: "·"; color: var(--line-3); margin-right: var(--s2);
}

/* ============================ page grid ================================= */
.with-rail { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--s6); align-items: start; }
/* The rail collapses at 1340px, not 1000px. Below that the sidebar (248) plus
   the gutters plus a 300px rail leave the list about 510px, and at 510 every
   target row wraps its meta to a second line — six rows paying 20px each so a
   promo card can sit beside them. On the most common laptop in this market that
   is the wrong trade: the fleet is the product, the rail is context. The rail
   keeps its place on screens with room for both. */
@media (max-width: 1339px) { .with-rail { grid-template-columns: minmax(0, 1fr); } }

/* The verdict is a header band, not a peer section, so the section below it
   doesn't pay the full between-sections distance on top of the band's own. */
.verdict + .section { margin-top: var(--s7); }
.rail { display: flex; flex-direction: column; gap: var(--s4); position: sticky; top: calc(56px + var(--s6)); }
@media (max-width: 1339px) { .rail { position: static; } }

.promo h3 { font-size: var(--t-md); margin-bottom: var(--s2); }
.promo p { font-size: var(--t-sm); color: var(--text-2); margin-bottom: var(--s4); }
.promo p b { color: var(--text); font-weight: 600; }
.promo .price { font-family: var(--mono); color: var(--accent); }
/* `earned`: this fleet has actually had a regression, so the card is reporting
   rather than pitching. It gets the crit hairline the fact deserves — and only
   then. A permanently-urgent upsell is just decoration with a price on it. */
.promo.earned { border-color: color-mix(in srgb, var(--crit) 32%, transparent); }
.promo.earned h3 { color: var(--crit); }

.note { display: flex; align-items: flex-start; gap: var(--s3); padding: var(--s3) var(--s4);
        background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
        font-size: var(--t-sm); color: var(--text-2); }
.note .i { color: var(--text-2); margin-top: 2px; }

/* ============================ live scan ================================= */
.scan-hero { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s6) var(--s8);
             align-items: end; padding-bottom: var(--s6); border-bottom: 1px solid var(--line); }
@media (max-width: 720px) { .scan-hero { grid-template-columns: minmax(0, 1fr); align-items: start; } }
.scan-domain { font-family: var(--mono); font-size: var(--t-xl); color: var(--text-hi);
               letter-spacing: -.02em; overflow-wrap: anywhere; }
.scan-sub { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
            margin-top: var(--s2); font-size: var(--t-sm); color: var(--text-2); }
.scan-sub .sep { color: var(--line-3); }

.scan-progress { min-width: 260px; }
.scan-figure { display: flex; align-items: baseline; gap: var(--s2); justify-content: flex-end; }
@media (max-width: 720px) { .scan-figure { justify-content: flex-start; } }
.scan-figure b { font-family: var(--mono); font-size: var(--t-xl); font-weight: 700;
                 color: var(--text-hi); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.scan-figure span { font-size: var(--t-sm); color: var(--text-2); }
.bar { height: 6px; border-radius: var(--r-full); background: var(--surface-2);
       overflow: hidden; margin-top: var(--s3); }
.bar .fill { height: 100%; border-radius: var(--r-full); background: var(--accent-strong);
             transition: width var(--dur-3) var(--ease); }

/* class chips — the Certified-10, shared by Live Scan and the Report */
.chips { display: flex; gap: var(--s2); flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 32px; padding: 0 var(--s3); border-radius: var(--r-full);
  border: 1px solid var(--line-2); background: var(--surface);
  font-family: var(--mono); font-size: var(--t-xs); color: var(--text-2);
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease);
}
button.chip { cursor: pointer; }
button.chip:hover { border-color: var(--line-3); background: var(--surface-2); color: var(--text); }
.chip .cdot { width: 6px; height: 6px; border-radius: 50%; background: var(--line-3); flex: none; }
.chip .i { width: 12px; height: 12px; stroke-width: 2.2; }
.chip.held    { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.chip.held .cdot { background: var(--ok); }
.chip.proven  { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 38%, transparent);
                background: color-mix(in srgb, var(--crit) 9%, transparent); }
.chip.proven .cdot { background: var(--crit); }
/* Label takes --accent (the voice), dot takes --accent-strong (the indicator) —
   BDS usage law #2. Setting the label in --accent-strong put 12px text at
   3.99:1 on the light surface; the voice gold clears it at 5.37:1. */
.chip.running { color: var(--accent); border-color: color-mix(in srgb, var(--accent-strong) 42%, transparent); }
.chip.running .cdot { background: var(--accent-strong); }
@media (prefers-reduced-motion: no-preference) {
  .chip.running .cdot { animation: pulse 1.5s var(--ease) infinite; }
}
.chip[aria-pressed="true"] { border-color: var(--accent); background: var(--surface-3); color: var(--text-hi); }

/* ---- feed ---- */
.feed-head { position: sticky; top: 0; z-index: 1;
             background: var(--surface); border-radius: var(--r-lg) var(--r-lg) 0 0; }
.feed-body {
  /* viewport-relative, not a fixed 420px — the feed is the hero of this page
     and should use the screen it is given */
  max-height: clamp(320px, 52vh, 620px); overflow-y: auto;
  padding: var(--s2) 0; scrollbar-width: thin; scrollbar-color: var(--line-2) transparent;
}
.feed-line { display: flex; gap: var(--s4); padding: var(--s2) var(--s5); align-items: flex-start; }
.feed-line[hidden] { display: none; }
.feed-line time { flex: none; width: 62px; padding-top: 2px; font-family: var(--mono);
                  font-size: var(--t-xs); color: var(--text-2); font-variant-numeric: tabular-nums; }
/* --text-2, not --line-3: at --line-3 the neutral "·" sat at 2.91:1, which is
   under AA even for a decorative glyph and simply hard to see. */
.feed-line .fmark { flex: none; width: 14px; padding-top: 1px; text-align: center; color: var(--text-2); }
.feed-line .ftext { font-size: var(--t-sm); color: var(--text-2); line-height: 1.55; }
.feed-line .ftext b { color: var(--text-hi); font-weight: 600; }
.feed-line .ftag { display: block; margin-top: 2px; font-family: var(--mono);
                   font-size: var(--t-xs); color: var(--text-2); }
.feed-line.ok .fmark { color: var(--ok); }
.feed-line.crit .fmark { color: var(--crit); }
.feed-line.crit .ftext { color: var(--text); }
.feed-line.crit .ftag { color: var(--crit); }
@media (prefers-reduced-motion: no-preference) {
  .feed-line.is-new { animation: feed-in var(--dur-3) var(--ease); }
}
@keyframes feed-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ---- cockpit phase strip -----------------------------------------------
   The agent's current mind-state: const words that only change brightness, so
   reduced-motion and no-JS both leave a truthful (if dim) map. The active
   segment takes the voice gold; the dot pulses only where motion is allowed. */
.cockpit { display: flex; align-items: center; gap: var(--s3);
           padding: var(--s2) var(--s5); border-bottom: 1px solid var(--line);
           font-family: var(--mono); font-size: var(--t-xs); }
.cp-seg { display: inline-flex; align-items: center; gap: var(--s2);
          color: var(--line-3); letter-spacing: .14em; text-transform: uppercase;
          transition: color var(--dur-2) var(--ease); }
.cp-seg i { width: 8px; height: 8px; border-radius: 50%; background: var(--line-3);
            transition: background var(--dur-2) var(--ease); }
.cp-seg.on { color: var(--accent); }
.cp-seg.on i { background: var(--accent-strong); }
@media (prefers-reduced-motion: no-preference) {
  .cp-seg.on i { animation: pulse 1.6s var(--ease) infinite; }
}
.cp-seg.on.feel-ok { color: var(--ok); }
.cp-seg.on.feel-ok i { background: var(--ok); }
.cp-seg.on.feel-crit { color: var(--crit); }
.cp-seg.on.feel-crit i { background: var(--crit); }
.cp-arrow { color: var(--line-3); }

/* ============================ report ==================================== */
.report-hero { display: flex; align-items: center; gap: var(--s6); flex-wrap: wrap; }
.grade {
  display: grid; place-items: center; gap: 2px; flex: none;
  width: 96px; height: 96px; border-radius: var(--r-lg);
  background: var(--surface-2); border: 1px solid var(--line-2);
}
.grade b { font-size: var(--t-2xl); font-weight: 700; line-height: 1;
           letter-spacing: -.045em; color: var(--text-hi); }
.grade span { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .12em;
              text-transform: uppercase; color: var(--text-2); }
.grade.ok b { color: var(--ok); }
.grade.crit b { color: var(--crit); }
.report-id { flex: 1; min-width: 240px; }
.report-id h1 { font-family: var(--mono); font-size: var(--t-lg); overflow-wrap: anywhere; }

.finding { padding: var(--s5); border-top: 1px solid var(--line); }
.finding:first-child { border-top: 0; }
.finding-head { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s2); }
.finding-head h3 { font-size: var(--t-md); }
.finding-head .endpoint { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-2); }
.finding p { font-size: var(--t-base); color: var(--text-2); max-width: 72ch; }
.finding .evidence { display: inline-flex; align-items: center; gap: var(--s2); margin-top: var(--s3);
                     font-family: var(--mono); font-size: var(--t-xs); color: var(--text-2); }
.finding .evidence:hover { color: var(--accent); }

/* ============================ key/value ================================= */
.kv { display: flex; flex-direction: column; }
.kv li { display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
         padding: var(--s4) var(--s5); border-top: 1px solid var(--line); font-size: var(--t-base); }
.kv li:first-child { border-top: 0; }
.kv span { color: var(--text-2); }
.kv b { font-family: var(--mono); font-weight: 600; color: var(--text); text-align: right; }

/* ============================ verify box ================================ */
.verify { margin-top: var(--s4); padding: var(--s4) var(--s5); background: var(--bg);
          border: 1px solid var(--line-2); border-radius: var(--r); }
.verify h4 { font-size: var(--t-sm); margin-bottom: var(--s2); }
.verify p { font-size: var(--t-sm); color: var(--text-2); margin-bottom: var(--s3); }
.verify code { display: block; padding: var(--s3); background: var(--surface-2);
               border: 1px solid var(--line); border-radius: var(--r-sm);
               font-family: var(--mono); font-size: var(--t-xs); color: var(--accent);
               overflow-x: auto; white-space: pre; }
.verify .actions { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s3); }
.verify .result { margin-top: var(--s3); font-size: var(--t-sm); }
.verify .result.ok { color: var(--ok); }
.verify .result.bad { color: var(--crit); }

/* ============================ auth / login ==============================
   A brand surface, not a task surface — so it earns a colour strategy the
   dashboard never would: the panel is DRENCHED in Revelation Gold. That is
   brand-legal, not a liberty: --accent-fill and --accent-ink are the same hex
   in both of BDS's lights and are verified at 10.15:1 (usage law #4), so the
   panel is identical and accessible in Range and Report alike.

   Structure follows the reference the client liked (quiet form, drenched
   panel), but the panel's texture is ours: a dot matrix that resolves into the
   Loop-Eye and orbits it, rather than decorative noise.
   ====================================================================== */
.auth { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); min-height: 100dvh; }

/* ---- form side ---- */
.auth-form-side { display: flex; flex-direction: column; padding: var(--s7) var(--gutter); background: var(--bg); }
.auth-brand { display: flex; align-items: center; gap: var(--s3); flex: none; }
.auth-brand .mark { width: 26px; height: 26px; color: var(--mark); }
.auth-brand .word { font-weight: 800; font-size: var(--t-md); letter-spacing: .14em; color: var(--text-hi); }

.auth-body { flex: 1; display: flex; flex-direction: column; justify-content: center;
             align-items: center; padding: var(--s9) 0; }
.auth-card { width: 100%; max-width: 380px; }
.auth-card h1 { font-size: var(--t-xl); letter-spacing: -.02em; text-align: center; }
.auth-card > p.lede { margin-top: var(--s3); text-align: center; font-size: var(--t-sm);
                      color: var(--text-2); text-wrap: pretty; }
.auth-form { display: flex; flex-direction: column; gap: var(--s3); margin-top: var(--s7); }
.auth-form label.lbl { font-size: var(--t-sm); font-weight: 600; color: var(--text); }
.auth-note { margin-top: var(--s5); padding-top: var(--s5); border-top: 1px solid var(--line);
             font-size: var(--t-xs); color: var(--text-2); text-align: center; text-wrap: pretty; }
.auth-note a { color: var(--accent); font-weight: 600; }

.auth-msg { display: flex; align-items: flex-start; gap: var(--s3); margin-top: var(--s4);
            padding: var(--s3) var(--s4); border-radius: var(--r); font-size: var(--t-sm); }
.auth-msg.ok  { color: var(--ok);   background: color-mix(in srgb, var(--ok) 12%, transparent);
                border: 1px solid color-mix(in srgb, var(--ok) 26%, transparent); }
.auth-msg.bad { color: var(--crit); background: color-mix(in srgb, var(--crit) 12%, transparent);
                border: 1px solid color-mix(in srgb, var(--crit) 26%, transparent); }

.auth-foot { flex: none; display: flex; align-items: center; justify-content: space-between;
             gap: var(--s4); flex-wrap: wrap; }
.auth-foot .seg { width: auto; grid-auto-columns: auto; }
.auth-foot .seg button { padding: 0 var(--s3); }
.auth-foot small { font-size: var(--t-xs); color: var(--text-2); }

/* ---- brand panel ---- */
.auth-panel {
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: var(--s10) var(--s9);
  background: var(--accent-fill); color: var(--accent-ink);
}
/* On the split layout the art is a full-bleed backdrop behind the copy. On a
   phone it becomes its own band above the copy — physically confined, so the
   mark cannot land on the headline no matter how the text reflows. A gradient
   scrim was the wrong tool there: it has to be tuned to a text position that
   changes with every copy edit. */
.auth-art { position: absolute; inset: 0; z-index: -2; overflow: hidden;
            --eye-cx: .56; --eye-cy: .25; --eye-r: .256; }
.auth-matrix { width: 100%; height: 100%; display: block; }
/* Legibility scrim: the matrix is dense where the eye is, and the copy has to
   win. Gold fades in toward the bottom so the headline sits on flat colour
   while the dots stay visible up top. z-index -1 keeps it above the canvas
   (-2) and below the copy (auto). */
.auth-panel::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(to top,
    var(--accent-fill) 30%,
    color-mix(in srgb, var(--accent-fill) 70%, transparent) 45%,
    transparent 60%);
}
.auth-panel .eyebrow { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .16em;
                       text-transform: uppercase; opacity: .72; }
.auth-panel h2 { font-size: clamp(1.9rem, 3.4vw, 3rem); line-height: 1.05; letter-spacing: -.035em;
                 color: var(--accent-ink); margin-top: var(--s4); max-width: 15ch; text-wrap: balance; }
.auth-panel .panel-sub { margin-top: var(--s4); max-width: 44ch; font-size: var(--t-md);
                         line-height: 1.5; opacity: .8; text-wrap: pretty; }
.auth-panel .loop { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s7);
                    font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .14em;
                    text-transform: uppercase; opacity: .78; flex-wrap: wrap; }
/* no extra opacity here: nested inside .loop's own .78 it compounded to
   2.29:1. The mono tracking already separates the arrows from the words. */
/* on gold, a bordered ink button is the right primary — a gold fill would vanish */
.btn-on-gold { border: 1px solid color-mix(in srgb, var(--accent-ink) 45%, transparent);
               color: var(--accent-ink); font-weight: 700; background: transparent; }
.btn-on-gold:hover { background: var(--accent-ink); color: var(--accent-fill); border-color: var(--accent-ink); }
.auth-panel :focus-visible { outline-color: var(--accent-ink); }

@media (max-width: 900px) {
  /* Stacked, each keeping its own space — the mark in its corner, the copy
     beneath it, the card beneath that. Everything is in normal flow and sized
     by its own content, so nothing can ride over the headline on a short
     viewport (an earlier version pinned the card to the bottom of 100dvh, and
     with devtools open it covered the copy). The only overlap is the card
     tucking into the panel's bottom padding, which is empty gold. */
  /* flex column, not block: `order` is what puts the brand first, and block
     layout ignores it (DOM order is form-then-panel, for the desktop split) */
  .auth { display: flex; flex-direction: column; position: relative; }

  .auth-panel {
    order: 1; position: relative; z-index: 0;
    display: block;
    padding: calc(42vw + var(--s6)) var(--gutter) var(--s10);
  }
  /* the mark is a corner motif: a square in the top-right, copy starting
     beneath it. Its own box, never behind the text. */
  .auth-art { position: absolute; top: var(--s4); right: var(--s4); left: auto;
              width: 42vw; height: 42vw; z-index: 0; margin: 0;
              --eye-cx: .5; --eye-cy: .5; --eye-r: .40; }
  .auth-panel::before { display: none; }
  .auth-panel h2 { font-size: clamp(1.75rem, 8.4vw, 2.4rem); margin-top: var(--s3); }
  /* the card carries the CTA; the loop and sub-paragraph are desktop breathing
     room that would push the form too far down here */
  .auth-panel .panel-sub, .auth-panel .loop, .auth-panel .btn-on-gold { display: none; }

  .auth-form-side {
    order: 2; position: relative; z-index: 1;
    margin-top: calc(var(--s9) * -1);   /* tucks into the empty gold, not the text */
    background: transparent; min-height: 0;
    padding: 0 var(--gutter) var(--s7);
  }
  .auth-brand { display: none; }        /* the panel above IS the brand here */
  .auth-body { flex: none; padding: 0; align-items: stretch; }
  .auth-card {
    max-width: none; background: var(--surface);
    border: 1px solid var(--line-2); border-bottom: 0;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    padding: var(--s6) var(--s5) var(--s5); box-shadow: var(--e3);
  }
  .auth-card h1 { font-size: var(--t-lg); }
  .auth-note { margin-top: var(--s4); padding-top: var(--s4); }
  /* footer flush with the card so the two read as one surface */
  .auth-foot {
    margin-top: 0; padding: var(--s3) var(--s5);
    background: var(--surface); border: 1px solid var(--line-2); border-top: 0;
    border-radius: 0 0 var(--r-lg) var(--r-lg); box-shadow: var(--e3);
  }
}

/* ---- glitch / decode ------------------------------------------------------
   The client asked for a "hacky glitch" feel. BDS §0 rejects the security
   cliché (neon-green-on-black = menace) by name, so this is the same energy in
   Baseer's own material: gold and ink, signal-tearing rather than Matrix rain.
   It reads as a machine decoding, not a hacker threatening.

   The canvas (auth.js) draws the Loop-Eye as a dot field and animates it per
   LOGO_SPEC §5 — the arcs orbit, the pupil never moves. All of it is inert
   under prefers-reduced-motion, which the same spec requires. */
[data-decode] { min-height: 1.05em; }
[data-decode] .scram { opacity: .55; }

.tear { position: relative; }
@media (prefers-reduced-motion: no-preference) {
  /* two clipped copies slip sideways for a few frames, like a torn signal */
  .tear::before, .tear::after {
    content: attr(data-text); position: absolute; inset: 0;
    color: var(--accent-ink); pointer-events: none;
  }
  .tear::before { clip-path: inset(12% 0 58% 0); animation: tear-a 5.5s steps(1) infinite; }
  .tear::after  { clip-path: inset(62% 0 14% 0); animation: tear-b 5.5s steps(1) infinite; }
}
@keyframes tear-a {
  0%, 92%, 100% { transform: none; opacity: 0; }
  93% { transform: translateX(-7px); opacity: .85; }
  95% { transform: translateX(4px);  opacity: .85; }
  97% { transform: none; opacity: 0; }
}
@keyframes tear-b {
  0%, 93%, 100% { transform: none; opacity: 0; }
  94% { transform: translateX(6px);  opacity: .7; }
  96% { transform: translateX(-3px); opacity: .7; }
  98% { transform: none; opacity: 0; }
}

/* ============================ landing ===================================
   Brand register, so it takes colour strategies the dashboard can't: the hero
   and the price block are DRENCHED Revelation Gold (same licence as /login —
   --accent-fill/--accent-ink are one hex in both lights at 10.15:1), and the
   argument sections sit on THE RANGE between them. Type and components are the
   app's own, deliberately: a visitor who converts should land in an interface
   that looks like what they were shown. Strategy + copy rationale:
   docs/specs/2026-08-10-landing-page-strategy.md
   ====================================================================== */
.lp { --pad: clamp(var(--s8), 7vw, var(--s12)); }
.lp section { padding-block: var(--pad); }
.lp-inner { width: min(1180px, 100% - 2 * var(--gutter)); margin-inline: auto; }
.lp-narrow { width: min(760px, 100% - 2 * var(--gutter)); margin-inline: auto; }

.lp-gold { background: var(--accent-fill); color: var(--accent-ink); position: relative;
           overflow: hidden; isolation: isolate;
           /* the drawing has to read on gold too — ink rails, not grey ones */
           --tick: rgba(0,0,0,.42); }
.lp-gold h1, .lp-gold h2, .lp-gold h3 { color: var(--accent-ink); }

/* ============================ the read field ============================
   The gold was a flat fill, and a flat fill has no light in it. This gives the
   hero a ground — but not texture for its own sake:

   · The dots are a MEASURING FIELD, the surface the instrument is looking at.
   · Their column rhythm is 100 100 100 — ب-ص-ر = 292 in binary, the same three
     bits the register mark spells out above the headline and the same three
     arcs the logo is built from. Every third column carries the 1 and reads
     heavier. You are not meant to decode it; you are meant to feel a rhythm
     that turns out to be the name.
   · The light has a source and falls off, because Revelation Gold is the
     colour of a lightbulb (BDS §0) — illumination, not menace.
   · Grain in `overlay` so it modulates the gold's luminance rather than
     washing it toward grey.

   Ink is capped at 9% / 14%. Worst case is a glyph landing on an accent dot,
   which leaves the quietest text on this surface at 4.84:1 — still AA. There is
   deliberately no dark vignette: stacked under an accent dot it took that same
   text to 4.3:1, and depth here can come from the light instead.
   ====================================================================== */
.lp-field {
  position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
  background: radial-gradient(118% 88% at 10% -12%, rgba(255,255,255,.22), transparent 60%);
}
/* the 0 columns */
.lp-field::before,
.lp-field::after {
  content: ""; position: absolute; inset: -26px 0;
  -webkit-mask-image: radial-gradient(128% 108% at 26% 42%, #000 34%, transparent 84%);
          mask-image: radial-gradient(128% 108% at 26% 42%, #000 34%, transparent 84%);
}
.lp-field::before {
  background-image: radial-gradient(circle, rgba(0,0,0,.09) 1.1px, transparent 1.2px);
  background-size: 13px 13px;
}
/* the 1 column, every third */
.lp-field::after {
  background-image: radial-gradient(circle, rgba(0,0,0,.14) 1.5px, transparent 1.6px);
  background-size: 39px 13px;
}
.lp-field .grain {
  position: absolute; inset: 0; opacity: .5; mix-blend-mode: overlay;
  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='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.16'/%3E%3C/svg%3E");
}
/* The mark at scale, mostly off the left edge — the subject of the field
   rather than another pattern in it. 6% ink: present when you look for it,
   never competing with the headline sitting over it. */
.lp-eye {
  position: absolute; left: 50%; top: 46%;
  width: min(46vw, 560px); height: min(46vw, 560px);
  translate: -140% -50%;
  color: color-mix(in srgb, var(--accent-ink) 5%, transparent);
}
@media (max-width: 940px) { .lp-eye { translate: -50% -50%; top: 30%; } }

@media (prefers-reduced-motion: no-preference) {
  /* one cell, vertically, so the loop is seamless in both tiles — the field is
     never quite still, and never asks to be watched */
  .lp-field::before { animation: field-drift 26s linear infinite; }
  .lp-field::after  { animation: field-drift 26s linear infinite reverse; }
  /* Everything that moves here moves by `transform`, so it runs on the
     compositor. A "breathing" light was the obvious next idea and it animated
     `background-position` on a full-bleed layer — a whole-hero repaint every
     frame, forever, for an effect nobody would name. A light source does not
     need to wander. */
  /* LOGO_SPEC §5, "the Orbit": the arcs turn, the pupil is the constant and
     never moves. Ninety seconds a revolution — you will not catch it moving,
     you will only notice it is not where it was. */
  .lp-eye .arcs { animation: orbit 90s linear infinite; transform-origin: 60px 60px; }
}
@keyframes field-drift { to { transform: translateY(13px); } }
@keyframes orbit { to { transform: rotate(360deg); } }
.lp-gold .eyebrow { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .16em;
                    text-transform: uppercase; opacity: .72; }
.lp-gold :focus-visible { outline-color: var(--accent-ink); }

/* ---- top bar ---- */
.lp-nav { display: flex; align-items: center; gap: var(--s4); padding: var(--s5) 0; }
.lp-nav .brand { padding: 0; }
.lp-nav .brand .mark, .lp-nav .brand .word { color: var(--accent-ink); }
.lp-nav .end { margin-left: auto; display: flex; align-items: center; gap: var(--s4); }
.lp-nav a.quiet { font-size: var(--t-sm); font-weight: 600; color: var(--accent-ink); opacity: .78; }
.lp-nav a.quiet:hover { opacity: 1; }

/* ---- hero ---- */
.lp-hero { padding-block: 0 clamp(var(--s7), 5vw, var(--s10)); }
.lp-hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
                gap: clamp(var(--s7), 5vw, var(--s10)); align-items: center;
                padding-block: clamp(var(--s7), 5vw, var(--s10)); }
@media (max-width: 940px) { .lp-hero-grid { grid-template-columns: minmax(0, 1fr); } }
.lp-hero h1 { font-size: clamp(2.4rem, 6vw, 4.2rem); line-height: 1.02; letter-spacing: -.04em;
              margin-top: var(--s4); text-wrap: balance; }
/* De-emphasised by colour, not by `opacity` — the rule the rest of this file
   learned the hard way. It matters more here than anywhere: these two sit over
   a textured ground now, and element opacity would compound with every dot
   underneath them instead of being a colour you can check. */
.lp-hero .lede { margin-top: var(--s5); font-size: clamp(1rem, 1.6vw, 1.2rem); line-height: 1.55;
                 max-width: 52ch; text-wrap: pretty;
                 color: color-mix(in srgb, var(--accent-ink) 84%, transparent); }
.lp-meta { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin-top: var(--s6);
           font-family: var(--mono); font-size: var(--t-xs);
           color: color-mix(in srgb, var(--accent-ink) 78%, transparent); }
.lp-meta > span + span::before { content: "·"; margin-right: var(--s3);
                                 color: color-mix(in srgb, var(--accent-ink) 45%, transparent); }
.lp-cta { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s7); }
.btn-ink { background: var(--accent-ink); color: var(--accent-fill); font-weight: 700; }
.btn-ink:hover { background: color-mix(in srgb, var(--accent-ink) 84%, var(--accent-fill)); }

.lp-scan { background: var(--bg); border: 1px solid rgba(0,0,0,.22); border-radius: var(--r-lg);
           box-shadow: 0 2px 4px rgba(60,40,6,.18), 0 18px 40px rgba(60,40,6,.28),
                       0 40px 80px rgba(60,40,6,.20); overflow: hidden; }
.lp-scan-head { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s4);
                border-bottom: 1px solid var(--line); font-family: var(--mono);
                font-size: var(--t-xs); color: var(--text-2); }
.lp-scan-head .grow { margin-left: auto; }
.lp-scan-body { padding: var(--s4); }
.lp-scan .chips { gap: 6px; }
.lp-scan .chip { min-height: 26px; padding: 0 var(--s2); font-size: 11px; }
.lp-scan .feed-body { max-height: 224px; padding: var(--s3) 0 0; }
.lp-scan .feed-line { padding: var(--s1) 0; }
.lp-scan .feed-line time { width: 58px; }
.lp-scan .bar { margin-top: var(--s4); }
.lp-scan-foot { display: flex; align-items: baseline; gap: var(--s2); padding: var(--s3) var(--s4);
                border-top: 1px solid var(--line); font-family: var(--mono);
                font-size: var(--t-xs); color: var(--text-2); }
.lp-scan-foot b { color: var(--text-hi); font-size: var(--t-sm); }

/* ---- argument sections (THE RANGE) ---- */
.lp-dark { background: var(--bg); color: var(--text); }
.lp-h2 { font-size: clamp(1.6rem, 3.4vw, 2.5rem); line-height: 1.1; letter-spacing: -.03em;
         text-wrap: balance; max-width: 20ch; }
.lp-body { margin-top: var(--s5); font-size: var(--t-md); line-height: 1.65; color: var(--text-2);
           max-width: 62ch; text-wrap: pretty; }
.lp-body b { color: var(--text); font-weight: 650; }
.lp-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
            gap: clamp(var(--s6), 4vw, var(--s9)); align-items: start; }
@media (max-width: 860px) { .lp-split { grid-template-columns: minmax(0, 1fr); } }

.lp-facts { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s6); }
.lp-facts span { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-2);
                 border: 1px solid var(--line-2); border-radius: var(--r-full);
                 padding: 6px var(--s3); }
.lp-gold .lp-facts span { color: var(--accent-ink); border-color: rgba(0,0,0,.32); opacity: .9; }

.lp-versus { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
             gap: var(--s4); margin-top: var(--s7); }
.lp-versus > div { border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s5); }
.lp-versus .k { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .12em;
                text-transform: uppercase; }
.lp-versus p { margin-top: var(--s2); font-size: var(--t-sm); color: var(--text-2); }
.lp-versus .them .k { color: var(--warn); }
.lp-versus .us { border-color: color-mix(in srgb, var(--crit) 34%, transparent);
                 background: color-mix(in srgb, var(--crit) 7%, transparent); }
.lp-versus .us .k { color: var(--crit); }

.lp-limits { margin-top: var(--s6); display: flex; flex-direction: column; }
.lp-limits li { padding: var(--s4) 0; border-top: 1px solid var(--line);
                font-size: var(--t-md); color: var(--text-2); max-width: 66ch; }
.lp-limits li:first-child { border-top: 0; }
.lp-limits b { color: var(--text-hi); font-weight: 650; }

/* ---- price ---- */
.lp-price { text-align: center; }
.lp-price .figure { font-size: clamp(3rem, 9vw, 5.5rem); font-weight: 800; line-height: 1;
                    letter-spacing: -.05em; margin-top: var(--s5); font-variant-numeric: tabular-nums; }
.lp-price .approx { font-family: var(--mono); font-size: var(--t-sm); opacity: .72; margin-top: var(--s3); }
.lp-included { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s2) var(--s4);
               margin-top: var(--s7); font-size: var(--t-sm); opacity: .85; max-width: 64ch;
               margin-inline: auto; }
.lp-included > span + span::before { content: "·"; margin-right: var(--s4); opacity: .5; }
.lp-price .lp-cta { justify-content: center; }
.lp-price .fine { margin-top: var(--s5); font-size: var(--t-sm); opacity: .75; }

/* ---- faq ---- */
.lp-faq { margin-top: var(--s7); }
.lp-faq details { border-top: 1px solid var(--line); }
.lp-faq details:last-child { border-bottom: 1px solid var(--line); }
.lp-faq summary { display: flex; align-items: center; gap: var(--s3); cursor: pointer;
                  padding: var(--s5) 0; font-size: var(--t-md); font-weight: 600;
                  color: var(--text-hi); list-style: none; }
.lp-faq summary::-webkit-details-marker { display: none; }
.lp-faq summary::after { content: "+"; margin-left: auto; font-family: var(--mono);
                         font-size: var(--t-lg); color: var(--text-2); }
.lp-faq details[open] summary::after { content: "\2013"; }
.lp-faq p { padding-bottom: var(--s5); font-size: var(--t-base); color: var(--text-2); max-width: 66ch; }

/* ---- footer ---- */
.lp-foot { border-top: 1px solid var(--line); padding-block: var(--s7); }
.lp-foot-in { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
              font-size: var(--t-sm); color: var(--text-2); }
.lp-foot-in .end { margin-left: auto; display: flex; align-items: center; gap: var(--s4); }
.lp-foot a:hover { color: var(--accent); }

@media (max-width: 560px) {
  /* the in-page anchors crowd the wordmark at phone widths; Sign in is the only
     nav item a visitor actually needs here — the sections are a scroll away */
  .lp-nav a.quiet[href^="#"] { display: none; }
  .lp-hero-grid { padding-block: var(--s6) var(--s8); }
}

/* ============================ blueprint substrate =======================
   The page as a technical drawing rather than a stack of sections: dotted
   rails running the FULL page height at the content gutters, hairline section
   boundaries, and registration ticks where the two meet.

   Adopted from Cloudflare's system, but it earns its place here for a reason
   theirs doesn't: Baseer is a measuring instrument that proves things. Drawing
   the page like a schematic is the thesis, not decoration. What stays ours is
   everything inside it — the Loop-Eye geometry, the Certified-10 strip, the
   LOOP trust-state axis and the proof language.
   ====================================================================== */
.lp { --rail-w: min(1180px, 100% - 2 * var(--gutter));
      --rail: rgba(140,140,140,.38);   /* fixed overlay: must read on gold AND on black */
      --tick: var(--line-2); }
.lp-inner, .lp-narrow { position: relative; z-index: 1; }
.lp-inner { width: var(--rail-w); }

/* continuous rails — one fixed overlay, so they never break between sections */
.bp-rails { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.bp-rails i {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: repeating-linear-gradient(to bottom, var(--rail) 0 4px, transparent 4px 11px);
}
.bp-rails i.l { left: calc(50% - var(--rail-w) / 2); }
.bp-rails i.r { left: calc(50% + var(--rail-w) / 2); }
@media (max-width: 700px) { .bp-rails { display: none; } }

/* section boundary + registration ticks at the rail intersections */
.lp section, .lp .lp-foot { position: relative; border-top: 1px solid var(--line); }
.lp section::before, .lp section::after {
  content: ""; position: absolute; top: -4px; width: 7px; height: 7px; z-index: 2;
  background: var(--bg); border: 1px solid var(--tick); pointer-events: none;
}
.lp section::before { left: calc(50% - var(--rail-w) / 2 - 3px); }
.lp section::after  { left: calc(50% + var(--rail-w) / 2 - 3px); }
.lp .lp-gold section::before, .lp section.lp-gold::before,
.lp .lp-gold section::after,  .lp section.lp-gold::after {
  background: var(--accent-fill); border-color: rgba(0,0,0,.34);
}
@media (max-width: 700px) { .lp section::before, .lp section::after { display: none; } }

/* ---- annotation: a dotted callout pinned onto a visual, with its own ticks -- */
.anno {
  position: relative; padding: var(--s3) var(--s4);
  border: 1px dashed color-mix(in srgb, var(--accent-strong) 62%, transparent);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(6px);
  font-size: var(--t-sm); color: var(--text-2); max-width: 30ch;
}
.anno b { display: block; color: var(--accent); font-weight: 650; margin-bottom: 2px; }
.anno::before, .anno::after {
  content: ""; position: absolute; width: 5px; height: 5px;
  border: 1px solid color-mix(in srgb, var(--accent-strong) 70%, transparent);
  background: var(--bg);
}
.anno::before { top: -3px; left: -3px; }
.anno::after  { bottom: -3px; right: -3px; }

/* ---- the diptych: chaos on the left, calm drenched on the right ---------- */
.diptych { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch;
           border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; }
@media (max-width: 780px) { .diptych { grid-template-columns: 1fr; } }
.dip-side { padding: clamp(var(--s6), 3.4vw, var(--s9)); min-height: 340px;
            display: flex; flex-direction: column; }
.dip-side.chaos { background: var(--surface); position: relative; overflow: hidden; }
.dip-side.calm { background: var(--accent-fill); color: var(--accent-ink); }
.dip-k { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .14em;
         text-transform: uppercase; }
.dip-side.chaos .dip-k { color: var(--warn); }
.dip-side.calm .dip-k { color: var(--accent-ink); opacity: .7; }
.dip-h { font-size: clamp(1.5rem, 2.6vw, 2.1rem); line-height: 1.08; letter-spacing: -.03em;
         margin-top: var(--s4); text-wrap: balance; }
.dip-side.calm .dip-h { color: var(--accent-ink); }
.dip-p { margin-top: var(--s4); font-size: var(--t-sm); line-height: 1.6; max-width: 40ch; }
.dip-side.chaos .dip-p { color: var(--text-2); }
.dip-side.calm .dip-p { color: var(--accent-ink); opacity: .82; }
.dip-foot { margin-top: auto; padding-top: var(--s6); }

/* the noise pile: what a scanner actually hands you */
.pile { display: flex; flex-direction: column; gap: 5px; margin-top: var(--s5); }
.pile span {
  font-family: var(--mono); font-size: var(--t-2xs); color: var(--text-2);
  border: 1px solid var(--line); border-radius: 4px; padding: 4px var(--s2);
  background: var(--bg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pile span.hot { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 34%, transparent); }
/* one proven finding, stated plainly — the calm side needs no pile */
.proof-card { margin-top: var(--s5); border: 1px solid rgba(0,0,0,.28); border-radius: var(--r);
              background: rgba(0,0,0,.08); padding: var(--s4); }
/* subordinate by colour, not by opacity — at opacity .65 this label sat at
   4.15:1 on the drenched panel. Opacity is not a colour system: it compounds
   with every ancestor and it is invisible to review. Third time this exact bug
   has appeared in this file (see .regmark .lbl, .loopmark .step). */
.proof-card .pk { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .1em;
                  text-transform: uppercase;
                  color: color-mix(in srgb, var(--accent-ink) 78%, transparent); }
.proof-card .pv { margin-top: var(--s2); font-size: var(--t-sm); line-height: 1.5; }

/* ---- bento: live product surfaces, one cell drenched -------------------- */
.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--s4); margin-top: var(--s7); }
.bento > * { border: 1px dashed var(--line-2); border-radius: var(--r-lg); padding: var(--s5);
             position: relative; min-width: 0; background: var(--surface); }
.bento .b-wide { grid-column: span 4; }
.bento .b-tall { grid-column: span 2; }
.bento .b-half { grid-column: span 3; }
.bento .b-full { grid-column: span 6; }
@media (max-width: 900px) { .bento { grid-template-columns: 1fr; }
  .bento > * { grid-column: 1 / -1 !important; } }
.bento h3 { font-size: var(--t-md); }
.bento p { margin-top: var(--s3); font-size: var(--t-sm); color: var(--text-2); max-width: 48ch; }
.bento .drench { background: var(--accent-fill); color: var(--accent-ink); border-style: solid;
                 border-color: rgba(0,0,0,.2); }
.bento .drench h3 { color: var(--accent-ink); }
.bento .drench p { color: var(--accent-ink); opacity: .84; }
/* registration ticks on each cell — the drawing continues into the components */
.bento > *::before, .bento > *::after {
  content: ""; position: absolute; width: 5px; height: 5px; border: 1px solid var(--line-2);
  background: var(--bg); pointer-events: none;
}
.bento > *::before { top: -3px; left: -3px; }
.bento > *::after  { bottom: -3px; right: -3px; }
.bento .drench::before, .bento .drench::after { background: var(--accent-fill); border-color: rgba(0,0,0,.3); }

/* ---- scroll reveal: one motion idea, used consistently ------------------ */
@media (prefers-reduced-motion: no-preference) {
  /* `.armed` is added by JS, so the hidden state only ever exists when
     something is guaranteed to remove it. Without JS the content is simply
     visible — a reveal must enhance a visible default, never gate it. */
  .rise.armed { opacity: 0; transform: translateY(14px); }
  .rise.armed.in { opacity: 1; transform: none;
             transition: opacity .6s var(--ease), transform .6s var(--ease); }
  .rise.d1.in { transition-delay: .07s } .rise.d2.in { transition-delay: .14s }
  .rise.d3.in { transition-delay: .21s } .rise.d4.in { transition-delay: .28s }
  .lp-scan { animation: rise-in .8s var(--ease) .15s both; }
}

/* ---- ours, not theirs ---------------------------------------------------
   Cloudflare's schematic is a metaphor — they're a CDN. Baseer is literally an
   instrument that takes a reading and proves it, so the drawing gets to mean
   something. Three additions, each derived from our own system:

   1. THE READING — a measuring sweep crosses the hero once on arrival. Not a
      scroll gimmick: it is the instrument taking its reading, and it happens
      exactly once, like a scan does.
   2. THE ENCODED RHYTHM — the mark is three arcs because ب-ص-ر = 292 =
      100100100 (LOGO_SPEC §1). That binary is printed into the substrate as a
      register mark. Nobody else can claim it; it is the one detail that could
      not have come from any other brand.
   3. THE LOOP AS STRUCTURE — sections are marked PROVE / FIX / VERIFY because
      the page genuinely is that sequence. Numbered markers are scaffolding when
      the content isn't ordered; here the order is the product.
   -------------------------------------------------------------------- */
.sweep { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 2; }
.sweep i {
  position: absolute; left: 0; right: 0; height: 140px; top: -140px;
  background: linear-gradient(to bottom, transparent,
              color-mix(in srgb, var(--accent-ink) 12%, transparent) 62%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent-ink) 34%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .sweep i { animation: reading 1500ms cubic-bezier(.32,.72,.3,1) 260ms 1 both; }
}
@keyframes rise-in { from { opacity:0; transform: translateY(18px) } to { opacity:1; transform:none } }
@keyframes reading { from { transform: translateY(0); } to { transform: translateY(115vh); } }

/* the encoded register mark — 100100100, the three 1-bits 120° apart */
.regmark { display: inline-flex; align-items: center; gap: var(--s3);
           font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .28em; }
.regmark .bits { display: inline-flex; gap: 3px; }
.regmark .bits b { width: 5px; height: 5px; border: 1px solid currentColor; opacity: .38; }
.regmark .bits b.on { opacity: 1; background: currentColor; }
.regmark .lbl { letter-spacing: .16em; }   /* no opacity: stacked on --text-2 it fell to 2.42:1 */

/* LOOP section markers — the page is the sequence, so it says so */
.loopmark { display: inline-flex; align-items: center; gap: var(--s3); margin-bottom: var(--s5);
            font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .16em;
            text-transform: uppercase; color: var(--text-2); }
/* The current step is marked by HUE and a filled dot — never by dimming the
   other two into illegibility. At opacity .34 the words "Fix" and "Verify" sat
   at 1.84:1 dark / 1.59:1 light: real copy, unreadable, so that the third word
   could be emphasised. Emphasis is what you add to one thing, not what you take
   from the others. */
.loopmark .step { display: inline-flex; align-items: center; gap: var(--s2); }
.loopmark .step .d { width: 6px; height: 6px; border-radius: 50%; border: 1px solid currentColor; }
.loopmark .step.now { color: var(--text-hi); }
.loopmark .step.now .d { background: currentColor; }
.loopmark .step.prove.now { color: var(--crit); }
.loopmark .step.fix.now { color: var(--info); }
.loopmark .step.verify.now { color: var(--ok); }
.loopmark .sep { color: var(--text-2); }

/* hero annotations pinned to the scan panel */
/* Annotations sit UNDER the panel, not over it. Pinned on top they obscured the
   very thing they annotate — Cloudflare's float in the empty space around the
   globe precisely because the subject has to stay readable. */
.rel { position: relative; }
.annos { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); margin-top: var(--s5); }
@media (max-width: 620px) { .annos { grid-template-columns: 1fr; } }
.annos .anno { max-width: none; }
/* a short leader line back up to the panel it measures */
.annos .anno { --leader: color-mix(in srgb, var(--accent-strong) 55%, transparent); }
.annos .anno > .lead { position: absolute; left: var(--s5); top: calc(-1 * var(--s5));
                       width: 1px; height: var(--s5);
                       background: repeating-linear-gradient(to bottom, var(--leader) 0 3px, transparent 3px 6px); }


/* ---- the hero's centrepiece: the pass, performed --------------------------
   The panel is the argument, so the beats are staged rather than sprinkled:

   1. THE MARK ORBITS WHILE THE SCAN RUNS. LOGO_SPEC §5 defines the Orbit as
      "the arcs rotate while a scan runs (the loop working)" — so the wordmark
      is not decorated, it is *reporting*. It stops when the pass completes,
      and the pupil never moves because the pupil is the constant.
   2. THE PROBE IS VISIBLY IN FLIGHT — a beam crosses the feed while a class is
      being tested, so waiting reads as work rather than latency.
   3. THE PROVEN MOMENT LANDS. Everything else in the pass is calm; the one
      finding gets the only sharp motion on the page — the panel edge flashes,
      the class chip snaps, the line arrives with weight. Von Restorff: the
      exception is the thing you remember.
   4. THE GRADE STAMPS. The pass resolves into an artifact, which is the product.
   ------------------------------------------------------------------------ */
.brand .mark svg g { transform-origin: 60px 60px; }
@media (prefers-reduced-motion: no-preference) {
  .brand.scanning .mark svg g { animation: orbit 3.4s linear infinite; }
}

.lp-scan { transition: box-shadow .5s var(--ease), border-color .5s var(--ease); }
.lp-scan .feed-body { position: relative; }
/* the beam: a probe is in flight */
.lp-scan.probing .feed-body::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, transparent,
              color-mix(in srgb, var(--accent-strong) 13%, transparent), transparent);
  height: 46px;
}
@media (prefers-reduced-motion: no-preference) {
  .lp-scan.probing .feed-body::after { animation: probe 900ms var(--ease) infinite; }
}
@keyframes probe { from { transform: translateY(-46px) } to { transform: translateY(240px) } }

/* the proven moment */
.lp-scan.hit { border-color: color-mix(in srgb, var(--crit) 60%, transparent);
               box-shadow: 0 0 0 1px color-mix(in srgb, var(--crit) 40%, transparent),
                           0 18px 50px color-mix(in srgb, var(--crit) 22%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  .feed-line.landed { animation: land 520ms cubic-bezier(.2,.9,.3,1) both; }
  .chip.snap { animation: snap 460ms cubic-bezier(.34,1.4,.5,1) both; }
}
@keyframes land { from { opacity: 0; transform: translateX(-10px) } to { opacity: 1; transform: none } }
@keyframes snap { 0% { transform: scale(1) } 42% { transform: scale(1.16) } 100% { transform: scale(1) } }

/* the grade stamps in when the pass resolves */
.lp-grade { display: inline-flex; align-items: baseline; gap: var(--s2);
            font-family: var(--mono); font-size: var(--t-xs); color: var(--text-2); }
.lp-grade b { font-family: var(--sans); font-size: var(--t-md); font-weight: 800;
              letter-spacing: -.02em; color: var(--crit); }
@media (prefers-reduced-motion: no-preference) {
  .lp-grade { animation: stamp 460ms cubic-bezier(.3,1.3,.4,1) both; }
}
@keyframes stamp { 0% { opacity:0; transform: scale(1.5) } 100% { opacity:1; transform: none } }

/* The hero scan panel and its annotations are a SPECIMEN of THE RANGE, not a
   themed surface: a scan looks like this whichever palette the visitor picked,
   and a light panel floating on the gold hero is neither legible (it fell to
   3.1:1 in light) nor true to the product. Pin the Range tokens on the subtree
   so every component inside resolves against a dark ground in both themes. */
.lp-scan, .annos {
  --bg: #0e0e0e; --surface: #161616; --surface-2: #1e1e1e;
  --line: #2c2c2c; --line-2: #404040; --line-3: #616161;
  --text: #ececec; --text-2: #9f9f9f; --text-hi: #f7f7f7;
  --accent: #ffcf7a; --accent-strong: #f5b23c;
  --crit: #f9736b; --warn: #e8873a; --ok: #57cc8a; --info: #5ab0f2;
  color: var(--text);
}

/* ============================ app: the same hand ========================
   The dashboard is a task surface, so it takes the drawing language at a much
   lower volume than the landing page — registration ticks and the live mark,
   not rails and drenched panels. Consistency of voice, not of treatment.
   ====================================================================== */

/* registration ticks on panels: the schematic continues into the product,
   quietly. Two corners, not four — enough to read as drawn, not as chrome. */
.panel { position: relative; }
.panel::before, .panel::after {
  content: ""; position: absolute; width: 5px; height: 5px; pointer-events: none;
  border: 1px solid var(--line-2); background: var(--bg);
}
.panel::before { top: -3px; left: -3px; }
.panel::after  { bottom: -3px; right: -3px; }

/* THE MARK REPORTS. LOGO_SPEC §5: "the arcs rotate while a scan runs (the loop
   working)". The sidebar already knows whether one is running, so the wordmark
   becomes a live status rather than a logo — and the pupil holds, because the
   pupil is the constant. This is the single best use of the mark we have. */
@media (prefers-reduced-motion: no-preference) {
  .brand.scanning .mark svg g { animation: orbit 3.4s linear infinite; }
}
.brand.scanning { position: relative; }

/* the live feed gets the same beats as the hero — it is the same component,
   and the proven moment deserves the same weight in the product as in the pitch */
.feed.hit { border-color: color-mix(in srgb, var(--crit) 55%, transparent);
            box-shadow: 0 0 0 1px color-mix(in srgb, var(--crit) 34%, transparent); }
.feed { transition: border-color .5s var(--ease), box-shadow .5s var(--ease); }
.feed-body { position: relative; }
.feed.probing .feed-body::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; height: 44px;
  background: linear-gradient(to bottom, transparent,
              color-mix(in srgb, var(--accent-strong) 11%, transparent), transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .feed.probing .feed-body::after { animation: probe 900ms var(--ease) infinite; }
}

/* ============================ state components ==========================
   P1 of the state map (docs/specs/2026-08-10-state-model.md). These exist to
   serve one rule: the interface must never overstate certainty. A banner is how
   the app says "this is not what it looks like" — so it is loud, it is above
   the content it qualifies, and it never appears without naming the next move.
   ====================================================================== */
.banner {
  display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s4) var(--s5); border-radius: var(--r); border: 1px solid;
  font-size: var(--t-base);
}
.banner + * { margin-top: var(--s6); }
.banner b { display: block; color: var(--text-hi); margin-bottom: 2px; }
.banner p { color: var(--text-2); font-size: var(--t-sm); max-width: 74ch; }
.banner .ic { flex: none; width: 20px; height: 20px; border-radius: 50%; display: grid;
              place-items: center; border: 1px solid currentColor;
              font-family: var(--mono); font-size: 11px; margin-top: 1px; }
.banner .actions { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s4); }
.banner.err  { border-color: color-mix(in srgb, var(--crit) 40%, transparent);
               background: color-mix(in srgb, var(--crit) 7%, transparent); }
.banner.warn { border-color: color-mix(in srgb, var(--warn) 40%, transparent);
               background: color-mix(in srgb, var(--warn) 7%, transparent); }
.banner.info { border-color: color-mix(in srgb, var(--info) 40%, transparent);
               background: color-mix(in srgb, var(--info) 7%, transparent); }
.banner.err .ic  { color: var(--crit); }
.banner.warn .ic { color: var(--warn); }
.banner.info .ic { color: var(--info); }
.banner[hidden] { display: none; }

/* all-green: the SAME measurement reporting a good value — deliberately not a
   congratulation card. Rule 1: a clean result is a result, not an absence. */
.allgreen { display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap;
            padding: var(--s6); border-radius: var(--r-lg);
            border: 1px solid color-mix(in srgb, var(--ok) 34%, transparent);
            background: color-mix(in srgb, var(--ok) 5%, transparent); }
.allgreen .fig { font-size: var(--t-2xl); font-weight: 700; line-height: 1;
                 letter-spacing: -.04em; color: var(--ok); font-variant-numeric: tabular-nums; }
.allgreen .cp { flex: 1; min-width: 240px; }
.allgreen .cp b { display: block; color: var(--text-hi); font-size: var(--t-md); font-weight: 650; }
.allgreen .cp p { color: var(--text-2); font-size: var(--t-sm); margin-top: 4px; max-width: 62ch; }
/* On a phone the copy wraps to five lines and `align-items: center` parks the
   figure beside the middle of the paragraph, reading as decoration. Stack it:
   the number leads the sentence, which is what it is doing. */
.allgreen .fig span { font-size: var(--t-xs); color: var(--text-2); display: block; }
@media (max-width: 559px) {
  .allgreen { flex-direction: column; align-items: flex-start; gap: var(--s4); }
  .allgreen .cp { min-width: 0; }
  .allgreen .btn { width: 100%; }
}

/* ---- the trophy roster: held attacks, listed ------------------------------ */
/* The held set rendered as the proof it is — "we ran N real attacks, every one
   failed — here's each." Lives on the same chip states, so a new Certified-10
   class slots in with no view change. */
.roster { margin-top: var(--s5); padding: var(--s4) var(--s5);
          border: 1px solid color-mix(in srgb, var(--ok) 34%, transparent);
          border-radius: var(--r); background: color-mix(in srgb, var(--ok) 4%, transparent); }
.roster-head { display: flex; gap: var(--s3); align-items: flex-start; }
.roster-head .i { width: 18px; height: 18px; flex: none; margin-top: 2px; stroke: var(--ok); }
.roster-head b { display: block; color: var(--text-hi); font-size: var(--t-md); font-weight: 650; }
.roster-head p { color: var(--text-2); font-size: var(--t-sm); margin-top: 4px; max-width: 66ch; }
.roster-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
               gap: var(--s1) var(--s4); margin-top: var(--s4); list-style: none; }
.roster-list li { display: flex; align-items: baseline; gap: var(--s2);
                  padding: var(--s2) 0; border-top: 1px solid var(--line); }
.roster-tick { color: var(--ok); font-weight: 700; flex: none; }
.roster-name { font-size: var(--t-sm); color: var(--text); }
.roster-code { margin-left: auto; font-family: var(--mono); font-size: var(--t-2xs);
               color: var(--text-2); }

/* ---- the verdict's third state and its evidence -------------------------- */
/* Not measured. Same size and same position as a real number, because it is
   answering the same question — it just answers "we don't know yet", which is
   a legitimate answer and a green 0 is not. */
.verdict-figure.none { color: var(--text-2); }
.verdict-flag { display: flex; gap: var(--s2); align-items: flex-start; margin-top: var(--s4);
                font-size: var(--t-sm); color: var(--text-2); max-width: 66ch; }
.verdict-flag .i { flex: none; margin-top: 2px; }
.verdict-flag b { font-weight: 600; }
.verdict-flag.ok .i { stroke: var(--ok); }
.verdict-flag.ok b { color: var(--text); }
.verdict-flag.regress .i { stroke: var(--crit); }
.verdict-flag.regress b { color: var(--crit); }

/* ============================ the loop bar ==============================
   PROVE → FIX → VERIFY, with a position on it. The owner label is the part
   that earns its keep: `prove_complete` is not us being slow, it is the
   customer's move, and a wait nobody owns reads as a stall (rule 2).
   ====================================================================== */
.loopwrap { margin-top: var(--s6); }
.loopbar { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4); }
.loopbar .lb { display: grid; gap: 2px; position: relative; padding-top: var(--s4); }
.loopbar .track { position: absolute; top: 0; left: 0; right: 0; height: 2px;
                  border-radius: 2px; overflow: hidden; background: var(--line-2); }
.loopbar .track i { display: none; height: 100%; width: 34%;
                    background: var(--accent-fill); border-radius: 2px; }
.loopbar .waiting .track { background: repeating-linear-gradient(90deg,
                             var(--line-2) 0 3px, transparent 3px 7px); }
.loopbar .done .track    { background: var(--ok); }
.loopbar .stopped .track { background: var(--crit); }
.loopbar .active .track  { background: color-mix(in srgb, var(--accent-fill) 22%, transparent); }
.loopbar .active .track i { display: block; }
.loopbar .lb-t { font-size: var(--t-sm); font-weight: 600; color: var(--text-2); }
.loopbar .done .lb-t   { color: var(--text); }
.loopbar .active .lb-t { color: var(--text-hi); }
/* The owner label stays neutral in every state. It was the accent gold on the
   active stage, which is fine on the page ground and 4.49:1 once the bar sits
   inside the report's info-tinted banner — a component that only passes on one
   background is a component that will fail on the next one. The active stage is
   already carried by a moving gold track and the brightest title; the owner does
   not need to shout as well. */
.loopbar .lb-o { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .08em;
                 text-transform: uppercase; color: var(--text-2); }
.banner .loopbar { margin-top: var(--s4); max-width: 520px; }
.loopline { margin-top: var(--s4); font-size: var(--t-sm); color: var(--text-2); max-width: 74ch; }
@media (prefers-reduced-motion: no-preference) {
  .loopbar .active .track i { animation: lbrun 2.6s var(--ease) infinite; }
}
@keyframes lbrun { 0% { transform: translateX(-100%); } 100% { transform: translateX(295%); } }
@media (max-width: 559px) { .loopbar { grid-template-columns: 1fr; gap: var(--s3); } }

/* ---- a finding explains its own status, once, in a line ------------------ */
.finding-note { margin-top: var(--s2); font-size: var(--t-xs); color: var(--text-2); max-width: 72ch; }
.finding-note.regress { color: var(--text); padding-left: var(--s3);
                        border-left: 2px solid var(--crit); }
/* dashed = a decision, not a demonstration — the same meaning the `decided`
   pill carries, so the two reinforce instead of each inventing a language */
.finding-note.decided { padding-left: var(--s3);
                        border-left: 2px dashed color-mix(in srgb, var(--info) 65%, transparent); }
.finding.regress { background: color-mix(in srgb, var(--crit) 4%, transparent); }

/* ============================ skeletons =================================
   A spinner says "something is happening". A skeleton says "here is what is
   coming, and where it will be". Every skeleton below is built from the same
   box metrics as the component it stands in for, so nothing moves when the
   real content lands — a layout shift at the end of a wait is worse than
   having shown nothing at all.
   ====================================================================== */
.sk { position: relative; overflow: hidden; border-radius: var(--r-sm);
      background: var(--surface-2); }
.sk::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
             background: linear-gradient(90deg, transparent,
               color-mix(in srgb, var(--text) 7%, transparent), transparent); }
@media (prefers-reduced-motion: no-preference) {
  .sk::after { animation: skshine 1.5s ease-in-out infinite; }
}
@keyframes skshine { to { transform: translateX(100%); } }
.sk-circle { border-radius: 50%; }

/* shaped like .feed-line: 62px time column, 14px mark, flexible text */
.sk-feed { display: flex; gap: var(--s4); padding: var(--s2) var(--s5); align-items: flex-start; }
.sk-feed .t { width: 62px; height: 11px; flex: none; margin-top: 3px; }
.sk-feed .m { width: 14px; height: 11px; flex: none; margin-top: 3px; }
.sk-feed .x { height: 11px; margin-top: 3px; flex: 1; }
/* three lines of decreasing width reads as prose rather than as a table */
.sk-feed:nth-child(2) .x { max-width: 78%; }
.sk-feed:nth-child(3) .x { max-width: 56%; }

/* shaped like .row: 34px glyph, title + meta, trailing pill */
.sk-row { display: flex; align-items: center; gap: var(--s4);
          padding: var(--s4) var(--s5); min-height: 68px; border-top: 1px solid var(--line); }
.sk-row:first-child { border-top: 0; }
.sk-row .g { width: 34px; height: 34px; flex: none; }
.sk-row .c { flex: 1; min-width: 0; display: grid; gap: var(--s2); }
.sk-row .c .a { height: 13px; max-width: 42%; }
.sk-row .c .b { height: 11px; max-width: 26%; }
.sk-row .p { width: 66px; height: 18px; flex: none; border-radius: var(--r-full); }

/* shaped like the .verify card it replaces, down to the two kv rows */
.sk-verify { margin-top: var(--s4); padding: var(--s4) var(--s5); background: var(--bg);
             border: 1px solid var(--line-2); border-radius: var(--r); display: grid; gap: var(--s3); }
.sk-verify .h { height: 13px; max-width: 46%; }
.sk-verify .p { height: 11px; max-width: 78%; }
.sk-verify .k { height: 34px; }
.sk-verify .btn-sk { height: 36px; max-width: 168px; border-radius: var(--r-sm); }

/* ---- page transition: what "loading" means in a server-rendered app -------
   There is no client router to show a skeleton for a whole page, so the honest
   signal is that the request is in flight. Two frames of delay before it
   appears, so a fast navigation never flashes a bar at anyone. */
.navbar-progress { position: fixed; inset: 0 auto auto 0; height: 2px; width: 0;
                   background: var(--accent-fill); z-index: var(--z-top);
                   opacity: 0; pointer-events: none; }
.navbar-progress.on { opacity: 1; width: 82%;
                      transition: width 8s cubic-bezier(.05,.7,.1,1); }
.navbar-progress.done { opacity: 0; width: 100%;
                        transition: width var(--dur-2) var(--ease), opacity var(--dur-3) var(--ease); }
/* An eight-second creeping bar is exactly the motion this preference is about.
   The information — a request is in flight — is not motion, so it stays: the
   bar appears at full width and disappears, with nothing travelling. */
@media (prefers-reduced-motion: reduce) {
  .navbar-progress.on,
  .navbar-progress.done { transition: none; width: 100%; }
}

/* standalone error page — no shell, because the shell needs the database and a
   database failure is exactly when this page has to render */
.errpage { min-height: 100dvh; display: flex; flex-direction: column;
           padding: var(--s7) var(--gutter); background: var(--bg); }
.errpage .body { flex: 1; display: grid; place-items: center; }
.errpage .card { width: min(520px, 100%); text-align: center; }
.errpage .code { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .18em;
                 text-transform: uppercase; color: var(--text-2); }
.errpage h1 { font-size: var(--t-xl); letter-spacing: -.025em; margin-top: var(--s4); text-wrap: balance; }
.errpage p { margin-top: var(--s4); color: var(--text-2); text-wrap: pretty; }
.errpage .actions { display: flex; gap: var(--s3); justify-content: center;
                    flex-wrap: wrap; margin-top: var(--s7); }

/* ============================ touch targets =============================
   WCAG 2.5.8 / Fitts. This replaces a `.hit` pseudo-element expander that
   carried the comment "applied to every control under 44px" and was applied to
   nothing — a policy written down and never run. Growing the control itself is
   also the sturdier mechanism: an invisible ::after overlay is clipped by any
   `overflow: hidden` ancestor and silently overlaps its neighbours.

   Keyed on `pointer: coarse`, not on viewport width, because the question is
   what is doing the pointing — a 1280px touchscreen needs the bigger target and
   a 400px window driven by a mouse does not.

   Last in the file on purpose: these selectors have the same specificity as the
   component rules they override, so placing them earlier meant they lost, which
   is exactly what happened on the first attempt.
   ====================================================================== */
@media (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn-lg { min-height: 48px; }
  .field { min-height: 44px; }
  .field-lg { min-height: 48px; }
  .nav-toggle { width: 44px; height: 44px; }
  button.chip { min-height: 44px; padding-inline: var(--s3); }
  .acct { min-height: 44px; }
  /* The one deliberate exception: three segmented buttons at 44px each make a
     132px slab inside a popover. 40px clears WCAG 2.5.8 AA (24px) with room and
     keeps the control the size of the choice it represents. */
  .seg button { min-height: 40px; }
  .crumbs a, .pop-list a, .finding .evidence { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ============================ /what-we-test ============================
   THE COVERAGE SPECIFICATION — its own identity, not the landing.
   The landing is the performance (THE RANGE, gold, the test playing). This is
   the document (BDS's THE REPORT — the datasheet a founder reads and forwards):
   a contents rail that tracks position, numbered sections, and the ten as a
   coverage matrix a developer scans. Restrained; gold is an accent, not a wash.
   Theme-aware (dark default / light = THE REPORT). Built like a spec.
   ====================================================================== */
.spec { background: var(--bg); color: var(--text); }
.spec-wrap { width: min(1060px, 100% - 2 * var(--gutter)); margin-inline: auto; }

/* ---- slim document header ---- */
.spec-top { border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: var(--z-sticky);
            background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(8px); }
.spec-top-in { display: flex; align-items: center; justify-content: space-between; min-height: 56px; }
.spec-top .brand { display: inline-flex; align-items: center; gap: var(--s3); }
.spec-top .brand .mark { color: var(--mark); display: inline-flex; }
.spec-top .brand .mark svg { width: 22px; height: 22px; }
.spec-top .brand .word { font-weight: 700; letter-spacing: .16em; font-size: var(--t-sm); color: var(--text-hi); }
.spec-topnav { display: flex; gap: var(--s5); font-size: var(--t-sm); }
.spec-topnav a { color: var(--text-2); }
.spec-topnav a:hover { color: var(--accent); }
@media (max-width: 560px) { .spec-topnav a:not(:last-child) { display: none; } }

/* ---- masthead: a document title block, not a hero ---- */
.spec-masthead { padding-block: clamp(var(--s6), 3.5vw, var(--s8)) clamp(var(--s7), 4vw, var(--s9)); }
.spec-doclabel { display: inline-flex; align-items: center; gap: var(--s3); font-family: var(--mono);
                 font-size: var(--t-xs); letter-spacing: .08em; text-transform: uppercase; color: var(--text-2); }
.spec-code { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .14em;
             color: var(--accent-ink); background: var(--accent-fill); border-radius: 4px;
             padding: 2px 7px; font-weight: 700; }
.spec-masthead h1 { margin-top: var(--s4); font-size: clamp(2.2rem, 5vw, 3.3rem); line-height: 1.02;
                    letter-spacing: -.035em; }
.spec-standfirst { margin-top: var(--s4); font-size: clamp(1rem, 1.6vw, 1.18rem); line-height: 1.55;
                   max-width: 62ch; color: var(--text-2); text-wrap: pretty; }
.spec-standfirst b { color: var(--text); font-weight: 650; }
.spec-meta { margin-top: var(--s7); display: flex; flex-wrap: wrap; gap: 0;
             border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; width: fit-content; }
.spec-meta > div { padding: var(--s3) var(--s5); border-right: 1px solid var(--line); }
.spec-meta > div:last-child { border-right: 0; }
.spec-meta dt { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .08em;
                text-transform: uppercase; color: var(--text-2); }
.spec-meta dd { margin-top: 3px; font-size: var(--t-sm); color: var(--text-hi); font-weight: 600; }
@media (max-width: 620px) { .spec-meta { width: 100%; }
  .spec-meta > div { flex: 1 1 45%; border-bottom: 1px solid var(--line); } }

/* ---- the body: contents rail + spec column ---- */
.spec-body { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: clamp(var(--s7), 5vw, var(--s10));
             align-items: start; padding-bottom: var(--s12); }
@media (max-width: 860px) { .spec-body { grid-template-columns: 1fr; gap: var(--s6); } }

.spec-rail { position: sticky; top: calc(56px + var(--s6)); display: flex; flex-direction: column; gap: var(--s5); }
@media (max-width: 860px) { .spec-rail { position: static; top: 0; } }
.spec-toc-h { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .14em;
              text-transform: uppercase; color: var(--text-2); margin-bottom: var(--s3);
              padding-bottom: var(--s3); border-bottom: 1px solid var(--line); }
.spec-toc ol { list-style: none; padding: 0; display: flex; flex-direction: column; }
.spec-toc a { display: flex; align-items: baseline; gap: var(--s3); padding: var(--s2) 0 var(--s2) var(--s3);
              font-size: var(--t-sm); color: var(--text-2); border-left: 2px solid transparent;
              transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease); }
/* --text-2, not --line-3: these numbers carry the section index, so they're
   informational text and must clear AA (line-3 sits at 3.4:1). */
.spec-toc a .s-n { font-family: var(--mono); font-size: var(--t-2xs); color: var(--text-2);
                   font-variant-numeric: tabular-nums; }
.spec-toc a:hover { color: var(--text); }
.spec-toc a.is-current { color: var(--text-hi); border-left-color: var(--accent-fill); }
.spec-toc a.is-current .s-n { color: var(--accent); }
@media (max-width: 860px) {
  /* rail becomes a horizontal jump-bar on narrow screens */
  .spec-toc ol { flex-direction: row; flex-wrap: wrap; gap: var(--s2); }
  .spec-toc a { border-left: 0; border: 1px solid var(--line-2); border-radius: var(--r-full);
                padding: 5px var(--s3); }
  .spec-toc a.is-current { border-color: var(--accent); }
}
.spec-cta { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s4) var(--s5);
            border: 1px solid var(--line-2); border-radius: var(--r); color: var(--text);
            transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease); }
.spec-cta:hover { border-color: var(--accent); background: var(--surface); }
.spec-cta .cta-label { font-size: var(--t-sm); font-weight: 600; color: var(--text-hi); }
.spec-cta .cta-price { display: flex; align-items: center; justify-content: space-between; }
.spec-cta .cta-price b { font-family: var(--mono); color: var(--accent); font-size: var(--t-sm); }
.spec-cta .cta-price .i { stroke: var(--accent); }
@media (max-width: 860px) { .spec-cta { flex-direction: row; align-items: center; justify-content: space-between; }
  .spec-cta .cta-price { flex: none; gap: var(--s3); } }

/* ---- spec sections ---- */
.spec-doc { min-width: 0; }
.spec-sec { padding-top: var(--s9); margin-top: var(--s9); border-top: 1px solid var(--line);
            scroll-margin-top: 72px; }
.spec-sec:first-child { padding-top: 0; margin-top: 0; border-top: 0; }
.spec-secnum { font-family: var(--mono); font-size: var(--t-sm); color: var(--accent);
               letter-spacing: .08em; margin-bottom: var(--s3); }
.spec-sec h2 { font-size: clamp(1.4rem, 2.6vw, 1.9rem); line-height: 1.15; letter-spacing: -.025em;
               max-width: 24ch; text-wrap: balance; }
.spec-lead { margin-top: var(--s4); font-size: var(--t-md); line-height: 1.65; color: var(--text-2);
             max-width: 68ch; }
.spec-lead b { color: var(--text); font-weight: 600; }

/* scope */
.spec-scope { margin-top: var(--s6); display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
@media (max-width: 620px) { .spec-scope { grid-template-columns: 1fr; } }
.scope-col { padding: var(--s5); border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
.scope-col.yes { border-color: color-mix(in srgb, var(--ok) 28%, transparent);
                 background: color-mix(in srgb, var(--ok) 4%, transparent); }
.scope-h { display: flex; align-items: center; gap: var(--s2); font-family: var(--mono);
           font-size: var(--t-2xs); letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
           margin-bottom: var(--s4); }
.scope-col.yes .scope-h .i { stroke: var(--ok); }
.scope-col.yes .scope-h { color: var(--ok); }
.scope-x { color: var(--text-2); font-family: var(--mono); }
.scope-col ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.scope-col li { font-size: var(--t-sm); color: var(--text); padding-left: var(--s4); position: relative; }
.scope-col.yes li::before { content: "+"; position: absolute; left: 0; color: var(--ok); font-family: var(--mono); }
.scope-col.no li { color: var(--text-2); }
.scope-col.no li::before { content: "–"; position: absolute; left: 0; color: var(--line-3); font-family: var(--mono); }
.scope-note { margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--line);
              font-size: var(--t-sm); color: var(--text-2); }

/* three layers */
.spec-layers { margin-top: var(--s6); list-style: none; padding: 0; display: flex; flex-direction: column; }
.spec-layers li { display: flex; gap: var(--s5); padding: var(--s5) 0; border-top: 1px solid var(--line); }
.spec-layers li:first-child { border-top: 0; }
.lyr-n { font-family: var(--mono); font-size: var(--t-sm); color: var(--accent); flex: none; width: 2.4rem;
         padding-top: 3px; font-variant-numeric: tabular-nums; }
.spec-layers h3 { font-size: var(--t-lg); letter-spacing: -.02em; }
.spec-layers p { margin-top: var(--s2); font-size: var(--t-sm); color: var(--text-2); line-height: 1.6; max-width: 60ch; }

/* the coverage matrix */
.spec-matrix { margin-top: var(--s6); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; }
.mx-head, .mx-row { display: grid; grid-template-columns: 2.4rem minmax(150px, 210px) 1fr 3.2rem; gap: var(--s4);
                    padding: var(--s4) var(--s5); align-items: baseline; }
.mx-head { background: var(--surface-2); border-bottom: 1px solid var(--line);
           font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .08em;
           text-transform: uppercase; color: var(--text-2); }
.mx-head .mx-status { text-align: center; }
.mx-grouprow { display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap;
               padding: var(--s4) var(--s5) var(--s2); background: color-mix(in srgb, var(--accent-fill) 5%, transparent);
               border-top: 1px solid var(--line); }
.mx-group:first-of-type .mx-grouprow { border-top: 0; }
.mx-grouptitle { font-family: var(--mono); font-size: var(--t-2xs); letter-spacing: .08em;
                 text-transform: uppercase; color: var(--accent); }
.mx-groupframe { font-size: var(--t-xs); color: var(--text-2); }
.mx-row { border-top: 1px solid var(--line); }
.mx-row:hover { background: var(--surface); }
.mx-n { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-2);
        font-variant-numeric: tabular-nums; padding-top: 2px; }
.mx-type b { display: block; font-size: var(--t-sm); color: var(--text-hi); letter-spacing: -.01em; }
.mx-code { display: inline-block; margin-top: 4px; font-family: var(--mono); font-size: var(--t-2xs);
           letter-spacing: .06em; color: var(--text-2); background: var(--surface-2);
           border: 1px solid var(--line); border-radius: 4px; padding: 1px 6px; }
.mx-q { display: block; font-size: var(--t-sm); color: var(--text); font-weight: 600; line-height: 1.4; }
.mx-a { display: block; margin-top: 4px; font-size: var(--t-xs); color: var(--text-2); line-height: 1.55; }
.mx-status { text-align: center; }
.mx-row .mx-status .i { stroke: var(--ok); }
@media (max-width: 680px) {
  .mx-head { display: none; }
  .mx-row { grid-template-columns: 1fr; gap: var(--s2); position: relative; padding-right: var(--s8); }
  .mx-n { position: absolute; right: var(--s5); top: var(--s4); }
  .mx-row .mx-status { position: absolute; right: var(--s5); bottom: var(--s4); text-align: right; }
}

/* the loop */
.spec-loop { margin-top: var(--s6); list-style: none; padding: 0; display: grid; gap: var(--s4);
             grid-template-columns: repeat(3, 1fr); }
@media (max-width: 720px) { .spec-loop { grid-template-columns: 1fr; } }
.spec-loop li { padding: var(--s5); border: 1px solid var(--line); border-top-width: 2px;
                border-radius: var(--r-lg); background: var(--surface); }
.spec-loop .lp-prove  { border-top-color: var(--crit); }
.spec-loop .lp-fix    { border-top-color: var(--info); }
.spec-loop .lp-verify { border-top-color: var(--ok); }
.loop-k { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .08em; text-transform: uppercase;
          color: var(--text-2); }
.lp-prove .loop-k { color: var(--crit); } .lp-fix .loop-k { color: var(--info); } .lp-verify .loop-k { color: var(--ok); }
.spec-loop p { margin-top: var(--s3); font-size: var(--t-sm); color: var(--text-2); line-height: 1.6; }
.spec-loop p b { color: var(--text); font-weight: 600; }

/* proof link */
.spec-link { display: inline-flex; align-items: center; gap: var(--s2); margin-top: var(--s5);
             font-family: var(--mono); font-size: var(--t-sm); color: var(--accent); }
.spec-link:hover { color: var(--accent-strong); }
.spec-link .i { stroke: currentColor; }

/* safety */
.spec-safety { margin-top: var(--s6); list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--s4); }
.spec-safety li { display: flex; gap: var(--s3); font-size: var(--t-sm); color: var(--text-2); line-height: 1.6;
                  max-width: 74ch; }
.spec-safety .i { flex: none; stroke: var(--ok); margin-top: 2px; }
.spec-safety b { color: var(--text); font-weight: 600; }
.spec-end { margin-top: var(--s8); padding: var(--s7); border: 1px solid var(--line); border-radius: var(--r-lg);
            background: var(--surface); text-align: center; }
.spec-end p { font-size: var(--t-lg); color: var(--text-hi); letter-spacing: -.02em; margin-bottom: var(--s5);
              text-wrap: balance; }

/* footer */
.spec-foot { border-top: 1px solid var(--line); padding-block: var(--s7); }
.spec-foot-in { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
                font-size: var(--t-sm); color: var(--text-2); }
.spec-foot .regmark { display: inline-flex; align-items: center; gap: var(--s3); font-family: var(--mono);
                      font-size: var(--t-2xs); letter-spacing: .28em; color: var(--text-2); }
.spec-foot .regmark .bits { display: inline-flex; gap: 3px; }
.spec-foot .regmark .bits b { width: 5px; height: 5px; border: 1px solid currentColor; }
.spec-foot .regmark .bits b.on { background: currentColor; }
.spec-foot .regmark .lbl { letter-spacing: 0; }
.spec-foot-in .end { margin-left: auto; display: flex; gap: var(--s4); }
.spec-foot a:hover { color: var(--accent); }
