/* ============================================================================
   ks-tokens.css — the single place font sizes and colours are defined.

   Loaded by BOTH surfaces, so there is one file to edit rather than ~700 call
   sites:
     - the React app          <link> in /index.html
     - the legacy query page  <link> in /legacy/query.html

   Two rules for anything added here:

   1. A token's value at desktop width must equal the literal it replaced.
      This file was introduced as a pure refactor — the large-monitor rendering
      is required to be unchanged, so every value below was read off the code it
      replaced rather than chosen fresh.

   2. Colour tokens alias the PRIMITIVES (--acc, --tx, --s1 …) wherever the two
      surfaces agree, and carry their own literal only where they do not.
      The primitives are already defined per-surface with different values:
      --mu is #FFFFFFCC on the query page but #787b86 in the app, and --bg is
      #111B28 vs #131722. Aliasing means each surface resolves to its own value
      automatically, and a light theme flips the primitives in one place instead
      of being a find-and-replace over the whole tree.
   ========================================================================= */


/* ── Type scale ──────────────────────────────────────────────────────────────
   Fluid rather than stepped. Each size is pinned to its exact current value at
   and above --fs-anchor-max, eases down linearly as the viewport narrows, and
   holds at a floor at and below --fs-anchor-min. No breakpoints to land badly
   between, and one definition per size instead of a parallel table per screen.

   Why this replaced the old approach: query.html carried a `max-width: 1400px`
   block that was written against pre-16px defaults and never updated. By the
   time it was removed it set .vstrip-btn LARGER on the smaller screen (24→26px)
   while dropping textarea.sdql-input a quarter (16→12px). That is what a
   hand-maintained override table decays into.

   Anchors: 1024px is iPad landscape — the narrow end of "desktop-ish", below
   which the existing mobile rules take over. 1600px is set above a Surface Pro
   (~1368px logical) and below a 1920 monitor, so the Surface Pro sits partway
   down the ramp and anything 1600+ renders at the pinned maximum.

   If your monitor is between 1368 and 1600 wide and you want it pinned too,
   raise --fs-anchor-max. It is the only number that needs to move. */

/* The anchors are literals in every line below rather than variables, because
   calc() may only divide by a plain number, never by a var(). Changing the ramp
   means changing 1024/576 throughout this block — the two numbers appear
   nowhere else in the codebase. */
:root {
  /* ceiling = today's value (do not change without intending a visual change)
                        floor      linear ramp between the anchors            ceiling */
  --fs-tiny:    10px;   /* already at the legibility floor; does not scale */
  --fs-mini:    11px;   /* dense table text — shrinking this hurts more than it helps */
  --fs-xs:      clamp(11px,  calc(11px  + 1 * (100vw - 1024px) / 576),  12px);
  --fs-sm:      clamp(12px,  calc(12px  + 1 * (100vw - 1024px) / 576),  13px);
  --fs-md:      clamp(13px,  calc(13px  + 1 * (100vw - 1024px) / 576),  14px);
  --fs-ml:      clamp(14px,  calc(14px  + 1 * (100vw - 1024px) / 576),  15px);
  --fs-base:    clamp(14px,  calc(14px  + 2 * (100vw - 1024px) / 576),  16px);
  --fs-lg:      clamp(15px,  calc(15px  + 2 * (100vw - 1024px) / 576),  17px);
  --fs-xl:      clamp(16px,  calc(16px  + 2 * (100vw - 1024px) / 576),  18px);
  --fs-2xl:     clamp(17px,  calc(17px  + 3 * (100vw - 1024px) / 576),  20px);
  --fs-3xl:     clamp(19px,  calc(19px  + 3 * (100vw - 1024px) / 576),  22px);
  --fs-4xl:     clamp(20px,  calc(20px  + 4 * (100vw - 1024px) / 576),  24px);
  --fs-5xl:     clamp(22px,  calc(22px  + 4 * (100vw - 1024px) / 576),  26px);
  --fs-6xl:     clamp(23px,  calc(23px  + 5 * (100vw - 1024px) / 576),  28px);
  --fs-7xl:     clamp(25px,  calc(25px  + 5 * (100vw - 1024px) / 576),  30px);
  --fs-8xl:     clamp(26px,  calc(26px  + 6 * (100vw - 1024px) / 576),  32px);
  --fs-9xl:     clamp(29px,  calc(29px  + 7 * (100vw - 1024px) / 576),  36px);
  --fs-10xl:    clamp(36px,  calc(36px  + 12 * (100vw - 1024px) / 576), 48px);

  /* The legacy nav bar's own size. 17.5px is not a step on the scale above (it
     was one of the one-off literals), but it is the top-level nav link size and
     the bar runs out of room before anything else does, so it needs to scale.
     17.5px at >=1600px, exactly as before. */
  --fs-nav:     clamp(12.5px, calc(12.5px + 5 * (100vw - 1024px) / 576), 17.5px);

  /* Semantic aliases. Prefer these at call sites where the role is obvious —
     they survive a change to which step the role maps to. */
  --fs-body:    var(--fs-base);   /* 16px — the dominant size on both surfaces */
  --fs-table:   var(--fs-ml);     /* 15px — the results grid */
  --fs-label:   var(--fs-xs);     /* 12px — field labels, badges, meta */
  --fs-caption: var(--fs-mini);   /* 11px — the smallest text that still reads */
  --fs-h1:      var(--fs-10xl);
  --fs-h2:      var(--fs-9xl);
  --fs-h3:      var(--fs-7xl);
}

