:root{
  --void:#070502;--gold-lite:#F8E39A;--gold:#E6B325;--gold-deep:#B9840C;
  --grad-gold:linear-gradient(135deg,#F8E39A 0%,#E6B325 46%,#B9840C 100%);
  --hair:rgba(230,179,37,.20);--hair-2:rgba(230,179,37,.12);
  --ink:#F4ECD8;--muted:#9b8d78;
  --bull:#25c088;--bear:#ef5a5f;--wash:rgba(230,179,37,.10);--accent:var(--gold);

  /* ── SKIN-AWARE LAYER (added 13 Aug 2026, white-skin work) ─────────────
     Everything above is the original dark palette and keeps its exact
     values, so no existing rule changes meaning. What follows are the
     tokens the white skin actually needs to flip, and they exist for one
     reason: a page can only be re-skinned through a token it references.
     Before this, ~600 colour LITERALS were baked into 18 page modules --
     `#0a0704`, `rgba(255,255,255,.03)`, `rgba(230,179,37,.14)` -- and a
     literal cannot be overridden by any parent selector. The literals were
     mechanically converted to the tokens below; this is the vocabulary
     they were converted TO.

     SURFACE LADDER: the four near-blacks that had drifted into the pages
     (#070502 / #0a0704 / #0d0a06 / #171310) were never arbitrary -- they
     are a depth ladder. Naming them makes that intent explicit and gives
     the white skin four correspondingly light steps.

     CHANNEL TRIPLETS (--*-rgb): CSS cannot change the colour inside an
     already-written rgba(); it can substitute the channels. So every
     `rgba(230,179,37,.14)` became `rgba(var(--gold-rgb),.14)`, preserving
     each rule's own alpha while letting the hue flip with the skin.

     --ov is the same trick for the overlay film: on a dark canvas a subtle
     lift is white at low alpha, on a light canvas it must be black at low
     alpha. One token, opposite values per skin, every `rgba(255,255,255,a)`
     rewritten to `rgba(var(--ov),a)` keeps its own intent in both. */
  --surface:#0c0906;--surface-1:#0a0704;--surface-2:#0d0a06;--surface-3:#171310;
  --ink-2:#e8dcc4;--ink-3:#c9bda6;
  --gold-rgb:230,179,37;--bull-rgb:37,192,136;--bear-rgb:239,90,95;
  --emerald-rgb:61,224,160;
  --bull-lite:#3de0a0;--bear-lite:#ff8a8e;
  --ov:255,255,255;      /* overlay film channels -- flips to 0,0,0 on white */
  --shadow:0,0,0;        /* drop-shadow channels -- stays black on both */

  /* Tinted surfaces. The pages had accumulated a dozen one-off deep
     greens (#0f5c3f, #123b2a, #0a4d32) and deep reds (#7a1e27, #3b1216)
     that all meant the same two things: "a surface carrying bullish
     weight" and "a surface carrying bearish weight", at two depths.
     Naming the two roles collapses the drift and gives the white skin a
     single place to answer it. */
  --bull-dim:#0f5c3f;--bull-dimmer:#062418;
  --bear-dim:#7a1e27;--bear-dimmer:#3b1216;

  /* The emerald language. /brain, the landing page and /hello-score are
     deliberately emerald rather than gold; those pages used to each carry
     their own copy of these values, which is exactly the drift this module
     exists to stop. Defined once here, aliased there. */
  --emerald:#0F5C3F;--emerald-lite:#3DE0A0;--emerald-deep:#062418;
  --grad-emerald:linear-gradient(135deg,#B9FFE0 0%,#3DE0A0 55%,#0F8F5F 100%);

  /* Extra channel triplets, same purpose as the ones above: they exist so
     an already-written rgba() keeps its own alpha while its hue follows
     the skin. */
  --void-rgb:7,5,2;--surface-1-rgb:10,7,4;--surface-3-rgb:20,16,8;
  --bull-dim-rgb:8,28,18;--gold-lite-rgb:248,227,154;
  --ink-rgb:244,236,216;--muted-rgb:155,141,120;

  /* Depth. Dark skin gets none: on a near-black canvas a soft shadow is
     invisible and a highlight reads as a bug. The white skin redefines
     these into the neumorphic set Home already shipped, which is what
     makes one shared component look native in both skins. */
  --lift:none;--lift-sm:none;--lift-lg:none;--inset:none;

  --fs-micro:11px;      /* timestamps, footer meta -- the floor, never below */
  --fs-label:11.5px;    /* uppercase eyebrows, nav labels, table headers */
  --fs-caption:12.5px;  /* secondary/supporting text */
  --fs-body:14.5px;     /* default reading size -- data rows, paragraphs */
  --fs-body-lg:16px;    /* drawer narrative, anything meant to be read at length */
  --fs-value:18px;      /* secondary numeric readouts */
  --fs-value-lg:22px;   /* key stat numbers (win rate, etc.) */
  --fs-headline:20px;   /* page h1 */
  --fs-display:32px;    /* the one strike price that survived every rule */
  --fs-hero:42px;       /* Nifty spot -- the single biggest number on screen */

  --ls-tight:-0.01em;   /* large numerals/display */
  --ls-normal:0;        /* body text */
  --ls-label:.08em;     /* uppercase eyebrows/labels/nav (was up to .42em) */

  /* ADDED 22 Jul 2026 -- two extra tiers, for data-dense readouts only.
     These do NOT reopen the pre-9-Jul free-for-all: the original problem
     was 19 distinct ad hoc values with no names or rules, not the
     existence of wider/tighter tracking as such. These are named, scoped,
     and documented, and the 3 tiers above remain the default for
     everything else.

     --ls-numeral: for stacked numeric columns (an option ladder, a comp
     table) where rows of monospace figures at --ls-tight still read
     crowded. Tighter tracking pulls each figure into one visual unit so
     the eye scans down a column instead of across digits. Use ONLY on
     mono numerals, never on prose.

     --ls-label-wide: the deliberate counterpart. Wide tracking on a small
     uppercase label next to tight numerals is what separates the two into
     distinct layers rather than one grey texture. Use ONLY on short
     uppercase labels sitting directly against --ls-numeral figures. This
     is NOT a general replacement for --ls-label. */
  --ls-numeral:-0.045em;
  --ls-label-wide:.30em;

  /* ── FONT STACK (14 Aug 2026) ────────────────────────────────────────
     System fonts, not a webfont. This is a PERFORMANCE fix, measured:
     /home renders in 45ms and every other page took 12.9 SECONDS in a
     browser, while the server returned all of them in 1-4ms. The whole
     gap was the render-blocking <link> to fonts.googleapis.com that every
     page except Home carried -- Home was already on system fonts, which
     is exactly why it was the only fast page.

     A webfont costs two extra origins (googleapis + gstatic), a DNS
     lookup, a TLS handshake and a blocking stylesheet before first paint,
     on EVERY navigation, on mobile data. The system stack costs none of
     that and ships better optical sizing and legibility tuning than a
     round-tripped Manrope ever did. */
  --font:-apple-system,BlinkMacSystemFont,'SF Pro Text','SF Pro Display',
         'Segoe UI','Inter',system-ui,'Helvetica Neue',Arial,sans-serif;

  --sp-1:4px;--sp-2:8px;--sp-3:12px;--sp-4:16px;
  --sp-5:20px;--sp-6:24px;--sp-7:32px;--sp-8:40px;--sp-9:48px;

  --ease-snappy:cubic-bezier(.22,1,.36,1);
  --ease-smooth:cubic-bezier(.32,.72,0,1);
  --dur-snappy:220ms;--dur-smooth:320ms;
  --glass-fill:rgba(var(--surface-3-rgb),.46);--glass-fill-lite:rgba(var(--ov),.07);
  --glass-rim:rgba(var(--ov),.16);--glass-rim-soft:rgba(var(--ov),.08);
  --glass-blur:saturate(180%) blur(16px);

  --r-sm:12px;--r-lg:20px;--r-xl:26px;
  --mat-fill:rgba(var(--surface-3-rgb),.42);
  --mat-blur:saturate(180%) blur(20px);
  --mat-rim:rgba(var(--ov),.14);
  --mat-shadow:0 8px 26px -10px rgba(0,0,0,.42),0 1px 3px rgba(0,0,0,.18);
}

/* No @view-transition here on purpose (15 Aug 2026): the cross-document
   crossfade stalled navigation on memory-constrained iOS. See
   test_no_unconditional_cross_document_view_transition. */
*{box-sizing:border-box;margin:0;padding:0}
/* BODY HEIGHT FOLLOWS CONTENT -- never pinned to the viewport (5 Sep 2026).
   This was `html,body{height:100%}`, and with the border-box rule above it
   pinned the body box to exactly one screen: `padding-bottom` then sat
   INSIDE that box instead of below the content, so it reserved nothing the
   moment a page grew past a screen and the last ~84px (~108px with an
   iPhone home indicator) of EVERY scrolling page ended up under the fixed
   .bnav with no way to scroll them clear.
   REPORTED AS "I am not able to switch to Classic". The appearance switch
   is the last control on /system, so it was the element the bar covered:
   measured at 390x664 with a 34px inset, the Classic chip sat entirely
   behind .bnav and a tap at its centre hit the Markets tab instead. Not a
   broken switch -- an unreachable one.
   gamma_landscape_page.py already carried this exact override locally and
   its comment says "any page here that grows past a screen needs the same"
   -- so it belongs in the one sheet all ~21 pages already include, not
   copied per page. `html` keeps its height so `min-height:100%` below still
   resolves against the viewport and a short page still fills the screen.
   CHECK IT SCROLLED TO THE BOTTOM: at scroll-top the fault is invisible. */
html{height:100%}
body{background:var(--void);color:var(--ink);font-family:var(--font);
  font-size:var(--fs-body);line-height:1.5;letter-spacing:var(--ls-normal);
  -webkit-font-smoothing:antialiased;min-height:100%;
  padding-bottom:calc(84px + env(safe-area-inset-bottom,0px))}
.eyebrow,.label,.pill{font-size:var(--fs-label);letter-spacing:var(--ls-label);
  text-transform:uppercase}

/* ---- The responsive content column -----------------------------------
   ADDED 15 Aug 2026 (Ravi: "the whole page is not aligned, the iPad view
   very haphazard").

   `max-width:430px` was hardcoded in 21 separate modules -- every page
   carried its own private copy of the column width, and NONE of them had a
   breakpoint. home_page.py alone had min-width:768px/1180px rules, so on an
   iPad Home reflowed into two columns while all ~20 other pages stayed a
   430px strip stranded in the middle of a 1024px screen. Two layout systems
   in one app is what reads as haphazard.

   This is the same defect shape as the ~600 colour literals that became
   tokens: a value duplicated per page cannot be changed centrally. Fixed
   the same way -- one definition here, in BASE_RULES_CSS, which all 21
   pages already include.

   SPECIFICITY IS DELIBERATE. Each page still declares its own bare
   `.wrap{max-width:430px}` AFTER this block, and media queries add no
   specificity -- so an equally-specific rule here would lose on source
   order. `body .wrap` is (0,0,1,1) and beats a bare `.wrap` (0,0,1,0)
   regardless of order, with no !important anywhere. Exactly the technique
   `html[data-skin="white"]` already uses to beat `:root`.

   The column only WIDENS; no grid is imposed. These pages are single-column
   reading surfaces -- a verdict, then its reasoning -- so widening the
   measure is the honest fix. Inventing a two-column layout for a document
   that reads top-to-bottom is not.
   ---------------------------------------------------------------------- */
/* 12 Sep 2026 -- .shell and .stage joined .wrap and .shell-v2 here, and the
   reason is the same defect the block above was written to fix, found again
   by measuring rather than reading. A Playwright pass over all 55 routes at
   1440px found 13 pages still stranded at phone width, because they name
   their column .shell (430px) or .stage (460px) and the escalation only knew
   two of the four names:

     .shell   /option-chain /oi-pulse /smart-chain /trader5 /wyckoff /ui-preview
     .stage   /brain /close-snapshot /constituents /global-positions
              /parameters /smart-chain-legacy /system-audit

   /option-chain was the worst of them: a 430px strip inside a 1440px screen
   with ~490px of dead space each side, on the one page in the app that is a
   wide data table and gains the most from width.

   NOT /home. Home carries no BASE_RULES_CSS at all -- verified 12 Sep, it is
   self-contained and runs its own 27.5rem -> 46rem -> 58rem system -- so its
   own .stage at 928px is untouched by this. Checked before editing precisely
   because `body .stage` would otherwise have beaten a bare `.stage` and
   quietly shrunk the best page in the app. */
@media (min-width:768px){
  body{padding-left:32px;padding-right:32px}
  body .wrap,body .shell-v2,body .shell,body .stage{max-width:680px}
  /* NOT enlarged on tablet. The phone-sized glow is left alone here on
     purpose: body::before is a full-width radial gradient that iOS Safari
     promotes to its own compositing layer, and growing it on the exact
     device class that is reporting crashes trades a cosmetic gain for
     memory pressure. It reads fine at its phone size on a wider screen. */
  /* The bar stays edge-to-edge -- it is the app's ground line -- but its
     items stop stretching across a full tablet width, where the labels were
     pulled so far apart the group no longer read as one control. */
  body .bnav{padding-left:calc((100vw - 680px) / 2);
        padding-right:calc((100vw - 680px) / 2)}
  body .bnav a{max-width:136px}
}
@media (min-width:1180px){
  body{padding-left:48px;padding-right:48px}
  body .wrap,body .shell-v2,body .shell,body .stage{max-width:860px}
  body .bnav{padding-left:calc((100vw - 860px) / 2);
        padding-right:calc((100vw - 860px) / 2)}
}

.pane,.card,.section{
  background:var(--mat-fill);
  -webkit-backdrop-filter:var(--mat-blur);backdrop-filter:var(--mat-blur);
  border:1px solid var(--mat-rim);
  box-shadow:var(--mat-shadow)}

.bnav{position:fixed;left:0;right:0;bottom:0;z-index:50;
  display:flex;justify-content:space-between;align-items:stretch;gap:2px;
  padding:10px 10px calc(10px + env(safe-area-inset-bottom,0px));
  /* iOS MEMORY, 15 Aug 2026 -- same fix as home_page's .nav, applied to the
     shared bar every other page renders. Fixed position plus backdrop-filter
     means Safari holds a live snapshot of the whole page behind this bar for
     the entire scroll; at a 22px radius that was the largest standing cost
     on pages like /hello-score. Radius cut and the background raised from
     .78 to .88 so the frosted look holds. */
  background:rgba(var(--void-rgb),.88);
  backdrop-filter:saturate(150%) blur(10px);-webkit-backdrop-filter:saturate(150%) blur(10px);
  border-top:0.5px solid rgba(var(--gold-rgb),.22);
  box-shadow:0 -8px 28px rgba(var(--shadow),.28)}
.bnav a{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:5px;padding:8px 4px;min-height:52px;
  text-decoration:none;color:rgba(var(--muted-rgb),.92);border-radius:16px;
  transition:background .18s ease,color .18s ease,transform .12s ease;
  -webkit-tap-highlight-color:transparent}
.bnav a:active{transform:scale(.96);background:rgba(var(--gold-rgb),.08)}
.bnav a.current{color:var(--gold-lite);background:rgba(var(--gold-rgb),.14);
  box-shadow:inset 0 0 0 0.5px rgba(var(--gold-rgb),.22)}
.bnav .bnav-icon{font-size:20px;line-height:1;font-weight:500;
  text-shadow:0 0 18px rgba(var(--gold-rgb),.0)}
.bnav a.current .bnav-icon{text-shadow:0 0 18px rgba(var(--gold-rgb),.35)}
.bnav .bnav-lbl{font-size:10.5px;font-weight:700;letter-spacing:.04em;
  line-height:1.1}

.nav-back{display:inline-flex;align-items:center;gap:4px;
  color:var(--gold-lite);text-decoration:none;
  font-size:17px;font-weight:600;letter-spacing:-0.015em;line-height:1;
  padding:10px 2px;min-height:44px;-webkit-tap-highlight-color:transparent;
  transition:opacity .15s ease}
.nav-back:active{opacity:.55}
.nav-back-chev{font-size:28px;font-weight:300;line-height:1;margin-top:-1px;
  color:var(--gold)}
.nav-back-lbl{padding-top:1px}
.nav-topbar{display:flex;align-items:center;justify-content:space-between;
  gap:12px;margin:0 0 14px;min-height:44px}
.nav-topbar .nav-title{font-size:17px;font-weight:700;letter-spacing:-0.02em;
  color:var(--ink);text-align:right}
