/* Scryos design tokens — THE INSTRUMENT (2026-08-11).
   ------------------------------------------------------------------
   Alex's brief: "light, usable and functional, but it should be scary
   powerful… it's a density/layout/font thing." Approved from the v3
   mockup. Shopify keeps only the anatomy (top bar, left nav, right
   rail); Foundry supplies the density; the monospace becomes the
   product's primary voice and prose is the exception.

   THE NAMES ARE KEPT ON PURPOSE. ~200 renderer call sites read
   --ink/--card/--line/--muted, so redefining the values here restyles
   the whole app without touching a single renderer — the same trick
   the 2026-08-09 merge used, and the reason this pass is a reskin
   rather than a rebuild.

   TWO REAL THEMES, not an inversion. `data-theme` on <html> wins in
   both directions; the OS preference is the default when nothing is
   stamped. Dark is graphite-cool with terminal accents, because a
   green-tinted inversion of a light palette reads as a filter rather
   than a second design.

   BRAND V2 (2026-08-14, Alex's explicit instruction this evening):
   the evergreen/moss green identity is RETIRED as the brand accent,
   product-wide. The one accent (--blue/--teal/--moss/--moss-ink/--bz)
   moves from green to the pastel-purple family from the reskinned
   marketing site (scryos-marketing/site/preview.html, the source of
   truth read for these exact values): #7c5cc4 is the text-safe purple
   for light-surface text/borders/fills (≥4.5:1 on white), #a78bfa is
   the lighter "core" purple used where dark surfaces give more
   contrast headroom, #ede9fe/#241f38 are the pale/deep tint
   backgrounds. --ink/--bg move from near-black/cool-grey to the
   marketing site's concrete-grey family (#3a3d42 ink, #f6f6f8 field);
   --muted (body copy) moves to its #5b5f5a. Dark surfaces move to a
   neutral concrete family (#0b0d0c/#141614…) instead of the old
   green-tinted graphite, per Alex's explicit "not pure green-tinted
   obsidian" instruction.
   --good/--bad/--warn stay their semantic hexes untouched — status
   colour is not brand colour, and a "healthy" green dot must keep
   meaning healthy. --future keeps its own distinct violet
   (#5f4fa8/#a795e8) on purpose: it already read as a different hue
   family from the old green accent, and keeping it different from the
   NEW purple accent still matters — "declared but unbuilt" must not
   collide with ordinary brand emphasis now that emphasis is purple
   too. --ai (amber) is untouched for the same reason.
   --reticle (the corner-bracket ticks on every panel/card via
   .panel::before/::after etc. — see port.css/styles.css/setup.css)
   moves from neutral grey to a purple tint, so the existing
   corner-mark instrumentation reads as brand-v2 on sight rather than
   needing new markup. Five tokens are new: --dot (the page-canvas
   dot-grid, matching marketing's #d9dae0) and --glass-bg/
   --glass-card-bg/--glass-border/--glass-hi (the rgba glass surfaces
   for topbars and major cards — backdrop-filter blur/saturate is
   applied at the call site since a filter isn't a colour token).

   2026-08-28: Summit/Browserbase reskin — same names, new values. */
