/* ── Typography ───────────────────────────────────────────────────────────
   Three faces, three jobs. Self-hosted rather than linked from a font CDN:
   this repo's rule is that the app runs from `python run.py` on a laptop with
   nothing installed but Flask and still opens in five years, and a <link> to
   fonts.googleapis.com breaks both halves of that. 183 KB of Latin-subset
   woff2, served from /fonts, no third party in the request path.

   Instrument Serif carries the display sizes. It is the single change that
   moves this from "a game" to "a publication that happens to have levels in
   it" — a high-contrast serif reads as considered in a way that a UI sans at
   40px never does, and the app's whole claim is that the numbers were arrived
   at carefully.

   Manrope runs the interface. Warmer and more distinctive than the Inter it
   replaces, without giving up any legibility at 12px.

   IBM Plex Mono handles anything that is data rather than prose: tickers,
   eyebrow labels, figures in columns. Tabular by construction, so numbers stop
   shifting under each other when a value updates. */
@font-face { font-family: "Instrument Serif"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/fonts/InstrumentSerif-400.woff2") format("woff2"); }
@font-face { font-family: "Instrument Serif"; font-style: italic; font-weight: 400;
  font-display: swap; src: url("/fonts/InstrumentSerif-400i.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/fonts/Manrope-400.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/fonts/Manrope-500.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("/fonts/Manrope-600.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 700;
  font-display: swap; src: url("/fonts/Manrope-700.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/fonts/IBMPlexMono-400.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/fonts/IBMPlexMono-500.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("/fonts/IBMPlexMono-600.woff2") format("woff2"); }

/* ── Motif keyframes (theme.js / .atmosphere-motifs) ──────────────────────
   The shared vocabulary of motion behind each mood family's structure and
   particle system, most of them lifted verbatim (name and easing included)
   from the Website Design V3 canvas's own <style> block -- see PROJECT-
   BRIEF.md section 3 for what each family's structure/particles are meant
   to be, and index.html's per-family comments for which keyframe plays
   which part. Not every one of the canvas's ~453 elements is here (see
   PROJECT-BRIEF.md doctrine #3, "balance over density" -- the source itself
   was thinned ~40% after review), but each family's real named structure
   and its real particle behaviour are, not an invented placeholder. */
@keyframes env-bolt { 0%, 100% { opacity: 0; } 3% { opacity: 1; } 6% { opacity: .1; }
  9% { opacity: .9; } 13% { opacity: 0; } 45% { opacity: 0; } 48% { opacity: .7; } 52% { opacity: 0; } }
@keyframes env-rise { 0% { transform: translateY(90px); opacity: 0; }
  18% { opacity: .9; } 82% { opacity: .6; } 100% { transform: translateY(-320px); opacity: 0; } }
@keyframes env-ember { 0% { transform: translate3d(0,0,0) scale(.5); opacity: 0; }
  12% { opacity: 1; } 70% { opacity: .55; }
  100% { transform: translate3d(var(--dx,30px), -260px, 0) scale(1.1); opacity: 0; } }
@keyframes env-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes env-spinr { from { transform: rotate(0deg); } to { transform: rotate(-360deg); } }
@keyframes env-pulse { 0%, 100% { opacity: .5; transform: scale(1); } 50% { opacity: 1; transform: scale(1.05); } }
@keyframes env-dash { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: var(--dash, -900); } }
@keyframes env-blossom { 0% { transform: translate3d(0,-60px,0) rotate(0deg); opacity: 0; }
  10% { opacity: .9; } 90% { opacity: .6; }
  100% { transform: translate3d(var(--dx,40px), 620px, 0) rotate(220deg); opacity: 0; } }
/* A drift goes out and comes back. It used to run from 0 to --dx and restart,
   so every loop ended by snapping the whole layer back in one frame. */
@keyframes env-drift { 0%, 100% { transform: translate3d(0,0,0); } 50% { transform: translate3d(var(--dx,50px),0,0); } }
@keyframes env-tape { from { transform: translate3d(0,0,0); } to { transform: translate3d(-2012px,0,0); } }
@keyframes env-mote { 0% { transform: translate3d(0,0,0); opacity: 0; }
  22% { opacity: 1; } 100% { transform: translate3d(var(--dx,20px), -300px, 0); opacity: 0; } }
@keyframes env-banner { 0%, 100% { transform: skewX(0deg) scaleY(1); } 50% { transform: skewX(2.6deg) scaleY(1.02); } }
@keyframes env-swell { 0%, 100% { transform: scale(1); opacity: .55; } 50% { transform: scale(1.05); opacity: .3; } }
/* Vanguard's wedges breathe -- 15 rays fanning from the upper left. */
/* ---------------------------------------------------------------------------
   Scene keyframes ported with the canvas's six background scenes. Its own file
   defines 73; these are the 24 those scenes animate with that this app did not
   already have. Verbatim, so a scene behaves the way it was authored.
   --------------------------------------------------------------------------- */
@keyframes env-arc { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -240; } }
@keyframes env-cell { 0% { opacity: 0; transform: scaleY(.04) scaleX(1.06); filter: brightness(2.4) saturate(.3); } 8% { opacity: .95; transform: scaleY(.04) scaleX(1.06); } 17% { transform: scaleY(1.05) scaleX(1); filter: brightness(1.5); } 22% { opacity: .4; } 27% { opacity: .9; filter: none; transform: none; } 68% { opacity: .55; transform: translate3d(calc(var(--dx,0px) * .4), -70px, 0); } 100% { opacity: 0; transform: translate3d(var(--dx,0px), -190px, 0) scale(1.06); } }
@keyframes env-curl { 0%,100% { transform: rotate(0deg) scale(1); } 50% { transform: rotate(7deg) scale(1.035); } }
@keyframes env-drawin { 0% { transform: translate3d(var(--dx,60px), var(--dy,40px), 0) scale(1.15); opacity: 0; } 20% { opacity: .9; } 74% { opacity: .55; } 100% { transform: translate3d(0,0,0) scale(.12); opacity: 0; } }
@keyframes env-droplet { 0% { transform: translate3d(0,0,0) scale(.3); opacity: 0; } 9% { opacity: 1; } 42% { transform: translate3d(calc(var(--dx,20px) * .55), -132px, 0) scale(1); opacity: 1; } 100% { transform: translate3d(var(--dx,20px), 26px, 0) scale(.55); opacity: 0; } }
@keyframes env-flicker { 0%, 100% { opacity: .55; transform: scale(1); } 42% { opacity: 1; transform: scale(1.14); } 68% { opacity: .68; transform: scale(.96); } }
@keyframes env-flow { 0% { transform: translate3d(-180px,0,0) scaleX(.5); opacity: 0; } 20% { opacity: .9; } 68% { opacity: .55; } 100% { transform: translate3d(var(--dx,460px), var(--dy,-50px), 0) scaleX(1.35); opacity: 0; } }
@keyframes env-frame { 0% { transform: scale(.6) rotate(0deg); opacity: 0; } 8% { transform: scale(1.02); opacity: .95; } 20% { transform: scale(1); opacity: .6; } 100% { transform: scale(1.75); opacity: 0; } }
@keyframes env-gleam { 0%,72%,100% { opacity: 0; } 78% { opacity: .95; } 86% { opacity: .2; } }
@keyframes env-lantern { 0%,100% { transform: translate3d(0,0,0) scale(1); opacity: .4; } 32% { transform: translate3d(calc(var(--dx,18px) * .4), -6px, 0) scale(1.14); opacity: 1; } 68% { transform: translate3d(var(--dx,18px), 4px, 0) scale(.94); opacity: .62; } }
@keyframes env-plume { 0% { transform: translate3d(0, 110px, 0) scaleY(.08) scaleX(1.6); opacity: 0; } 20% { opacity: .82; } 56% { transform: translate3d(calc(var(--dx,20px) * .4), -20px, 0) scaleY(1.25) scaleX(1); opacity: .66; } 100% { transform: translate3d(var(--dx,20px), -210px, 0) scaleY(2.3) scaleX(.36); opacity: 0; } }
@keyframes env-rail { 0% { transform: translate3d(0,0,0) scaleY(.2); opacity: 0; } 12% { opacity: .9; } 26% { opacity: .35; } 33% { opacity: .85; } 70% { opacity: .5; } 100% { transform: translate3d(var(--dx,0px), -300px, 0) scaleY(1.3); opacity: 0; } }
@keyframes env-reg { 0%, 100% { opacity: .16; } 26% { opacity: .82; } 40% { opacity: .28; } 56% { opacity: .6; } 74% { opacity: .34; } }
@keyframes env-ring { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
@keyframes env-ringr { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } }
@keyframes env-row { 0% { transform: translate3d(-34px,0,0) scaleX(.16); opacity: 0; } 13% { transform: translate3d(0,0,0) scaleX(1); opacity: .95; } 26% { opacity: .5; } 32% { opacity: .82; } 70% { opacity: .46; } 100% { transform: translate3d(var(--dx,54px), -34px, 0) scaleX(1.03); opacity: 0; } }
@keyframes env-shard { 0% { transform: translate3d(0,0,0) scale(1.8) rotate(-9deg); opacity: 0; } 11% { transform: translate3d(0,0,0) scale(1) rotate(0deg); opacity: .95; } 22% { opacity: .6; } 30% { opacity: .88; } 66% { opacity: .5; } 100% { transform: translate3d(var(--dx,0px), -62px, 0) scale(1.08) rotate(5deg); opacity: 0; } }
@keyframes env-skill { 0% { transform: translate3d(0,0,0) scaleX(.08); opacity: 0; } 10% { transform: translate3d(0,0,0) scaleX(1); opacity: 1; } 30% { opacity: .8; } 100% { transform: translate3d(var(--dx,220px), var(--dy,-70px), 0) scaleX(1.7) scaleY(.55); opacity: 0; } }
@keyframes env-slashx { 0% { stroke-dashoffset: 1400; opacity: 0; } 5% { opacity: 1; } 22% { stroke-dashoffset: 0; opacity: .95; } 34% { opacity: .3; } 40% { opacity: .7; } 100% { stroke-dashoffset: 0; opacity: 0; } }
@keyframes env-spark { 0% { transform: translate3d(0,0,0) scale(.2); opacity: 0; } 8% { transform: scale(1.35); opacity: 1; } 20% { opacity: .5; } 32% { opacity: 1; } 62% { transform: translate3d(calc(var(--dx,20px) * .7), calc(var(--dy,-40px) * .7), 0) scale(.75); opacity: .55; } 100% { transform: translate3d(var(--dx,20px), var(--dy,-40px), 0) scale(.2); opacity: 0; } }
@keyframes env-sweep { 0% { transform: translate3d(0,0,0); opacity: 0; } 12% { opacity: .9; } 88% { opacity: .5; } 100% { transform: translate3d(var(--dx,260px),0,0); opacity: 0; } }
@keyframes env-window { 0% { transform: translate3d(0, 70px, 0) scale(.84); opacity: 0; } 9% { opacity: .95; } 13% { opacity: .45; } 17% { opacity: .9; } 56% { opacity: .5; } 100% { transform: translate3d(var(--dx,20px), -240px, 0) scale(1.14); opacity: 0; } }
@keyframes win-flash { 0% { opacity: .85; transform: scaleX(.2); } 100% { opacity: 0; transform: scaleX(1); } }
@keyframes win-open { 0% { opacity: 0; transform: translateY(10px) scaleY(.05); } 40% { opacity: 1; transform: translateY(0) scaleY(1.03); } 70% { transform: translateY(0) scaleY(.995); } 100% { opacity: 1; transform: translateY(0) scaleY(1); } }

@keyframes env-ray { 0%, 100% { opacity: .16; transform: translateX(-14px) scaleX(.94); }
  50% { opacity: .5; transform: translateX(16px) scaleX(1.06); } }
/* Aegis: the Blood-Oath cross "breathes" slowly; cross-shards/shield-hexes
   snap into an over-bright state, hold, then rise and fade -- the snap is
   the point (his motif is assembly, not drift), never a smooth fade-in. */
@keyframes env-crest { 0%, 100% { opacity: .32; transform: scale(1); } 50% { opacity: .55; transform: scale(1.014); } }
@keyframes env-barrier { 0% { transform: scale(.18); opacity: 0; }
  6% { transform: scale(1.2); opacity: 1; } 13% { transform: scale(1); opacity: .8; }
  56% { transform: scale(1); opacity: .48; } 70% { opacity: .58; } 100% { transform: scale(1.08); opacity: 0; } }
/* Sovereign: shadow particles rise from below -- direction is
   non-negotiable, they never fall. */
@keyframes env-arise { 0% { transform: translate3d(0, var(--dy,240px), 0) scaleY(.18) scaleX(1.4); opacity: 0; }
  15% { opacity: .95; }
  44% { transform: translate3d(calc(var(--dx,20px) * .35), calc(var(--dy,240px) * .3), 0) scaleY(1.4) scaleX(.75); opacity: .8; }
  100% { transform: translate3d(var(--dx,20px), -160px, 0) scaleY(2.6) scaleX(.24); opacity: 0; } }
/* Aether: foam bubbles wander; spray droplets arc up and fall back. */
@keyframes env-wander { 0% { transform: translate3d(0,0,0) scale(.75); opacity: 0; } 12% { opacity: .9; }
  32% { transform: translate3d(calc(var(--dx,60px) * .35), -50px, 0) scale(1.06); opacity: 1; }
  56% { transform: translate3d(calc(var(--dx,60px) * .8), -14px, 0) scale(.9); opacity: .8; }
  80% { transform: translate3d(var(--dx,60px), -70px, 0) scale(1.1); opacity: .5; }
  100% { transform: translate3d(calc(var(--dx,60px) * 1.25), -32px, 0) scale(.7); opacity: 0; } }
@keyframes env-spray { 0% { transform: translate3d(0,0,0) scale(.28); opacity: 0; } 11% { opacity: 1; }
  44% { transform: translate3d(calc(var(--dx,60px) * .5), var(--dy,-80px), 0) scale(1); opacity: .9; }
  100% { transform: translate3d(var(--dx,60px), 46px, 0) scale(.45); opacity: 0; } }
/* Eclipse: dual-blade slivers streak, one blade's worth each way. */
@keyframes env-thrust { 0% { transform: translate3d(-90px, 28px, 0) scaleX(.18); opacity: 0; } 11% { opacity: 1; }
  34% { opacity: .9; } 100% { transform: translate3d(var(--dx,520px), var(--dy,-160px), 0) scaleX(1.55); opacity: 0; } }
/* Scoped to :root without a recorded motion choice. The environment is a
   product feature with its own Still/Calm/Surge control, so someone who picks
   a level has said what they want and that beats the OS default; someone who
   has not picked still gets the accessible behaviour. theme.js sets
   data-motion-choice="user" the moment the picker is used. */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion-choice="user"]) .atmosphere-motifs * { animation: none !important; }
}
/* Motion level (theme.js applyMotion / --envPlay). !important is required: the
   motif elements set their own `animation` shorthand inline, which resets
   animation-play-state on the element itself and outranks an external
   stylesheet at equal specificity. */
.atmosphere-motifs, .atmosphere-motifs * { animation-play-state: var(--envPlay, running) !important; }

/* **Still removes the moving parts rather than freezing them.**
   Chris: *"when I turn it to still the objects of motion disappear, makes no
   sense to just have them stop in the background, looks dumb."* He is right --
   a paused animation is a film stopped on a frame, not a still image, and an
   ember halted halfway up the screen reads as a bug. `theme.js:markMovingParts`
   tags every element that actually animates, so this hides exactly those and
   leaves the structure: the market grid, the candlesticks, the shield lattice,
   the sunburst. The background stays; the traffic across it goes.

   Transitioned, so switching away from Still brings them back in rather than
   snapping. */
.atmosphere-motifs [data-env-moving] { transition: opacity .5s var(--ease, ease); }
:root[data-motion="still"] .atmosphere-motifs [data-env-moving]:not([data-env-struct]) {
  opacity: 0 !important; visibility: hidden;
}
@media (prefers-reduced-motion: reduce) {
  .atmosphere-motifs [data-env-moving] { transition: none; }
  /* With animations off by the block above, every motif element is a frozen
     frame -- which is the thing this hides at Still. Same treatment, so the
     accessible default and the Still setting agree. */
  :root:not([data-motion-choice="user"]) .atmosphere-motifs [data-env-moving]:not([data-env-struct]) {
    opacity: 0 !important; visibility: hidden;
  }
}