/* Sizes deliberately left as literals at their call sites: 8px, 9px, 17.5px and
   19px, six occurrences between them. Each is a one-off (an icon nudge, a
   single heading) and giving each a scale step would add more names than it
   removes literals. They do not scale with the viewport; if any turns out to
   matter on a small screen, promote it to a token then. */


/* ── Colour ──────────────────────────────────────────────────────────────────
   Aliases over the existing primitives. The primitives themselves stay where
   they are (theme/tokens.css for the app, the :root block in query.html) so
   that each surface keeps its own values — see rule 2 at the top of this file.

   For a light theme: redefine the PRIMITIVES under [data-theme="light"] and
   everything below follows. Nothing in this block should ever need editing for
   a theme swap; that is the whole point of it. */
:root {
  /* text */
  --text-primary:    var(--tx);
  --text-muted:      var(--mu);
  --text-on-accent:  #000000;
  --text-inverse:    #FFFFFF;
  /* Ink for a control that is white on BOTH themes — the search inputs, the
     login card. Constant on purpose: those surfaces do not flip, so their text
     must not either. Several of them were reading var(--s1), which is near-black
     while every surface is dark and near-white the moment one is not: white text
     in a white box. */
  --text-on-white:   #131722;

  /* surfaces */
  --surface-base:    var(--bg);
  --surface-raised:  var(--s1);
  --surface-hover:   var(--s2);
  --surface-line:    var(--s3);

  /* intent */
  --intent-accent:   var(--acc);
  /* The accent when it is INK rather than a fill.
     On the dark theme they are the same yellow. On a white ground that yellow
     is about 1.6:1 against the page — legible as a 3px border, unreadable as a
     word — so it darkens to a gold that clears 4.5:1 while still reading as the
     brand colour. Fills keep --acc: a yellow button with black text on it is
     just as readable on either theme, and darkening the fill would break that
     pairing instead of fixing it. */
  --acc-text:        var(--acc);
  --intent-accent-w: var(--acclt);   /* the low-alpha wash behind an active accent */
  /* The wash behind ACCENT INK — an active tab, a selected chip. It has to
     follow --acc-text, not --acc: on light the ink is blue, and a blue word on
     a yellow tint is neither the old look nor a new one. */
  --acc-text-wash:   rgba(243, 213, 39, 0.12);
  --intent-pos:      var(--pos);
  --intent-neg:      var(--neg);

  /* The VIVID pair. Six page modules override --pos/--neg locally with a bright
     green and red, because on those pages a win and a loss want to shout rather
     than sit at the app's muted teal. Those overrides were literals (#07C34F,
     #ef5350) or aliases of --intent-kc, none of which can move — so on light
     they landed at 2.4:1 and 3.2:1, on the very figures the page exists for.
     Named separately from --pos/--neg because a module that redefines those
     cannot then reference them: var(--pos) inside a block redefining --pos is a
     cycle, and the property silently drops. */
  --pos-vivid:       #07C34F;
  --neg-vivid:       #ef5350;

  /* Status colours for the admin panels — run states, health pills, log lines.
     They were literals chosen against a dark panel, so on a light one they sat
     between 1.5:1 and 2.8:1 on their own tints. Dark keeps every original
     value; light darkens each to clear the floor. */
  --status-ok:       #4ac878;
  --status-warn:     #e0a13a;
  --status-error:    #e8624a;
  --status-info:     #5aa0ff;
  --status-note:     #c3a8ff;

  /* Ink on a SATURATED fill (a live badge, a status pill). The fill itself
     darkens on light, so this is the one ink that runs the opposite way to
     --text-on-navy: near-black over a bright fill on dark, white over the
     darkened fill on light. */
  --text-on-vivid:   #0a0e1a;
  --intent-info:     var(--blue);

  /* Link text — the fire counts on the alerts pages, the saved-trend captions,
     the breakdown links on the query page.

     The cyan stays. It is the most visible thing on these pages at 10.9:1
     against the page navy, which is the point of it: a fire count is a number
     you are meant to spot in a dense table. #2196f3 was tried here and reads at
     5.5:1 — legible, but half the presence.

     The light theme cannot use this exact value: cyan on white is 1.6:1, barely
     a colour. It takes the same hue two steps down instead, which keeps these
     links reading as the cyan family rather than turning into generic blue. */
  --link:            #77DCEA;
  --link-wash:       rgba(119, 220, 234, 0.16);

  /* Legacy-only colours with no primitive to alias. These carry literals
     because they appear on the legacy surfaces and nowhere else — they are the
     ones to revisit first when the light theme happens. */
  --text-soft:       #FFFFFFCC;  /* 29 uses in the legacy CSS modules, and the
                                    query page's own --mu. NOT the same as the
                                    app's --mu (#787b86) — do not merge them. */
  --text-dim:        #A0A4A9;
  --border-legacy:   #CECECE;
  --border-soft:     #EFEFEF;
  --surface-navy:    #081828;    /* legacy page background */
  --surface-navy-2:  #131D2C;    /* legacy nav / elevated */
  /* The nav bar and its spacer/drawer, split out from --surface-navy-2 so the
     bar can be white on light while dropdowns stay one shade off it — a white
     menu opening out of a white bar has no edge to read. Same navy on dark. */
  --nav-bg:          #131D2C;
  --surface-navy-3:  #111B28;    /* query page background */
  --surface-navy-4:  #111926;    /* legacy hero band */
  --surface-slate:   #1F2937;    /* the raised panel inside a legacy page: cards,
                                    table headers, the trend list's own --s2 */

  /* Zebra striping. A PAIR: the even row is --surface-navy-2 and the odd row is
     this, one step off it. They have to move together — pairing a themed
     surface with a literal is what left every table half-dark on light, the
     even rows white and the odd ones still navy. --row-stripe-wash is the
     translucent version the query page needs, where the stripe sits over cells
     that paint their own background. */
  /* The zebra is a PAIR of its own, no longer borrowing --surface-navy-2 for
     the even row: that token is also the nav's ground, and the moment the nav
     and the page swapped shades the tables followed it and lost their stripe. */
  --row-base:        #131D2C;
  --row-stripe:      #172131;
  /* Row hover. The teal this replaces is a mid-tone: fine under the light text
     of the dark theme (7.3:1), and a wall under the dark text of the light one,
     where the green and red numbers in those very rows fell to 2.0:1 and 2.2:1
     — the row lit up and its contents stopped being readable. Dark keeps the
     teal; light gets a pale blue tint that shifts the row without swallowing
     what is written on it. */
  --row-hover:       #7ca8b1;

  /* The records band across the top of the query page, and the hairline the
     gradient fades to at its edges. Its own tokens because the light value has
     to be a real step off the page — the band was reading as white-on-white
     once the page went white — while dark keeps the slate it always had. */
  --records-band:    #3a475b;
  --records-edge:    #081828;

  /* A table's header band and its sticky first column: a step off the rows in
     whichever direction the theme has contrast. On dark that is darker than the
     table (#081828 under a #111B28 surface); on light it has to be darker too,
     which the page ground cannot be once the ground is white. */
  --table-head:      #081828;

  /* main.css writes its palette as var(--token, <literal>) with the tokens
     defined nowhere, so the literal always won and those pages could not
     follow a theme. --blue-6100 is the safe one to define: it is used three
     times and every use is a `background` — the hero band and the two content
     bands under it. The rest of that family (--white-100, --yellow-100,
     --blue-3100, --blue-5100) is used as BOTH ink and surface, so defining any
     of them would flip text and its background to the same colour; those are
     handled by explicit rules in ks-legacy-light.css instead. */
  --blue-6100:       #111926;

  /* Save / copy-link. These are <img> buttons whose artwork is fixed white and
     fixed yellow inside the SVG, which CSS cannot reach. So the chip stays dark
     on BOTH themes: a dark button on a white page reads, and the icons stay the
     colour they were drawn. */
  --icon-btn-bg:     #1F2937;
  --row-stripe-wash: rgba(255, 255, 255, 0.012);

  /* The admin/affiliate panels' own three surfaces. They were only ever
     FALLBACKS — `var(--panel, #1b1e24)`, 76 declarations across 8 files naming
     tokens that nothing defined, so the dark literal always won and those
     panels could not follow a theme. Defining them here is the whole fix: not
     one of those files needs editing, and the fallbacks stay put as the value
     they already had. Dark values are exactly the literals they replace. */
  --panel:           #1b1e24;
  --panel2:          #22262e;
  --border:          #2c3038;

  /* A card raised off the legacy navy — the plans cards and the plans table's
     own zebra row. Lighter than --surface-slate, which is the flatter panel. */
  --surface-raised-2: #3a475b;

  /* The top stop of the hero gradient on the contact / affiliate pages. Its
     other stop is already --surface-navy-4, which is what made those headers
     light at the bottom and navy at the top the moment the theme flipped. */
  --hero-gradient-top: #102038;

  /* The KC investment pages' own three grounds — a section band, a card and the
     homepage hero. Distinct hexes from the KS navies above and kept distinct
     here, so dark renders byte-for-byte as it did. */
  --kc-section:      #101f37;
  --kc-card:         #081528;
  --kc-hero:         #0D1321;

  /* The sport pages' hero band. Sampled from the hero photographs themselves:
     all six (mlb, nba, ncaabb, ncaafb, nfl, wnba) carry #101820 down the edge
     that meets the band — between 54% and 99% of that column — so the photo
     dissolves into it instead of ending on a seam.
     Fixed on both themes for the same reason the icon buttons are: the artwork
     is a dark photograph and cannot follow a palette, so its surround must not
     either. Everything written on it takes --text-on-photo below. */
  --kc-photo-band:   #101820;
  --text-on-photo:        #FFFFFF;
  --text-on-photo-soft:   rgba(255, 255, 255, 0.8);
  --wash-on-photo:        rgba(255, 255, 255, 0.08);
  --intent-kc:       #07C34F;    /* KC green, fixed across themes */
  --intent-danger:   #cb3737;    /* destructive action red (19 uses in TSX) */

  /* Ink that sits ON the navy surfaces above. Always the counterpart of
     --surface-navy*, never a fixed white: the moment those surfaces flip to
     grey, white text on them is invisible. Any rule pairing a colour with
     var(--surface-navy…) must take its text from here. */
  --text-on-navy:    #FFFFFF;

  /* The three washes the legacy CSS builds out of rgba(255,255,255,α): a
     hairline border, a hover fill, a divider. White at 5-12% is meaningless on
     a light ground, so they are tokens rather than literals and inverse
     themselves below. Three, not thirty — the source had eleven distinct
     alphas between .02 and .15 describing what are really these three jobs. */
  /* Faint strokes and captions — the dropdown chevrons and the smallest
     labels. Kept apart from --text-dim (#A0A4A9) because folding them into it
     brightened the chevrons by about 12% on the dark bar, and "dark is
     unchanged" is the rule this whole theme is built under.

     0.5 is the midpoint of the two alphas the source used for the same glyph
     at two sizes (.55 desktop, .45 mobile). The tolerance applied throughout
     this sweep: text and washes keep their exact dark value, decorative marks
     may move by at most .05 alpha to avoid a token per glyph. */
  --text-faint:      rgba(255, 255, 255, 0.5);

  --wash-subtle:     rgba(255, 255, 255, 0.05);
  --wash-soft:       rgba(255, 255, 255, 0.08);
  --wash-firm:       rgba(255, 255, 255, 0.12);
  --wash-strong:     rgba(255, 255, 255, 0.3);   /* a visible edge: button hover borders */
}