:root {
  color-scheme: light;

  /* ink + surfaces */
  --ink:#000000; --text:#000000; --navy:#6247a3;
  --bg:#ffffff; --card:#ffffff; --surface2:#faf9f7; --subtle:#fbfaf8;
  --line:#e5e2dc; --soft:#efeeea; --chip:#f4f2ee;
  /* GROUPED-SURFACE FILL (Round 5, 2026-08-30, owner ruling #3): a
     dedicated fill token for the "several siblings compared at a
     glance" cards (.kcard, .bcard, .brf2-card, .chgcell/.smcell, the
     kanban .lg-pcard…) that lost their stroke in the box-doctrine pass
     and, until now, borrowed --line — a BORDER color, not a fill one —
     to stay visible. That regression is documented at each call site's
     own "CONTRAST FIX" comment; this token is the actual fix: its own
     value, sitting between --chip (too pale once the stroke is gone)
     and --line (a color that means "edge" everywhere else in the app),
     so a reader can tell a filled card from a bare one without this
     token ever being mistaken for a border by the next person editing
     it. --line itself is untouched — it keeps meaning "edge." */
  --fill:#ece8e0;
  /* reticle token stays defined (test_p4b resolves it); the bracket
     rules that consumed it are removed in port.css/styles.css/setup.css */
  --reticle:#c9c0e8;

  /* interactive — the one accent is iris, text-safe on white */
  --blue:#8064d8; --teal:#8064d8; --blue-bg:#efeafb;
  --moss:#8064d8; --moss-ink:#6247a3; --moss-bg:#efeafb;

  /* semantic state — the only place red/amber/green may come from */
  --good:#177347; --good-bg:#e2f1e8;
  --bad:#ad2917;  --bad-bg:#f8e9e6;
  --warn:#a55311; --warn-bg:#f9efe2;

  /* text hierarchy */
  --muted:#55524e; --faint:#9a9f9a; --disabled:#c8c4bc;

  /* AI surfaces (amber-ink register, readable on white) */
  --ai:#9a7524; --ai-bg:#f7edd9;

  /* Declared and not built. Its own hue on purpose: good/warn/bad all
     judge something that EXISTS, and this judges nothing — it marks a
     concept the system intends. Kept a distinct violet from the Brand
     v2 accent purple on purpose (see file header). */
  --future:#5f4fa8; --future-bg:#edeaf7;

  /* the status line is dark in BOTH themes — it is a terminal, and a
     terminal that turns white in daylight stops reading as one.
     Brand v2: the concrete family (#0b0d0c/#101211…), not the old
     green-tinted graphite. */
  --topbar:#101211; --topbar-ink:#e6eae8; --topbar-dim:#8b968f;
  --term-good:#3ddc97; --term-warn:#e8b04b; --term-dim:#6d7a74;

  /* Brand v2 glass: rgba surfaces for topbars and major cards. Blur/
     saturate live at the call site (backdrop-filter is not a colour). */
  --dot:#e9e6f0;
  --glass-bg:#ffffff;
  --glass-card-bg:#ffffff;
  --glass-border:#e5e2dc;
  --glass-hi:#ffffff;

  /* geometry — SHARP corners (design prototype, 2026-08-15, owner
     instruction: "sharp rectangular borders separating pertinent
     information"). --r drops from 2px to 0; port.css additionally
     force-zeroes every hardcoded border-radius left in ledger.css and
     any JS inline style, so nothing in the app stays soft by accident. */
  --r:0; --headh:46px;
  /* ONE CONTROL HEIGHT (Round 5, 2026-08-30, Control Kit grammar): every
     capsule/choice in the app resolves to the same physical height, so a
     row mixing a button and a select never shows one control taller than
     its neighbor. Replaces the handful of hand-typed 36px/38px/40px/44px
     min-heights scattered across ledger.css/port.css/entry-app/setup-app —
     those call sites now read this token instead of re-typing the number. */
  --ctl-h:36px;
  /* THE WHITESPACE SCALE (2026-08-30, the unboxing pass). --s2/--s3
     are the original two; --s1/--s4/--s5 join them because removing
     `.panel`'s border left nothing marking where one topic ends and
     the next begins, and a gap can only do that job if the steps are
     a real rhythm rather than scattered magic numbers:
       --s1  4px   micro    - a label to its own value
       --s2  12px  within   - a heading to its first line of body
       --s3  18px  sub-block- a hero line to the table supporting it
       --s4  32px  distinct - related but separate blocks in one topic
       --s5  56px  region   - between major topics: the number that
                             stands in for the border that used to
                             draw that boundary
     They live HERE, not in port.css where the pass first wrote them,
     because tokens.css owns every :root in the product (UI-B1,
     2026-08-09) - a component sheet defining its own tokens is how
     one design system quietly becomes two, which is the rule
     test_p4_shared_design_tokens_and_vendored_assets exists to hold. */
  --s1:4px; --s2:12px; --s3:18px; --s4:32px; --s5:56px;
  --shadow:none;

  /* type — mono FIRST. This one line is most of the character change. */
  --mono:"GT America Mono","JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --sans:"GT America","Switzer","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;
  --disp:"GT Planar",var(--sans);

  /* functional frames: border color = function. */
  --bz:#8064d8;

  /* motion — one duration family, reduced-motion stands it all down.
     N4 (2026-08-18) widens the three durations that were already here
     (fast/base/slow) with the fourth tier the brief calls for
     (instant, for the disabled-state flip below) and a distance scale
     the entrance/attention keyframes read instead of hardcoding their
     own px offsets - one number line, not one per keyframe. */
  --dur-instant:80ms; --dur-fast:140ms; --dur:220ms; --dur-slow:340ms;
  --ease:cubic-bezier(0.2, 0, 0, 1);
  --motion-d1:4px; --motion-d2:8px; --motion-d3:16px;

  /* STRATA INK (2026-08-28) — an office's colour wherever that colour is a
     CSS value: its name in a table, its mark, its sparkline. The stratum is
     chosen by the office's stable display ORDER, so --strata-0-ink is the
     first office listed, and a fourth office cycles back to 0.

     The authority for the strata is dashboard-app/manifest.js
     (PALETTE_STRATA); these mirror its `deep` (on paper) and `bright` (on a
     dark canvas) members. They exist as TOKENS, rather than page code
     writing the hex it looked up, for one reason: the theme toggle only
     stamps data-theme on <html> and does NOT re-render the page, so a hex
     baked in at build time would stay in the wrong theme's ink until the
     next navigation. Canvas and Chart.js consumers cannot resolve var() and
     do read the manifest hexes directly - those surfaces re-render on a
     theme change through their own observers. */
  --strata-0-ink:#46326e;
  --strata-1-ink:#2f6395;
  --strata-2-ink:#b0641f;
}