:root {
  /* dark is the default: highest contrast for the charts, and red and gold
     read strongest against lacquer black */
  color-scheme: dark;

  --plane: #0a0807;
  --surface-1: #16110e;
  --surface-2: #1f1813;
  --surface-sunken: #100c0a;

  /* The plate a company logo sits on. Always light, in BOTH themes, which is
     why it is not one of the surface tokens: a favicon is authored for a white
     page, so a dark-inked one vanishes on a dark tile. Coca-Cola's and Apple's
     are exactly that. Giving every logo the same pale plate is what a broker's
     app does and is the only treatment that works for all of them. */
  --logo-plate: #f4f1ea;

  --text-primary: #f9f4ea;
  --text-secondary: #d3c7b3;
  --text-muted: #97897a;

  /* The chevron a <select> draws instead of the operating system's.
     A data URI rather than a file so it costs no request, and a token rather
     than a literal so the light theme can restate it -- an SVG in a
     background-image cannot read currentColor, which is the whole reason this
     is two declarations instead of one. See the select rules in
     il-app-skin.css for why the native one had to go. */
  --select-chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1.4 1.8 6 6.2 10.6 1.8' fill='none' stroke='%2397897a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --select-chevron-lit: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1.4 1.8 6 6.2 10.6 1.8' fill='none' stroke='%23e0b64a' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  --grid: #33291f;
  --axis: #55463a;
  --border: rgba(249, 244, 234, .11);
  --border-strong: rgba(249, 244, 234, .24);

  --brand: #d6293f;
  --brand-deep: #8c1122;
  /* Soft/deep tints are derived from --brand/--gold rather than stored
     per-mood: theme.js only ever sets --brand/--gold/--plane directly, and
     these formulas keep every tint in sync automatically across all twelve
     moods instead of needing a hand-tuned soft/deep pair for each. */
  --brand-soft: color-mix(in srgb, var(--brand) 68%, white 32%);
  --gold: #e0b64a;
  --gold-soft: color-mix(in srgb, var(--gold) 65%, white 35%);
  --gold-deep: color-mix(in srgb, var(--gold) 72%, black 28%);
  --amber: color-mix(in srgb, var(--gold) 88%, #fff200 12%);
  --amber-soft: color-mix(in srgb, var(--amber) 65%, white 35%);
  --amber-glow: color-mix(in srgb, var(--amber) 26%, transparent);
  --lacquer: var(--plane);
  --hazelnut: #c1793f;
  --hazelnut-soft: #dba573;
  --hazelnut-deep: #7a4f28;

  /* Atmosphere (mood.js): a flat backdrop gradient, not a scene. Four
     rejected background attempts (see docs/HANDOFF.md) established that a
     picture behind this UI never settles at a stable density; these three
     vars carry only colour and light, no drawn subject, and every mood in
     theme.js overrides them. Fallback here matches the default dark mood
     (Sovereign / "night") so there's no flash before theme.js runs. */
  --env-sky: linear-gradient(180deg, #05040a 0%, #07060e 14%, #0a0819 28%, #0f0c28 44%, #17102f 58%, #24122f 72%, #3d1226 86%, #5a1122 100%);
  --env-late: radial-gradient(120% 58% at 22% 32%, rgba(150,110,255,.2), rgba(150,110,255,0) 66%), linear-gradient(180deg, rgba(5,4,10,.5) 0%, rgba(30,12,40,.22) 42%, rgba(196,26,44,.3) 100%);
  --env-vignette: radial-gradient(105% 66% at 22% 76%, rgba(124,156,255,.10), rgba(124,156,255,0) 62%), linear-gradient(180deg, rgba(5,4,10,.62) 0%, rgba(5,4,10,.10) 26%, rgba(5,4,10,.16) 58%, rgba(5,4,10,.72) 100%);

  /* Categorical chart slots. theme.js overrides 1-3 per mood so a chart looks
     like the theme it is drawn in; 4-6 stay fixed because class chips key off
     them by name and a class should not change colour mid-session. */
  --series-1: #b8860b;  --series-2: #199e70;  --series-3: #3d7fd8;
  --series-4: #c93a52;  --series-5: #8a63d2;  --series-6: #1a93a6;
  /* Informational chrome — callouts, info alerts, the "watch" verdict. This
     used to borrow --series-3, which is a categorical slot: the data-viz rule
     is that status colours are reserved and never double as "series 3", and
     the moment series-3 became theme-dependent every callout in the app would
     have changed colour with the mood for no reason a reader could name. */
  --info: #3d7fd8;

  --good: #2aa87e;  --warning: #b8860b;  --critical: #cf3348;
  /* Fourth step of the score ramp (30-50). Separated from --warning and
     --critical by OKLab ΔE 0.14 in normal vision. Under red-green CVD the
     three warm steps do collapse, which is why the score is always printed
     beside its colour — the hue is never the only channel. */
  --serious: #fe683c;
  --success-text: #35c294;

  --sf-ring-a: rgba(249, 244, 234, .045);
  --sf-ring-b: rgba(249, 244, 234, .085);
  --sf-ring-line: rgba(249, 244, 234, .13);

  --radius-xs: 8px;
  --radius-sm: 12px;
  --radius: 18px;
  --radius-lg: 26px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.55), 0 2px 10px rgba(0,0,0,.4);
  --shadow: 0 2px 6px rgba(0,0,0,.6), 0 16px 38px rgba(0,0,0,.5);
  --shadow-lift: 0 6px 16px rgba(0,0,0,.65), 0 28px 56px rgba(0,0,0,.6);
  --shadow-lg: var(--shadow-lift);      /* alias: floating panels and modals */
  --ring: 0 0 0 3px color-mix(in srgb, var(--brand) 38%, transparent);
  --glow-gold: 0 0 24px color-mix(in srgb, var(--gold) 26%, transparent);

  --font: "Manrope", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: "Instrument Serif", ui-serif, Georgia, "Times New Roman", serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --ease: cubic-bezier(.22, .61, .36, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);   /* overshoot-free settle, for panels rising in */
  --fast: 130ms;
  --base: 200ms;
  --med: 240ms;
}

:root[data-theme="light"] {
  /* Knights of the Blood Oath colours: a crisper white uniform-ground, a
     brighter flag-red than the dark-mode lacquer allows, brass trim left
     alone (it was already right), and a hazelnut accent -- her hair --
     for the moments that want warmth instead of alarm: a rank badge, a
     hover, a rule under a name, the scenery below. --brand still clears
     4.5:1 on every surface it sits on as text (5.4-5.8:1, checked against
     --plane and white) and as white-on-brand button fill (5.84:1);
     --hazelnut clears 4.6:1+ wherever it carries text. */
  color-scheme: light;
  --plane: #faf6ef;
  --surface-1: #ffffff;
  --surface-2: #ffffff;
  --surface-sunken: #f2ebdd;

  /* Same plate in the light theme -- see the dark block. Near-white against a
     parchment card still reads as a tile rather than disappearing into it. */
  --logo-plate: #fffdf8;

  --text-primary: #17120f;
  --text-secondary: #524a43;
  --text-muted: #877d72;

  /* Same chevron, restated in this theme's ink -- see the dark block. */
  --select-chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1.4 1.8 6 6.2 10.6 1.8' fill='none' stroke='%23877d72' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --select-chevron-lit: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1.4 1.8 6 6.2 10.6 1.8' fill='none' stroke='%23b8860b' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  --grid: #e6dccc;
  --axis: #c6b8a3;
  --border: rgba(23, 18, 15, .12);
  --border-strong: rgba(23, 18, 15, .22);

  --brand: #c81430;
  --brand-deep: #8a0f22;
  --brand-soft: color-mix(in srgb, var(--brand) 68%, white 32%);
  --gold: #b8860b;
  --gold-soft: color-mix(in srgb, var(--gold) 65%, white 35%);
  --gold-deep: color-mix(in srgb, var(--gold) 72%, black 28%);
  --amber: #e0a209;
  --amber-soft: color-mix(in srgb, var(--amber) 65%, white 35%);
  --amber-glow: color-mix(in srgb, var(--amber) 20%, transparent);
  --lacquer: var(--plane);
  --hazelnut: #8a5a30;
  --hazelnut-soft: #c99a66;
  --hazelnut-deep: #6b4522;

  --env-sky: linear-gradient(180deg, #f9fcff 0%, #fffdf6 12%, #fff4dc 26%, #ffe6b8 38%, #ffd28c 48%, #ffc46e 55%, #ffdca6 66%, #fff0d6 79%, #fffaf0 91%, #ffffff 100%);
  --env-late: radial-gradient(130% 58% at 74% 22%, rgba(255,241,214,.55), rgba(255,241,214,0) 66%), linear-gradient(180deg, rgba(255,255,255,.3) 0%, rgba(252,230,204,.2) 48%, rgba(232,181,106,.22) 100%);
  --env-vignette: radial-gradient(120% 78% at 50% 2%, rgba(255,255,255,.62), rgba(255,255,255,0) 54%), linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.2) 34%, rgba(255,253,250,.08) 60%, rgba(255,255,255,.3) 100%);

  --series-1: #c8961e;  --series-2: #1b8f6a;  --series-3: #2a6fd6;
  --series-4: #b3243c;  --series-5: #7a4fbf;  --series-6: #0f8fa3;
  --info: #2a6fd6;

  /* --warning was #c8961e, which only reached 2.5:1 on the light page ground.
     Darkened to clear 3:1 without changing its hue. */
  --good: #1b8f6a;  --warning: #a28e00;  --critical: #b3243c;
  --serious: #c1601b;
  --success-text: #14724f;

  --sf-ring-a: rgba(23, 18, 15, .045);
  --sf-ring-b: rgba(23, 18, 15, .085);
  --sf-ring-line: rgba(23, 18, 15, .14);

  --shadow-sm: 0 1px 2px rgba(23,18,15,.06), 0 2px 8px rgba(23,18,15,.05);
  --shadow: 0 2px 4px rgba(23,18,15,.06), 0 12px 32px rgba(23,18,15,.09);
  --shadow-lift: 0 4px 10px rgba(23,18,15,.1), 0 22px 48px rgba(23,18,15,.14);
  --ring: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
  --glow-gold: 0 0 20px color-mix(in srgb, var(--gold) 22%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion-choice="user"]) *,
  :root:not([data-motion-choice="user"]) *::before,
  :root:not([data-motion-choice="user"]) *::after { animation-duration: .01ms !important;
    animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

* { box-sizing: border-box; }
/* The [hidden] attribute's implicit display:none is only a UA-stylesheet
   rule, which any component class setting its own `display` (.badge,
   .cardgrid, .onboard-overlay, ...) silently outranks -- found live via
   #providerBadge staying laid-out (just empty) instead of actually hidden.
   One !important here is cheaper and safer than auditing every `display:`
   declaration for a class that might someday sit on a hidden element. */
[hidden] { display: none !important; }
/* `overflow-x: clip`, not `hidden`.
   -------------------------------------------------------------------------
   `hidden` makes the element a scroll container. On `html`/`body` that quietly
   moved the scrollport off the viewport, and **every `position: sticky` in the
   app stopped sticking** — the top bar included. It had been that way long
   enough to read as intended: the chrome scrolled away with the page and
   nothing looked broken, it just never followed you down.

   `clip` does the same visual job — no horizontal overflow escapes — without
   creating a scroll container, so sticky resolves against the viewport again.
   This is the specific reason `clip` exists.

   Keep it as `clip`. Reverting to `hidden` to "fix" a stray horizontal
   scrollbar will silently take the top bar, the section bar, the view rail and
   the company page's section index with it. */
html, body { margin: 0; padding: 0; max-width: 100%; overflow-x: clip; }
body {
  font-family: var(--font);
  background:
    radial-gradient(1200px 520px at 8% -10%, color-mix(in srgb, var(--brand) 20%, transparent), transparent 58%),
    radial-gradient(1000px 460px at 95% -6%, color-mix(in srgb, var(--gold) 15%, transparent), transparent 60%),
    radial-gradient(900px 700px at 50% 115%, color-mix(in srgb, var(--brand-deep) 16%, transparent), transparent 62%),
    var(--plane);
  background-attachment: fixed;
  color: var(--text-primary);
  font-size: 14px; line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
/* ---------- the atmosphere ----------
   Four attempts at a picture behind this UI were rejected (three flat-filled
   SVG landscapes, then a procedurally generated fog photograph) — see
   docs/HANDOFF.md. Every attempt was competing with dense financial panels
   for the same visual attention, whatever the medium. This replaces the
   scene with what the design brief calls a mood: colour and light only, no
   drawn subject, carried entirely by --env-sky/--env-late/--env-vignette
   (set per mood in theme.js). Fixed to the viewport so the page scrolls
   over a still backdrop; changes are a CSS transition, not a cross-fade
   between two images, so switching mood costs one paint, not two assets. */
.atmosphere {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: var(--env-sky);
  transition: background 700ms var(--ease);
}
.atmosphere::before {
  content: ""; position: absolute; inset: 0;
  background: var(--env-late);
  mix-blend-mode: screen;
  opacity: .8;
}
.atmosphere::after {
  content: ""; position: absolute; inset: 0;
  background: var(--env-vignette);
}
@media (prefers-reduced-motion: reduce) { .atmosphere { transition: none; } }

.atmosphere-motifs { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.atmosphere-motifs .motif {
  position: absolute; inset: 0; transition: opacity 1.1s ease;
}
.atmosphere-motifs svg { position: absolute; inset: 0; width: 100%; height: 100%; }
@media (prefers-reduced-motion: reduce) { .atmosphere-motifs .motif { transition: none; } }

a { color: var(--brand); text-decoration-color: color-mix(in srgb, var(--brand) 35%, transparent);
    text-underline-offset: 2px; transition: color var(--fast) var(--ease); }
a:hover { color: var(--brand-soft); }
/* h1/h2/h3 are the display sizes and take the serif, per il-app-skin.css
   (loaded after this file) -- h3 used to be a small mono panel LABEL here,
   but il-app-skin.css's h1,h2,h3 rule already wins font-family/color/size on
   equal specificity by load order, leaving only this file's own
   text-transform:uppercase behind on top of 21px serif -- a broken hybrid of
   both designs, not either one. h4 stays the small UI-sans label instead. */
h1, h2, h3, h4 { margin: 0 0 .5rem; letter-spacing: -.015em; }
h1, h2 {
  font-family: var(--font-display); font-weight: 400; letter-spacing: -.005em;
  line-height: 1.12; text-wrap: balance;
}
h4 { font-weight: 650; }
h1 { font-size: 1.72rem; }
h2 { font-size: 1.34rem; }
p { margin: 0 0 .75rem; }
code { font-family: var(--font-mono); font-size: .92em;
       background: var(--surface-sunken); padding: .05rem .3rem; border-radius: var(--radius-xs); }

/* ---------- chrome ----------
   These were `header.topbar`/`nav.tabs`/`nav.subtabs` until 2026-09-04: an
   element+class selector out-specificities il-app-skin.css's plain
   .topbar/.tabs/.subtabs for every shared property (padding, gap,
   background, backdrop-filter, ...), so the pre-V3 header/nav values were
   silently winning despite il-app-skin.css loading after this file --
   confirmed live via getComputedStyle, not assumed from reading the CSS
   (padding was .55rem 1rem here, not the intended 14px var(--il-gutter)).
   Plain classes now, so cascade order does what it looks like it does:
   il-app-skin.css wins the properties it sets, this file's own properties
   (hover states, aria-selected treatment, etc.) still layer in normally. */
.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: .7rem;
  padding: .55rem 1rem;
  background: linear-gradient(180deg, color-mix(in srgb, var(--brand-deep) 26%, var(--surface-1)),
                              color-mix(in srgb, var(--surface-1) 92%, transparent));
  backdrop-filter: saturate(1.6) blur(16px);
  -webkit-backdrop-filter: saturate(1.6) blur(16px);
  border-bottom: 1px solid color-mix(in srgb, var(--gold) 34%, transparent);
  box-shadow: 0 2px 18px rgba(0,0,0,.35);
}
.brand {
  display: flex; align-items: center; gap: .6rem; min-width: 0; flex-shrink: 0;
  appearance: none; background: none; border: 0; padding: .2rem .35rem .2rem .1rem;
  border-radius: var(--radius-xs); cursor: pointer; font: inherit; text-align: left;
  transition: background var(--fast) var(--ease);
}
.brand:hover { background: color-mix(in srgb, var(--gold) 12%, transparent); }
.brand:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.brandmark { display: flex; flex: 0 0 auto; }
.brandmark svg { display: block; width: 28px; height: 28px; flex-shrink: 0;
  filter: drop-shadow(0 2px 7px rgba(0,0,0,.5)); }
.brandtext { display: flex; align-items: baseline; gap: .55rem; min-width: 0; }
.brandtext strong { white-space: nowrap; }
.brandtext > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1500px) { .brandtext > span { display: none; } }
.searchwrap { min-width: 0; }
.moodpicker {
  position: fixed; top: 56px; right: .8rem; z-index: 60; display: none;
  background: var(--surface-1); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: .85rem;
  width: min(360px, calc(100vw - 1.6rem));
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}
.moodpicker.on { display: block; animation: fadeUp var(--fast) var(--ease); }
.moodpicker h4 { margin: 0 0 .3rem; font-size: .88rem; }
/* Every section heading after the first sits under a .moodblurb, whose
   bottom margin is the gap between sections -- except Shape (under the light/
   dark row) and Background (under the shape chips), which have no blurb and
   were sitting flush against the controls above them. Same gap as .moodblurb's bottom margin, supplied. */
/* Two hosts: the header popover is .moodpicker, the landing playground is
   .lplay-panel without it. */
:is(.moodpicker, .lplay-panel) :is(.lumrow, .shapeoptions) + h4 { margin-top: .7rem; }
/* Real tile recipe, ported from the canvas's own onboarding "Design" tab
   (Context\Website Design V3\export\Investor Leveling.dc.html, tileStyle())
   -- an icon-plus-label tile in a 3-column grid, not a bare text pill.
   The icon is logo.js's own mark (window.Logo.svg via FAM_LOGO in app.js),
   the same one already driving the auto-swapping brand mark -- no new icon
   art invented for this. Shape/motion stay plain pills: the canvas itself
   only tiles the theme/mood row, its own shape row is text pills too. */
.moodoptions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; margin: .5rem 0; }
.moodopt {
  appearance: none; cursor: pointer; padding: .85rem .5rem; border-radius: var(--radius);
  background: var(--surface-sunken); border: 1px solid var(--border-strong);
  display: flex; flex-direction: column; align-items: center; gap: .5rem; text-align: center;
  font: inherit; color: var(--text-primary);
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.moodopt-icon { display: flex; flex: none; }
.moodopt-icon svg { display: block; width: 26px; height: 26px; }
.moodopt-label { font-size: .74rem; font-weight: 600; }
.moodopt:hover { transform: translateY(-1px); border-color: color-mix(in srgb, var(--brand) 55%, var(--border-strong)); }
.moodopt[aria-pressed="true"] {
  border-color: var(--brand); background: var(--surface-1);
  box-shadow: 0 12px 30px -16px color-mix(in srgb, var(--brand) 50%, transparent);
}
.moodblurb { font-size: .74rem; color: var(--text-muted); margin: .3rem 0 .7rem; min-height: 2.2em; }
.shapeoptions { display: flex; flex-wrap: wrap; gap: .35rem; margin: .4rem 0 0; }
.shapeopt {
  appearance: none; cursor: pointer; padding: .3rem .6rem; border-radius: var(--radius-pill);
  background: var(--surface-sunken); border: 1px solid var(--border);
  font: inherit; font-size: .68rem; font-weight: 600; color: var(--text-secondary);
}
.shapeopt[aria-pressed="true"] { background: var(--brand); color: #fff; border-color: var(--brand); }

.onboard-overlay {
  position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--plane) 72%, black 28%);
  backdrop-filter: blur(6px); padding: 1rem;
  animation: fadeUp var(--base) var(--ease);
}
.onboard-modal { width: min(560px, 100%); margin: 0; max-height: 88vh; overflow: auto; }
.onboard-modal .moodoptions, .onboard-modal .shapeoptions { margin-top: .5rem; }
.tabsep {
  width: 1px; align-self: center; height: 15px; flex: 0 0 auto; margin: 0 .3rem;
  background: color-mix(in srgb, var(--gold) 30%, transparent);
}
.brand strong {
  font-size: 1.24rem; letter-spacing: -.045em; font-weight: 900;
  background: linear-gradient(120deg, var(--brand-soft) 0%, var(--gold-soft) 55%, var(--gold) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 0 12px color-mix(in srgb, var(--gold) 30%, transparent));
}
.brand span { color: var(--text-muted); font-size: .76rem; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .18rem .6rem; border-radius: var(--radius-pill);
  font-size: .68rem; font-weight: 700; letter-spacing: .045em; text-transform: uppercase;
  border: 1px solid var(--border-strong); white-space: nowrap;
}
.badge.demo { background: color-mix(in srgb, var(--gold) 18%, transparent);
  border-color: color-mix(in srgb, var(--gold) 60%, transparent); color: var(--text-primary); }