/* ── Light theme ─────────────────────────────────────────────────────────────
   The primitives (--bg, --tx, --s1 …) already flip on their own — each surface
   defines its own [data-theme="light"] block, the app in theme/tokens.css and
   the query page in its own :root. Everything aliased above therefore follows
   for free, and nothing in the alias block needed editing, exactly as its note
   promised.

   What did NOT follow are the literals above: they were read off the legacy
   CSS, which only ever had a dark surface to describe. A white-at-80% text
   colour and a navy page background are correct statements about a dark theme
   and nonsense on a light one, so each gets its light counterpart here.

   Paired deliberately with the light primitives rather than picked fresh:
   --surface-navy* land on the same greys as --bg/--s1/--s2 so a legacy surface
   and an app surface next to each other are the same colour, and the text
   colours are the light --tx softened by the same amount the dark ones were
   lightened. --intent-kc and --intent-danger stay put: a brand green and a
   destructive red mean the same thing on either ground, and both already carry
   enough contrast against white. */
/* KC's hero stays as it is. Its main.css reads the same --blue-6100 as the KS
   one, so lightening that token moved both — and only the KS hero was meant to
   change. This is after the light block on purpose: same specificity plus a
   data-site of its own, so it wins on the KC document and is inert everywhere
   else. Placed here rather than inside the light block because the value is the
   DARK one: KC's hero is the same navy on either theme. */
