/* ============================================================
   base.css - shared across every page: design tokens, header/nav,
   buttons, layout grid, sidebar, footer, login modal, misc utilities
   ============================================================ */

/* ============================================================
   VENDORED WEBFONT - Inter. NO EXTERNAL ORIGIN.
   ============================================================
   This used to be two <link> tags in index.html to
   fonts.googleapis.com and fonts.gstatic.com. Both are gone. See
   deploy/10-uk-webtlp01/README.md for why: each was a render-blocking
   stylesheet on a COLD connection (DNS + TCP + TLS + fetch) to a host
   the browser had never spoken to, before a single character of text
   could paint, from Mauritius, on mobile - and an availability and
   data-protection dependency on a third party nobody here contracts
   with.

   ONE FILE, ALL SIX WEIGHTS. The CSS in this tree uses font-weight
   400, 500, 600, 700, 800 and 900 (counted across all six
   stylesheets and the uiux_*.js template literals). As six static
   Google-style latin subsets that is six requests and ~124 KB. As one
   VARIABLE font it is one request and 45 KB, and it renders 600 and
   800 as true instances rather than as a browser-synthesised fake of
   the nearest static weight.

   Provenance, so this file can be reproduced rather than trusted:
     source  Inter v4.001 (git-9221beed3), rsms/inter, SIL OFL 1.1
             https://raw.githubusercontent.com/rsms/inter/master/docs/font-files/InterVariable.woff2
     step 1  fonttools varLib.instancer ... opsz=14   (pin optical size)
     step 2  pyftsubset ... --flavor=woff2 --unicodes=<latin + symbols>
   The exact commands are in frontend/web/fonts/README.md.

   SUBSET SCOPE - stated rather than hidden. The file carries the
   Google "latin" range (which includes every accented character
   French needs) plus the arrows, bullets and geometric shapes this
   UI draws. It does NOT carry latin-ext: including it doubled the
   file to 136 KB for glyphs no captured string uses. A Polish or
   Vietnamese team name arriving on a live feed will render in the
   fallback face below, which is correct-looking text, not tofu.

   Three glyphs this UI uses are not in Inter AT ALL and never were -
   U+25BE, U+2715 and U+2630. They fell through to a system font when
   the font came from Google too. Nothing regressed.
   ------------------------------------------------------------ */
@font-face{
  font-family:'Inter';
  font-style:normal;
  font-weight:100 900;              /* one variable face covers the lot */
  font-display:swap;                /* text paints immediately in the fallback */
  src:url('../fonts/inter-latin-variable.woff2') format('woff2-variations'),
      url('../fonts/inter-latin-variable.woff2') format('woff2');
}

/* The fallback stack is load-bearing, not decoration. font-display:swap
   means EVERY visitor reads this stack for the first paint, and a
   visitor whose font request 404s or whose browser cannot do variable
   woff2 reads it forever. It must name real faces that exist on the
   device, never a bare sans-serif and never nothing - "invisible text
   until a font arrives" is the failure mode this avoids. */
:root{
  --font-sans:'Inter',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
}

/* ============================================================
   VENDORED WEBFONT #2 - Poppins ExtraBold. NO EXTERNAL ORIGIN.
   ============================================================
   R-025 (4 Oct): the designer's mockups set every page heading ("Create
   Your Account", "RACE 1", "SPORTS", "Deposit Funds") in a bold,
   geometric, rounded-terminal display face -- visibly not Inter, whose
   letterforms are a neutral UI humanist sans. Operator's own instruction
   on this (relayed by CI/CD): prefer standard web fonts where that reads
   close enough, but where it genuinely doesn't, source and vendor the
   closest real match rather than wait for an exact file/name from the
   designer. This is that match, chosen by eye against the mockups'
   letterforms (geometric circular bowls, soft rounded terminals, true
   ExtraBold weight) -- Poppins is the closest common, freely-licensed
   face with that character; it is a judgment call, not a pixel-measured
   one the way the palette findings were.

   ONE STATIC WEIGHT, NOT A VARIABLE FONT, unlike Inter above. Unlike body
   text, which uses six weights across the whole site, this face is used
   for page/section headings only, at one weight (800/ExtraBold). A
   single latin-subsetted static woff2 is smaller than pulling in a
   variable font for a weight range nothing here uses.

   Provenance:
     source  Poppins ExtraBold, itfoundry/Poppins via the google/fonts
             mirror, SIL Open Font License 1.1
             https://raw.githubusercontent.com/google/fonts/main/ofl/poppins/Poppins-ExtraBold.ttf
     step    pyftsubset --flavor=woff2, same Latin unicode range as
             Inter's own subset above (see frontend/web/fonts/README.md)
     result  7,992 bytes, 223 glyphs
   ------------------------------------------------------------ */