.badge.live { background: color-mix(in srgb, var(--good) 16%, transparent);
  border-color: color-mix(in srgb, var(--good) 55%, transparent); color: var(--success-text); }

.tabs {
  display: flex; gap: .1rem; padding: 0 1rem;
  background: color-mix(in srgb, var(--surface-1) 97%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none; position: sticky; top: var(--il-topbar-h); z-index: 39;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs button {
  appearance: none; border: 0; background: none; cursor: pointer; position: relative;
  padding: .6rem .78rem; font: inherit; font-size: .81rem; font-weight: 550;
  color: var(--text-secondary); white-space: nowrap; border-radius: var(--radius-xs);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
/* :not([data-il-nav]) hands the section rail's selected-marker over to
   il-aura.css, which draws it per mood family instead of one generic gradient
   underline for everyone -- they are the same feature, two implementations.
   Mutual exclusion rather than out-specificity-ing one with the other: this
   file's `.tabs button[aria-selected="true"]::after` is (0,2,2) and would
   otherwise silently win `transform` over il-aura.css's (0,2,1) rule, which is
   exactly the cascade bug class 5b9e6bb/a30160e/78c91ea/c08634c were spent on.
   Anything wearing .tabs without the hook keeps this underline. */
.tabs button:not([data-il-nav])::after {
  content: ""; position: absolute; left: .5rem; right: .5rem; bottom: 0; height: 2px;
  border-radius: 2px; background: linear-gradient(90deg, var(--brand), var(--gold));
  transform: scaleX(0); transform-origin: center; transition: transform var(--med) var(--ease);
}
.tabs button:hover { color: var(--text-primary); background: color-mix(in srgb, var(--gold) 9%, transparent); }
.tabs button[aria-selected="true"] {
  color: var(--gold-soft); font-weight: 700;
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.tabs button:not([data-il-nav])[aria-selected="true"]::after { transform: scaleX(1); }
.tabs .sec-ic { margin-right: .38rem; opacity: .75; font-size: .9em; }
.tabs button[aria-selected="true"] .sec-ic { opacity: 1; }

/* Second level. Deliberately quieter than the first — a section bar that shouts
   as loudly as the one above it just recreates the twenty-two-tab problem with
   an extra row. */
.subtabs {
  display: flex; gap: .1rem; padding: .34rem 1rem;
  background: color-mix(in srgb, var(--surface-sunken) 88%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none; position: sticky; top: 92px; z-index: 38;
}
.subtabs::-webkit-scrollbar { display: none; }
.subtabs button {
  appearance: none; border: 0; background: none; cursor: pointer; font: inherit;
  padding: .28rem .62rem; font-size: .755rem; font-weight: 550; white-space: nowrap;
  color: var(--text-muted); border-radius: var(--radius-pill);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.subtabs button:hover { color: var(--text-primary);
  background: color-mix(in srgb, var(--gold) 10%, transparent); }
.subtabs button[aria-selected="true"] {
  color: var(--lacquer); font-weight: 700;
  background: linear-gradient(120deg, var(--amber-soft), var(--gold));
}

main { padding: 1.1rem; max-width: 1440px; margin: 0 auto; }
.view { display: none; }
.view.active { display: block; animation: fadeUp var(--med) var(--ease); }
@keyframes fadeUp { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ---------- controls ----------
   Was `button.btn, .btn` -- the button.btn half is (0,1,1), out-specificities
   il-app-skin.css's plain .btn (0,1,0) for every shared property regardless
   of load order, same bug class as .topbar/.tabs/.field (see the chrome
   comment above; found this one with a script, not by eye, after that
   pattern had shown up enough times to be worth searching for). Confirmed
   real: il-app-skin.css wants transparent background + text-secondary +
   10px/17px padding; this rule was forcing surface-2 fill + text-primary +
   .45rem/.95rem padding on every .btn in the app. */
.btn {
  appearance: none; cursor: pointer; font: inherit; font-size: .81rem; font-weight: 600;
  padding: .45rem .95rem; border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); background: var(--surface-2); color: var(--text-primary);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
              border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
/* Dropped the `button` prefix on every rule below (2026-09-04): out-
   specificities il-app-skin.css's plain .btn/.btn.primary rules the exact
   same way .topbar/.tabs/.subtabs did -- confirmed live, primary buttons
   were still rendering the old gradient fill instead of il-app-skin.css's
   flat one. See the ---------- chrome ---------- comment above for the
   full explanation; same fix, same reason. */
.btn:hover { border-color: color-mix(in srgb, var(--gold) 65%, transparent);
  box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.btn:active { transform: translateY(0) scale(.985); box-shadow: none; }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn.primary {
  background: linear-gradient(135deg, var(--brand-deep), var(--brand) 55%, color-mix(in srgb, var(--brand) 55%, var(--gold)));
  border-color: color-mix(in srgb, var(--gold) 40%, transparent); color: #fff;
  box-shadow: 0 2px 12px color-mix(in srgb, var(--brand) 35%, transparent);
}
.btn.primary:hover { filter: brightness(1.07); box-shadow: var(--shadow); }
.btn:disabled { opacity: .45; cursor: default; transform: none; box-shadow: none; }

.seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  overflow: hidden; background: var(--surface-2); }
.seg button { border: 0; border-radius: 0; padding: .34rem .72rem; font-size: .75rem; font-weight: 600;
  background: transparent; color: var(--text-secondary); cursor: pointer; font-family: inherit;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease); }
.seg button:hover { background: color-mix(in srgb, var(--gold) 12%, transparent); color: var(--text-primary); }
.seg button[aria-pressed="true"] {
  background: linear-gradient(135deg, var(--brand), color-mix(in srgb, var(--brand) 65%, var(--gold)));
  color: #fff; }

/* min-width:0 is doing real work here, not tidying. A text input's automatic
   minimum size is its default preferred width -- about 461px at this font --
   and `width: 100%` does not lower it. So as a flex item the input refused to
   shrink, its .field refused with it, and the card around it laid out 765px
   wide inside a 400px viewport, where body's overflow-x:hidden simply cut the
   right-hand side off. That was every form on the site below ~800px. */
input[type=text], input[type=number], textarea, select {
  font: inherit; font-size: .82rem; padding: .45rem .7rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text-primary);
  width: 100%; min-width: 0; max-width: 100%;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
input:hover, textarea:hover, select:hover { border-color: color-mix(in srgb, var(--gold) 55%, transparent); }
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--brand); box-shadow: var(--ring); }
input[type=checkbox] { accent-color: var(--brand); width: auto; }
/* Same fix as .topbar/.tabs/.btn above: was label.field, out-specificitied
   il-app-skin.css's .field (confirmed live -- display was still block, not
   the intended flex column). */
.field { display: block; margin-bottom: .6rem; min-width: 0; }
.field > span { display: block; font-size: .71rem; color: var(--text-secondary);
  margin-bottom: .22rem; font-weight: 550; }

/* ---------- layout ---------- */
.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1rem 1.15rem; margin-bottom: 1rem;
  box-shadow: var(--shadow-sm);
  /* No backdrop-filter: il-app-skin.css gives every card an opaque
     --panel-solid, so there is nothing behind it left to blur. The canvas
     calls backdrop-filter the most expensive thing on the page and takes a
     near-opaque tint instead; this was the surviving copy, and it still cost
     a compositor layer per card on every view. */
  transition: box-shadow var(--med) var(--ease), border-color var(--med) var(--ease);
}
.card:hover { box-shadow: var(--shadow); }
/* il-app-skin.css's .view is display:flex;gap:18px, which already spaces
   any .card that is a direct child -- this file's own margin-bottom above
   still applies on top (equal specificity, different property, both stick),
   so a direct-child card was getting 18px gap + 16px margin = ~34px between
   cards instead of the design's clean 18px rhythm. Zero it only for direct
   children; a .card nested one level deeper (e.g. #dashboardBody's cards)
   has no flex-gap ancestor of its own, so its margin-bottom is still the
   only thing spacing it from its siblings -- leave that path alone. */
.view > .card { margin-bottom: 0; }
.card.hero { position: relative; overflow: hidden; }
.card.hero::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--brand), var(--gold-soft) 50%, var(--brand));
}
.card.hero { border-color: color-mix(in srgb, var(--gold) 26%, transparent); }
.card.gilded { border-color: color-mix(in srgb, var(--gold) 34%, transparent);
  box-shadow: var(--shadow-sm), var(--glow-gold); }

/* ---------- shapes (theme.js + PROJECT-BRIEF.md §4) ----------
   The corner language is already live everywhere via --radius/--radius-lg/
   --radius-pill, set per shape in theme.js -- all seven shapes get correct
   corners (including 0 for the clip-path shapes below) with no CSS here.

   What follows is ported from the real, generic (not per-instance)
   [data-shape="..."] rule set in the canvas's own <helmet><style> block
   (Context\Website Design V3\export\Investor Leveling.dc.html, ~line
   60-230) -- unlike the background motifs, this was already reusable CSS,
   so the port is a selector translation, not a redraw:
     [style*="var(--panel"]  (a translucent panel)   -> .card
     [style*="var(--panelHi"] (an opaque raised panel) -> .card.hero
     [style*="var(--btnBg"]  (the filled CTA)        -> .btn.primary
     [style*="var(--ghostBg"] (the outline button)   -> .btn
     [data-tog-btn]          (segmented/toggle controls) -> .seg button, #themeToggle
     [style*="var(--rowPad"] (a selectable row)      -> tbody tr.clickable, .il-scr-row
       (two row components here where the canvas only had one -- the old
       table and the newer screener grid both get the same idiom)
   Recoloured the same way as every other pass: var(--accent) -> var(--brand),
   var(--accentHi) -> var(--brand-deep). The canvas needed !important to beat
   its own inline styles; our shape selectors already out-specificity the
   plain .card/.btn/.il-scr-row rules elsewhere (one extra attribute clause),
   so it's dropped throughout except where noted. Durations/easings/keyframe
   percentages are the real ones, not the previous pass's approximations.
   Doctrine #4: "a shape that only changes border-radius is not a shape." */
[data-shape] .card, [data-shape] .btn { transition-duration: var(--med), var(--fast); }

/* ===== Soft -- ordinary good product design, the neutral baseline ===== */
@keyframes shape-soft-in { 0% { opacity: 0; transform: translateY(18px) scale(.978); filter: blur(7px); }
  55% { opacity: 1; filter: blur(0); } 76% { transform: translateY(-2px) scale(1.003); }
  100% { opacity: 1; transform: none; filter: blur(0); } }
[data-shape="soft"] .card {
  animation: shape-soft-in .74s cubic-bezier(.16,.84,.24,1) backwards;
  box-shadow: inset 0 1px 0 0 color-mix(in srgb, #fff 34%, transparent),
    inset 0 -34px 44px -44px var(--brand), var(--shadow-sm);
  transition: transform .45s cubic-bezier(.2,.9,.25,1), filter .45s ease, box-shadow .45s ease;
}
[data-shape="soft"] .card.hero { animation-delay: .08s; }
[data-shape="soft"] .card:hover {
  transform: translateY(-3px) scale(1.004); filter: brightness(1.02);
  box-shadow: inset 0 1px 0 0 color-mix(in srgb, #fff 52%, transparent),
    inset 0 -34px 44px -40px var(--brand), 0 24px 60px -26px color-mix(in srgb, var(--brand) 40%, transparent);
}
[data-shape="soft"] .btn { transition: transform .42s cubic-bezier(.2,1.25,.3,1), box-shadow .42s ease, color .3s ease; }
[data-shape="soft"] .btn:hover { transform: translateY(-2px); box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand) 10%, transparent); }
[data-shape="soft"] .btn.primary:hover {
  transform: translateY(-3px) scale(1.018);
  box-shadow: 0 16px 36px -14px var(--brand), 0 0 0 6px color-mix(in srgb, var(--brand) 13%, transparent);
}
[data-shape="soft"] .seg button, [data-shape="soft"] #themeToggle { transition: transform .42s cubic-bezier(.2,1.35,.3,1), background .35s ease, color .35s ease; }
[data-shape="soft"] .seg button:hover, [data-shape="soft"] #themeToggle:hover { transform: translateY(-2px) scale(1.1); }
[data-shape="soft"] .ccard:hover, [data-shape="soft"] .il-scr-row:hover {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 14%, transparent); }

/* ===== Blade -- the sword-strike itself, the cut, the live edge ===== */
@keyframes shape-blade-in { 0% { clip-path: inset(0 100% 0 0); opacity: .2;
    box-shadow: inset -3px 0 0 0 var(--brand-deep); }
  22% { opacity: 1; } 92% { box-shadow: inset -3px 0 0 0 var(--brand-deep); }
  100% { clip-path: inset(0 0 0 0); opacity: 1; box-shadow: inset 0 0 0 0 transparent; } }
@keyframes shape-blade-slash { 0% { background-position: -140% 0; } 100% { background-position: 240% 0; } }
[data-shape="blade"] .card {
  animation: shape-blade-in .4s cubic-bezier(.22,1,.3,1) backwards;
  box-shadow: inset 0 1px 0 0 var(--brand-deep); transition: box-shadow .16s linear;
}
[data-shape="blade"] .card.hero { animation-delay: .06s; }
[data-shape="blade"] .card:hover { box-shadow: inset 3px 0 0 var(--brand), inset 0 0 0 1px color-mix(in srgb, var(--brand) 22%, transparent); }
/* The CTA alone gets clipped into a parallelogram -- the ghost button and
   toggles stay rectangular, matching the canvas (only [style*="var(--btnBg"]
   is clipped there, not [style*="var(--ghostBg"]). */
[data-shape="blade"] .btn.primary { clip-path: polygon(7px 0, 100% 0, calc(100% - 7px) 100%, 0 100%); }
[data-shape="blade"] .btn, [data-shape="blade"] .seg button, [data-shape="blade"] #themeToggle { position: relative; border-color: transparent; }
[data-shape="blade"] .btn::after, [data-shape="blade"] .seg button::after, [data-shape="blade"] #themeToggle::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background-image: linear-gradient(104deg, transparent 42%, color-mix(in srgb, #fff 60%, transparent) 50%, transparent 58%);
  background-repeat: no-repeat; background-size: 220% 100%; background-position: -140% 0;
}
[data-shape="blade"] .btn:hover::after, [data-shape="blade"] .seg button:hover::after, [data-shape="blade"] #themeToggle:hover::after {
  animation: shape-blade-slash .52s cubic-bezier(.3,.7,.4,1) 1;
}
[data-shape="blade"] .btn:hover, [data-shape="blade"] .seg button:hover, [data-shape="blade"] #themeToggle:hover {
  box-shadow: inset 3px 0 0 var(--brand); transition: box-shadow .14s linear;
}
[data-shape="blade"] .ccard:hover, [data-shape="blade"] .il-scr-row:hover {
  box-shadow: inset 3px 0 0 var(--brand); }
[data-shape="blade"] tbody tr.clickable:hover { border-left-color: var(--brand); }

/* ===== Frame -- generic game HUD, a readable bracket-heavy overlay ===== */
@keyframes shape-frame-in { 0% { transform: scaleX(.12) scaleY(.05); opacity: 0; box-shadow: 0 0 0 1px var(--brand); }
  30% { transform: scaleX(1) scaleY(.05); opacity: 1; } 62% { transform: scaleX(1) scaleY(1.04); box-shadow: 0 0 0 1px var(--brand); }
  100% { transform: none; box-shadow: 0 0 0 0 transparent; } }
/* The eight-gradient corner-bracket trick: four L-shaped brackets (each two
   2px lines) grown from 0 via background-size on hover. Same mechanism at
   button scale via a ::after so the border-radius:0 shapes below can reuse
   the geometry unmodified. */
[data-shape="frame"] .card {
  animation: shape-frame-in .46s cubic-bezier(.16,.9,.2,1) backwards; position: relative;
  transition: box-shadow .2s ease;
  background-image: linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%);
  background-repeat: no-repeat;
  background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
  background-size: 0 2px, 2px 0, 0 2px, 2px 0, 0 2px, 2px 0, 0 2px, 2px 0;
  transition: background-size .24s cubic-bezier(.2,.9,.25,1), box-shadow .2s ease;
}
[data-shape="frame"] .card.hero { animation-delay: .07s; }
[data-shape="frame"] .card:hover {
  background-size: 13px 2px, 2px 13px, 13px 2px, 2px 13px, 13px 2px, 2px 13px, 13px 2px, 2px 13px;
  box-shadow: inset 0 0 0 1px var(--brand);
}
[data-shape="frame"] .btn, [data-shape="frame"] .seg button, [data-shape="frame"] #themeToggle { position: relative; }
[data-shape="frame"] .btn::after, [data-shape="frame"] .seg button::after, [data-shape="frame"] #themeToggle::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background-image: linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%);
  background-repeat: no-repeat;
  background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
  background-size: 0 2px, 2px 0, 0 2px, 2px 0, 0 2px, 2px 0, 0 2px, 2px 0;
  transition: background-size .24s cubic-bezier(.2,.9,.25,1);
}
[data-shape="frame"] .btn:hover::after, [data-shape="frame"] .seg button:hover::after, [data-shape="frame"] #themeToggle:hover::after {
  background-size: 9px 2px, 2px 9px, 9px 2px, 2px 9px, 9px 2px, 2px 9px, 9px 2px, 2px 9px;
}
[data-shape="frame"] .btn:hover, [data-shape="frame"] .seg button:hover, [data-shape="frame"] #themeToggle:hover {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 55%, transparent);
}
[data-shape="frame"] .ccard:hover, [data-shape="frame"] .il-scr-row:hover {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 42%, transparent); }
[data-shape="frame"] tbody tr.clickable:hover { border-left-color: color-mix(in srgb, var(--brand) 60%, transparent); }

/* ===== Cardinal -- the original SAO Cathedral console, Aincrad-era ===== */
@keyframes shape-cardinal-in { 0% { transform: scaleY(.04); filter: brightness(2.2) saturate(1.4); }
  44% { transform: scaleY(1.035); } 72% { transform: scaleY(.99); filter: none; } 100% { transform: scaleY(1); filter: none; } }
@keyframes shape-cardinal-flicker { 0%, 100% { filter: none; } 18% { filter: brightness(1.9) saturate(.7); }
  26% { filter: none; } 42% { filter: brightness(1.5); } 55% { filter: none; } }