[data-theme="light"][data-site="KC"] {
  --blue-6100:       #111926;
}

[data-theme="light"] {
  /* text — the mirror of #FFFFFFCC / #A0A4A9 against the light --tx (#0e1117) */
  --text-soft:       rgba(14, 17, 23, 0.78);
  --text-dim:        #6d7482;

  /* borders — #CECECE and #EFEFEF vanish on white, so both step darker */
  --border-legacy:   #ccd3df;
  --border-soft:     #e4e8f0;

  /* surfaces — the page is WHITE and everything raised off it is the grey.
     That is the inverse of how this block started: the page carried the grey
     and cards were white, which reads as a tinted sheet of paper with white
     patches on it rather than as a white page with furniture on it.

     One scale, four steps, and every surface token below lands on one of them:
        #ffffff  page          the ground you read against
        #f0f3f8  raised        nav, cards, panels, the zebra's odd row
        #e9edf5  inset/hover   a step further in
        #dde1eb  line          borders */
  --surface-navy:    #ffffff;   /* page background        (was #081828) */
  --surface-navy-2:  #f0f3f8;   /* elevated / dropdowns   (was #131D2C) */
  --nav-bg:          #ffffff;   /* the bar itself */
  --surface-navy-3:  #ffffff;   /* query page background  (was #111B28) */
  --surface-navy-4:  #f0f3f8;   /* hero band              (was #111926) */
  --surface-slate:   #e9edf5;   /* raised panel           (was #1F2937) */
  /* One step off white, the same distance #172131 sits off #131D2C. */
  --row-base:        #ffffff;   /* zebra, even row */
  --row-hover:       #e3ecf7;   /* worst ink on it 4.3:1, vs 2.0:1 on the teal */
  --records-band:    #e4eaf3;   /* a real step off the white page */
  --table-head:      #e4eaf3;
  --blue-6100:       #979797;   /* the hero band */
  --records-edge:    #d3dae6;
  /* --icon-btn-bg is deliberately NOT redefined: see the dark block. */
  --row-stripe:      #f0f3f8;   /* zebra, odd row — the page's old grey */
  --row-stripe-wash: rgba(0, 0, 0, 0.028);
  --panel:           #f0f3f8;   /* admin panel            (was #1b1e24) */
  --panel2:          #e9edf5;   /* panel header / inset   (was #22262e) */
  --border:          #dde1eb;   /* panel border           (was #2c3038) */
  --surface-raised-2: #f0f3f8;  /* plans card             (was #3a475b) */
  --hero-gradient-top: #e9edf5; /* hero gradient top      (was #102038) */
  --kc-section:      #ffffff;   /* KC section band        (was #101f37). White,
                                   flat with the bio/logos block on it — that
                                   block draws no edge of its own. */
  --kc-card:         #ffffff;   /* KC card                (was #081528) */
  --kc-hero:         #f0f3f8;   /* KC hero                (was #0D1321) */

  /* On a dark ground the accent's own black text reads; on white it does not
     have to change. */
  --text-on-accent:  #000000;

  /* The navy surfaces are now near-white, so their ink is the page's own text
     colour rather than white-on-navy. */
  --text-on-navy:    #0e1117;
  /* Blue, not a darkened yellow. Yellow-as-ink at a readable contrast stops
     looking like the brand and starts looking like mustard; the light theme
     hands the ink job to blue instead and leaves the yellow to the fills, where
     it still reads. #0000FF is the same blue the legacy CSS already uses for
     its links (#2200F3 in header.css), so it is not a new colour on the page. */
  --acc-text:        #0000FF;   /* 8.6:1 on white */
  --acc-text-wash:   rgba(0, 0, 255, 0.08);
  --link:            #0b6d85;   /* the cyan, dark enough to read: 5.9:1 on white */
  --pos-vivid:       #067a33;   /* 5.5:1 on white, was 2.4:1 */
  --status-ok:       #1a7a3f;
  --status-warn:     #8a5a00;
  --status-error:    #b8341c;
  --status-info:     #1d5fd0;
  --status-note:     #5b3fa8;
  --text-on-vivid:   #ffffff;
  --neg-vivid:       #c62828;   /* 5.1:1 on white, was 3.2:1 */
  --link-wash:       rgba(11, 109, 133, 0.12);

  /* Inverted: dark at a low alpha over a light ground. Slightly weaker than the
     white equivalents because black reads heavier over white than white does
     over navy at the same alpha. */
  --text-faint:      rgba(14, 17, 23, 0.5);    /* 3.3:1 — the 3:1 floor for a non-text mark */

  --wash-subtle:     rgba(0, 0, 0, 0.045);
  --wash-soft:       rgba(0, 0, 0, 0.065);
  --wash-firm:       rgba(0, 0, 0, 0.10);
  --wash-strong:     rgba(0, 0, 0, 0.22);
}