@font-face{
  font-family:'Poppins';
  font-style:normal;
  font-weight:800;                  /* this file is the ExtraBold instance only */
  font-display:swap;
  src:url('../fonts/poppins-extrabold-latin.woff2') format('woff2');
}

:root{
  /* Headings/display text only -- body, table and odds text stays on
     --font-sans, which already reads fine against every mockup screen. */
  --font-display:'Poppins','Inter',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
}

/* ============================================================
   R-012 -- DARK IS NOW THE DEFAULT THEME, 26 Sep.
   ============================================================
   The operator retinted the desktop site from a white background to a
   colored one (design mockup: header #05329B, nav/sidebar/filter-pills
   #061D3F, page-bg/bet-slip #001C50), then -- once Design's own two open
   questions came back -- decided the dark theme is not an opt-in skin
   but the new DEFAULT, with cards going dark too (not just chrome), and
   today's light theme kept only as an explicit opt-in via
   `<html data-theme="light">`.

   MECHANISM: no `data-theme` attribute, or `data-theme="dark"`, means
   dark (the two `:root{}` blocks below carry dark values now). A
   `[data-theme="light"]` block further down overrides every themed
   token back to the values this file shipped with before this window --
   nothing in that block is new, it is today's palette moved rather than
   deleted. jscripts/shared/uiux_shared.js reads/writes
   localStorage['theme'] and sets the attribute; a tiny inline script in
   index.html's <head> sets it again before first paint so a visitor who
   chose light does not see a flash of dark first.

   THREE VALUES CAME FROM THE MOCKUP DIRECTLY (--navy-1, --navy-2, --bg /
   --card). EVERYTHING ELSE BELOW IS A DERIVED OR FLAGGED ASSUMPTION,
   documented token by token, because the mockup only annotated those
   three:
     --navy-0 (topbar)     no mockup annotation -- reused --navy-2's
                            value: the topbar is the same kind of chrome
                            strip as the nav/sidebar, and the mockup
                            named only three tones total.
     --navy-3 (footer)     no mockup annotation -- set to the new --bg.
                            The OLD --navy-3 (#00154b) and OLD --bg
                            (#f2f3f7) were unrelated hues; but #00154b
                            sits almost exactly on top of the mockup's
                            OWN new --bg (#001C50) by luminance, so
                            reusing --bg keeps the footer-vs-page
                            relationship this site already had rather
                            than inventing a fourth tone.
     --navy-deep, the new  no mockup annotation (the hero banner is not
     token --hero-grad-2    a mockup screen) -- chosen dark-to-less-dark
                            for gradient depth; WCAG-checked only for
                            the white hero text that sits on top of it
                            (>=12:1 at both ends), not against --bg,
                            since it is a decorative panel background,
                            not page chrome.
     --card                 same value as --bg (matches the one dark
                            panel the mockup shows, the bet slip, which
                            reads as flush with the page, not a raised
                            white sheet). Cards are told apart from the
                            page by --line borders and --shadow now,
                            never by a lighter fill.
     --live / --green(ok)  the site's OWN existing status reds/greens
     / --warn / --bad       (#e1122f / #0e8a4b / #8a6300 / #c0392b) all
                            fail badly as TEXT on every one of the three
                            new dark backgrounds (1.99-3.79:1, checked
                            against bg/band/header) -- they were tuned
                            for a WHITE page and never had to clear a
                            text-contrast floor on a dark one. Replaced
                            with the app's (frontend/apps/web) own
                            already-shipped, already-vetted dark-navy
                            equivalents (DESIGN-TOKENS.md) rather than
                            inventing a fourth set: #ff5a52 / #29d07f /
                            #ffcb45, all >=5.3:1 on every one of this
                            site's three backgrounds. Downstream: several
                            of these are ALSO used as solid tag/badge
                            FILLS with white text on top (.hr-scratched-
                            tag, .sidebar .tag) -- white-on-#ff5a52 is
                            only 3.07:1, below the 4.5:1 small-text floor.
                            That fill-vs-text conflict is flagged for
                            racing/sports's own dark-mode pass on their
                            own stylesheets (one-file-one-owner); base.css
                            only owns the token's value, not every
                            consumer's use of it.
     --shadow               the old value (a navy-tinted rgba shadow) is
                            invisible once --card equals --bg -- it was
                            only ever visible against a WHITE card. Now a
                            hairline ring in --line plus a real dark drop
                            shadow, so .card/.sidebar/.sidebar-panel keep
                            a visible edge without a lighter fill.
   ------------------------------------------------------------ */