[data-shape="cardinal"] .card {
  clip-path: polygon(15px 0, 100% 0, 100% calc(100% - 15px), calc(100% - 15px) 100%, 0 100%, 0 15px);
  animation: shape-cardinal-in .48s cubic-bezier(.16,.9,.2,1) backwards;
  border-color: transparent; position: relative;
  background-image: linear-gradient(135deg, color-mix(in srgb, var(--brand-deep) 55%, transparent) 0 100%);
  background-repeat: no-repeat; background-position: 100% 0; background-size: 0 0;
  transition: background-size .26s cubic-bezier(.2,.9,.25,1), filter .25s ease;
}
[data-shape="cardinal"] .card.hero { animation-delay: .07s; }
[data-shape="cardinal"] .card:hover { filter: brightness(1.06); background-size: 46px 2px; }
[data-shape="cardinal"] .btn.primary, [data-shape="cardinal"] .btn:not(.primary) {
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
  border-color: transparent; position: relative;
}
[data-shape="cardinal"] .btn::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: linear-gradient(135deg, color-mix(in srgb, var(--brand-deep) 70%, transparent) 0 100%);
  background-repeat: no-repeat; background-position: 100% 0; background-size: 0 0;
  transition: background-size .26s cubic-bezier(.2,.9,.25,1);
}
[data-shape="cardinal"] .btn:hover::after { background-size: 30px 2px; }
[data-shape="cardinal"] .btn:hover, [data-shape="cardinal"] .seg button:hover, [data-shape="cardinal"] #themeToggle:hover {
  animation: shape-cardinal-flicker .34s steps(1, end) 1;
}
[data-shape="cardinal"] .seg button, [data-shape="cardinal"] #themeToggle { transition: box-shadow .2s ease, background .3s ease, color .3s ease; }
[data-shape="cardinal"] .seg button:hover, [data-shape="cardinal"] #themeToggle:hover {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 60%, transparent), 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent);
}
[data-shape="cardinal"] .ccard:hover, [data-shape="cardinal"] .il-scr-row:hover {
  box-shadow: inset 3px 0 0 var(--brand), inset 0 0 0 1px color-mix(in srgb, var(--brand) 20%, transparent); }
[data-shape="cardinal"] tbody tr.clickable:hover { border-left-color: var(--brand); }

/* ===== Cardinal 2.0 -- the Cardinal system rebuilt for modern hardware ===== */
@keyframes shape-cardinal2-in { 0% { opacity: 0; transform: scaleX(.05) scaleY(.42); filter: brightness(2.8) saturate(.15); }
  15% { opacity: 1; transform: scaleX(1.006) scaleY(.42); } 33% { transform: scaleX(1) scaleY(1.024); filter: brightness(1.55) saturate(.72); }
  47% { transform: scaleX(1) scaleY(.995); filter: brightness(1.16); } 100% { transform: none; filter: none; opacity: 1; } }
[data-shape="cardinal2"] .card {
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 17px), calc(100% - 17px) 100%, 0 100%);
  position: relative; border-color: transparent;
  background-image: linear-gradient(180deg, color-mix(in srgb, #fff 20%, transparent) 0, transparent 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(90deg, color-mix(in srgb, #fff 90%, transparent) 0 100%);
  background-repeat: no-repeat; background-position: 0 0, 0 0, 100% 0;
  background-size: 100% 56px, 44% 2px, 30px 2px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #fff 22%, transparent);
  animation: shape-cardinal2-in .62s cubic-bezier(.16,.9,.2,1) backwards; transform-origin: left center;
  transition: box-shadow .24s ease, filter .24s ease, background-size .3s cubic-bezier(.2,.9,.25,1);
}
[data-shape="cardinal2"] .card.hero { animation-delay: .09s; }
[data-shape="cardinal2"] .card:hover {
  background-size: 100% 56px, 100% 2px, 30px 2px;
  box-shadow: inset 3px 0 0 0 var(--brand-deep), inset 0 0 0 1px color-mix(in srgb, #fff 38%, transparent);
  filter: brightness(1.03) saturate(1.05);
}
[data-shape="cardinal2"] .btn.primary, [data-shape="cardinal2"] .btn:not(.primary) {
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 13px, 100% 100%, 0 100%);
  border-color: transparent; position: relative;
}
[data-shape="cardinal2"] .btn::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(90deg, color-mix(in srgb, #fff 88%, transparent) 0 100%);
  background-repeat: no-repeat; background-position: 0 0, 100% 100%; background-size: 0 2px, 0 2px;
  transition: background-size .3s cubic-bezier(.2,.9,.25,1);
}
[data-shape="cardinal2"] .btn:hover::after { background-size: 100% 2px, 18px 2px; }
[data-shape="cardinal2"] .btn:hover, [data-shape="cardinal2"] .seg button:hover, [data-shape="cardinal2"] #themeToggle:hover {
  box-shadow: inset 3px 0 0 var(--brand-deep); transition: box-shadow .24s ease;
}
[data-shape="cardinal2"] .ccard:hover, [data-shape="cardinal2"] .il-scr-row:hover {
  box-shadow: inset 4px 0 0 var(--brand-deep), inset 0 1px 0 0 color-mix(in srgb, #fff 28%, transparent); }
[data-shape="cardinal2"] tbody tr.clickable:hover { border-left-color: var(--brand-deep); }

/* ===== Fluctlight -- SAO Alicization, the soul read through a Window ===== */
@keyframes shape-flc-in { 0% { opacity: 0; clip-path: inset(0 50% 0 50%); filter: brightness(2.6) saturate(.3); }
  9% { opacity: 1; } 36% { clip-path: inset(0 0 0 0); filter: brightness(1.7) saturate(.7); } 44% { opacity: .5; }
  50% { opacity: 1; filter: brightness(1.3); } 57% { opacity: .8; transform: translateX(2.5px); }
  64% { opacity: 1; transform: translateX(-1.5px); } 100% { transform: none; filter: none; opacity: 1; } }
[data-shape="fluctlight"] .card {
  animation: shape-flc-in .78s cubic-bezier(.2,.85,.24,1) backwards;
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--brand-deep) 62%, transparent),
    inset 0 1px 0 0 color-mix(in oklab, #fff 26%, transparent),
    inset 78px 0 110px -84px color-mix(in oklab, var(--brand) 85%, transparent),
    inset -78px 0 110px -84px color-mix(in oklab, var(--brand-deep) 78%, transparent),
    0 0 0 1px color-mix(in oklab, var(--brand) 16%, transparent), 0 0 24px -16px var(--brand-deep);
  transition: box-shadow .3s ease, filter .3s ease;
}
[data-shape="fluctlight"] .card.hero { animation-delay: .11s; }
[data-shape="fluctlight"] .card:hover {
  box-shadow: inset 0 0 0 1px var(--brand-deep), inset 0 1px 0 0 color-mix(in oklab, #fff 40%, transparent),
    inset 96px 0 120px -80px color-mix(in oklab, var(--brand) 95%, transparent),
    inset -96px 0 120px -80px color-mix(in oklab, var(--brand-deep) 92%, transparent),
    0 0 0 1px color-mix(in oklab, var(--brand-deep) 34%, transparent), 0 0 34px -12px var(--brand-deep);
  filter: brightness(1.04) saturate(1.06);
}
[data-shape="fluctlight"] .btn, [data-shape="fluctlight"] .seg button, [data-shape="fluctlight"] #themeToggle {
  transition: box-shadow .34s ease, filter .34s ease, color .3s ease;
}
[data-shape="fluctlight"] .btn:hover {
  box-shadow: inset 0 0 0 1px color-mix(in oklab, #fff 46%, transparent),
    inset 34px 0 44px -34px var(--brand), inset -34px 0 44px -34px var(--brand-deep), 0 0 26px -8px var(--brand-deep);
  filter: brightness(1.06) saturate(1.08);
}
@keyframes shape-flc-rim { 0% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--brand-deep) 55%, transparent); }
  100% { box-shadow: 0 0 0 9px color-mix(in oklab, var(--brand-deep) 0%, transparent); } }
[data-shape="fluctlight"] .seg button:hover, [data-shape="fluctlight"] #themeToggle:hover {
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--brand-deep) 70%, transparent), 0 0 18px -6px var(--brand-deep);
  animation: shape-flc-rim .9s ease-out infinite;
}
[data-shape="fluctlight"] .ccard:hover, [data-shape="fluctlight"] .il-scr-row:hover {
  box-shadow: inset 34px 0 44px -38px var(--brand), inset -34px 0 44px -38px var(--brand-deep); }
[data-shape="fluctlight"] tbody tr.clickable:hover {
  background: linear-gradient(90deg, color-mix(in srgb, var(--brand) 14%, transparent),
    transparent 30%, transparent 70%, color-mix(in srgb, var(--brand-deep) 14%, transparent)); }

/* ===== System -- Solo Leveling's Player's window, a notification feed ===== */
@keyframes shape-system-in { 0% { opacity: 0; transform: scaleY(.02) scaleX(1.08); filter: brightness(3.2) saturate(0); }
  14% { opacity: 1; transform: scaleY(.02) scaleX(1.08); } 28% { transform: scaleY(1.08) scaleX(1.012); filter: brightness(2.3) saturate(.4); }
  36% { transform: scaleY(.975) scaleX(.998); } 42% { opacity: .3; } 47% { opacity: 1; filter: brightness(1.75); }
  53% { transform: translateX(3.5px) skewX(-1.4deg); opacity: .78; } 59% { transform: translateX(-2.5px) skewX(.9deg); opacity: 1; }
  66% { transform: translateX(1px); filter: brightness(1.2); } 100% { transform: none; filter: none; } }
/* Corner brackets (the same eight-gradient trick as Frame, at System's own
   scale) plus the twin neon side rails the previous pass already had. */
[data-shape="system"] .card {
  animation: shape-system-in .84s cubic-bezier(.16,.9,.2,1) backwards;
  background-image: linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%);
  background-repeat: no-repeat;
  background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
  background-size: 17px 2px, 2px 17px, 17px 2px, 2px 17px, 17px 2px, 2px 17px, 17px 2px, 2px 17px;
  box-shadow: inset 3px 0 0 0 var(--brand-deep), inset -3px 0 0 0 var(--brand-deep),
    inset 0 0 0 1px color-mix(in oklab, var(--brand-deep) 24%, transparent),
    0 0 0 1px color-mix(in oklab, var(--brand-deep) 18%, transparent), 0 0 30px -13px var(--brand-deep);
  transition: box-shadow .26s ease, filter .26s ease;
}
[data-shape="system"] .card.hero { animation-delay: .12s; }
[data-shape="system"] .card:hover {
  box-shadow: inset 4px 0 0 0 var(--brand-deep), inset -4px 0 0 0 var(--brand-deep),
    inset 0 0 0 1px color-mix(in oklab, var(--brand-deep) 58%, transparent),
    0 0 0 1px color-mix(in oklab, var(--brand-deep) 48%, transparent), 0 0 46px -10px var(--brand-deep);
  filter: brightness(1.06) saturate(1.08);
}
[data-shape="system"] .btn, [data-shape="system"] .seg button, [data-shape="system"] #themeToggle { position: relative; }
[data-shape="system"] .btn.primary, [data-shape="system"] .btn:not(.primary) {
  clip-path: polygon(0 0, calc(100% - 11px) 0, 100% 11px, 100% 100%, 11px 100%, 0 calc(100% - 11px));
  border-color: transparent;
}
[data-shape="system"] .btn::after, [data-shape="system"] .seg button::after, [data-shape="system"] #themeToggle::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background-image: linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%),
    linear-gradient(90deg, var(--brand-deep) 0 100%), linear-gradient(180deg, var(--brand-deep) 0 100%);
  background-repeat: no-repeat;
  background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
  background-size: 0 2px, 2px 0, 0 2px, 2px 0, 0 2px, 2px 0, 0 2px, 2px 0;
  transition: background-size .22s cubic-bezier(.2,.9,.25,1);
}
[data-shape="system"] .btn:hover::after, [data-shape="system"] .seg button:hover::after, [data-shape="system"] #themeToggle:hover::after {
  background-size: 11px 2px, 2px 11px, 11px 2px, 2px 11px, 11px 2px, 2px 11px, 11px 2px, 2px 11px;
}
[data-shape="system"] .btn:hover, [data-shape="system"] .seg button:hover, [data-shape="system"] #themeToggle:hover {
  box-shadow: inset 3px 0 0 0 var(--brand-deep), inset -3px 0 0 0 var(--brand-deep), 0 0 24px -10px var(--brand-deep);
  filter: brightness(1.06) saturate(1.08); transition: box-shadow .22s ease, filter .22s ease;
}
[data-shape="system"] .ccard:hover, [data-shape="system"] .il-scr-row:hover {
  box-shadow: inset 2px 0 0 var(--brand-deep), inset -2px 0 0 var(--brand-deep); }
[data-shape="system"] tbody tr.clickable:hover {
  border-left-color: var(--brand-deep); border-right: 2px solid var(--brand-deep); }

/* <tr> silently drops box-shadow in every current browser (computed style
   still reports it, nothing paints -- confirmed live, not assumed), so
   table rows get each shape's idiom via border-left/background instead of
   the box-shadow every other row-like component (.ccard, .il-scr-row) uses. */
tbody tr.clickable {
  border-left: 3px solid transparent;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}

/* Scoped to :root without a recorded motion choice — the shape entrance is a
   product feature with its own Still/Calm/Surge control, and an explicit
   choice beats the OS default. Unscoped, this killed every shape's entrance
   grammar outright on a reduced-motion machine, which is why switching view
   was a hard cut with no animation at all. */
@media (prefers-reduced-motion: reduce) {
  /* data-shape and data-motion-choice both live on <html>, so this is one
     compound on the root element, not a descendant pair. */
  [data-shape]:not([data-motion-choice="user"]) .card { animation: none; }
}

/* ---------- ranks (app.js rankOf/rankBadge/rankLadder) ----------
   A company's rank is power_score re-cut onto six bands, nothing more —
   the badge is presentation, the number underneath is still the source of
   truth (every rankchip carries a title with the band's blurb). */
.rankchip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--radius-sm);
  font-family: var(--font-display); font-size: 15px; font-weight: 400;
  color: #fff; flex: none;
  box-shadow: 0 4px 14px -6px rgba(0,0,0,.5);
}
.rankchip[data-letter="S"] { background: linear-gradient(160deg, var(--gold-soft), var(--gold) 45%, var(--brand)); }
.rankchip[data-letter="A"] { background: linear-gradient(160deg, color-mix(in srgb, var(--good) 70%, white), var(--good)); }
.rankchip[data-letter="B"] { background: var(--surface-sunken); color: var(--text-secondary); box-shadow: inset 0 0 0 1px var(--border-strong); }
.rankchip[data-letter="C"] { background: linear-gradient(160deg, var(--gold-soft), var(--warning)); color: #241a05; }
.rankchip[data-letter="D"] { background: linear-gradient(160deg, color-mix(in srgb, var(--critical) 55%, var(--warning)), var(--critical)); }
.rankchip[data-letter="E"] { background: linear-gradient(160deg, color-mix(in srgb, var(--critical) 80%, black), var(--critical)); }
.rankchip[data-letter="_"] { background: var(--surface-sunken); color: var(--text-muted); }

.rankladder { display: grid; grid-template-columns: repeat(6, 1fr); gap: .6rem; margin: .8rem 0; }
@media (max-width: 900px) { .rankladder { grid-template-columns: repeat(3, 1fr); } }
.rankladder-tile {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: .7rem .6rem; text-align: left;
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.rankladder-tile:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--brand) 40%, transparent); }
.rankladder-tile .rankchip { margin-bottom: .4rem; }
.rankladder-meta { display: flex; align-items: baseline; gap: .35rem; font-family: var(--font-mono); }
.rankladder-meta b { font-size: 1.05rem; }
.rankladder-meta span { font-size: .68rem; color: var(--text-muted); }
.rankladder-tile p { margin: .25rem 0 0; font-size: .68rem; color: var(--text-muted); line-height: 1.35; }

.grid { display: grid; gap: 1rem; }
.grid.c2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.c3 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid.c4 { grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); }
.row { display: flex; gap: .65rem; align-items: flex-end; flex-wrap: wrap; }
/* A bare input dropped into a .row has no label wrapper to give it height, so it
   sat visibly shorter than the buttons next to it. Match the control height. */