/* ── Mobile: no scrollbar gutter ─────────────────────────────────────────────
   Touch platforms draw their own overlay indicator that fades itself out, and
   nobody needs telling that a phone scrolls. A persistent bar is a permanent
   stripe down the edge of the content and, on the narrow viewports where space
   is tightest, it is the least useful thing occupying it.

   Scrolling itself is untouched — this hides the indicator, it does not set
   overflow. The desktop scrollbar styling in theme/tokens.css is already
   guarded by `@media (hover: hover)`, so this only removes what the platform
   draws underneath it. */
@media (max-width: 991px) {
  html, body {
    scrollbar-width: none;        /* Firefox */
    -ms-overflow-style: none;     /* legacy Edge */
  }

  html::-webkit-scrollbar,
  body::-webkit-scrollbar { display: none; }   /* WebKit / Blink */
}

/* ── Phone in landscape ──────────────────────────────────────────────────────
   The scale above ramps on 100vw, which is the wrong axis once a phone is
   turned sideways: an iPhone 14 is 844px WIDE in landscape, so it reads as a
   small desktop and gets close to the full-size type — on a screen that is
   only 390px tall. Every size lands a step or two too large.

   Pinned to the same floors the portrait phone gets, so rotating the device
   does not change the type size. Layout is deliberately NOT touched here: the
   extra width is real and columns should still use it. This block is type only.

   max-height 500px with orientation:landscape picks out phones and cannot
   catch a tablet — an iPad in landscape is 768px tall. */
@media (max-height: 500px) and (orientation: landscape) {
  :root {
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-md: 13px;
  --fs-ml: 14px;
  --fs-base: 14px;
  --fs-lg: 15px;
  --fs-xl: 16px;
  --fs-2xl: 17px;
  --fs-3xl: 19px;
  --fs-4xl: 20px;
  --fs-5xl: 22px;
  --fs-6xl: 23px;
  --fs-7xl: 25px;
  --fs-8xl: 26px;
  --fs-9xl: 29px;
  --fs-10xl: 36px;
  --fs-nav: 12.5px;
  }
}