:root{
  --navy-0:#061D3F;      /* topbar -- flagged assumption, see block comment above */
  --navy-1:#05329B;      /* brand bar / header -- mockup: "Tote Lepep" desktop, 26 Sep */
  --navy-2:#061D3F;      /* nav bar / sidebar headers / filter-pills -- mockup */
  --navy-3:#001C50;      /* footer -- flagged assumption, see block comment above */
  --navy-deep:#020817;   /* hero panel deep bg -- flagged assumption */
  --hero-grad-2:#0a2f7a; /* hero gradient's bright end -- flagged assumption */
  --gold:#ffd400;        /* primary CTA - unchanged in both themes */
  --gold-ink:#141b3d;    /* text on gold - unchanged in both themes */
  --bg:#001C50;          /* page background -- mockup */
  --card:#001C50;        /* mockup (bet slip panel); see block comment above */
  --ink:#ffffff;
  --muted:#a9b8d6;
  --line:#1d3f6b;
  --live:#ff5a52;
  --green:#29d07f;
  --panel-tint:#061D3F;        /* sidebar section headers -- reuses the mockup's own nav/sidebar tone */
  --panel-hover:#0e2a52;
  --panel-active-hover:#153868;
  --panel-active:#1b4478;
  --panel-tile:#132748;        /* R25-02: small key/value stat tiles (odds panels, pool-bet dividends) -- measured from the Oct designer mockup's own "Exotic Markets"/pool-bets tiles; sits between --panel-tint and --panel-hover, no existing token matched it */
  --fi-ring:rgba(255,255,255,.15); /* hairline ring around flag icons, dark surface */
  --input-bg:var(--panel-hover);  /* text inputs: no explicit bg before R-012 (browser white worked on a white card) -- dark cards need one */
  --tag-ink:var(--gold-ink);      /* .sidebar .tag's white-on-live text was 3.07:1, below the 4.5:1 floor, once --live moved to a brighter dark-mode red; --gold-ink clears 5.45:1 on the same fill */
  --radius:8px;
  --radius-lg:14px;
  --shadow:0 0 0 1px var(--line), 0 6px 20px rgba(0,0,0,.45);
  font-family:var(--font-sans);
}

/* ============================================================
   THE SHARED SCALE AND THE SHARED NAMES - this site's third of
   deploy/handover/ui-ux/DESIGN-TOKENS.md (sections 2, 3 and 6).
   ============================================================
   Three stylesheets in three workstreams (the app's app.css, the
   inspector portal's app.css, this file) were drifting: the same
   idea under three names, and type sizes measured on a phone at
   9, 10 and 11 px (F4-12, F5-11). The sheet fixes one vocabulary;
   each file adopts it on its own. The app adopted in 5944bdf.

   ALIASES FIRST, NO VISUAL CHANGE. Every shared colour name below
   points at the value this file already had (--navy-1, --gold,
   --card, --line ...). Nothing on screen changes colour because of
   this block; a later window may move the ad-hoc greens and reds
   in the five feature stylesheets onto --ok / --warn / --bad, one
   owner and one file at a time.

   THE FLOOR IS THE POINT. --text-xs is 12 px and nothing in this
   file is set below it any more (the 9 px LIVE tag, the 10 px
   league links and bottom-bar labels, the 10.5 px form labels, the
   11 px footer). frontend/web/check-type-scale.sh fails the build
   if a smaller number comes back.

   A FINGER IS NOT A MOUSE POINTER. Controls (.btn, the inputs, the
   nav tabs, the avatar, the close cross, the bottom bar) are sized
   with --control-h: 36 px with a fine pointer, 44 px under
   (pointer: coarse), one media query below and nowhere else.

   Radius is a theme value, not a scale value: this site keeps its
   own 8 / 14 (the app is 4 / 8) and only gains the small-corner
   name.
   ------------------------------------------------------------ */