/* Dark: the cockpit. Neutral concrete with the same purple accent,
   lifted to its lighter "core" value where dark surfaces give more
   contrast headroom — not a green-tinted inversion of the light
   palette, and not the old green-tinted graphite either. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ink:#f7f7f3; --text:#f7f7f3; --navy:#c4b5fd;
    --bg:#0b0d0c; --card:#141614; --surface2:#181a18; --subtle:#111311;
    --line:#282a28; --soft:#202220; --chip:#1c1e1c;
    /* --fill: same reasoning as the light block above — a step lighter
       than --chip, a step darker than --line, its own value rather than
       a borrowed border color. */
    --fill:#232523;
    --reticle:#8f7cc7;
    --blue:#a58de8; --teal:#a58de8; --blue-bg:#241f38;
    --moss:#a58de8; --moss-ink:#c4b5fd; --moss-bg:#241f38;
    --good:#3ddc97; --good-bg:#0e2b1c;
    --bad:#ef7059;  --bad-bg:#2d1510;
    --warn:#e8b04b; --warn-bg:#2a2010;
    --muted:#b9bcc2; --faint:#6f736f; --disabled:#3a3d3a;
    --ai:#d9a13d; --ai-bg:#2a2010;
    --future:#a795e8; --future-bg:#1f1a33;
    --topbar:#0b0d0c; --topbar-dim:#727f78;
    --dot:#202220;
    --glass-bg:#0b0d0c;
    --glass-card-bg:#141614;
    --glass-border:#282a28;
    --glass-hi:#141614;
    --bz:#a58de8;
    /* strata ink on a dark canvas — see the light block's note */
    --strata-0-ink:#a58de8; --strata-1-ink:#79b0e0; --strata-2-ink:#e8b884;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --ink:#f7f7f3; --text:#f7f7f3; --navy:#c4b5fd;
  --bg:#0b0d0c; --card:#141614; --surface2:#181a18; --subtle:#111311;
  --line:#282a28; --soft:#202220; --chip:#1c1e1c;
  /* --fill: same reasoning as the light block above — a step lighter
     than --chip, a step darker than --line, its own value rather than
     a borrowed border color. */
  --fill:#232523;
  --reticle:#8f7cc7;
  --blue:#a58de8; --teal:#a58de8; --blue-bg:#241f38;
  --moss:#a58de8; --moss-ink:#c4b5fd; --moss-bg:#241f38;
  --good:#3ddc97; --good-bg:#0e2b1c;
  --bad:#ef7059;  --bad-bg:#2d1510;
  --warn:#e8b04b; --warn-bg:#2a2010;
  --muted:#b9bcc2; --faint:#6f736f; --disabled:#3a3d3a;
  --ai:#d9a13d; --ai-bg:#2a2010;
  --future:#a795e8; --future-bg:#1f1a33;
  --topbar:#0b0d0c; --topbar-dim:#727f78;
  --dot:#202220;
  --glass-bg:#0b0d0c;
  --glass-card-bg:#141614;
  --glass-border:#282a28;
  --glass-hi:#141614;
  --bz:#a58de8;
  /* strata ink on a dark canvas — see the light block's note */
  --strata-0-ink:#a58de8; --strata-1-ink:#79b0e0; --strata-2-ink:#e8b884;
}