.row > input, .row > select { margin: 0; min-height: 2.3rem; }
.spacer { flex: 1; }
.muted { color: var(--text-muted); }
.small { font-size: .77rem; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ---------- stat tiles ---------- */
.stat {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: .8rem .9rem; position: relative; overflow: hidden;
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.stat:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.stat .label { font-family: var(--font-mono); font-size: .62rem; text-transform: uppercase;
  letter-spacing: .15em; color: var(--text-muted); font-weight: 500; }
.stat .value {
  font-family: var(--font-display); font-size: 2.15rem; font-weight: 400;
  letter-spacing: -.012em; line-height: 1.04; margin-top: .1rem;
  font-variant-numeric: tabular-nums;
}
.stat .sub { font-size: .72rem; color: var(--text-secondary); margin-top: .18rem; }
.pos, .stat .value.pos { color: var(--success-text); }
.neg, .stat .value.neg { color: var(--critical); }
.stat.accent::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: linear-gradient(180deg, var(--brand), var(--gold)); }
.stat.gold .value { color: var(--gold-soft); }
.stat .value.gold { background: linear-gradient(120deg, var(--gold-soft), var(--gold));
  -webkit-background-clip: text; background-clip: text; color: transparent; }

/* ---------- verdict ---------- */
.verdict { display: inline-flex; align-items: center; gap: .4rem; padding: .32rem .78rem;
  border-radius: var(--radius-pill); font-size: .76rem; font-weight: 700; border: 1px solid;
  transition: transform var(--fast) var(--ease); }
.verdict:hover { transform: translateY(-1px); }
.verdict::before { font-size: .85em; }
.verdict.core_buy { color: var(--success-text); border-color: color-mix(in srgb, var(--good) 60%, transparent);
  background: color-mix(in srgb, var(--good) 14%, transparent); }
.verdict.core_buy::before { content: "\25B2"; }
.verdict.candidate, .verdict.deep_value { color: var(--text-primary);
  border-color: color-mix(in srgb, var(--gold) 65%, transparent);
  background: color-mix(in srgb, var(--gold) 17%, transparent); }
.verdict.candidate::before, .verdict.deep_value::before { content: "\25CF"; }
.verdict.watch { color: var(--text-primary); border-color: color-mix(in srgb, var(--info) 55%, transparent);
  background: color-mix(in srgb, var(--info) 12%, transparent); }
.verdict.watch::before { content: "\25CB"; }
.verdict.pass, .verdict.unknown, .verdict.quality_only, .verdict.too_hard,
.verdict.not_readable {
  color: var(--text-secondary); border-color: var(--axis); background: var(--surface-sunken); }
.verdict.pass::before, .verdict.unknown::before, .verdict.quality_only::before { content: "\2014"; }
.verdict.too_hard::before { content: "?"; }
/* A refusal, not a failure: the model declined to read this one. Deliberately
   the neutral badge rather than the critical one — "we cannot measure this"
   must never be mistaken for "this is bad", which is the exact confusion the
   whole holding-company change exists to prevent. */
.verdict.not_readable::before { content: "\2298"; }
/* Since 2026-09-28 a holding company is valued -- on what it owns -- and only
   its score is withheld. Informational, like watch: it is an answer, not a
   refusal and not a warning. */
.verdict.holding { color: var(--text-primary); border-color: color-mix(in srgb, var(--info) 55%, transparent);
  background: color-mix(in srgb, var(--info) 12%, transparent); }
.verdict.holding::before { content: "\25C8"; }
.verdict.avoid, .verdict.knockout, .verdict.error { color: var(--critical);
  border-color: color-mix(in srgb, var(--critical) 55%, transparent);
  background: color-mix(in srgb, var(--critical) 12%, transparent); }
.verdict.avoid::before, .verdict.knockout::before, .verdict.error::before { content: "\2715"; }
/* The only badge that is filled rather than tinted. It sits above "knocked
   out" in severity and is the strongest thing the model says — it should not
   look like one more shade of the same red. */
.verdict.uninvestable {
  color: var(--surface-1); border-color: var(--critical);
  background: var(--critical); font-weight: 700;
}
.verdict.uninvestable::before { content: "\26A0"; }

/* ---------- score chip ---------- */
.chip { display: inline-block; min-width: 2.5rem; text-align: center; padding: .14rem .5rem;
  border-radius: var(--radius-pill); font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: .77rem; border: 1px solid var(--border); }
.chip.s90 { background: color-mix(in srgb, var(--good) 26%, transparent); color: var(--success-text); }
.chip.s70 { background: color-mix(in srgb, var(--good) 14%, transparent); }
.chip.s50 { background: color-mix(in srgb, var(--warning) 20%, transparent); }
.chip.s30 { background: color-mix(in srgb, var(--warning) 30%, transparent); }
.chip.s0 { background: color-mix(in srgb, var(--critical) 16%, transparent); color: var(--critical); }

/* ---------- tables ---------- */
.tablewrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-1); }
table { border-collapse: collapse; width: 100%; font-size: .8rem; }
th, td { text-align: right; padding: .48rem .6rem; border-bottom: 1px solid var(--grid); white-space: nowrap; }
/* A column header is a label until something makes it a control.
   This rule used to hand every `th` in the app `cursor: pointer` and a
   hover-to-brand colour, which is the visual grammar of a button. Not one
   table header here is clickable — the screener sorts through `button.il-sort`
   inside a grid, not through a `th` — so every table in the app was
   advertising a click that does not exist, and someone had already patched
   `.senstable` out of it one table at a time.

   The default is inverted instead: headers are labels, and a header that
   really does sort opts in with `.sortable`. A control that looks like one is
   cheap; a label that looks like one costs the reader a click and some trust. */
th { position: sticky; top: 0; background: var(--surface-1); color: var(--text-secondary);
  font-weight: 700; font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; z-index: 1;
  cursor: default; user-select: none; transition: color var(--fast) var(--ease); }
th.sortable { cursor: pointer; }
th.sortable:hover { color: var(--brand); }
th.left, td.left { text-align: left; }
tbody tr { transition: background var(--fast) var(--ease); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: color-mix(in srgb, var(--gold) 9%, transparent); }
tbody tr.clickable { cursor: pointer; }
td.num { font-variant-numeric: tabular-nums; }
td.best { background: color-mix(in srgb, var(--good) 13%, transparent); }

/* ---------- DCF sensitivity grid ---------- */
/* This used to undo the base `th` rule's pointer cursor and hover-brand colour
   one table at a time. The base rule was the bug and has been inverted, so the
   two lines below are now the same as the default and are kept only because a
   sensitivity axis must never become `.sortable` by someone's reflex. */
.senstable th { cursor: default; }
.senstable th:hover { color: var(--text-secondary); }
.senstable .senscentre { position: relative; font-weight: 800; }
.senstable th.senscentre { color: var(--brand); }
.senstable td.senscentre::after {
  content: ""; position: absolute; inset: 2px; border: 1.5px solid var(--brand);
  border-radius: var(--radius-xs); pointer-events: none;
}
.senstable td { position: relative; }

/* ---------- criteria ---------- */
.crit { display: grid; grid-template-columns: 1fr auto auto 96px; gap: .55rem;
  align-items: center; padding: .38rem .3rem; border-radius: var(--radius-xs);
  border-bottom: 1px solid var(--grid); font-size: .8rem;
  transition: background var(--fast) var(--ease); }
.crit:hover { background: color-mix(in srgb, var(--gold) 8%, transparent); }
.crit:last-child { border-bottom: 0; }
.crit .cl { min-width: 0; }
.crit .cl small { display: block; color: var(--text-muted); font-size: .69rem; white-space: normal; }
.crit .cv, .crit .ct { font-variant-numeric: tabular-nums; text-align: right; }
.crit .ct { color: var(--text-muted); }
.meter { height: 8px; border-radius: var(--radius-pill); background: var(--surface-sunken);
  overflow: hidden; border: 1px solid var(--border); }
.meter > i { display: block; height: 100%; border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--series-3), color-mix(in srgb, var(--series-3) 70%, var(--gold)));
  transition: width 700ms var(--ease); }
.meter.g > i { background: linear-gradient(90deg, var(--good), color-mix(in srgb, var(--good) 60%, var(--gold))); }
.meter.y > i { background: linear-gradient(90deg, color-mix(in srgb, var(--warning) 80%, var(--critical)), var(--warning)); }
.meter.r > i { background: linear-gradient(90deg, var(--critical), color-mix(in srgb, var(--critical) 65%, var(--warning))); }

/* ---------- notes / callouts ---------- */
.notes { list-style: none; margin: 0; padding: 0; }
.notes li { padding: .34rem 0 .34rem 1.4rem; position: relative; border-bottom: 1px solid var(--grid);
  font-size: .8rem; line-height: 1.55; }
.notes li:last-child { border-bottom: 0; }
.notes li::before { content: "\2013"; position: absolute; left: .1rem; color: var(--text-muted); }
.notes li.pen::before { content: "\2212"; color: var(--critical); font-weight: 700; }
.notes li.bon::before { content: "+"; color: var(--success-text); font-weight: 700; }
.notes li.ko::before { content: "\2715"; color: var(--critical); font-weight: 700; }

/* Callout tints composite over --surface-1, not over `transparent`.
   Mixing a tint into `transparent` yields an 8%-alpha fill, which was invisible
   while the page background was flat -- and stopped being invisible the moment
   the scenery went in behind everything: the sakura trunk read straight through
   the empty-diff callout on the dashboard. The scenery is fixed at z-index -1
   and is meant to show only in the margins, so anything carrying text has to be
   opaque. Same colours, same weight, no longer a window. */
.callout { border-left: 3px solid var(--info);
  background: color-mix(in srgb, var(--info) 8%, var(--surface-1));
  padding: .6rem .85rem; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: .8rem; margin-bottom: .85rem; }
.callout.warn { border-color: var(--gold);
  background: color-mix(in srgb, var(--gold) 13%, var(--surface-1)); }
.callout.crit { border-color: var(--critical);
  background: color-mix(in srgb, var(--critical) 9%, var(--surface-1)); }
.callout.gold { border-color: var(--gold);
  background: linear-gradient(100deg, color-mix(in srgb, var(--gold) 15%, var(--surface-1)),
                              color-mix(in srgb, var(--brand) 7%, var(--surface-1))); }
/* A callout nested inside a card sits on --surface-2, so it composites against
   that instead -- otherwise the tint carries the wrong base and the callout
   reads as a slightly wrong-coloured patch rather than a tinted region. */
.card .callout { background: color-mix(in srgb, var(--info) 8%, var(--surface-2)); }
.card .callout.warn { background: color-mix(in srgb, var(--gold) 13%, var(--surface-2)); }
.card .callout.crit { background: color-mix(in srgb, var(--critical) 9%, var(--surface-2)); }
.card .callout.gold {
  background: linear-gradient(100deg, color-mix(in srgb, var(--gold) 15%, var(--surface-2)),
                              color-mix(in srgb, var(--brand) 7%, var(--surface-2))); }

/* ---------- loading & feedback ---------- */
.loading { display: inline-block; width: 13px; height: 13px; border: 2px solid var(--grid);
  border-top-color: var(--brand); border-radius: 50%; animation: spin .7s linear infinite;
  vertical-align: -2px; }
@keyframes spin { to { transform: rotate(360deg); } }

.skeleton { border-radius: var(--radius-sm); background:
  linear-gradient(90deg, var(--surface-sunken) 25%, color-mix(in srgb, var(--gold) 10%, var(--surface-sunken)) 50%, var(--surface-sunken) 75%);
  background-size: 200% 100%; animation: shimmer 1.35s linear infinite; }
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.skeleton.line { height: 12px; margin: .45rem 0; }
.skeleton.tile { height: 84px; }
.skeleton.wheel { height: 300px; border-radius: 50%; width: 300px; margin: 0 auto; }

#toasts { position: fixed; right: 1rem; bottom: 1rem; z-index: 200; display: flex;
  flex-direction: column; gap: .5rem; pointer-events: none; }
.toast { background: var(--surface-2); border: 1px solid var(--border-strong);
  border-left: 3px solid var(--gold); border-radius: var(--radius-sm); padding: .55rem .8rem;
  box-shadow: var(--shadow-lift); font-size: .8rem; max-width: 320px;
  animation: toastIn var(--med) var(--ease); }
.toast.good { border-left-color: var(--good); }
.toast.bad { border-left-color: var(--critical); }
.toast.out { animation: toastOut var(--med) var(--ease) forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
@keyframes toastOut { to { opacity: 0; transform: translateX(18px); } }

.chart { width: 100%; }
.chart text { font-family: var(--font); }
.tooltip { position: fixed; pointer-events: none; z-index: 100; background: var(--surface-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: .5rem .65rem;
  font-size: .76rem; box-shadow: var(--shadow-lift); max-width: 280px; opacity: 0;
  transition: opacity var(--fast) var(--ease); }
.tooltip.on { opacity: 1; }
.tooltip b { display: block; margin-bottom: .2rem; }
.legend { display: flex; flex-wrap: wrap; gap: .2rem .9rem; font-size: .74rem;
  color: var(--text-secondary); margin-top: .45rem; align-items: center; }
.legend i { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: .32rem; }

details.doc summary { cursor: pointer; font-weight: 650; padding: .45rem .2rem;
  border-radius: var(--radius-xs); transition: background var(--fast) var(--ease); list-style: none; }
details.doc summary::-webkit-details-marker { display: none; }
details.doc summary::before { content: "\25B8"; display: inline-block; margin-right: .45rem;
  color: var(--gold); transition: transform var(--fast) var(--ease); }
details.doc[open] summary::before { transform: rotate(90deg); }
details.doc summary:hover { background: color-mix(in srgb, var(--gold) 9%, transparent); }
details.doc { border-bottom: 1px solid var(--grid); }
details.doc[open] { padding-bottom: .5rem; }

/* ---------- global search ---------- */
.searchwrap { position: relative; flex: 1 1 220px; max-width: 400px; }
.searchwrap input { width: 100%; border-radius: var(--radius-pill); padding-left: .95rem; }
.searchresults {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lift); overflow: hidden;
  max-height: 62vh; overflow-y: auto; display: none;
}
.searchresults.on { display: block; animation: fadeUp var(--fast) var(--ease); }
.searchresults .hit { padding: .48rem .7rem; cursor: pointer; border-bottom: 1px solid var(--grid);
  display: flex; gap: .5rem; align-items: baseline; font-size: .82rem;
  transition: background var(--fast) var(--ease); }
.searchresults .hit:last-child { border-bottom: 0; }
.searchresults .hit:hover, .searchresults .hit.sel {
  background: color-mix(in srgb, var(--gold) 14%, transparent); }
.searchresults .hit b { min-width: 76px; }
.searchresults .hit .nm { color: var(--text-secondary); flex: 1; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.searchresults .meta { padding: .38rem .7rem; font-size: .7rem; color: var(--text-muted);
  background: var(--surface-sunken); }

/* ---------- availability ---------- */
.avail { display: inline-block; width: 1em; text-align: center; font-weight: 700; cursor: help; }
.avail.likely { color: var(--good); }
.avail.partial { color: var(--warning); }
.avail.unlikely { color: var(--critical); }
tbody tr.unavailable, .il-scr-row.unavailable, .ccard.unavailable { opacity: .48; }
tbody tr.unavailable:hover, .il-scr-row.unavailable:hover, .ccard.unavailable:hover { opacity: 1; }
tbody tr.toohard, .il-scr-row.toohard { opacity: .5; }
tbody tr.toohard:hover, .il-scr-row.toohard:hover { opacity: 1; }
/* il-design-bridge.css gives every .il-scr-row a pointer cursor and hover
   tint unconditionally; an errored row (no click handler) isn't one. */
.il-scr-row:not(.clickable) { cursor: default; }
.il-scr-row:not(.clickable):hover { background: none; }
/* The old <thead th> was sticky; the grid header (il-design-bridge.css)
   isn't by default, and a 20-column screen is long enough to want it. */
#screenTable .il-scr-head {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface-sunken); backdrop-filter: var(--il-panel-blur, blur(18px) saturate(1.15));
}

/* ---------- cards ---------- */
.cardgrid { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.ccard { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: .85rem .95rem; cursor: pointer;
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
              border-color var(--fast) var(--ease); }
.ccard:hover { transform: translateY(-3px); box-shadow: var(--shadow);
  border-color: color-mix(in srgb, var(--gold) 55%, transparent); }
.ccard:active { transform: translateY(-1px) scale(.995); }
.ccard .top { display: flex; align-items: center; gap: .4rem; }
.ccard .top b { font-size: .97rem; }
.ccard .nm { color: var(--text-muted); font-size: .73rem; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.ccard .body { display: flex; gap: .8rem; align-items: center; margin-top: .5rem; }
.ccard .kv { display: grid; grid-template-columns: auto auto; gap: .1rem .6rem;
  font-size: .74rem; flex: 1; }
.ccard .kv span:nth-child(odd) { color: var(--text-muted); }
.ccard .kv span:nth-child(even) { text-align: right; font-variant-numeric: tabular-nums; font-weight: 650; }

.cmpgrid { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.cmpcell { display: flex; flex-direction: column; gap: .2rem; }
.cmpcell .v { font-variant-numeric: tabular-nums; font-weight: 650; }

/* ---------- area score gauges ---------- */
.gaugerow { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); }
.gaugecell { text-align: center; padding: .3rem; border-radius: var(--radius-sm);
  transition: background var(--fast) var(--ease); cursor: default; }
.gaugecell:hover { background: color-mix(in srgb, var(--gold) 10%, transparent); }
.gaugecell .gl { font-size: .68rem; color: var(--text-secondary); margin-top: .1rem;
  font-weight: 600; letter-spacing: .02em; }

/* Was `.brand span`, which also matches .brandmark and .brandtext themselves
   (both spans) -- collapsed the whole brand button to nothing below 860px,
   found via the 400px mobile check. Only the muted subtitle line should go. */
@media (max-width: 860px) { .brandtext > span { display: none; } }
@media (max-width: 760px) {
  main { padding: .65rem; }
  .card { padding: .8rem .85rem; border-radius: var(--radius-sm); }
  .crit { grid-template-columns: minmax(0,1fr) auto 62px; gap: .4rem; }
  .crit .ct { display: none; }
  .crit .cl small { font-size: .66rem; }
  .stat { padding: .6rem .65rem; }
  .stat .value { font-size: 1.3rem; }
  .stat .label { font-size: .61rem; }
  .grid { gap: .65rem; }
  .grid.c4, .grid.c3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid.c2 { grid-template-columns: minmax(0, 1fr); }
  .topbar { padding: .5rem .65rem; gap: .5rem; flex-wrap: wrap; }
  .searchwrap { order: 10; flex: 1 1 100%; max-width: none; }
  .tabs { top: 0; position: static; padding: 0 .35rem; }
  .tabs button { padding: .52rem .58rem; font-size: .77rem; }
  .tabs .sec-ic { display: none; }
  .subtabs { position: static; padding: .3rem .35rem; }
  .cardgrid { grid-template-columns: minmax(0, 1fr); }
  h1 { font-size: 1.05rem; }
}
@media (max-width: 420px) { .grid.c4, .grid.c3 { grid-template-columns: minmax(0, 1fr); } }


/* ---------- rewards & risks (the at-a-glance read) ---------- */
.rrlist { list-style: none; margin: 0; padding: 0; }
.rrlist li { display: flex; gap: .6rem; align-items: flex-start; padding: .45rem .2rem;
  border-bottom: 1px solid var(--grid); font-size: .82rem;
  transition: background var(--fast) var(--ease); border-radius: var(--radius-xs); }
.rrlist li:last-child { border-bottom: 0; }
.rrlist li:hover { background: color-mix(in srgb, var(--gold) 8%, transparent); }
.rrlist .ic { flex: 0 0 18px; height: 18px; border-radius: 50%; display: grid; place-items: center;
  font-size: .68rem; font-weight: 800; margin-top: .08rem; }
.rrlist.reward .ic { background: color-mix(in srgb, var(--good) 22%, transparent); color: var(--good); }
.rrlist.risk .ic { background: color-mix(in srgb, var(--critical) 22%, transparent); color: var(--critical); }
.rrlist .tx b { font-weight: 700; }
.rrlist .tx small { display: block; color: var(--text-muted); font-size: .71rem; }

/* A serious risk has to look serious.
   `highlights.py` marks the findings that are categorically worse than the
   rest and sorts them to the top; the list drew all of them identically, so
   the sort order was the only trace of that judgement left. The mark changes
   from "!" to a filled diamond, the rail on the left says it without being
   read, and the text is full-strength ink rather than the list's default. */
.rrlist.risk li.sev { box-shadow: inset 2px 0 0 var(--critical);
  background: color-mix(in srgb, var(--critical) 6%, transparent); }
.rrlist.risk li.sev .ic { background: var(--critical); color: var(--lacquer); }
.rrlist.risk li.sev .tx b { color: var(--text-primary); }
/* Priority 0: a caveat that qualifies every other line, so it is labelled
   rather than only placed first. */
.rr-tag { font-family: var(--font-mono); font-size: 9px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--warning); border: 1px solid var(--border);
  border-radius: var(--radius-pill); padding: .05rem .35rem; margin-left: .25rem;
  white-space: nowrap; }

.sectionhead { display: flex; align-items: center; gap: .5rem; margin-bottom: .35rem; }
.sectionhead h3 { margin: 0; }
.sectionhead .count { font-size: .68rem; font-weight: 800; padding: .05rem .45rem;
  border-radius: var(--radius-pill); background: var(--surface-sunken); color: var(--text-secondary); }

/* ---------- dashboard ---------- */
.dashgrid { display: grid; gap: .9rem; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.movecard { display: flex; align-items: center; gap: .7rem; padding: .6rem .7rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2);
  cursor: pointer; transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease); }
.movecard:hover { transform: translateX(3px);
  border-color: color-mix(in srgb, var(--gold) 50%, transparent); }
.movecard .delta { font-variant-numeric: tabular-nums; font-weight: 800; font-size: .95rem; }
.movecard .delta.up { color: var(--success-text); }
.movecard .delta.down { color: var(--critical); }

/* ---------- narrative ---------- */
.narrative { border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
  border-radius: var(--radius); padding: .9rem 1rem; background:
  linear-gradient(140deg, color-mix(in srgb, var(--gold) 8%, transparent), transparent 60%); }

/* ---------- print / memo ---------- */
@media print {
  body { background: #fff !important; color: #000 !important; }
  .topbar, .tabs, #toasts, .noprint, button { display: none !important; }
  main { padding: 0; max-width: none; }
  .card { break-inside: avoid; box-shadow: none; border: 1px solid #ccc; margin-bottom: .5rem; }
  .view { display: none !important; }
  .view.printing { display: block !important; }
}

/* ==========================================================================
   PLAYER CARDS
   The read has to be instant: overall top-left, tier written out in words
   (never colour alone), six attributes on a fixed grid so the eye can compare
   the same position across cards without reading the labels again.
   ========================================================================== */
.deck { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); }

.pcard {
  position: relative; border-radius: var(--radius); padding: .8rem .8rem .7rem;
  background:
    linear-gradient(157deg, color-mix(in srgb, var(--tier-a) 30%, transparent) 0%,
                    color-mix(in srgb, var(--tier-b) 16%, transparent) 46%,
                    var(--surface-1) 100%),
    var(--surface-1);
  border: 1.5px solid color-mix(in srgb, var(--tier-a) 46%, var(--border));
  box-shadow: var(--shadow-sm); overflow: hidden; cursor: pointer;
  transition: transform var(--med) var(--ease), box-shadow var(--med) var(--ease),
              border-color var(--med) var(--ease);
}
/* The foil. One static diagonal band, and it does not move.
   -------------------------------------------------------------------------
   It used to be a 900ms sweep fired on hover. The motion default on this site
   is Still, and a deck is twenty of these at once: a shimmer per card is
   exactly the "too much confetti" that got the animated backgrounds cut. The
   band earns its place by being the one spot where the game layer is allowed
   to look like a game — so it stays, and it holds still. Hover keeps the lift
   and the border, which are feedback rather than decoration. */
.pcard::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 34%,
              color-mix(in srgb, var(--tier-a) 20%, transparent) 49%,
              color-mix(in srgb, var(--tier-a) 6%, transparent) 56%, transparent 70%);
}
/* The class glyph, behind the content and barely there — the card says which
   shape this is in the footer, and this is the same claim at the size of an
   impression rather than a sentence. 7%: legible as a texture, never as a
   thing competing with the six stats in front of it. */