:root{
  --text-xs:12px; --text-sm:13px; --text-md:14px; --text-lg:16px; --text-xl:20px; --text-2xl:24px; --text-num:34px; --text-display:32px;
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px; --space-5:24px; --space-6:32px;
  --radius-sm:4px;
  --control-h:36px; --hit-min:24px; --row-min:44px; --gutter:16px;

  /* R-012, 26 Sep: --link/--info/--focus used to alias --navy-1, which
     was safe only because --navy-1 was a strong blue on a WHITE page.
     Dark-default made --navy-1 the header's OWN dark-on-dark tone
     (#05329B on #001C50 is 1.51:1, unreadable as text) -- so these three
     are decoupled here to --gold, the same accent the app's own
     already-shipped dark theme uses for exactly this role. --warn/--bad
     move for the same reason the status colours above did (see the big
     R-012 comment on the first :root block). Nothing here changes what
     any of these NAMES mean; --link/--focus/--info still point at
     accessible values, just not the ones the light theme (below) uses. */
  --surface:var(--card); --surface-2:var(--panel-hover); --band:var(--navy-1);
  --line-strong:#7391c4; --ink-dim:#c7d2e6;
  --accent:var(--gold); --accent-ink:var(--gold-ink); --link:var(--gold);
  --ok:var(--green); --warn:#ffcb45; --bad:#ff5a52; --info:#b9cdf2; --focus:var(--gold);
  --font-ui:var(--font-sans);
}

/* ============================================================
   LIGHT THEME - opt-in, `<html data-theme="light">`. Every value below
   is exactly what this file shipped with before the R-012 retint; this
   block only moved them, it invented nothing. jscripts/shared/
   uiux_shared.js sets the attribute from localStorage['theme'].
   ============================================================ */
[data-theme="light"]{
  --navy-0:#0a0f2b; --navy-1:#1c3fb7; --navy-2:#0e1433; --navy-3:#00154b;
  --navy-deep:#050a1c; --hero-grad-2:#10245e;
  --bg:#f2f3f7; --card:#ffffff; --ink:#171b26; --muted:#5b6472; --line:#e4e7ee;
  --live:#e1122f; --green:#0e8a4b;
  --panel-tint:#fafbfd; --panel-hover:#f7f8fb; --panel-active-hover:#f0f2f8; --panel-active:#eef1fb; --panel-tile:#f8f9fc;
  --shadow:0 1px 2px rgba(10,20,50,.06), 0 6px 20px rgba(10,20,50,.06);
  --surface-2:#f2f3f7;
  --line-strong:#7f8898; --ink-dim:#4b5563;
  --link:var(--navy-1); --warn:#8a6300; --bad:#c0392b; --info:var(--navy-1); --focus:var(--navy-1);
  --fi-ring:rgba(0,0,0,.08);
  --input-bg:#ffffff;
  --tag-ink:#ffffff;
}
@media (pointer: coarse){
  :root{ --control-h:44px; }
}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--bg);color:var(--ink);font-family:var(--font-sans);}
a{color:inherit;text-decoration:none}
button{font-family:inherit;cursor:pointer}
img{max-width:100%;display:block}
.wrap{padding:0 20px}