/* =========================================================================
   MOTION VOCABULARY (N4, 2026-08-18): "we never considered motion design" -
   Alex, 2026-08-18. This is the foundation, not a redesign: a handful of
   named keyframes defined ONCE, here, because tokens.css is the one file
   already loaded first by all three apps (dashboard, entry, setup - see
   each app's own stylesheet header: "ONE TOKEN AUTHORITY"/"loads
   /dashboard/tokens.css and nothing else"). A shared keyframe belongs
   beside the shared tokens it reads (--motion-d1/2/3, --dur-*, --ease)
   for the same reason the colour tokens do: one definition, everyone
   inherits, nobody re-declares it and drifts.

   TWO KEYFRAMES ONLY, deliberately narrow (the brief: "a small, tasteful
   first application - not choreography of every surface"):
     rt-note-enter    - arrives, never teleports. Used for confirmations
                         (a saved note, a page/route arriving) - the
                         gentle case.
     rt-refusal-cue   - a refusal that slides in gets seen. One shot, no
                         bounce, no loop: motion in SERVICE of the
                         honesty law (server/kpi's own doctrine - a
                         refusal must be seen, never dressed up), not
                         decoration on top of it.

   SECOND BOUNDARY, matching the first (nothing here touches a chart):
   charts carry meaning, animating one can misrepresent a trend that
   never moved, so no keyframe in this file is ever applied to Chart.js
   canvases or their data - see dashboard-app/charts.js, untouched.

   REDUCED MOTION: each app already carries its own blanket standdown
   (`@media (prefers-reduced-motion: reduce) { * { animation: none
   !important; transition: none !important; } }` - port.css/ledger.css,
   entry-app/styles.css, setup-app/setup.css). These keyframes need no
   standdown of their own; the existing ones already reach every
   selector that names rt-note-enter or rt-refusal-cue, because the
   selector is `*`. Nothing here may regress that - it is verified by
   scan_2a's motion checks (server/tests/scan_2a.py). */
@keyframes rt-note-enter {
  from { opacity: 0; transform: translateY(var(--motion-d1)); }
  to   { opacity: 1; transform: none; }
}
@keyframes rt-refusal-cue {
  from { opacity: 0; transform: translateX(calc(-1 * var(--motion-d2))); }
  to   { opacity: 1; transform: none; }
}