.pcard-glyph {
  position: absolute; right: -.18em; bottom: -.3em; z-index: 0;
  font-size: 6.6rem; line-height: 1; pointer-events: none;
  color: var(--tier-a); opacity: .07;
}
.pcard:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg);
  border-color: color-mix(in srgb, var(--tier-a) 78%, transparent); }

/* Tier ramps, all from tokens. Silver and bronze were `#8d97a4` and `#8a5a3b`,
   the only two literal hexes in this component — so under all twelve moods
   those two tiers were the one thing on the page that never repainted. */
.pcard[data-tier="legendary"] { --tier-a: var(--amber); --tier-b: var(--brand); }
.pcard[data-tier="elite"]     { --tier-a: var(--gold);  --tier-b: var(--brand-deep); }
.pcard[data-tier="gold"]      { --tier-a: var(--gold-deep); --tier-b: var(--surface-2); }
.pcard[data-tier="silver"]    { --tier-a: var(--text-muted); --tier-b: var(--surface-2); }
.pcard[data-tier="bronze"]    { --tier-a: var(--hazelnut-deep); --tier-b: var(--surface-2); }
.pcard[data-tier="unknown"]   { --tier-a: var(--border-strong); --tier-b: var(--surface-2); }
.pcard.ko { filter: saturate(.45); }

/* In the memo, a card is a record. The foil and the glyph are screen
   decoration, and a browser that drops backgrounds prints them as a grey wash
   over the numbers — so they come off, and the border carries the tier. */
@media print {
  .pcard::before, .pcard-glyph { display: none; }
  .pcard { background: none; box-shadow: none;
    border: 1.5px solid var(--border-strong); break-inside: avoid; }
  .pcard:hover { transform: none; }
}

.pcard-top { display: flex; gap: .6rem; align-items: flex-start; position: relative; z-index: 1; }
.pcard-ovr { display: flex; flex-direction: column; align-items: center; line-height: 1; flex: 0 0 auto; }
.pcard-ovr b {
  font-size: 2rem; font-weight: 800; letter-spacing: -.03em;
  background: linear-gradient(140deg, var(--amber-soft), var(--tier-a));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* "level = score" is thirteen characters where "PWR" was three. At the old
   .16em tracking it ate a third of a 224px card, so the tracking comes off and
   the label wraps rather than pushing the ticker out of its own column. */
.pcard-ovr { max-width: 5.4rem; }
.pcard-ovr i { font-style: normal; font-size: .53rem; letter-spacing: .04em;
  color: var(--text-muted); margin-top: .22rem; text-align: center;
  text-transform: uppercase; font-weight: 700; line-height: 1.25; }
.pcard-id { min-width: 0; flex: 1; }
.pcard-id b { display: block; font-size: 1rem; letter-spacing: -.01em; }
.pcard-id span { display: block; font-size: .7rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* .pcard-id span sets colour and display for the name line; the tier pill lives
   inside it, so it needs the same specificity to win rather than one class. */
.pcard-id span.pcard-tier {
  display: inline-block; margin-top: .3rem; font-size: .58rem; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; padding: .16rem .46rem;
  border-radius: 99px; color: var(--lacquer); white-space: nowrap;
  background: linear-gradient(120deg, var(--amber-soft), var(--tier-a));
}
.pcard[data-tier="silver"] .pcard-id span.pcard-tier,
.pcard[data-tier="bronze"] .pcard-id span.pcard-tier,
.pcard[data-tier="unknown"] .pcard-id span.pcard-tier { color: var(--text-primary);
  background: color-mix(in srgb, var(--tier-a) 40%, transparent); }

.pcard-attrs {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: .3rem .5rem;
  margin: .7rem 0 .55rem; position: relative; z-index: 1;
}
.pattr { display: flex; align-items: baseline; gap: .3rem; }
.pattr b { font-size: .92rem; font-weight: 750; min-width: 1.6em; font-variant-numeric: tabular-nums; }
.pattr span { font-size: .58rem; letter-spacing: .1em; color: var(--text-muted); font-weight: 600; }
.pattr[data-band="hi"] b { color: var(--amber); }
.pattr[data-band="mid"] b { color: var(--text-primary); }
.pattr[data-band="lo"] b { color: var(--critical); }
.pattr[data-band="na"] b { color: var(--text-muted); }
.pcard-foot {
  border-top: 1px solid color-mix(in srgb, var(--tier-a) 26%, transparent);
  padding-top: .45rem; font-size: .7rem; color: var(--text-secondary);
  position: relative; z-index: 1; display: flex; justify-content: space-between;
  gap: .4rem; align-items: center;
}
.pcard-foot em { font-style: normal; font-weight: 650; color: var(--gold-soft); }
.pcard-bar { height: 3px; border-radius: 2px; background: var(--surface-sunken);
  overflow: hidden; margin-top: .4rem; position: relative; z-index: 1; }
.pcard-bar i { display: block; height: 100%; border-radius: 2px;
  background: linear-gradient(90deg, var(--tier-a), var(--amber)); }

.squadline { margin-bottom: 1rem; }
.squadline h3 { margin: 0 0 .1rem; font-size: .95rem; }
.squadline h3 small { font-weight: 500; color: var(--text-muted); margin-left: .4rem; font-size: .74rem; }
.squadline > p { margin: .1rem 0 .55rem; font-size: .76rem; color: var(--text-muted); }

/* ---------- alerts ---------- */
.alert {
  display: flex; gap: .65rem; align-items: flex-start; padding: .6rem .75rem;
  border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface-2); margin-bottom: .45rem;
  border-left: 3px solid var(--axis);
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.alert:hover { transform: translateX(2px); border-color: var(--border-strong); }
.alert[data-sev="action"] { border-left-color: var(--amber);
  background: linear-gradient(90deg, color-mix(in srgb, var(--amber) 11%, transparent), var(--surface-2) 55%); }
.alert[data-sev="warn"] { border-left-color: var(--critical); }
.alert[data-sev="info"] { border-left-color: var(--info); }
.alert b { font-size: .82rem; }
.alert p { margin: .1rem 0 0; font-size: .76rem; color: var(--text-secondary); }
.alert .sevtag { font-size: .56rem; letter-spacing: .13em; font-weight: 700; text-transform: uppercase;
  padding: .14rem .4rem; border-radius: 99px; flex: 0 0 auto; margin-top: .1rem;
  background: var(--surface-sunken); color: var(--text-muted); }
.alert[data-sev="action"] .sevtag { background: var(--amber); color: var(--lacquer); }
.alert[data-sev="warn"] .sevtag { background: var(--critical); color: #fff; }

/* ---------- calendar ---------- */
.calband { margin-bottom: .9rem; }
.calband h4 { margin: 0 0 .4rem; font-size: .8rem; letter-spacing: .04em;
  text-transform: uppercase; color: var(--gold-soft); }
.calrow {
  display: grid; grid-template-columns: 3.2rem 1fr auto auto; gap: .6rem; align-items: center;
  padding: .5rem .65rem; border-radius: var(--radius-sm); background: var(--surface-2);
  border: 1px solid var(--border); margin-bottom: .35rem; cursor: pointer;
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.calrow:hover { border-color: var(--gold); transform: translateX(2px); }
.calrow .days { font-weight: 800; font-size: 1.1rem; text-align: center;
  font-variant-numeric: tabular-nums; color: var(--amber); }
.calrow .days small { display: block; font-size: .54rem; letter-spacing: .1em;
  font-weight: 600; color: var(--text-muted); }
.calrow.quiet { border-color: color-mix(in srgb, var(--amber) 55%, transparent);
  background: linear-gradient(90deg, color-mix(in srgb, var(--amber) 10%, transparent), var(--surface-2) 60%); }
.freshtag { font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; font-weight: 700;
  padding: .15rem .42rem; border-radius: 99px; }
.freshtag[data-f="current"] { background: color-mix(in srgb, var(--good) 22%, transparent); color: var(--success-text); }
.freshtag[data-f="ageing"] { background: color-mix(in srgb, var(--gold) 22%, transparent); color: var(--gold-soft); }
.freshtag[data-f="stale"], .freshtag[data-f="very_stale"] {
  background: color-mix(in srgb, var(--critical) 22%, transparent); color: var(--critical); }
.freshtag[data-f="unknown"] { background: var(--surface-sunken); color: var(--text-muted); }

/* ---------- badges / arena ---------- */
.badgegrid { display: grid; gap: .55rem; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); }
.gbadge {
  display: flex; gap: .6rem; padding: .6rem .7rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface-2); align-items: flex-start;
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.gbadge:hover { transform: translateY(-2px); }
.gbadge.on { border-color: var(--amber);
  background: linear-gradient(150deg, color-mix(in srgb, var(--amber) 13%, transparent), var(--surface-2) 62%); }
.gbadge .mark { flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; font-size: .85rem; font-weight: 800;
  background: var(--surface-sunken); color: var(--text-muted); }
.gbadge.on .mark { background: linear-gradient(140deg, var(--amber-soft), var(--gold)); color: var(--lacquer); }
.gbadge b { font-size: .8rem; display: block; }
.gbadge p { margin: .1rem 0 0; font-size: .71rem; color: var(--text-muted); line-height: 1.45; }
.gbadge.on b { color: var(--amber-soft); }
/* The live count for this habit inside the window. It is the line that makes a
   lapsed habit legible — "0 written" says more than a dark circle does. */
.gbadge .hdetail {
  font-size: .69rem; font-weight: 700; letter-spacing: .03em;
  color: var(--text-secondary); margin: .18rem 0 .3rem;
}
.gbadge.on .hdetail { color: var(--amber); }

.streak {
  display: inline-flex; align-items: center; gap: .4rem; padding: .28rem .62rem;
  border-radius: 99px; font-size: .76rem; font-weight: 700;
  background: linear-gradient(120deg, color-mix(in srgb, var(--amber) 24%, transparent),
              color-mix(in srgb, var(--brand) 20%, transparent));
  border: 1px solid color-mix(in srgb, var(--amber) 50%, transparent); color: var(--amber-soft);
}

/* ---------- exposure ---------- */
.expo-split { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.expobar { display: flex; height: 26px; border-radius: var(--radius-xs); overflow: hidden;
  border: 1px solid var(--border); }
.expobar > i {
  display: grid; place-items: center; font-style: normal; font-size: .64rem; font-weight: 700;
  color: var(--lacquer); min-width: 0; overflow: hidden; white-space: nowrap;
  transition: filter var(--fast) var(--ease);
}
.expobar > i:hover { filter: brightness(1.18); }
.assumed { text-decoration: underline dotted; text-underline-offset: 3px; cursor: help; }

/* ---------- industry ---------- */
.waterfall { display: flex; flex-direction: column; gap: .3rem; }
.wfrow { display: grid; grid-template-columns: 9.5rem 1fr 3.6rem; gap: .55rem; align-items: center;
  font-size: .76rem; }
.wfrow .track { height: 16px; background: var(--surface-sunken); border-radius: 4px; position: relative; }
.wfrow .track i { position: absolute; top: 0; bottom: 0; border-radius: 4px; }
.wfrow[data-kind="level"] .track i { background: linear-gradient(90deg, var(--gold-deep), var(--amber)); }
.wfrow[data-kind="drop"]  .track i { background: color-mix(in srgb, var(--critical) 62%, transparent); }
.wfrow b { text-align: right; font-variant-numeric: tabular-nums; font-weight: 650; }

/* ---------- v6 tables: left-aligned by default, .r opts into right ---------- */
/* `display: block` here was redundant — .tablewrap already scrolls — and it made
   the table shrink-wrap its content, so a narrow table sat in a lake of empty
   card. Left as a real table it fills the width and still scrolls when it must. */
table.tbl { white-space: nowrap; }
table.tbl th, table.tbl td { text-align: left; }
table.tbl th.r, table.tbl td.r { text-align: right; }
table.tbl td.small, table.tbl td .small { white-space: normal; }
tbody tr.linkrow { cursor: pointer; }
.chip.good { min-width: 0; background: color-mix(in srgb, var(--good) 20%, transparent);
  color: var(--success-text); }
.chip.bad { min-width: 0; background: color-mix(in srgb, var(--critical) 18%, transparent);
  color: var(--critical); }

/* ==========================================================================
   THE MODEL — the version chip on the model page's own title.
   There was a header badge here too, stating the model's name beside the
   brand. It went with the rest of the header furniture the v2 canvas does not
   have: the model has its own page, reachable from System in the rail, and a
   permanent chip restating its name was one more thing between the reader and
   the screen. `.mb-name` went with it; only the version chip is still used.
   ========================================================================== */
.mb-ver {
  font-size: .6rem; font-weight: 800; letter-spacing: .08em; padding: .1rem .38rem;
  border-radius: 99px; color: var(--lacquer); flex: 0 0 auto;
  background: linear-gradient(120deg, var(--amber-soft), var(--gold));
}

.ratrow {
  padding: .75rem .85rem; border-radius: var(--radius-sm); margin-bottom: .5rem;
  border: 1px solid var(--border); background: var(--surface-2); position: relative;
  border-left: 3px solid var(--axis);
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.ratrow:hover { transform: translateX(2px); border-color: var(--border-strong); }
.ratrow.changed { border-left-color: var(--amber); }
.ratrow.changed:hover { border-left-color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); }
.ratdelta { display: grid; grid-template-columns: 1fr auto 1fr; gap: .7rem; align-items: stretch; }
.ratdelta > div:not(.arrow) {
  padding: .45rem .6rem; border-radius: var(--radius-xs); font-size: .82rem;
  background: var(--surface-sunken); line-height: 1.45;
}
.ratdelta span { display: block; font-size: .58rem; letter-spacing: .12em; font-weight: 700;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: .18rem; }
.ratdelta .now { color: var(--text-primary); }
.ratrow.changed .ratdelta .now { border: 1px solid color-mix(in srgb, var(--amber) 40%, transparent); }
.ratdelta .was { color: var(--text-secondary); }
.ratdelta .arrow { align-self: center; color: var(--gold); font-size: 1.05rem; }
.ratrow p { margin: .55rem 0 0; color: var(--text-secondary); line-height: 1.6; max-width: 82ch; }
.rattag {
  position: absolute; top: .6rem; right: .7rem; font-size: .55rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; padding: .13rem .42rem; border-radius: 99px;
  background: var(--surface-sunken); color: var(--text-muted);
}
.ratrow.changed .rattag { background: var(--amber); color: var(--lacquer); }
@media (max-width: 720px) {
  .ratdelta { grid-template-columns: 1fr; }
  .ratdelta .arrow { transform: rotate(90deg); justify-self: center; }
  .rattag { position: static; display: inline-block; margin-bottom: .4rem; }
}


/* ==========================================================================
   THE CHARACTER LAYER
   Every stat colour comes from the same 0-100 ramp the charts use, so a stat
   chip, its snowflake spoke and its score chip can never disagree.
   ========================================================================== */

.levelpip {
  display: inline-flex; align-items: baseline; gap: .3rem; padding: .22rem .6rem;
  border-radius: var(--radius-pill); background: var(--surface-sunken);
  border: 1px solid var(--border-strong); font-variant-numeric: tabular-nums;
}
.levelpip .lp-l { font-size: .58rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-muted); font-weight: 700; }
.levelpip b { font-size: 1.02rem; font-weight: 800; letter-spacing: -.02em; }
.levelpip .lp-r {
  font-size: .64rem; font-weight: 800; padding: .04rem .34rem; border-radius: 99px;
  background: linear-gradient(120deg, var(--amber-soft), var(--gold)); color: var(--lacquer);
}

.classchip {
  display: inline-flex; align-items: center; gap: .38rem; padding: .22rem .66rem;
  border-radius: var(--radius-pill); font-size: .76rem; font-weight: 700;
  background: color-mix(in srgb, var(--cc, var(--gold)) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--cc, var(--gold)) 48%, transparent);
  color: var(--cc, var(--gold));
}
.classchip.c-critical  { --cc: var(--critical); }
.classchip.c-good      { --cc: var(--good); }
.classchip.c-serious   { --cc: var(--serious); }
.classchip.c-brand     { --cc: var(--brand-soft); }
.classchip.c-amber     { --cc: var(--amber); }
.classchip.c-axis      { --cc: var(--text-muted); }
.classchip.c-series-3  { --cc: var(--series-3); }
.classchip.c-series-5  { --cc: var(--series-5); }
.classchip.c-series-6  { --cc: var(--series-6); }
.subclass {
  font-size: .68rem; color: var(--text-muted); padding: .2rem .5rem;
  border-radius: var(--radius-pill); background: var(--surface-sunken);
  border: 1px dashed var(--border-strong);
}

.charhero { display: flex; gap: 1.4rem; align-items: flex-start; flex-wrap: wrap; }
.charhero .ch-left { flex: 1 1 380px; min-width: 0; }
.charhero .ch-right { flex: 0 0 auto; margin-inline: auto; }

/* ---------- the run hero: four regions ----------
   Identity spans the top, the stake and the three-way comparison sit side by
   side beneath it, and the four qualifying figures span the bottom. Grid
   rather than the old flex row because the two middle regions have to share a
   baseline and a gutter; `minmax(0, …)` on both columns because a grid track
   is min-content by default and the bar labels would otherwise refuse to
   shrink, pushing the card past its own panel. */
.runhero { display: grid; gap: 1.1rem 1.6rem; align-items: start;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); }
.runhero .rh-id    { grid-column: 1 / -1; display: flex; gap: 1.1rem; align-items: center;
                     flex-wrap: wrap; }
.runhero .rh-tiles { grid-column: 1 / -1; display: grid; gap: .6rem;
                     grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); }