/* ---------- header ---------- */
.topbar{background:var(--navy-0);color:#c7cee4;font-size:var(--text-xs)}
/* R-012, 26 Sep: adding the theme toggle to `.topbar .right` grew this
   row's min-content width past 320px, the same flex-overflow-forces-the-
   page-wide pattern R9 found and fixed four other times in this file
   (.navtabs .wrap, .hero .actions, .brandbar .wrap) -- same fix, `flex-
   wrap:wrap`. `height:32px` stops being a fixed clip and starts being a
   normal value once this can wrap, exactly like `.brandbar`'s own note
   above. */
.topbar .wrap{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;height:32px}
.topbar nav{display:flex;gap:18px}
.topbar nav a:hover{color:#fff}
.topbar .right{display:flex;gap:14px;align-items:center}
.theme-toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer}
.theme-toggle:hover{color:#fff}

.brandbar{background:var(--navy-1);}
/* R9 (26 Sep): a fourth narrow-viewport forcer, found at the WCAG 1.4.10
   reflow floor (320 CSS px) once the larger ones above it were fixed. Below
   980px the searchbox is already hidden (rule below), so this row is just
   the logo and the Log In / Join Now buttons -- both `.btn`, both
   `white-space:nowrap` by design, together 19 px wider than 320px with
   nowhere to shrink to. Wrapping the row, not the buttons' own text, is the
   same fix as the hero actions row above: `height:74px` is a normal value
   here, not a clip, so a wrapped second line just makes the bar taller. */
.brandbar .wrap{display:flex;flex-wrap:wrap;align-items:center;gap:22px;height:74px}
.logo{display:flex;align-items:center;cursor:pointer}
.logo svg{display:block}
.searchbox{flex:1;max-width:420px;position:relative}
.searchbox input{width:100%;min-height:var(--control-h);border:none;border-radius:20px;padding:10px 16px 10px 36px;font-size:var(--text-sm);outline:none;background:rgba(255,255,255,.16);color:#fff}
.searchbox input::placeholder{color:#c7cee4}
.searchbox .icn{position:absolute;left:12px;top:50%;transform:translateY(-50%);opacity:.5;font-size:var(--text-sm)}
.header-actions{margin-left:auto;display:flex;align-items:center;gap:12px}
.balance-pill{background:rgba(255,255,255,.12);color:#fff;border-radius:20px;padding:6px 14px;font-size:var(--text-xs);line-height:1.3}
.balance-pill b{display:block;font-size:var(--text-md)}
.btn{border:none;border-radius:var(--radius-lg);padding:10px 24px;min-height:var(--control-h);display:inline-flex;align-items:center;justify-content:center;font-size:var(--text-sm);font-weight:700;cursor:pointer;white-space:nowrap}
.btn-gold{background:var(--gold);color:var(--gold-ink);font-weight:800;padding:11px 26px}
.btn-gold:hover{background:#ffd83d}
.btn-outline{background:transparent;border:2px solid #fff;color:#fff}
.btn-outline:hover{border-color:#fff}
.avatar-btn{width:var(--control-h);height:var(--control-h);border-radius:50%;background:rgba(255,255,255,.15);border:none;color:#fff;font-size:15px}

.navtabs{background:var(--navy-2)}
/* R9, root-caused 26 Sep by the operator's live browser check (relayed by
   CI/CD): this row has seven items, no flex-wrap, and no min-width:0 anywhere
   in the chain, so on a narrow viewport the flex box refuses to shrink below
   its own min-content width and forces the WHOLE PAGE at least that wide --
   the 611 px layout-viewport pan every phone-width finding in this project
   has been describing since flow 1. It hits every page, not just racing,
   because .navtabs is shared chrome loaded on every route.
   Letting the row wrap onto a second line is the minimal fix: it changes
   nothing at any width wide enough to hold all seven items in one line
   (which is every width this matters at, down past 980px), and below that
   it grows the header's height instead of the page's width. No JS, no
   scroll container, no change to any tab's own size. */
.navtabs .wrap{display:flex;flex-wrap:wrap;gap:2px}
.navtabs a{padding:13px 18px;min-height:var(--control-h);font-size:13.5px;font-weight:700;color:#c9cee0;border-bottom:3px solid transparent;display:flex;align-items:center;gap:6px}
.navtabs a:hover{color:var(--gold)}
.navtabs a.active{color:#fff;border-bottom-color:var(--gold)}
.live-dot{width:7px;height:7px;border-radius:50%;background:var(--live);display:inline-block;animation:pulse 1.4s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}

/* ---------- layout ---------- */
.layout{display:grid;grid-template-columns:220px 1fr;gap:18px;padding:18px 0;padding-left:20px;padding-right:20px;align-items:start}
.layout.with-slip{grid-template-columns:220px 1fr 300px}
.layout.no-sidebar{grid-template-columns:1fr}
.layout.video-docked{grid-template-columns:1fr 340px}
.layout.hr-slip-docked{grid-template-columns:220px 1fr 300px}

@media(max-width:980px){.layout,.layout.with-slip,.layout.video-docked,.layout.hr-slip-docked{grid-template-columns:1fr}}

.sidebar-col{display:flex;flex-direction:column;gap:0;min-width:0}

/* F1-12 (operator's decision, 25 Sep, relayed via the desktop DESIGN session's
   deploy/handover/design/DESKTOP-V1-20260925.md): hide the sidebar rather than
   redirect the phone to mobtlp01. At 390 CSS px the sidebar is 922 px tall and
   sits ABOVE the racing column (which is itself 868 px before this rule), so
   the runner list opened at y=1790 against a 1323 px fold -- below the first
   screen. Hiding the sidebar moves the runner list to y=868, inside the fold.
   Measured with frontend/web/tests/browser/racing-fold.mjs.

   980px, MATCHING THE LAYOUT'S OWN COLLAPSE POINT, not racing's 860 --
   corrected 26 Sep after the operator's live browser check found the two
   breakpoints disagreed. The first version of this rule fired at 860px, one
   number below, reasoning that it should match horse-racing.css's own
   @media(max-width:860px) block (a DIFFERENT, page-internal concern: which
   order .hr-hero/.hr-livenow/.hr-race-tiles render in, nothing to do with
   this file's own grid). Between 861 and 979px that left `.layout` already
   collapsed to a single column (the rule above) while `.sidebar-col` was
   still `display:flex` -- so the sidebar rendered full-width ABOVE the hero
   and odds instead of beside them, pushing everything else below the fold,
   on every page, not just racing's. A sidebar meant to sit beside content in
   a two-column grid has no reason to survive past the point that grid stops
   existing, so this now fires at the SAME width as the rule two lines up.
   Racing's 860px threshold is untouched and still its own; nothing requires
   the two to match, and the original claim that they should was the bug.

   PLACEMENT MATTERS: this rule must come AFTER the unconditional
   `.sidebar-col{display:flex}` above, not before it -- both have equal
   specificity (one class selector), so on a tie the cascade takes the rule
   that appears LAST in the file regardless of which one is inside a media
   query. Putting this earlier would compile, pass a linter, and silently
   never hide anything. */
@media(max-width:980px){.sidebar-col{display:none}}
.sidebar{background:var(--card);border-radius:var(--radius-lg);box-shadow:var(--shadow);overflow:hidden;position:sticky;top:14px}
.sidebar-panel{background:var(--card);border-radius:var(--radius-lg);box-shadow:var(--shadow);overflow:hidden}
.sidebar h4,.sidebar-panel h4{margin:0;padding:12px 16px 8px;font-size:var(--text-xs);letter-spacing:.5px;color:var(--muted);text-transform:uppercase;background:var(--panel-tint)}
.sidebar a,.sidebar-panel a{display:flex;align-items:center;gap:10px;padding:10px 16px;font-size:13.5px;font-weight:600;color:var(--ink);border-top:1px solid var(--line)}
.sidebar a:hover,.sidebar-panel a:hover{background:var(--panel-hover)}
.sidebar a .tag{margin-left:auto;background:var(--live);color:var(--tag-ink);font-size:var(--text-xs);font-weight:800;padding:2px 6px;border-radius:4px}
/* Level-specific overrides below use ".sidebar a.X" (class+element+ancestor) rather than
   just ".X" (class only) - a plain single-class selector has LOWER specificity than the
   ".sidebar a" rule above (which combines a class with an element type), so without this
   qualification the generic rule silently wins and every level renders identically
   regardless of the padding/font-size/weight set here. */

.sidebar a.sidebar-sport-head{display:flex;align-items:center;gap:10px;padding:10px 16px;font-size:var(--text-sm);font-weight:600;color:var(--ink);border-top:1px solid var(--line);cursor:pointer}
.sidebar a.sidebar-sport-head:hover{background:var(--panel-hover)}
.sidebar a.sidebar-sport-head .tag{background:var(--live);color:var(--tag-ink);font-size:var(--text-xs);font-weight:800;padding:2px 6px;border-radius:4px}
.sidebar a.sidebar-sport-head .caret{margin-left:auto;font-size:var(--text-xs);color:var(--muted);transition:transform .15s ease}
.sidebar a.sidebar-sport-head .caret.open{transform:rotate(180deg)}
.sidebar-sub{background:var(--panel-tint)}
.sidebar a.sidebar-sub-link{display:block;padding:8px 16px 8px 32px;font-size:var(--text-xs);font-weight:400;color:var(--muted);border-top:1px solid var(--line)}
.sidebar a.sidebar-sub-link:hover{color:var(--link);background:var(--panel-active-hover)}
.sidebar a.sidebar-sub-link.active{color:var(--link);font-weight:600;background:var(--panel-active)}
.sidebar-sub-empty{padding:10px 16px 10px 32px;font-size:var(--text-xs);color:var(--muted);font-style:italic;border-top:1px solid var(--line)}
.sidebar a.sidebar-country-head{display:flex;align-items:center;gap:8px;padding:7px 16px 7px 32px;font-size:var(--text-xs);font-weight:700;color:var(--ink);border-top:1px solid var(--line);cursor:pointer;background:var(--panel-tint)}
.sidebar a.sidebar-country-head:hover{background:var(--panel-active-hover)}
.sidebar a.sidebar-country-head .fi{width:18px;height:13px;border-radius:2px;flex:0 0 auto;box-shadow:0 0 0 1px var(--fi-ring)}
.sidebar a.sidebar-country-head .flag-fallback{font-size:var(--text-sm);line-height:1}
.sidebar a.sidebar-country-head .caret{margin-left:auto;font-size:var(--text-xs);color:var(--muted);transition:transform .15s ease}
.sidebar a.sidebar-country-head .caret.open{transform:rotate(180deg)}

/* ============================================================
   COUNTRY FLAGS - VENDORED. NO EXTERNAL ORIGIN.
   ============================================================
   This replaces the third <link> in index.html:

     https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/7.5.0/css/flag-icons.min.css

   which was a render-blocking stylesheet on a cold connection to
   Cloudflare that then went BACK to Cloudflare for each flag image
   it referenced. Two round trips to a third party, on the critical
   path, for eleven small pictures.

   WHICH FLAGS, AND WHY ONLY THESE. countryFlag()
   (jscripts/shared/uiux_shared.js:23) is the only thing in this tree
   that emits an `fi fi-xx` class, and the only ISO codes it can
   produce are the values of COUNTRY_ISO (jscripts/shared/data_shared.js:44).
   That is eleven codes. The upstream package ships ~260 flags; the
   other ~250 could never be requested by this site. Every one of the
   eleven is here as a REAL FILE under images/flags/ - never base64,
   per the house rule.

   Any country NOT in COUNTRY_ISO already falls back to the
   .flag-fallback white-flag glyph rather than a broken image, so a
   live feed naming a twelfth country degrades exactly as it did
   before. To add one: drop the 4x3 SVG in images/flags/, add the
   name->ISO pair to COUNTRY_ISO, and add one line below.

   Provenance: lipis/flag-icons v7.5.0 - the SAME upstream project and
   the SAME tag the cdnjs bundle was built from - flags/4x3/<iso>.svg,
   copied byte for byte, unmodified:
     https://raw.githubusercontent.com/lipis/flag-icons/v7.5.0/flags/4x3/<iso>.svg
   Licence: MIT (the code); the flag artwork itself is public domain.

   The `.fi` base rule reproduces upstream's. Both call sites already
   pin their own width/height (.sidebar .fi and .league-badge .fi
   in css/sports.css), so the 4:3 em-box below only matters if a third
   call site ever renders one unsized.
   ------------------------------------------------------------ */
.fi{
  background-size:contain;
  background-position:50%;
  background-repeat:no-repeat;
  position:relative;
  display:inline-block;
  width:1.3333333333em;            /* 4:3, as upstream */
  line-height:1em;
}
.fi:before{content:"\00a0"}        /* gives an unsized .fi a line box */

.fi-ar{background-image:url('../images/flags/ar.svg')}        /* Argentina */
.fi-br{background-image:url('../images/flags/br.svg')}        /* Brazil    */
.fi-cl{background-image:url('../images/flags/cl.svg')}        /* Chile     */
.fi-de{background-image:url('../images/flags/de.svg')}        /* Germany   */
.fi-ec{background-image:url('../images/flags/ec.svg')}        /* Ecuador   */
.fi-es{background-image:url('../images/flags/es.svg')}        /* Spain     */
.fi-fr{background-image:url('../images/flags/fr.svg')}        /* France    */
.fi-gb-eng{background-image:url('../images/flags/gb-eng.svg')}/* England   */
.fi-it{background-image:url('../images/flags/it.svg')}        /* Italy     */
.fi-mx{background-image:url('../images/flags/mx.svg')}        /* Mexico    */
.fi-us{background-image:url('../images/flags/us.svg')}        /* USA       */

.sidebar a.sidebar-league-link{display:block;padding:6px 16px 6px 48px;font-size:var(--text-xs);font-weight:400;color:var(--muted);border-top:1px solid var(--line)}
.sidebar a.sidebar-league-link:hover{color:var(--link);background:var(--panel-active-hover)}
.sidebar a.sidebar-league-link.active{color:var(--link);font-weight:600;background:var(--panel-active)}
.sidebar a.active-market,.sidebar-panel a.active-market{background:var(--navy-1);color:#fff}

.main-col{min-width:0}

/* ---------- hero ---------- */
.hero{position:relative;border-radius:var(--radius-lg);overflow:hidden;background:linear-gradient(115deg,var(--navy-deep) 45%, var(--hero-grad-2) 100%);color:#fff;padding:34px 34px;margin-bottom:16px;min-height:200px}
.hero .eyebrow{color:var(--gold);font-size:var(--text-xs);font-weight:800;letter-spacing:.8px;text-transform:uppercase;margin-bottom:10px}
.hero h1{font-family:var(--font-display);font-size:36px;line-height:1.05;margin:0 0 12px;font-weight:800;max-width:520px;text-shadow:0 2px 12px rgba(0,0,0,.4)}
.hero h1 span{color:var(--gold)}
.hero p{max-width:460px;color:#c7cee4;font-size:var(--text-md);margin:0 0 20px}
/* R9 (26 Sep): a third narrow-viewport forcer, found only once .navtabs's
   larger one was fixed. .btn keeps `white-space:nowrap` (a button label
   should never break mid-word); the two buttons together don't fit one row
   under ~450 px, so the ROW wraps them onto two lines instead of forcing
   the page wide. Neither button's own text ever wraps. */
.hero .actions{display:flex;flex-wrap:wrap;gap:10px}
.hero .btn-gold{padding:14px 26px}
.hero .btn-outline{padding:12px 26px}

/* ---------- cards / sections ---------- */
.card{background:var(--card);border-radius:var(--radius-lg);box-shadow:var(--shadow);padding:18px;margin-bottom:16px}
.section-title{display:flex;align-items:center;justify-content:space-between;margin:26px 0 12px}
.section-title h2{font-family:var(--font-display);font-size:17px;margin:0;font-weight:800}
.section-title a{font-size:12.5px;font-weight:700;color:var(--link)}

/* ---------- account / login ---------- */
.overlay{position:fixed;inset:0;background:rgba(5,10,28,.55);display:flex;align-items:center;justify-content:center;z-index:50;padding:20px}
.auth-card{background:var(--card);border-radius:var(--radius-lg);width:400px;max-width:100%;box-shadow:0 20px 60px rgba(0,0,0,.3);overflow:hidden}
.auth-tabs{display:flex}
.auth-tabs div{flex:1;min-height:var(--control-h);text-align:center;padding:14px;font-weight:800;font-size:13.5px;color:var(--muted);border-bottom:2px solid var(--line)}
.auth-tabs div.active{color:var(--link);border-color:var(--link)}
.auth-body{padding:24px}
.auth-body h3{margin:0 0 4px;font-size:19px}
.auth-body p{margin:0 0 18px;color:var(--muted);font-size:12.5px}
.field{margin-bottom:12px}
.field label{display:block;font-size:var(--text-xs);letter-spacing:.4px;color:var(--muted);text-transform:uppercase;margin-bottom:5px;font-weight:700}
.field input{width:100%;min-height:var(--control-h);border:1px solid var(--line);border-radius:8px;padding:11px 12px;font-size:13.5px;background:var(--input-bg);color:var(--ink)}
.auth-body .btn-gold{width:100%;padding:13px;margin-top:6px}
.close-x{position:absolute;top:8px;right:8px;min-width:var(--control-h);min-height:var(--control-h);display:inline-flex;align-items:center;justify-content:center;padding:0;background:none;border:none;font-size:var(--text-xl);color:var(--muted);cursor:pointer}


/* ---------- footer ---------- */
footer{background:var(--navy-3);color:#c7cee4;margin-top:30px;font-size:12.5px}
.foot-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:20px;padding:34px 0 20px}
@media(max-width:820px){.foot-grid{grid-template-columns:repeat(2,1fr)}}
.foot-grid h4{color:#fff;font-size:var(--text-xs);letter-spacing:.5px;margin:0 0 10px}
.foot-grid a{display:block;padding:4px 0;color:#a9b3d1}
.foot-grid a:hover{color:#fff}
.foot-pay{border-top:1px solid rgba(255,255,255,.1);padding:16px 0;display:flex;gap:10px;flex-wrap:wrap;align-items:center;justify-content:space-between}
.chip-pay{background:rgba(255,255,255,.08);border-radius:6px;padding:6px 12px;font-size:var(--text-xs);font-weight:700}
.foot-bottom{border-top:1px solid rgba(255,255,255,.1);padding:16px 0 26px;font-size:var(--text-xs);color:#8592b8;line-height:1.6}

/* ---------- misc ---------- */
.demo-banner{background:#101a3d;color:#c7cee4;font-size:var(--text-xs);text-align:center;padding:7px 12px;line-height:1.5}
.demo-banner.status-live{background:#0a3d1f;color:#c9f2d6}
.demo-banner.status-offline{background:#3d0a0a;color:#f2c9c9}
.demo-banner b{color:#fff}
.demo-banner b{color:var(--gold)}
.hidden{display:none !important}
.mobile-nav{display:none}
@media(max-width:980px){
  .mobile-nav{display:flex;position:fixed;bottom:0;left:0;right:0;background:var(--card);border-top:1px solid var(--line);z-index:40}
  .mobile-nav a{flex:1;min-height:var(--control-h);text-align:center;padding:8px 0 10px;font-size:var(--text-xs);font-weight:700;color:var(--muted)}
  .mobile-nav a.active{color:var(--link)}
  .mobile-nav .ic{font-size:var(--text-lg);display:block;margin-bottom:2px}
  body{padding-bottom:56px}
  .searchbox{display:none}
}
/* On a touch screen an input below 16 px makes iOS Safari zoom the page on focus; the sheet's rule (section 2). */
@media (pointer: coarse){
  .field input,.searchbox input{font-size:var(--text-lg)}
}