/* Disabled-state flip (N4): role-gated controls ease rather than pop.
   Native selectors only, so every button/input/select/textarea across
   all three apps inherits the SAME transition timing without a per-app
   rule - genuine inheritance, not duplication. The opacity value
   (.55) is not a new invention: it matches the "de-emphasized" value
   already used app-wide (entry-app's .setup-step.skipped, setup-app's
   .pane-glyph.is-off) rather than adding a fourth dimming value to the
   system. Each app's own button/input rule (port.css, styles.css,
   setup.css) adds `opacity` to ITS OWN transition list beside its
   existing properties - see those files - because a second `transition`
   declaration on the same selector REPLACES rather than merges with an
   earlier one; only the end-state value is safe to centralise here. */
:disabled { opacity: .55; }

/* =========================================================================
   BRAND KIT v2.0 (2026-08-18): ScryOS design tokens, added ALONGSIDE the
   existing --blue/--teal/--moss/--bz accent family above rather than
   renaming it. The brand kit's asset-manifest.csv and design-tokens.css
   (12_Developer_Kit/) are the source of truth for these exact hex values;
   do not hand-guess them. These are NEW names (--scryos-*) so nothing
   already reading --bz/--bz/--moss/--future/etc. changes behavior — a
   wholesale token rename is a bigger, riskier change than a brand-asset
   swap and was explicitly ruled out for this pass. Any call site that
   wants the v2.0 palette directly (new components going forward) should
   reach for these; existing renderers keep working unmodified through
   the old names, which the light/dark blocks above already alias to
   values in the same family (--blue:#7c5cc4 light / #a78bfa dark are
   close cousins of --scryos-iris:#8064D8 / --scryos-iris-bright:#A58DE8,
   not identical — no attempt was made to force them equal). */
:root {
  --scryos-graphite: #1D1B21;
  --scryos-concrete: #F4F2EE;
  --scryos-ink-light: #5B5863;
  --scryos-ink-dark: #C4C0C8;
  --scryos-iris: #8064D8;
  --scryos-iris-bright: #A58DE8;
  --scryos-white: #FFFFFF;
  --scryos-grid-dark: rgba(196, 192, 200, 0.075);
  --scryos-grid-light: rgba(91, 88, 99, 0.07);
}

/* =========================================================================
   SCROLL-X AFFORDANCE (VISUAL-QA-2026-08-19, findings #2/#3, 2026-08-19).
   ------------------------------------------------------------------------
   Root cause common to all three findings: a fixed-width `overflow-x:
   auto` container holding wider auto-sized content (a table, a nav bar)
   with NO visible cue that it scrolls — the cutoff reads as "broken",
   not "more here." Finding #1 (the Daily Review T7 column) was fixed by
   abbreviating the header text so the data fits without scrolling at
   all — fitting beats scrolling when it's this cheap. Findings #2 (the
   11-column Hygiene table, 344px overhang — no amount of header
   abbreviation makes 11 columns fit a 973px panel) and #3 (the
   entry-app top nav at mobile widths, 861px overhang) genuinely need a
   scroll affordance, so this ONE utility covers both rather than two
   bespoke patches.
   Two-part cue, both pure CSS, no JS, no extra markup:
   1. A visible thin scrollbar (::-webkit-scrollbar for Chrome/Safari,
      `scrollbar-width: thin` for Firefox) — many of this app's scroll
      containers otherwise hide their native scrollbar entirely (see
      entry-app's own `#mainnav::-webkit-scrollbar{display:none}`,
      which this class's selector below overrides once #mainnav also
      carries .scroll-x), so a scrollbar existing is not on its own a
      cue in this app.
   2. An edge-fade "scroll shadow": the classic background-attachment
      local/scroll trick (four background layers — two solid-to-
      transparent "cover" gradients that travel WITH the content via
      `local`, two `scroll`-attached hint gradients that stay put
      relative to the box). The net effect: the fade is invisible at
      the natural resting edge (fully covered) and appears the moment
      there is unscrolled content in that direction — it reflects real
      scroll position, not a permanent decoration on every panel.
   `--scroll-x-fade` is the one thing call sites may need to override:
   default is `var(--card)` (right for any `.panel`), entry-app's
   `#mainnav` re-points it at `var(--glass-bg)` to match the glass
   topbar it sits in (see entry-app/styles.css) rather than showing a
   flat-card-colored seam against a translucent bar. */