.runhero .rh-main, .runhero .rh-vs { min-width: 0; }
.runhero .rh-wheel { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
.runhero .rh-idtext { flex: 1 1 260px; min-width: 0; display: grid; gap: .45rem; }
.runhero .rh-rankline { margin: 0; max-width: 56ch; }

/* The grade and its two labels. The sub-label is not a tooltip on purpose: a
   letter that can still change has to say so where it is read, not on hover. */
.runhero .rh-grade { display: flex; align-items: center; gap: .6rem; }
.runhero .rh-gradelab { display: block; font-size: .82rem; font-weight: 700; }
.runhero .rh-gradesub { display: block; }

.rh-eyebrow {
  font-family: "IBM Plex Mono", monospace; font-size: .68rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted);
  margin: 0 0 .1rem;
}
.runhero .rh-ladder { margin: .45rem 0 0; color: var(--text-secondary); }
.runhero .rh-scale  { margin: .7rem 0 0; max-width: 62ch; }

/* The three-way bars.
   -------------------------------------------------------------------------
   The columns live on the *list*, and each row borrows them with `subgrid`.
   Giving every row its own `grid-template-columns` looked equivalent and was
   not: each row then sized its own label column, so "MSCI ACWI" pushed its
   track 17px right of "You" and the three tracks came out 111, 111 and 93px
   wide. Bars normalised to the largest of the three mean nothing at all if the
   tracks they sit in are different lengths — the whole comparison was being
   quietly rescaled per row. One shared column set is the fix. */
.rhbars {
  display: grid; gap: .7rem;
  grid-template-columns: minmax(52px, max-content) minmax(0, 1fr) max-content max-content;
}
.rhbar {
  display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;
  align-items: center; gap: .1rem .6rem;
}
.rhbar-l { font-size: .82rem; font-weight: 700; }
.rhbar-track {
  position: relative;
  height: 12px; border-radius: var(--radius-pill); overflow: hidden;
  background: color-mix(in oklab, var(--border-strong) 60%, transparent);
}
/* The stake, marked on every track.
   -------------------------------------------------------------------------
   All three bars are drawn from zero, which is the honest baseline and also
   the reason the differences between them looked trivial: three numbers a few
   percent apart, each carrying the same 10,000 underneath. Truncating the axis
   at the stake would fix the legibility by lying about the proportions. A tick
   fixes it without: the bars stay to scale, and the part that is actually the
   reader's own doing is the piece past the mark. */
.rhbar-stake {
  position: absolute; top: -2px; bottom: -2px; width: 2px; margin-left: -1px;
  background: var(--surface-1);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--text-muted) 55%, transparent);
}
.rhbar-fill {
  display: block; height: 100%; border-radius: inherit;
  background: color-mix(in oklab, var(--text-muted) 55%, transparent);
  transition: width var(--med) var(--ease);
}
/* Only the reader's own bar is in the brand ramp. If all three were coloured
   the block would read as a ranking of three competitors rather than as one
   stake measured against what it could have been. */
.rhbar[data-key="you"] .rhbar-fill {
  background: linear-gradient(90deg, var(--brand), var(--gold) 85%);
}
.rhbar[data-key="you"] .rhbar-l { color: var(--text-primary); }
.rhbar-v {
  font-family: "IBM Plex Mono", monospace; font-size: .88rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.rhbar-r { font-size: .78rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.rhbar-n { grid-column: 2 / -1; }
.rhbar-key { margin: .75rem 0 0; max-width: 54ch; }

@media (max-width: 900px) {
  .runhero { grid-template-columns: minmax(0, 1fr); }
}
/* Below this the label column costs more than the track it is labelling — at
   383px a 70px label left the bar 93px, which is not a bar. The label moves
   above its own full-width track instead, and every row is identical again. */
@media (max-width: 560px) {
  .rhbars { grid-template-columns: minmax(0, 1fr) max-content; }
  .rhbar { gap: .25rem .6rem; }
  .rhbar-l, .rhbar-track, .rhbar-n { grid-column: 1 / -1; }
}

/* Stat chips. The colour is passed in as --sc so the chip, the snowflake and
   the score badge all read from one ramp rather than three. */
.statchips { display: flex; gap: .5rem; flex-wrap: wrap; }
.statchip {
  appearance: none; cursor: pointer; font: inherit; text-align: left;
  display: grid; grid-template-columns: auto 1fr auto; grid-template-rows: auto auto;
  gap: 0 .45rem; padding: .5rem .7rem; min-width: 138px; flex: 1 1 138px;
  border-radius: var(--radius-sm); background: var(--surface-2);
  border: 1px solid var(--border); border-left: 3px solid var(--sc, var(--axis));
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
.statchip:hover { transform: translateY(-2px); border-color: var(--border-strong);
  box-shadow: var(--shadow-sm); }
.statchip[aria-pressed="true"] {
  border-color: var(--amber); box-shadow: 0 0 0 1px var(--amber) inset, var(--glow-gold);
}
.statchip .sc-ic { grid-row: 1 / 3; align-self: center; font-size: 1.15rem; opacity: .9; }
.statchip .sc-short { font-size: .66rem; font-weight: 800; letter-spacing: .14em;
  color: var(--text-muted); }
.statchip .sc-val { grid-row: 1 / 3; align-self: center; font-size: 1.5rem; font-weight: 800;
  letter-spacing: -.03em; color: var(--sc); font-variant-numeric: tabular-nums; }
.statchip .sc-sub { grid-column: 2; font-size: .7rem; color: var(--text-secondary); }

.statpanel {
  margin-top: .85rem; padding: .9rem 1rem; border-radius: var(--radius-sm);
  background: var(--surface-sunken); border: 1px solid var(--border-strong);
  border-left: 3px solid var(--amber); animation: riseIn var(--base) var(--ease-out);
}
.sp-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .4rem; }
.sp-ic { font-size: 1.3rem; }
.sp-head b { font-size: .92rem; }
.sp-head .small { margin-left: .4rem; }
.sp-val { font-size: 1.7rem; font-weight: 800; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; }
.sp-flav { font-style: italic; color: var(--gold-soft); font-size: .82rem; margin: 0 0 .5rem; }
.statpanel p.small { line-height: 1.62; max-width: 82ch; }
.sp-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: .4rem; margin-top: .6rem; }
.sp-metrics > div { display: flex; justify-content: space-between; gap: .5rem;
  padding: .34rem .5rem; border-radius: var(--radius-xs); background: var(--surface-1);
  font-size: .76rem; }
.sp-metrics span { color: var(--text-muted); text-transform: capitalize; }
.sp-metrics b { font-variant-numeric: tabular-nums; }

.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: .6rem; }
.statdef { display: flex; gap: .6rem; padding: .6rem .7rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border); }
.statdef .sd-ic { font-size: 1.3rem; flex: 0 0 auto; }
.statdef p { margin: .2rem 0 0; }

/* ---------- the run ---------- */
/* The hero number, in the same grammar as `.sh-num` and `.twoq-n`: mono, 600,
   tabular. It was Manrope 850, which is the interface face — a figure that
   large in the interface face reads as a headline rather than as a measurement,
   and the three heroes now agree with each other. */
.equity {
  font-family: "IBM Plex Mono", monospace;
  font-size: clamp(2.6rem, 5vw, 3.4rem); font-weight: 600;
  letter-spacing: -.03em; line-height: 1;
  margin: .35rem 0 .1rem; font-variant-numeric: tabular-nums;
  background: linear-gradient(120deg, var(--amber-soft), var(--gold) 55%, var(--brand-soft));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.seasonchip {
  font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  padding: .22rem .58rem; border-radius: var(--radius-pill);
  background: var(--surface-sunken); border: 1px solid var(--border-strong);
  color: var(--text-secondary);
}
.xpbar {
  height: 9px; border-radius: 99px; background: var(--surface-sunken);
  border: 1px solid var(--border); overflow: hidden;
}
.xpbar span {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--brand), var(--gold) 70%, var(--amber-soft));
  transition: width var(--med) var(--ease);
}

/* ---------- scenarios ---------- */
.scenariogrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: .8rem; margin-bottom: .9rem; }
.scase {
  padding: .85rem .95rem; border-radius: var(--radius-sm); background: var(--surface-1);
  border: 1px solid var(--border); border-top: 3px solid var(--cc, var(--gold));
}
.scase.c-critical { --cc: var(--critical); }
.scase.c-gold     { --cc: var(--gold); }
.scase.c-good     { --cc: var(--good); }
.sc-head { display: flex; justify-content: space-between; align-items: baseline; }
.sc-head b { font-size: .95rem; color: var(--cc); }
.sc-value { font-size: 1.3rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.sc-up { font-size: .76rem; font-weight: 700; margin-bottom: .4rem; }
.sc-prompt { margin: 0 0 .55rem; }
.scase textarea {
  width: 100%; resize: vertical; font: inherit; font-size: .78rem; padding: .45rem .55rem;
  border-radius: var(--radius-xs); background: var(--surface-sunken);
  border: 1px solid var(--border); color: var(--text-primary);
}
.scase textarea:focus { outline: none; border-color: var(--gold); box-shadow: var(--glow-gold); }

@media (max-width: 720px) {
  .charhero .ch-right { margin-inline: 0; width: 100%; }
  .equity { font-size: 2.2rem; }
  .statchip { min-width: 118px; }
}



/* ==========================================================================
   SYSTEM CUES
   Solo Leveling's "System" is a pane of light with a hard edge and a corner
   bracket — the visual language is precision, not decoration. Borrowed here in
   three narrow places only: the corner ticks on a hero, a scanline that runs
   once when a panel opens, and a level-up flash. Anything more and the app
   becomes cosplay, which reads as less trustworthy, not more — and this is a
   tool for deciding where money goes.
   ========================================================================== */

/* Corner brackets. Two pseudo-elements, top-left and bottom-right, so a hero
   card reads as a framed pane rather than a rounded rectangle. */
/* NOTE: .card.hero already spends ::before on its gradient top-line, so the
   brackets get their own hook rather than fighting it. Adding .framed to a hero
   is opt-in — the cue means something only while it stays rare. */
.card.framed { position: relative; }
.card.framed > .fr-tl, .card.framed > .fr-br {
  position: absolute; width: 15px; height: 15px; pointer-events: none;
  border-color: color-mix(in srgb, var(--gold) 60%, transparent); opacity: .85;
}
.card.framed > .fr-tl { top: 10px; left: 10px; border-top: 1.5px solid; border-left: 1.5px solid;
  border-top-left-radius: 4px; }
.card.framed > .fr-br { bottom: 10px; right: 10px; border-bottom: 1.5px solid;
  border-right: 1.5px solid; border-bottom-right-radius: 4px; }
:root[data-theme="light"] .card.framed > .fr-tl,
:root[data-theme="light"] .card.framed > .fr-br { opacity: .55; }

/* A single scanline sweep when a stat panel opens. Runs once, 700ms, then the
   element is inert — no looping glow, which would pull the eye forever. */
.statpanel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background: linear-gradient(180deg, transparent 0%,
              color-mix(in srgb, var(--amber) 16%, transparent) 48%, transparent 96%);
  animation: sweep 700ms var(--ease) 1 both;
}
.statpanel { position: relative; overflow: hidden; }
@keyframes sweep { from { transform: translateY(-100%); } to { transform: translateY(100%); } }

/* The level pip gains a quiet halo the higher the level runs. Set --lv from JS
   as a 0-1 fraction so the glow is data rather than decoration. */
.levelpip { position: relative; }
.levelpip[style*="--lv"]::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 calc(2px + 12px * var(--lv, 0)) color-mix(in srgb, var(--amber)
              calc(var(--lv, 0) * 55%), transparent);
}

/* Level-up / level-down. Applied for one animation then removed. */
@keyframes levelflash {
  0% { transform: scale(1); }
  35% { transform: scale(1.14); }
  100% { transform: scale(1); }
}
.levelpip.flash { animation: levelflash 520ms var(--ease-out) 1; }

/* A thin gold rule that draws itself in — used above section heads on the
   character and run pages, where a plain border would read as ordinary chrome. */
.systemrule {
  height: 1px; margin: 0 0 .8rem; border-radius: 1px;
  background: linear-gradient(90deg, var(--gold), color-mix(in srgb, var(--gold) 8%, transparent) 70%,
              transparent);
  transform-origin: left; animation: drawrule 520ms var(--ease-out) 1 both;
}
@keyframes drawrule { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Class chips get a faint diagonal hatch so a class reads as a *badge* rather
   than another coloured pill in a page that already has several. */
.classchip {
  position: relative; overflow: hidden;
  background-image: repeating-linear-gradient(115deg,
    color-mix(in srgb, var(--cc, var(--gold)) 22%, transparent) 0 2px,
    transparent 2px 7px);
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion-choice="user"]) .statpanel::after,
  :root:not([data-motion-choice="user"]) .systemrule,
  :root:not([data-motion-choice="user"]) .levelpip.flash { animation: none; }
  :root:not([data-motion-choice="user"]) .statpanel::after { display: none; }
}

/* ---------- sub-stats ----------
   The things that make a stat, each ranked on the same 0-100 the stat itself
   uses. The notch at 75% is the framework's target: everything to its left
   missed the bar, everything right of it beat one. */
.subhead {
  display: flex; align-items: baseline; gap: .5rem; margin: .7rem 0 .4rem;
  font-size: .62rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold-soft);
}
.subhead span { font-size: .66rem; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--text-muted); }
.substats { display: grid; gap: .28rem; }
.substat {
  display: grid; align-items: center; gap: .55rem;
  grid-template-columns: minmax(120px, 1.5fr) minmax(70px, 2fr) 2.1rem minmax(96px, auto) 1.9rem;
  padding: .26rem .4rem; border-radius: var(--radius-xs);
  transition: background var(--fast) var(--ease);
}
.substat:hover { background: var(--surface-1); }
.ss-label { font-size: .74rem; color: var(--text-secondary); }
.ss-info {
  display: inline-grid; place-items: center; width: 12px; height: 12px; margin-left: .25rem;
  border-radius: 50%; font-size: .55rem; font-weight: 800; vertical-align: middle;
  background: var(--surface-2); color: var(--text-muted); cursor: help;
}
.ss-bar {
  position: relative; height: 6px; border-radius: 99px; overflow: hidden;
  background: var(--surface-1); border: 1px solid var(--border);
}
.ss-bar i { display: block; height: 100%; border-radius: 99px;
  transition: width var(--med) var(--ease); }
.ss-bar u {
  position: absolute; top: -2px; bottom: -2px; width: 1px; text-decoration: none;
  background: var(--gold); opacity: .65;
}
.ss-score { font-size: .82rem; font-weight: 800; text-align: right;
  font-variant-numeric: tabular-nums; }
.ss-val { font-size: .72rem; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--text-primary); }
.ss-tgt { display: block; font-size: .62rem; color: var(--text-muted); }
.ss-w { font-size: .6rem; text-align: right; color: var(--text-muted);
  font-variant-numeric: tabular-nums; }
@media (max-width: 760px) {
  .substat { grid-template-columns: 1fr 2.1rem minmax(88px, auto); gap: .35rem .5rem; }
  .ss-bar, .ss-w { display: none; }
}

/* ---------- pre-mortem ----------
   Rendered inside a fired sell trigger, because the account you wrote before you
   owned anything and the rule that just broke belong on screen together. */
.premortem {
  margin-top: .6rem; padding: .6rem .75rem; border-radius: var(--radius-xs);
  background: var(--surface-sunken); border-left: 3px solid var(--series-5);
}
.pm-head {
  font-size: .6rem; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--series-5); margin-bottom: .3rem;
}
.premortem p { margin: 0 0 .3rem; }
.pm-prompt { color: var(--gold-soft); font-style: italic; }
.partymix { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .7rem; }
.partymix .classchip b { margin-left: .2rem; font-variant-numeric: tabular-nums; }