.scroll-x {
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
  --scroll-x-fade: var(--card);
  --scroll-x-fade-w: 26px;
  /* !important on the background-* longhands only: `.panel` (port.css,
     loaded after this file) sets `background: var(--glass-card-bg)` —
     the shorthand form, which resets background-image (and every other
     background-* sub-property) to its initial value even though
     `.panel` never mentions background-image by name. Same specificity
     (one class each), later file wins, so without !important the fade
     silently disappeared behind .panel's own background on every
     .panel.scroll-x site (confirmed via getComputedStyle during
     verification - backgroundImage read back as "none"). A utility
     class that must survive an unrelated shorthand reset on whatever
     it's mixed into is exactly the standard case for !important here. */
  background-image:
    linear-gradient(to right, var(--scroll-x-fade), transparent),
    linear-gradient(to left, var(--scroll-x-fade), transparent),
    linear-gradient(to right, rgba(0,0,0,.22), transparent),
    linear-gradient(to left, rgba(0,0,0,.22), transparent) !important;
  background-repeat: no-repeat !important;
  background-size: var(--scroll-x-fade-w) 100%, var(--scroll-x-fade-w) 100%,
    12px 100%, 12px 100% !important;
  background-position: left center, right center, left center, right center !important;
  background-attachment: local, local, scroll, scroll !important;
}
.scroll-x::-webkit-scrollbar { height: 8px; }
.scroll-x::-webkit-scrollbar-track { background: transparent; }
.scroll-x::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.scroll-x::-webkit-scrollbar-thumb:hover { background: var(--faint); }

/* =========================================================================
   GLASS MATERIAL TOKENS (docs/design/LIQUID-GLASS-SPEC.md, "The material").
   The raw ink/rim/border values port.css's .glass kit composes rgba()s
   from. --sheet-ink is only the RESTING default before a sheet has
   sampled its ground — law 03's runtime ink-solve (vibrancy-derived,
   contrast-clamped) overrides it per sheet once JS adapts; aliasing it
   to --ink here means an unadapted sheet still lands on the theme's
   real text color instead of a hand-picked literal that could drift
   from it. --sheet-base carries as a bare rgb triple (no `rgba(`
   wrapper) because .glass needs to compose its own alpha at each call
   site: `rgba(var(--sheet-base), 0.30)`. Dark's triple is 16,18,17 —
   the same hex as --topbar (#101211), not an invented near-black, so
   the sheet reads as the SAME dark register the terminal strip already
   established rather than a second one. */
:root {
  --sheet-base: 255,255,255; --sheet-ink: var(--ink);
  --rim-light: rgba(255,255,255,0.9); --rim-dark: rgba(16,18,17,0.16);
  --sheet-border: rgba(255,255,255,0.45);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sheet-base: 16,18,17; --sheet-ink: var(--ink);
    --rim-light: rgba(255,255,255,0.14); --rim-dark: rgba(0,0,0,0.5);
    --sheet-border: rgba(255,255,255,0.18);
  }
}
:root[data-theme="dark"] {
  --sheet-base: 16,18,17; --sheet-ink: var(--ink);
  --rim-light: rgba(255,255,255,0.14); --rim-dark: rgba(0,0,0,0.5);
  --sheet-border: rgba(255,255,255,0.18);
}

/* =========================================================================
   RADIUS SCALE (docs/design/LIQUID-GLASS-SPEC.md, "The radius scale").
   Radius encodes what a thing IS, never an aesthetic default — so it is
   NOT themed: what a thing is doesn't change when the toggle flips. */
:root {
  --r-record: 0;   /* sharp is what's true — panels, tables, charts, reporting chips */
  --r-touch: 6px;  /* soft is what you touch — inputs, selects, interactive chips */
  --r-cmd: 99px;   /* round is what you press — primary buttons, kbd chips */
  --r-sheet: 10px; /* glass is what's passing — transient sheets only (law 12) */
}