/* ---------- allocation bar ----------
   One row that shows the whole book at once: each segment is a position, width
   is its weight, colour is its quality score, and the muted tail is cash. The
   point is that a size is only meaningful next to the other sizes. */
.allocbar {
  display: flex; height: 26px; border-radius: var(--radius-pill); overflow: hidden;
  background: var(--surface-sunken); border: 1px solid var(--border);
}
.allocseg { transition: filter var(--fast) var(--ease), opacity var(--fast) var(--ease); }
.allocseg + .allocseg { border-left: 1px solid var(--surface-1); }
.allocbar:hover .allocseg { opacity: .5; }
.allocbar .allocseg:hover { opacity: 1; filter: brightness(1.25); }
.allocseg.cash {
  background: repeating-linear-gradient(45deg, var(--surface-2) 0 5px, var(--surface-sunken) 5px 10px);
}

/* ---------- modal (sell-rule editor) ---------- */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: 1.2rem;
  background: color-mix(in srgb, var(--lacquer) 72%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: fadeIn var(--fast) var(--ease);
}
.modal {
  width: min(560px, 100%); max-height: 88vh; overflow-y: auto; padding: 1.3rem 1.4rem;
  border-radius: var(--radius-lg); background: var(--surface-1);
  border: 1px solid var(--border-strong); box-shadow: var(--shadow-lg), var(--glow-gold);
  animation: riseIn var(--base) var(--ease-out);
}
.modal h3 { margin: 0 0 .3rem; }
.modal textarea {
  width: 100%; resize: vertical; font: inherit; padding: .5rem .65rem;
  border-radius: var(--radius-xs); background: var(--surface-sunken);
  border: 1px solid var(--border); color: var(--text-primary);
}
.modal textarea:focus { outline: none; border-color: var(--gold); box-shadow: var(--glow-gold); }
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes riseIn { from { opacity: 0; transform: translateY(10px) scale(.985) } to { opacity: 1; transform: none } }

.btn.tiny { padding: .16rem .5rem; font-size: .68rem; border-radius: var(--radius-xs); }
.linkish { cursor: pointer; border-bottom: 1px dashed var(--gold); }
.linkish:hover { color: var(--gold); }
.rr-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .25rem; }

/* ---------- company-page additions ---------- */
.cardslot { display: flex; gap: .9rem; flex-wrap: wrap; align-items: flex-start; }
.cardslot .pcard { flex: 0 0 250px; cursor: default; }
.geoeditor { display: grid; gap: .4rem; grid-template-columns: 1fr 6rem auto; align-items: center; }
.geoeditor input { margin: 0; }
.printstrip {
  display: flex; gap: .8rem; align-items: center; flex-wrap: wrap;
  padding: .55rem .8rem; border-radius: var(--radius-sm); margin-bottom: .7rem;
  background: linear-gradient(90deg, color-mix(in srgb, var(--gold) 12%, transparent), var(--surface-2) 62%);
  border: 1px solid color-mix(in srgb, var(--gold) 34%, transparent); font-size: .79rem;
}
.printstrip.quiet { background: linear-gradient(90deg, color-mix(in srgb, var(--amber) 17%, transparent), var(--surface-2) 62%);
  border-color: color-mix(in srgb, var(--amber) 58%, transparent); }
.printstrip b { color: var(--amber-soft); }
/* ---------- light-mode card tuning ----------
   The pale card back has far less contrast headroom than lacquer, so the
   "strong" attribute colour has to come down a step and the foil sweep has to
   stop being a white flash. */
:root[data-theme="light"] .pattr[data-band="hi"] b { color: var(--gold-deep); }
:root[data-theme="light"] .pcard::before {
  background: linear-gradient(115deg, transparent 32%,
              color-mix(in srgb, var(--gold) 26%, transparent) 50%, transparent 68%);
}
:root[data-theme="light"] .pcard-ovr b {
  background: linear-gradient(140deg, var(--gold), var(--brand-deep));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
:root[data-theme="light"] .calrow .days { color: var(--gold-deep); }
:root[data-theme="light"] .pcard[data-tier="legendary"] { --tier-a: var(--gold); --tier-b: var(--brand); }

/* ---------- the gate rank ----------
   The one place in the app where a single letter carries the answer, so it is
   allowed to be large. Everything else on the page exists to stop the letter
   being believed on its own: the meter, the components, the count, the ladder. */
.gaterank {
  display: flex; gap: 1.1rem; align-items: stretch;
  --gc: var(--text-muted);
}
.gaterank.c-critical { --gc: var(--critical); }
.gaterank.c-amber    { --gc: var(--amber); }
.gaterank.c-good     { --gc: var(--good); }
.gaterank.c-warning  { --gc: var(--warning); }
.gaterank.c-serious  { --gc: var(--serious); }
.gaterank.c-axis     { --gc: var(--text-muted); }
.gr-letter {
  flex: 0 0 auto; width: 96px; display: grid; place-items: center;
  font-size: 3.6rem; font-weight: 800; line-height: 1; letter-spacing: -.05em;
  color: var(--gc); border-radius: var(--radius-sm);
  border: 1px solid color-mix(in oklab, var(--gc) 45%, transparent);
  background:
    linear-gradient(180deg, color-mix(in oklab, var(--gc) 16%, transparent), transparent 78%),
    var(--surface-sunken);
  position: relative; overflow: hidden;
}
/* A slow sweep, borrowed from the System panels — one shared cue, used sparingly
   enough that it still reads as "this is a reading" rather than as decoration. */
.gr-letter::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(115deg, transparent 38%,
    color-mix(in oklab, var(--gc) 22%, transparent) 50%, transparent 62%);
  transform: translateX(-100%); animation: gr-sweep 5.5s var(--ease) infinite;
}
@keyframes gr-sweep { 0%, 62% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }
.gr-body { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; }
.gr-title { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.gr-title b { font-size: 1.05rem; letter-spacing: -.01em; }
.gr-score { font-size: .84rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.gr-score span { color: var(--text-muted); font-size: .74em; }
.gr-blurb { font-size: .8rem; color: var(--text-secondary); margin: .3rem 0 .55rem; max-width: 62ch; }
.gr-meter {
  height: 7px; border-radius: 99px; background: var(--surface-1);
  border: 1px solid var(--border); overflow: hidden;
}
.gr-meter i {
  display: block; height: 100%; border-radius: 99px;
  background: linear-gradient(90deg, color-mix(in oklab, var(--gc) 55%, transparent), var(--gc));
  transition: width var(--med) var(--ease-out);
}
@media (max-width: 620px) {
  .gaterank { flex-direction: column; gap: .7rem; }
  .gr-letter { width: 100%; height: 74px; font-size: 2.6rem; }
}

/* the ladder — every rank at once, so the one you got has a scale behind it */
.ladder { display: grid; gap: .3rem; }
.rung {
  display: grid; grid-template-columns: 2rem 1fr auto; gap: .65rem; align-items: center;
  padding: .42rem .55rem; border-radius: var(--radius-xs);
  border: 1px solid transparent; --gc: var(--text-muted);
  transition: background var(--fast) var(--ease);
}
.rung.c-critical { --gc: var(--critical); } .rung.c-amber { --gc: var(--amber); }
.rung.c-good     { --gc: var(--good); }     .rung.c-warning { --gc: var(--warning); }
.rung.c-serious  { --gc: var(--serious); }  .rung.c-axis { --gc: var(--text-muted); }
.rung:hover { background: var(--surface-1); }
.rung.here {
  background: color-mix(in oklab, var(--gc) 10%, transparent);
  border-color: color-mix(in oklab, var(--gc) 40%, transparent);
}
.rg-l {
  display: grid; place-items: center; width: 2rem; height: 2rem; border-radius: var(--radius-xs);
  font-weight: 800; font-size: 1rem; color: var(--gc);
  background: color-mix(in oklab, var(--gc) 12%, transparent);
}
.rung.here .rg-l { background: color-mix(in oklab, var(--gc) 24%, transparent); }
.rg-b { min-width: 0; }
.rg-b b { font-size: .8rem; display: block; }
.rg-b .small { display: block; line-height: 1.35; }
.rung:not(.here) .rg-b .small { display: none; }
.rg-c { font-size: .72rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* ---------- party synergy ----------
   A bar per class rather than per sector, because the thing that breaks a book
   is a shared sensitivity and sensitivity does not follow sector lines. */
/* ---------- the party card: what the book is, then what it has none of ----------
   The class colours are declared once, on the card, and inherited by the
   composition segments, the key and the chips alike — the same nine names the
   engine hands out (`rpg.CLASSES[].colour`). Repeating the mapping per
   component is how the sixth one gets forgotten. */
.partycard { --cc: var(--text-muted); }
.partycard .c-critical { --cc: var(--critical); }
.partycard .c-good     { --cc: var(--good); }
.partycard .c-serious  { --cc: var(--serious); }
.partycard .c-brand    { --cc: var(--brand-soft); }
.partycard .c-amber    { --cc: var(--amber); }
.partycard .c-axis     { --cc: var(--text-muted); }
.partycard .c-series-3 { --cc: var(--series-3); }
.partycard .c-series-5 { --cc: var(--series-5); }
.partycard .c-series-6 { --cc: var(--series-6); }

.pc-head { display: flex; align-items: baseline; gap: 1.1rem; flex-wrap: wrap;
  margin-bottom: .85rem; }
.pc-num {
  font-family: "IBM Plex Mono", monospace; font-size: 2.6rem; font-weight: 600;
  line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.pc-den { font-size: .78rem; color: var(--text-muted); }
.pc-sum { flex: 1 1 300px; margin: 0; line-height: 1.6; }

/* The composition. Each segment is flexed by its own weight, so the markup
   carries no percentage at all — the bar IS the book rather than a description
   of one. A 2px surface gap between segments, the same spacer the stacked
   marks elsewhere use, so two adjacent classes never blur into one block. */
.pcomp { display: flex; height: 34px; border-radius: var(--radius-xs); overflow: hidden;
  background: var(--surface-1); border: 1px solid var(--border); }
.pcomp-seg {
  min-width: 3px; display: grid; place-items: center; cursor: help;
  background: color-mix(in oklab, var(--cc) 78%, transparent);
  box-shadow: inset -2px 0 0 0 var(--surface-1);
  transition: filter var(--fast) var(--ease);
}
.pcomp-seg:last-child { box-shadow: none; }
.pcomp-seg:hover { filter: brightness(1.12); }
.pcomp-seg i { font-style: normal; font-size: .8rem; color: var(--surface-1); opacity: .92; }
.pcomp-key { display: flex; flex-wrap: wrap; gap: .3rem .9rem; margin-top: .5rem; }
.pck { display: inline-flex; align-items: center; gap: .3rem; }
.pck i { font-style: normal; color: var(--cc); }

/* The absences. Dashed, because they are holes rather than holdings — a solid
   chip beside a solid chip reads as two things you own. */
.pmiss { margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--border); }
.pmiss-l { margin: 0 0 .5rem; font-size: .82rem; font-weight: 700; }
.pmiss-chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.pmchip {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .28rem .6rem; border-radius: var(--radius-pill);
  border: 1px dashed color-mix(in oklab, var(--amber) 62%, transparent);
  background: color-mix(in oklab, var(--amber) 9%, transparent);
  color: var(--text-secondary); font-size: .76rem; font-weight: 600;
  cursor: help;
}
.pmchip:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.pmchip i { font-style: normal; color: var(--amber); }
.pmiss-why { margin: .65rem 0 0; max-width: 76ch; line-height: 1.6;
  color: var(--text-secondary); }
.pmiss-none { margin: 1rem 0 0; max-width: 76ch; line-height: 1.6; }

@media (max-width: 620px) {
  .pcomp { height: 26px; }
  .pcomp-seg i { display: none; }
  .pc-num { font-size: 2.1rem; }
}

.partybars { display: grid; gap: .32rem; }
.pbar {
  display: grid; gap: .55rem; align-items: center;
  grid-template-columns: 1.4rem minmax(74px, auto) minmax(80px, 1fr) 2.8rem minmax(96px, auto);
  padding: .3rem .4rem; border-radius: var(--radius-xs); --cc: var(--text-muted);
  transition: background var(--fast) var(--ease);
}
.pbar:hover { background: var(--surface-1); }
.pbar.c-critical { --cc: var(--critical); } .pbar.c-good { --cc: var(--good); }
.pbar.c-serious  { --cc: var(--serious); }  .pbar.c-brand { --cc: var(--brand-soft); }
.pbar.c-amber    { --cc: var(--amber); }    .pbar.c-axis { --cc: var(--text-muted); }
.pbar.c-series-3 { --cc: var(--series-3); } .pbar.c-series-5 { --cc: var(--series-5); }
.pbar.c-series-6 { --cc: var(--series-6); }
.pb-ic { color: var(--cc); font-size: .95rem; text-align: center; }
.pb-l { font-size: .76rem; font-weight: 600; }
.pb-track {
  height: 8px; border-radius: 99px; background: var(--surface-1);
  border: 1px solid var(--border); overflow: hidden;
}
.pb-track i {
  display: block; height: 100%; border-radius: 99px; background: var(--cc);
  transition: width var(--med) var(--ease-out);
}
.pb-w { font-size: .76rem; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums; }
.pb-e { font-size: .66rem; color: var(--text-muted); text-align: right; }
@media (max-width: 700px) {
  .pbar { grid-template-columns: 1.4rem 1fr 2.8rem; }
  .pb-track, .pb-e { display: none; }
}
/* penalty bars run the other way: full is bad */
.ss-bar.pen i { background: var(--critical); }

/* ---------- expiring passes ---------- */
.passcard {
  padding: .6rem .7rem; border-radius: var(--radius-xs); border: 1px solid var(--border);
  background: var(--surface-sunken); margin-bottom: .45rem; cursor: pointer;
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.passcard:hover { border-color: var(--brand-soft); transform: translateY(-1px); }
.passcard.hot { border-left: 3px solid var(--amber); }
.pc-head { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.pc-why { margin: .35rem 0 0; color: var(--text-secondary); font-style: italic; }

/* ---------- stat curves ---------- */
.curvehead { align-items: center; gap: .6rem; }
.curvelegend { display: flex; flex-wrap: wrap; gap: .5rem .9rem; margin: .3rem 0 .55rem; }
.cl-i { display: inline-flex; align-items: center; gap: .3rem; font-size: .7rem;
  color: var(--text-secondary); }
.cl-i i { width: 10px; height: 3px; border-radius: 99px; display: inline-block; }
.cl-i b { font-variant-numeric: tabular-nums; }

/* the four measurements behind a gate rank — a sub-stat row would be wrong here,
   since these have no target to be measured against and no unit of their own */
.gcomps { display: grid; gap: .38rem; }
.gcomp {
  display: grid; gap: .7rem; align-items: center;
  grid-template-columns: minmax(160px, 1.4fr) minmax(90px, 2fr) 2.4rem 2.6rem;
  padding: .3rem .4rem; border-radius: var(--radius-xs);
  transition: background var(--fast) var(--ease);
}
.gcomp:hover { background: var(--surface-1); }
.gc-l b { font-size: .78rem; display: block; }
.gc-l .small { display: block; line-height: 1.3; }
.gc-bar {
  height: 7px; border-radius: 99px; background: var(--surface-1);
  border: 1px solid var(--border); overflow: hidden;
}
.gc-bar i { display: block; height: 100%; border-radius: 99px;
  transition: width var(--med) var(--ease-out); }
.gc-v { font-size: .88rem; font-weight: 800; text-align: right;
  font-variant-numeric: tabular-nums; }
.gc-w { font-size: .7rem; color: var(--text-muted); text-align: right;
  font-variant-numeric: tabular-nums; }
.gaterank.c-gold     { --gc: var(--gold); }
.gaterank.c-series-3 { --gc: var(--series-3); }
.rung.c-gold         { --gc: var(--gold); }
.rung.c-series-3     { --gc: var(--series-3); }
@media (max-width: 700px) {
  .gcomp { grid-template-columns: 1fr 2.4rem 2.6rem; }
  .gc-bar { display: none; }
}
.curvelegend .cl-i {
  background: none; border: 1px solid transparent; border-radius: var(--radius-pill);
  padding: .12rem .42rem; cursor: pointer; font: inherit; font-size: .7rem;
  color: var(--text-muted); opacity: .45;
  transition: opacity var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.curvelegend .cl-i.on { opacity: 1; color: var(--text-secondary); }
.curvelegend .cl-i:hover { border-color: var(--border); }

/* ---------- panels materialising ----------
   The view already fades in as one block. Staggering the cards inside it is the
   difference between a page appearing and a system menu opening — the same
   motion vocabulary the rest of the app already uses (fadeUp, the stat-panel
   sweep, the card foil), applied to the thing you actually watch arrive.

   Capped at the first six children: past that the stagger stops reading as
   sequence and starts reading as lag. Everything below simply appears. */
@keyframes panelIn {
  from { opacity: 0; transform: translateY(9px) scale(.994); }
  to   { opacity: 1; transform: none; }
}
.view.active > .card,
.view.active > section > .card,
.view.active > div > .card {
  animation: panelIn 260ms var(--ease-out) both;
}
.view.active > .card:nth-child(1),
.view.active > div > .card:nth-child(1) { animation-delay: 0ms; }
.view.active > .card:nth-child(2),
.view.active > div > .card:nth-child(2) { animation-delay: 45ms; }
.view.active > .card:nth-child(3),
.view.active > div > .card:nth-child(3) { animation-delay: 90ms; }
.view.active > .card:nth-child(4),
.view.active > div > .card:nth-child(4) { animation-delay: 130ms; }
.view.active > .card:nth-child(5),
.view.active > div > .card:nth-child(5) { animation-delay: 165ms; }
.view.active > .card:nth-child(n+6),
.view.active > div > .card:nth-child(n+6) { animation-delay: 195ms; }

/* A hairline that runs the top edge of a hero card as it arrives, then stops.
   The HUD cue this app already uses for "this pane is about you", extended to
   the one card per view that introduces it. */
.card.hero { position: relative; overflow: hidden; }
.card.hero::after {
  content: ""; position: absolute; top: 0; left: 0; height: 2px; width: 100%;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--gold) 85%, transparent) 45%, transparent);
  transform: translateX(-100%);
  animation: heroSweep 900ms var(--ease-out) 220ms both;
  pointer-events: none;
}
@keyframes heroSweep { to { transform: translateX(100%); } }

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion-choice="user"]) .view.active > .card,
  :root:not([data-motion-choice="user"]) .view.active > section > .card,
  :root:not([data-motion-choice="user"]) .view.active > div > .card { animation: none; }
  :root:not([data-motion-choice="user"]) .card.hero::after { animation: none; opacity: 0; }
}
