/* dc-mobile.css - shared responsive layer for DeepCover 2.0.
 * Linked LAST in every page <head> so it overrides per-page inline styles.
 * Backbone fixes (nav + tables + base scaling) live here; per-page polish
 * stays in each page's own <style>. Breakpoint: phones ≤720px. */

/* WCAG-AA: the brand gold #c69200 on a white card is only 2.80:1 - below the 3:1
 * AA-Large bar (and well below 4.5:1 for small gold text like eyebrows / "go" links).
 * In LIGHT mode only, deepen gold to #9b6000 (5.16:1 on white) so gold text passes AA.
 * DARK mode keeps the bright brand gold (it already passes on the dark card). The
 * html[data-theme=...] selector outspecifies each page's :root, so this wins app-wide. */
html[data-theme="light"]{ --gold:#9b6000; --goldText:#755300; --positiveText:#0f766e; }
html[data-theme="dark"]{ --goldText:#f0d78c; --positiveText:#5eead4; }

/* THE SITE CHROME OWNS ITS OWN COLOURS.
 * The shell strip, the side rail, the mobile search bar and the tab bar used to
 * paint with the PAGE's --card / --line / --soft and a light fallback. That only
 * works on a page that happens to define those names. An /articles/ story defines
 * --panel and --line but never --card, so in dark mode the fallback fired and the
 * reader got a WHITE frame around a dark page (founder, 2026-08-04). Defining the
 * frame's own --dc* names HERE, in the stylesheet every page links synchronously,
 * means the chrome is correct from the first paint, before dc-shell.js even loads.
 * A page still styles its own content with its own variables; it simply cannot
 * decide what the site's chrome looks like any more. */
:root{
  --dcCard:#ffffff; --dcSoft:#f6f8fc; --dcTrack:#eef2f7; --dcField:#fbfcfe;
  --dcLine:#e8edf5; --dcLine2:#cdd6e4; --dcInk:#0f172a; --dcMuted:#64748b;
  --dcMuted2:#94a3b8; --dcText2:#334155;
}
html[data-theme="dark"]{
  --dcCard:#1d2e4d; --dcSoft:#131d31; --dcTrack:#273759; --dcField:#131d31;
  --dcLine:#304066; --dcLine2:#3b4a70; --dcInk:#eaf1fa; --dcMuted:#adbdd4;
  --dcMuted2:#9aadc8; --dcText2:#ccd8e8;
}

/* CURSOR HYGIENE (app-wide, desktop): plain text (headings, table cells, labels) showed
 * the text I-beam on hover - it reads like a stray blinking caret sitting on the page.
 * Force the normal arrow everywhere, then restore the pointer on interactive controls and
 * the text caret on real inputs. Text stays selectable (copy still works); only the cursor
 * shape changes. */
body{ cursor:default; }
/* World-class taps (founder): a tap / long-press on the UI was popping the native
   Copy / Share / Select-all callout, because placeholders, labels, chips and stat text
   are all selectable. Kill text-selection + the touch callout app-wide so a tap just
   acts; real inputs (and anything opting in via .dcSel) keep normal text selection. */
html, body{ -webkit-user-select:none; -moz-user-select:none; user-select:none; -webkit-touch-callout:none; }
input, textarea, select, [contenteditable="true"], .dcSel, .dcSel *{
  -webkit-user-select:text; -moz-user-select:text; user-select:text; -webkit-touch-callout:default; }
a[href], button, select, summary, label, [role="button"], [onclick],
th[onclick], th[data-k], th[data-sort], .cddBtn, .dclgBtn, .dcTab, .arTab, .plink{ cursor:pointer; }
input, textarea, [contenteditable]{ cursor:text; }

/* MATCHUP "advantage"/"trouble" strips (.gscroll #advWrap/#trbWrap → table.gtbl):
 * the data rows + stat-pill cells showed the text I-beam on hover. Pin them to the
 * arrow; the SORTABLE column headers (every table.gtbl th carries data-k and a click
 * handler) and the clickable player-name links (a.plink, already pointer above) keep
 * the pointer hand. Cells stay selectable - only the cursor shape changes. */
table.gtbl, table.gtbl tr, table.gtbl td, table.gtbl td .hpill{ cursor:default; }
table.gtbl th{ cursor:pointer; }
table.gtbl td a.plink{ cursor:pointer; }

/* SMOOTH CROSS-DOCUMENT NAVIGATION (Chromium View Transitions, progressive enhancement).
 * The site is multi-page, so every navigation is a full reload and the JS-built shell
 * (sidebar + top bar) used to flash/re-pop. With cross-document view transitions the
 * browser holds the old frame, then cross-fades to the new one - no hard white flash -
 * and naming the shell keeps it visually stable instead of cross-fading. Ignored where
 * unsupported (no effect, no risk). prefers-reduced-motion users get an instant swap. */
/* Gate the cross-document transition to DESKTOP only. Desktop pre-stages its whole shell
   (the >=980 anti-flash block far below), so the cross-fade lands on a matching first paint
   and is seamless. Mobile builds its chrome (bottom tab bar #dcTabbar + sticky search
   #dcMobSearch) in JS AFTER first paint and is NOT pre-staged, so a cross-document cross-fade
   lands on a chrome-less + skeleton page and flashes ("bijli chamakna") before the bars pop
   back in. On mobile we want a plain instant swap, so opt in only at >=980. */
/* #wave3 (founder, 15 Aug 2026: "ek menu se doosre menu par jao to ekdam se badal jata
   hai - Cricbuzz par sab kuch smooth khulta hai"). The cross-document transition was
   desktop-only for one honest reason, written above: on a phone the bottom bar is built
   by JS AFTER first paint, so a cross-fade landed on a page with no bar and flashed.
   That reason is now answered rather than avoided. A STATIC placeholder bar is painted
   from CSS on every page (below), so the new page's very first paint already has a bar
   in the right place; dc-nav.js marks html.dc-tabbed the moment the real one mounts and
   the placeholder disappears under it. With the bar present in both snapshots the
   cross-fade has nothing left to flash, so the transition is on at every width. */
@view-transition { navigation: auto; }
/* the placeholder: same height, same colours, no content - it exists only so the first
   paint is not chrome-less. It is behind the real bar (z-index 199 vs 200) and gone the
   moment dc-nav.js reports in, so it can never double up. */
@media (max-width: 979.98px){
  html:not(.dc-tabbed) body::after{
    content:""; position:fixed; left:0; right:0; bottom:0; z-index:199; pointer-events:none;
    height:calc(69px + env(safe-area-inset-bottom));
    background:var(--card, #fff); border-top:1px solid var(--line);
    box-shadow:0 -6px 22px rgba(15,23,42,.12);
  }
}
/* (3) NoJump Phase 2 (17 Aug 2026): A CUT BECOMES A GLIDE. Only the CONTENT ROOT animates. Every
 * page's .wrap carries the name dc-main: the old content fades out and the new content fades in
 * with a 6px rise, 160ms, while the header, the search bar and the tab bar (named below, animations
 * off) hold perfectly still and the root - now only the body background, since every named element
 * is cut out of the root snapshot - does not animate at all. The group does not morph its box (the
 * two pages' content heights differ; a 0s group jumps to the new box, both images top-aligned).
 * dc-mobile.js's pageswap handler drops the name on the OLD document when the reader is scrolled
 * (the old snapshot would show the top of the old page, not what was on screen) - then only the new
 * content fades in. Unsupported browsers: an instant swap, as ever. */
body > .wrap, .qcols > .wrap{ view-transition-name: dc-main; }   /* the page's one content root (a nested .wrap, similar.html, keeps no name: the name must be unique) */
::view-transition-old(root), ::view-transition-new(root){ animation:none; }
::view-transition-group(dc-main){ animation-duration:0s; }
@keyframes dcMainOut{ to{ opacity:0; } }
@keyframes dcMainIn{ from{ opacity:0; transform:translateY(6px); } to{ opacity:1; transform:none; } }
::view-transition-old(dc-main){ animation:dcMainOut 160ms cubic-bezier(.4,0,.2,1) both; }
::view-transition-new(dc-main){ animation:dcMainIn 160ms cubic-bezier(.2,.7,.2,1) both; }
/* NoJump (17 Aug 2026) - THE CHROME HOLDS STILL. The header, the search bar and the bottom
 * tab bar are in every page's HTML from the server now (api/dc_chrome.py), so each carries a
 * stable view-transition-name: the browser pairs old and new by name and, with every
 * animation on that pair switched off, the element simply stays where it is while the page
 * content cross-fades underneath. Names must be unique per document; each is one node.
 * (header.bar's stacking context is handled by its own z-index.) */
header.bar { view-transition-name: dc-header; }
#dcTabbar { view-transition-name: dc-tabbar; }
#dcMobSearch { view-transition-name: dc-search; }
::view-transition-group(dc-header), ::view-transition-group(dc-tabbar), ::view-transition-group(dc-search){ animation-duration:0s; }
::view-transition-old(dc-header), ::view-transition-new(dc-header),
::view-transition-old(dc-tabbar), ::view-transition-new(dc-tabbar),
::view-transition-old(dc-search), ::view-transition-new(dc-search){ animation:none; }
/* (1)+(2) The left sidebar is JS-built AFTER first paint, so it can't be in the NEW page's
 * transition snapshot. The old `view-transition-name: dcSidebar` made the sidebar SNAP-VANISH
 * then POP (the flash). Instead: no VT name → it fades OUT gently as part of the root
 * cross-fade, and fades IN softly (dcChromeIn) when dc-shell.js rebuilds it. The tab bar
 * only fades in when a page came WITHOUT the server bar (local preview, an old cached shell)
 * and dc-nav.js had to build it; the server bar is there in the first frame and never fades. */
/* NOTE: no `both`/backwards fill - a fade-in whose animation never runs (backgrounded tab,
 * throttled renderer) must degrade to VISIBLE (base opacity:1), never stay stuck at 0. */
html.dc-shelled aside.dcSide{ animation: dcChromeIn .18s ease; }
#dcTabbar:not([data-dcssr]){ animation: dcChromeIn .18s ease; }
@keyframes dcChromeIn{ from{ opacity:0 } to{ opacity:1 } }
/* (4) Reduced-motion users: no page cross-fade AND no chrome fade - instant, as before. */
@media (prefers-reduced-motion: reduce){
  ::view-transition-group(*){ animation:none !important; }
  ::view-transition-old(dc-main), ::view-transition-new(dc-main){ animation:none !important; }
  html.dc-shelled aside.dcSide, #dcTabbar{ animation:none !important; }
}
/* The server-painted search bar is phone chrome; the desktop shell has its own omnibox.
 * dc-mobile.js used to simply not build it above 979px; the CSS now does that job. */
@media (min-width: 980px){ #dcMobSearch{ display:none !important; } }
/* NoJump: the header wordmark's logo + gold "Cover" are server-painted too (api/dc_chrome.py);
 * dc-league.js injectPicker() used to add them on DOMContentLoaded and its own sheet with them,
 * so the wordmark row grew from the text's 26px to the logo's 33px after first paint. Byte-same
 * rules as dc-league.js injectStyles(), here so the first frame has them. */
header.bar .wordmark{ display:inline-flex; align-items:center; gap:9px; }
header.bar .wordmark .dcLogo{ width:1.8em; height:1.8em; flex:0 0 auto; display:block; object-fit:contain; }
:root[data-theme="light"] header.bar .wordmark .dcLogo{ background:#0b1018; border-radius:7px; padding:2px; box-sizing:border-box; }
header.bar .wordmark .g{ color:var(--gold,#c69200); }

/* SORT DISCOVERABILITY (app-wide): every clickable column header carries a faint
 * up/down hint so users know they can sort. The ACTIVE column already shows its
 * real ▲/▼ in text (and gets class .on), so the hint is suppressed there. Sortable
 * headers are marked, across the site, by onclick="sortBy" / data-k / data-sort /
 * role="button". Non-sortable headers (rank #, scorecard order, standings) carry
 * none of these, so they stay clean. */
th[onclick]:not(.on), th[data-k]:not(.on), th[data-sort]:not(.on), th[role="button"]:not(.on){ cursor:pointer; }
th[onclick]:not(.on)::after, th[data-k]:not(.on)::after, th[data-sort]:not(.on)::after, th[role="button"]:not(.on)::after{
  content:"⇅"; margin-left:.28em; opacity:.32; font-weight:400; font-size:.92em;
}
th[onclick]:hover::after, th[data-k]:hover::after, th[data-sort]:hover::after, th[role="button"]:hover::after{ opacity:.7; }

/* "Sort by" dropdown rendered above tableHTML()-based tables (team-season, squad,
 * match-centre leaderboards). Keeps the explicit control consistent app-wide. */
.tblSortBar{ display:flex; justify-content:flex-end; margin:2px 0 8px; }
.tblSortBar label{ color:var(--muted); font-weight:800; font-size:.7rem; text-transform:uppercase; letter-spacing:.04em; display:inline-flex; align-items:center; gap:6px; }
.tblSortBar select{ font-family:inherit; font-size:.74rem; font-weight:700; text-transform:none; letter-spacing:0; padding:4px 8px; border:1px solid var(--line); border-radius:8px; background:var(--card); color:var(--ink); cursor:pointer; }

/* NO HORIZONTAL TABLE SLIDERS (app-wide): a 2-up .grid2 that holds a wide table used to
 * shrink the column below the table's width, forcing a horizontal scrollbar. Make .grid2
 * container-aware - it stays 2-up only while each column can be >=520px (enough for the
 * widest dossier/venue/victims table), otherwise it drops to ONE full-width column where
 * the table fits with no slider. (Linked last, so this overrides each page's 1fr 1fr.) */
.grid2{ grid-template-columns:repeat(auto-fit, minmax(560px, 1fr)); }
/* very wide tables (the 9-column Compare opponent tables) need ~740px before going 2-up */
/* min(740px,100%) clamps the column floor to the viewport so a phone narrower than the
   floor stacks to ONE full-width column instead of a 740px column overflowing the page. */
.grid2.wideGrid{ grid-template-columns:repeat(auto-fit, minmax(min(740px,100%), 1fr)); }
/* MATCHUP grid (dossier "Bowler/Batter advantage" + "Trouble matchups" 8-column heat-pill
 * tables): too many columns to fit a HALF-width column, so #80's font-tighten still left an
 * overflow slider. Force the matchup grid to ONE full-width column so each 8-col table gets
 * the full content width and fits with NO slider (no-slider doctrine, #72/#73). Higher
 * specificity than the per-page `.mgrid{repeat(2)}` so it wins without !important. */
.grid2.mgrid{ grid-template-columns:1fr; }
/* dcPair / dcPairLg · pair two stacked full-width siblings into a side-by-side strip when the
 * row is wide enough, auto-stacking to 1-up when narrow (phone / tight column). Pure layout,
 * used across the dossiers. dcPair = compact stat-card pairs; dcPairLg = chart/table pairs that
 * need more width before they pair. (auto-fit handles the responsiveness.) */
/* min(floor,100%): on a phone narrower than the floor, the column shrinks to 100% of the
   container and stacks 1-up - without it, auto-fit keeps the hard 300/460px floor and the
   column (and its echarts canvas) overflows the page edge. */
.dcPair  { display:grid; gap:16px; align-items:start; grid-template-columns:repeat(auto-fit, minmax(min(300px,100%), 1fr)); }
.dcPairLg{ display:grid; gap:16px; align-items:start; grid-template-columns:repeat(auto-fit, minmax(min(460px,100%), 1fr)); }
.dcPair > *, .dcPairLg > * { min-width:0; }

/* NOTE: the page-entrance animation (a CSS transform on .wrap) was REMOVED - it is
 * the prime suspect for the mobile touch-scroll freeze. Animating a transform on
 * .wrap (the tall main scroll container) can lock scrolling on Android WebView.
 * Rolled back to guarantee scroll; can return later as an opacity-only fade if wanted. */

/* DESKTOP header: cluster the controls (league · language · dark-mode · refresh) on
 * the right of the top row, evenly aligned, with the nav on its own row below. The
 * theme toggle used to float far-right (margin-left:auto) away from the language
 * button, and Refresh wrapped onto a line of its own under the nav. (Mobile keeps its
 * own @media(max-width:720px) header rules, untouched.) */
/* Legacy in-page desktop header layout. Bumped 721 -> 980 so it ONLY applies where the
 * desktop shell already hides header.bar (>=980, inert) - never in the 721-979px band a
 * landscape phone sits in. Below 980 the mobile shell (next block) fully owns the chrome,
 * so the old horizontal pill-nav can no longer reappear on rotation. */
@media (min-width: 980px) {
  header.bar .dcLeagueBox { order: 2; margin-left: 16px; }   /* LEFT, right after the wordmark - not pushed to the far right */
  header.bar .dcLangBox   { order: 3; margin-left: 8px; }
  header.bar #themeBtn    { order: 4; margin-left: 8px; width: 44px; height: 44px; }
  header.bar .dcHdrShare  { order: 5; margin-left: 8px; }
  header.bar #dcHdrRefresh{ order: 6; margin-left: 8px; }
  header.bar .topnav      { order: 7; flex: 0 0 100%; margin-left: 0; }
}

/* mobile-only chrome hidden on desktop; shown only on mobile (rules in @media) */
.dcHamb, .dcTabbar, .dcScrim, .dcSheet{ display:none; }

/* "No photos" preview mode (set by dc-mobile.js on ?nophotos=1): force every
 * avatar to its initials, hiding the <img> regardless of where the photo URL
 * came from (API or DuckDB parquet). Off by default - zero effect normally. */
html.dc-nophotos .av img{ display:none !important; }
html.dc-nophotos .av i{ display:block !important; }

/* Mobile shell (hamburger header + bottom tab bar + mobile search + layout). Bumped the
 * ceiling 720 -> 979 so it runs right up to the 980px desktop-shell boundary: LANDSCAPE
 * phones and small tablets (721-979px) now get this mobile nav instead of falling into the
 * old gap that re-exposed the legacy horizontal nav. (Table-compaction at line ~270 stays
 * at <=720 - those widths have room for full tables.) */
@media (max-width:979px){
  /* ---- WebView zoom guard (fixes "poora page zoom" + "table slide nahi hota") ----
   * A data table renders full-width for a moment BEFORE dc-mobile.js wraps it in a
   * .tblScroll box. On Android WebView that brief page-level overflow makes the WHOLE
   * page zoom out to fit - and then a horizontal swipe pans the zoomed page instead of
   * scrolling the table. Clipping page-level horizontal overflow keeps the page locked
   * at 1:1 scale; wide tables still scroll inside their own .tblScroll container. */
  /* SCROLL-FREEZE FIX: overflow-x:hidden on the <html> root makes it a scroll
   * container and, on Android WebView, LOCKED vertical document scroll (content
   * frozen - only the fixed bottom tab bar still responded to touch). Use
   * overflow-x:clip instead - it clips the horizontal overflow (keeps the zoom
   * guard) WITHOUT becoming a scroll container, so vertical scroll works. Also
   * dropped overscroll-behavior-y:none (added recently). */
  html, body { overflow-x: clip; max-width: 100%; }

  /* ---- layout shell ---- */
  .wrap{ padding:14px 12px 18px !important; }
  /* clear space for the fixed bottom tab bar so the last row / footer isn't hidden */
  body{ padding-bottom:calc(76px + env(safe-area-inset-bottom)) !important; }

  /* ---- mobile search bar + inline suggestions (injected by dc-mobile.js) ---- */
  /* STICKY: pin the search to the top so it stays reachable while scrolling. z-index above
   * content, below the bottom tab bar (z:200).
   * The fill is only painted once it is actually STUCK. It used to carry a flat
   * background:var(--bg) at all times on the assumption that --bg is the page backdrop -
   * but the page is painted by radial gradients on <body>, so the flat fill blanked the
   * gradient behind the bar and read as a hard-edged rectangle around the pill. Sitting
   * unstuck it now shows the gradient through; stuck it needs the fill so scrolling
   * content cannot run under the pill. .dcStuck is toggled in dc-mobile.js. */
  #dcMobSearch{ position:sticky; top:0; z-index:60; margin:2px 0 15px;
    background:transparent; padding-top:8px; }
  /* When the search is focused (its suggestion dropdown open), lift the whole sticky search
   * ABOVE the player format rail. #dcFmtRail is z-index:120 in the page ROOT context; the
   * dropdown is z-index:120 but TRAPPED inside this sticky's z-index:60 stacking context, so
   * the rail's tabs bled THROUGH the open dropdown (founder regression). Only while focused,
   * so the closed search keeps z:60 under the header. */
  #dcMobSearch:focus-within, #dcMobSearch:has(.dcMobSugg:not([hidden])){ z-index:140; }
  #dcMobSearch.dcStuck{ background:var(--bg,#fff); }
  #dcMobSearch .dcMobSRow{ display:flex; align-items:center; gap:9px;
    background:var(--field,#fbfcfe); border:1px solid var(--line,#e8edf5); border-radius:13px;
    padding:2px 14px; box-shadow:0 1px 2px rgba(15,23,42,.04); }
  #dcMobSearch .dcMobSRow:focus-within{ border-color:var(--gold,#c69200); box-shadow:0 0 0 3px #c6920026; }
  #dcMobSearch .dcMobSIc{ color:var(--muted,#64748b); font-size:1.15rem; flex:0 0 auto; }
  #dcMobSearch input{ flex:1; min-width:0; border:0; background:transparent; outline:none;
    font:600 1rem Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; color:var(--ink,#0f172a); padding:12px 0; }
  #dcMobSearch input::-webkit-search-cancel-button{ -webkit-appearance:none; }
  .dcMobSugg{ position:absolute; z-index:120; left:0; right:0; top:calc(100% + 6px); max-height:62vh; overflow:auto;
    background:var(--card,#fff); border:1px solid var(--line,#e8edf5); border-radius:13px;
    box-shadow:0 18px 42px rgba(15,23,42,.22); padding:5px; }
  .dcMobSugg[hidden]{ display:none; }
  .dcMobGrp{ font:800 .6rem Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; letter-spacing:.13em; text-transform:uppercase;
    color:var(--muted,#64748b); padding:9px 10px 4px; }
  /* #ask-vs-page (founder 2026-07-25): a super-header ABOVE the per-type group labels
   * so a user instantly sees which whole block opens a site PAGE (player/venue/team/
   * trophy dossier, a match) vs which opens the ASK engine (a computed natural-
   * language answer) - kept in sync with dc-shell.js/dc-qsearch.js. */
  .dcMobSec{ display:flex; align-items:center; gap:6px; padding:10px 10px 2px; margin-top:2px;
    border-top:1px solid var(--line,#e8edf5); color:var(--ink,#0f172a);
    font:800 .74rem Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; }
  .dcMobSec:first-child{ border-top:0; margin-top:0; }
  .dcMobSec .h{ color:var(--muted,#64748b); font-weight:700; font-size:.66rem; }
  .dcMobSec.ask{ color:var(--goldText,#755300); }
  [data-theme="dark"] .dcMobSec.ask{ color:#f0d78c; }
  .dcMobOpt{ display:flex; align-items:center; gap:9px; padding:11px 10px; border-radius:9px;
    text-decoration:none; color:var(--ink,#0f172a); font:700 .94rem Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; }
  .dcMobOpt:active, .dcMobOpt:hover, .dcMobOpt.sel, .dcAskSrow.sel{ background:var(--soft,#f6f8fc); }
  .dcMobOpt .ic{ flex:0 0 auto; color:var(--gold,#c69200); }
  /* The TITLE is what the reader is choosing. The subtitle is context, and it must
     yield first.
     It was the other way round: .st carried flex:0 0 auto, so it never gave a pixel
     back, while .tt could shrink to nothing. Measured on production at 375px, on the
     first suggestion for "Virat Kohli": the row is 339px, the subtitle took 285 of it,
     and "Virat Kohli IPL career" rendered at ZERO width. Three suggestions had no
     visible title at all and three more were cut to ~50px, so six rows looked
     identical and the reader could not tell what any of them would open. The
     full-names rule cannot hold if the name is not on screen.
     .st may now shrink, and is capped so it can never take more than half the row. */
  .dcMobOpt .tt{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .dcMobOpt .st{ flex:0 1 auto; min-width:0; max-width:38%; overflow:hidden;
    text-overflow:ellipsis; white-space:nowrap;
    color:var(--muted,#64748b); font-size:.72rem; font-weight:700; }

  /* ---- header: tidy SINGLE-ROW mobile chrome ----
   * brand (left) + on the right: league switcher (essential), language, theme toggle.
   * align-items:center keeps every control on one baseline (the .dcLeagueBox /
   * .dcLangBox flex containers used to collapse to ~6px tall - the header's row-gap -
   * letting their .dclgBtn / select children overflow into a ragged second visual row).
   * The per-page Share + the Refresh buttons are REDUNDANT on mobile (Share is offered
   * per-page; Refresh = a page reload) so they're hidden here, freeing the width that
   * was forcing the controls to wrap. The .topnav still wraps to its own full-width row
   * below as the hamburger dropdown. Desktop (>=980px) shell layout is untouched. */
  /* header.bar establishes a stacking context (view-transition-name: dcTopbar), which TRAPPED
   * the open league menu's z-index inside it - so the sticky search (a sibling at z:60) painted
   * OVER the menu's middle, splitting it into the reported "two overlapping panels". Lift the
   * whole header context above the search/band (but below the bottom-nav z:200) so the menu
   * inside it paints on top. THIS is the real fix; the .dclgMenu z:1200 alone was confined. */
  /* #hdrhold (16 Aug 2026) - THE HEADER IS BORN ITS FINAL HEIGHT.
     dc-shell.js gathers the theme and language controls into a 34px #dcHdrCtl cluster about
     60ms after parse. Until that lands the bar stands ~22px, so every page below it is laid
     out too high and then dropped: measured at 360, 390, 414 and 768 as a single layout shift
     of 0.077 with sources div#roleView dy+33 and a.wordmark dy-5.4, and it fires before the
     page's own data has even been requested. After the skeleton fix it was the largest shift
     left on a phone open.
     55px is the settled height measured on home, Match Centre, leaderboard and the dossier -
     10px padding + the 34px control row + 10px padding + the hairline - so this is a floor
     the page already reaches, not new space. Above 979px the desktop shell hides header.bar
     outright, so the reservation lives inside this phone block only. player.html carries the
     same line for itself; this one covers the other thirty-three pages. */
  header.bar{ flex-wrap:wrap; align-items:center; row-gap:6px; column-gap:6px; padding:10px 2px 10px; position:relative; z-index:150; min-height:55px; }
  /* NoJump (17 Aug 2026): the server now wraps the page's #themeBtn in the #dcHdrCtl cluster
     (api/dc_chrome.py), so the cluster exists in the FIRST paint - and it must be laid out there
     exactly as dc-shell.js's injected sheet lays it out ~60ms later, or it is born at order 0
     (left of the wordmark, 14px lower) and slides to the right edge when the shell's CSS lands
     (measured 375px: #dcHdrCtl dy=-14, dx=+280). Byte-same rules as dc-shell.js, so the later
     sheet changes nothing. */
  header.bar{ padding:10px 2px 10px !important; margin-top:0 !important; }
  header.bar .dcHdrCtl{ order:4; display:flex; align-items:center; gap:8px; margin-left:auto !important; margin-right:0 !important; flex:0 0 auto; align-self:flex-start; height:34px; }
  header.bar .dcHdrCtl .dcLangBox{ order:1; margin:0 !important; height:34px; align-self:center; flex:0 0 auto; }
  header.bar .dcHdrCtl #themeBtn{ order:2; margin:0 !important; width:40px; height:34px; flex:0 0 auto; align-self:center; }
  /* 1.15rem (down from 1.34) + the trimmed header padding keep brand + league + language +
   * theme on ONE row down to ~360px (inside .wrap's 12px padding the header content box is
   * only ~328px, so the original 1.34rem wordmark pushed theme onto a second row). Below
   * ~360px it wraps gracefully to a clean 2-row layout (the boxes no longer collapse). */
  header.bar .wordmark{ order:1; font-size:1.15rem !important; min-width:0; padding-left:2px; }
  /* explicit height + align-self:center stops the .dcLeagueBox / .dcLangBox flex containers
   * from collapsing to the header's 6px row-gap (the bug that let their button/select spill
   * into a second visual row). flex:0 0 auto keeps each at its natural width, no squeeze. */
  header.bar .dcLeagueBox{ order:2; margin-left:auto; height:34px; align-self:center; flex:0 0 auto; }
  header.bar .dcLangBox{ order:3; margin-left:0; height:34px; align-self:center; flex:0 0 auto; }
  header.bar #themeBtn{ order:4; margin-left:0; flex:0 0 auto; width:40px; height:34px; }
  /* redundant on mobile - hide so the header stays a clean single row */
  header.bar #dcHdrShare, header.bar #dcHdrRefresh, header.bar .dcHamb{ display:none !important; }
  header.bar .topnav{
    order:5; flex:0 0 100%; width:100%; display:none !important;
    flex-direction:column; gap:2px; margin:6px 0 0; padding:6px;
    background:var(--card); border:1px solid var(--line); border-radius:12px;
    box-shadow:0 18px 42px rgba(15,23,42,.18);
  }
  header.bar .topnav.dcOpen{ display:flex !important; }
  header.bar .topnav a{ width:100%; padding:11px 12px; font-size:.96rem; border-radius:8px; }
  /* NAV DE-DUP · one "Matchups" entry. The rail + the More sheet already keep only
   * Matchups; the raw per-page topnav still lists a redundant "Compare" (-> /compare.html,
   * the same target matchups.html redirects to). Hide it so the mobile hamburger shows one. */
  header.bar .topnav a[href="/compare.html"]{ display:none !important; }

  /* ---- tables: scroll sideways inside a wrapper, never break the page ---- */
  /* HORIZONTAL-only scroll: the table slides sideways; the PAGE handles vertical.
   * (A max-height box with a sticky header made the inner vertical scroll fight the
   * page scroll on touch - the leaderboard "slide" felt janky / trapped. Removed.) */
  .tblScroll{ overflow-x:auto; overflow-y:visible; max-width:100%; -webkit-overflow-scrolling:touch;
    overscroll-behavior-x:contain; scrollbar-width:thin; border-radius:12px; }
  .tblScroll table{ white-space:nowrap; }

  /* SELF-WRAPPED TABLES - same clean horizontal-scroll behaviour as .tblScroll.
   * Several pages wrap their own data tables in a private scroll container
   * (tableHTML()'s .tableWrap on team-season / squad / match-centre / live-match /
   * data-trust / records / lab-wasm; venue's inline overflow-x:auto on teamTbl +
   * ledgerTbl; position-rankings' .card.scroll). Because those ancestors already
   * scroll, dc-mobile.js intentionally SKIPS the .tblScroll wrap for them - so they
   * never inherited the white-space:nowrap + touch-scroll affordance and their cells
   * wrapped to 2-3 ragged lines on a phone instead of staying single-line and sliding.
   * Give every self-wrapper the same treatment so wide tables scroll cleanly app-wide.
   * Numbers / colours / sort handlers are untouched - this only sets overflow + nowrap. */
  .wrap .tableWrap, .wrap .gscroll, .wrap .card.scroll{
    overflow-x:auto; -webkit-overflow-scrolling:touch;
    overscroll-behavior-x:contain; scrollbar-width:thin; max-width:100%;
  }
  .wrap .tableWrap > table, .wrap .gscroll > table, .wrap .card.scroll > table{ white-space:nowrap; }
  /* venue.html wraps two tables in a bare inline <div style="overflow-x:auto"> with no
   * class - match it by the inline style so teamTbl + ledgerTbl scroll cleanly too. */
  .wrap [style*="overflow-x:auto"] > table{ white-space:nowrap; }

  /* ---- common multi-column grids → single column ---- */
  .leaders, .boards{ grid-template-columns:1fr !important; }
  /* .grid2's desktop rule demands a 560px (wideGrid: 740px) MIN column so wide tables
   * never get squeezed into a slider. But on a phone that 560px is WIDER than the screen,
   * so the grid - and the strips/charts/tables inside it - overflowed the viewport and got
   * clipped by the page-level overflow-x:clip: frozen, unscrollable (the "trouble matchup
   * slide nahi hota" bug). On mobile there's only ever one column anyway, so force a single
   * shrink-to-fit track; inner wide tables still scroll in their own .tblScroll box. */
  .grid2, .grid2.wideGrid{ grid-template-columns:minmax(0,1fr) !important; }

  /* ---- universal overflow safety net ----
   * Root cause of leftover page-overflow: a flex/grid item defaults to
   * min-width:auto, so wide inner content (a peer-bar, a nowrap toolbar)
   * forces the whole column - and the page - wider than the screen.
   * Letting those items shrink (min-width:0) means inner tables scroll in
   * their .tblScroll box and everything else reflows to fit. */
  .wrap [class*="grid"] > *, .wrap [class*="Grid"] > *,
  .wrap .pstats, .wrap .pstats > *, .wrap .splitRow > *,
  .wrap .edge > *, .wrap .card > * { min-width:0; }
  .wrap .card, .wrap .panel, .wrap .pstats .bar, .wrap .src { max-width:100%; }
  /* loading skeletons sometimes carry hard-coded px widths (e.g. squad/matchup hero
   * bars at width:430px) that exceed a phone's content box and get clipped/frozen by
   * overflow-x:clip until the API resolves. A skeleton must never be wider than its
   * container - cap them so the pre-load state can't overflow the page. */
  .skel{ max-width:100% !important; }
  /* control / toolbar / tab rows wrap instead of pushing the page sideways */
  .wrap .tctl, .wrap .ctl, .wrap .ctrls, .wrap .controls, .wrap .modeRow,
  .wrap .kickRow, .wrap .lbctl, .wrap .heroControls, .wrap .tabRow,
  .wrap .matchTabs, .wrap .modeTabs, .wrap .seg, .wrap .addRow,
  .wrap .tabs, .wrap .filters{ flex-wrap:wrap; }

  /* ---- inputs / dropdown menus never overflow the viewport ---- */
  input, select, textarea, .cddBtn, .pick input, .cddMenu, .playerMenu, .dclgBtn, .dclgMenu{ max-width:100% !important; }
  /* OPEN LEAGUE DROPDOWN must sit ABOVE the sticky mobile search (#dcMobSearch) and the
   * live band - both z-index:60. At equal z the league menu and the search "panel"
   * stacked ambiguously and visually overlapped (the reported "two overlapping panels").
   * Lift it well clear; mobile-only so the desktop top-right menu is untouched. */
  header.bar .dclgMenu{ z-index:1200 !important; }
  .heroControls{ gap:10px; }
  .pick input{ width:100% !important; }

  /* ---- typographic scale-down for big hero text ---- */
  .htitle{ font-size:2rem !important; }
  .hero{ padding:22px 18px !important; }

  /* ---- scatter / peer-map charts: generous height on mobile so the dots spread
   * (the per-page tightened grid already frees the side-axis numbers). Wins over
   * per-page + inline heights so the plot never looks squished ("chipka"). ---- */
  .wrap #peerChart, .wrap #scatter, .wrap #armap,
  .wrap #batScatter, .wrap #bowlScatter{ height:clamp(440px,66vh,560px) !important; min-height:440px; }

  /* ---- BOTTOM TAB BAR + "More" sheet (mobile primary navigation) ----
   * Fixed to the viewport bottom (appended to <body> by dc-nav.js), always
   * reachable no matter how far the page is scrolled. Built from .topnav links. */
  .dcTabbar{
    position:fixed; left:0; right:0; bottom:0; z-index:200; display:flex;
    background:var(--card, var(--panel, var(--bg, #fff))); border-top:1px solid var(--line);
    padding-bottom:calc(env(safe-area-inset-bottom) + 12px); box-shadow:0 -6px 22px rgba(15,23,42,.12);
  }
  .dcTabbar .dcTab{
    position:relative; flex:1 1 0; min-width:0; border:0; background:transparent; cursor:pointer;
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
    padding:9px 2px 9px; color:var(--muted); text-decoration:none;
    font:800 .62rem Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; letter-spacing:.02em;
    transition:transform .12s ease, color .12s ease;
  }
  /* tap feedback: the tab dips slightly on press and the gold colour fades in */
  .dcTabbar .dcTab:active{ transform:scale(.93); }
  /* live-match red dot on the Live tab (class set by dc-nav.js when dc-has-band/ticker) */
  .dcTabbar .dcTab.dcTabLive::after{ content:""; position:absolute; top:7px; left:calc(50% + 9px);
    width:6px; height:6px; border-radius:50%; background:#dc2626; box-shadow:0 0 0 2px var(--card); }
  .dcTabbar .dcTab svg{ width:21px; height:21px; }
  .dcTabbar .dcTab span{ max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .dcTabbar .dcTab.dcActive{ color:var(--gold); }
  .dcTabbar .dcTab.dcActive::before{ content:""; position:absolute; top:0; left:50%; transform:translateX(-50%); width:30px; height:2px; background:var(--gold); border-radius:0 0 3px 3px; }

  .dcScrim{ position:fixed; inset:0; z-index:210; display:block; background:rgba(8,12,20,.55); opacity:0; pointer-events:none; transition:opacity .2s ease; }
  .dcScrim.dcShow{ opacity:1; pointer-events:auto; }
  .dcSheet{
    position:fixed; left:0; right:0; bottom:0; z-index:220; display:block; background:var(--card, var(--panel, var(--bg, #fff)));
    border-top-left-radius:18px; border-top-right-radius:18px;
    padding:8px 14px calc(18px + env(safe-area-inset-bottom));
    transform:translateY(105%); transition:transform .26s cubic-bezier(.22,.61,.36,1);
    max-height:80vh; overflow:auto; box-shadow:0 -16px 44px rgba(15,23,42,.30);
  }
  .dcSheet.dcShow{ transform:translateY(0); }
  .dcSheet .dcGrab{ width:42px; height:4px; border-radius:99px; background:var(--line); margin:6px auto 12px; }
  .dcSheet h4{ font-size:.64rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); margin:0 4px 10px; }
  .dcSheetGrid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
  .dcSheetGrid .dcTile{ display:flex; align-items:center; padding:13px 13px; border-radius:12px;
    text-decoration:none; color:var(--ink); font-weight:700; font-size:.9rem;
    background:var(--soft); border:1px solid var(--line);
    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .dcSheetGrid .dcTile.dcCur{ color:var(--gold); border-color:#c6920055; background:#c692001a; }
}

/* ---- MATCHUP "advantage"/"trouble" strips: PHONE COMPACTION (table.gtbl) ----
 * The "Matchups, venues & conditions" → Bowler/Batter advantage cards (#advWrap /
 * #trbWrap → table.gtbl) rendered tall on phones: a 26px avatar + roomy row padding
 * + .74rem body / .72rem pills meant only a handful of opponents fit per screen. The
 * user wants the whole opponent list digestible in ~one screen, so on ≤720px we:
 *   • HIDE the round avatar (the name + green pills carry the row; the photo is the
 *     biggest vertical cost and least information per pixel),
 *   • cut row padding + line-height so rows are much shorter,
 *   • shrink the stat pills + body font a notch,
 *   • tighten the name-column cap so 6 metric columns + name fit a ~380px viewport
 *     with no ugly horizontal overflow.
 * Numbers, the green control-bar/heat colours (inline DCScales bg) and pointer/sort
 * behaviour are all untouched. Desktop (>720px) keeps its full styling. */
@media (max-width:720px){
  table.gtbl{ font-size:.64rem; }
  table.gtbl th{ padding:3px 2px; font-size:.5rem; letter-spacing:0; }
  table.gtbl td{ padding:2px 2px; line-height:1.12; }
  table.gtbl td.heat{ padding:1px 1px; }
  table.gtbl td.heat .hpill{ padding:2px 2px; font-size:.6rem; border-radius:5px; }
  /* drop the avatar (biggest vertical cost, lowest info); name + pills carry the row */
  table.gtbl .gnmCell{ gap:0; }
  table.gtbl .gnmCell .av{ display:none; }
  table.gtbl .gnmCell .nmtxt{ max-width:64px; font-size:.66rem; }
  /* Cap the MATCHUP player-name text (.gnmCell .nmtxt) tight so the matchup table's Outs
   * column still fits a ~360px phone. The cell cap below is widened to 150px for the VENUE
   * table, whose cell (.gnm2) holds the ground name + "N matches · NN balls" - at 64px that
   * balls sub-line was being cut off. The matchup cell is already ~68px (its .nmtxt is
   * capped), so 150px never loosens it; only the venue cell uses the extra room. */
  table.gtbl td:first-child{ max-width:150px; }
}

/* A11y: visible keyboard focus ring - many controls set outline:none with no
   replacement, so keyboard users couldn't see focus. :focus-visible shows only
   for keyboard nav (not mouse clicks), so pointer users see no extra noise. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible, [role="button"]:focus-visible,
[role="tab"]:focus-visible, th[onclick]:focus-visible, [onclick]:focus-visible {
  outline: 2px solid var(--gold, #c69200);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---- The league bar's SPACE, reserved before the bar exists ------------------
 * <div id="lgTabsHost"></div> ships EMPTY on all seventeen pages that carry the
 * rail, and dc-leaguetabs.js fills it on DOMContentLoaded. Until then the host is
 * 0px tall, so the whole page below it sat 42px too high and then dropped. Measured
 * 2026-08-15 at 1440x900: a layout shift of 0.029 at t=176ms, sources div.homeGrid
 * dy+47 and section#homeLive dy+46 - the first of the four downward shoves in the
 * founder's "girta hai zor se".
 * The two numbers are the bar's own settled heights, measured at every width from
 * 320 to 1600: 42px, and 50px below dc-leaguetabs' own 720px breakpoint. The filled
 * bar is exactly that tall, so min-height is a no-op once the chips land, and no JS
 * has to clear anything. NOT :empty - dc-leaguetabs runs from <head> and inserts the
 * .dcLgBar > .dcLgRail SHELL immediately, then fills the chips on DOMContentLoaded,
 * so the host stops being :empty long before it stops being 0px tall. A page that
 * opts out sets [hidden] on the host (dc-leaguetabs.js:259), and display:none beats
 * this. */
#lgTabsHost { min-height: 42px; }
@media (max-width: 720px) { #lgTabsHost { min-height: 50px; } }

/* ---- Anti-FLASH: pre-stage the desktop shell layout in SYNCHRONOUS CSS ----
 * dc-shell.js (loaded async) hides the legacy in-page top bar, shifts the content
 * for the fixed rail + top bar, and builds the sidebar. Because it runs a beat
 * AFTER first paint, the OLD horizontal nav used to FLASH on every navigation.
 * Doing the same hide + space-reservation here (render-blocking <link>) means the
 * old bar never paints and the content doesn't jump - dc-shell just fills the rail. */
@media (min-width: 980px) {
  /* Gated on :not(.dc-shell-failed): identical to before when the shell loads (the normal
   * anti-flash pre-stage), but when dc-league.js marks the shell PERMANENTLY failed it
   * releases the hide + indent so the page's own header.bar becomes the fallback chrome
   * instead of a stranded 214px gutter. See dc-league.js settle() + dc-mobile.js loadCore. */
  html:not(.dc-shell-failed) header.bar { display: none !important; }
  /* 52px = dc-shell.js's TOPBAR (dc-shell.js:42). It read 60 here - the pre-slimming
   * height - so the pre-stage reserved 8px more than the real top bar and the page
   * snapped UP 8px when html.dc-shelled landed, the thing this rule exists to prevent.
   * Same stale number as dc-league.js's inline dcPreShellCss; both fixed together. */
  html:not(.dc-shell-failed) body { padding-left: 214px; padding-top: 52px; }
  html:not(.dc-shell-failed) .wrap { margin-left: 0 !important; margin-right: auto !important; padding-left: 16px !important; }
  /* FROZEN TABLE HEADERS (desktop only · option b) · long tables lost their column labels
   * on scroll. Give the long-table scroll boxes a HEIGHT CAP so they scroll INTERNALLY,
   * then pin the header to the box top. A cap (not a fixed height) leaves short tables at
   * their natural height with NO scrollbar - only genuinely long tables get an inner scroll.
   * Scoped to the leaderboard (#lbTbl, always wrapped in .tblScroll by dc-mobile.js) + the
   * page-level .tableWrap data tables (records / team-season / scorecards). The compact
   * dossier .gtbl / .gscroll heat-pill strips are NOT .tblScroll-wrapped, so untouched.
   * >=980px only - phones keep the horizontal .tblScroll behaviour unchanged.
   * NO-SLIDER DOCTRINE (founder, site-wide): the 78vh height-cap was killed per-table
   * (#71/#72 exceptions below) and is now GLOBAL - NO desktop data table gets an inner
   * vertical slider; long tables run full height and the PAGE scrolls. */
  html.dc-shelled .tblScroll, .wrap .tableWrap{ max-height:none; }
  /* …EXCEPT the live-match / match-centre scorecards (#scorecards): those are short, fixed
   * innings - an inner vertical scroll there is just annoying, so let them run FULL height
   * and the PAGE scrolls instead (no inner slider). id-specificity beats .wrap .tableWrap. */
  #scorecards .tableWrap{ max-height:none; }
  /* …and the dossier "Top partnerships" card (#71): a Top-20 list sits just over 78vh, so
   * the cap produced an inner slider for exactly one screenful - pointless. Full height,
   * the page scrolls. (wrapTables() DOES wrap dossier gtbl tables - the note above about
   * .gtbl being unwrapped predates it.) */
  #partnerCard .tblScroll{ max-height:none; }
  /* …and the Match Centre "Top partnerships" panel (#72): same story as #71 - a season's
   * Top-20 list sits just over 78vh, so the cap made an inner slider for one screenful.
   * Full height; the page scrolls and the #61 season list re-fits beside it. */
  #topPartnerships .tableWrap{ max-height:none; }
  #scorecards .tableWrap thead th, #topPartnerships .tableWrap thead th{ position:static; box-shadow:none; }
  #lbTbl thead th, .tableWrap thead th{
    position:static;   /* no inner scroll box to pin to now - page scrolls, header rides along */
    background:var(--card, var(--panel, var(--bg, #fff))); box-shadow:0 1px 0 var(--line);
  }
  /* #79 - compact the DESKTOP leaderboard rows (were too tall/spacious, ~45px): tighter
   * cell padding + smaller avatar + heat-pills → ~32px rows, uniform font, no name-wrap.
   * Scoped to #lbTbl (Batting/Bowling/All-rounder tabs all render into it); other tables
   * untouched. Phones keep their own compact treatment (this is >=980px only). */
  #lbTbl td{ padding:4px 5px; font-size:.8rem; }
  #lbTbl .av{ width:23px; height:23px; font-size:.6rem; }
  #lbTbl .hpill{ padding:2px 5px; font-size:.76rem; }
}

/* ============================================================================
 * DARK-MODE VISUAL FIXES (app-wide, linked-last overrides).
 * ==========================================================================*/
/* (1) CARD DEPTH - per-page .card shadow (rgba(15,23,42,.05)) is invisible on the
 *     dark canvas, so cards read flat + hover does nothing. Theme-aware tokens. */
html{
  --shadow:0 1px 2px rgba(15,23,42,.04), 0 10px 30px rgba(15,23,42,.05);
  --shadow-lift:0 10px 30px rgba(15,23,42,.12);
}
html[data-theme="dark"]{
  --shadow:0 1px 0 rgba(255,255,255,.04) inset, 0 12px 34px rgba(0,0,0,.5);
  --shadow-lift:0 1px 0 rgba(255,255,255,.06) inset, 0 18px 44px rgba(0,0,0,.62);
}
.card{ box-shadow:0 0 0 1px var(--line), var(--shadow) !important; }
.lift:hover, .card.lift:hover{ box-shadow:0 0 0 1px var(--line), var(--shadow-lift) !important; }

/* (2) HERO IDENTITY CHIPS - hardcoded dark inks (#8a6500 gold, #1e40af blue) go
 *     muddy on dark cards; route to the theme-aware brand gold + a blue-ink token. */
html{ --chip-blue-ink:#1e40af; }
html[data-theme="dark"]{ --chip-blue-ink:#93c5fd; }
.chip, .chip20{ color:var(--gold) !important; }

/* (2.5) BASE SKELETON - ONE canonical .skel placeholder + its `sh` sweep keyframes for
 *       every page (was ~19 near-identical inline copies, radius drifting 10-16px). This
 *       file is linked AFTER each page's inline <style>, so this base governs; pages no
 *       longer ship their own copy. The gold re-skin in (3) below layers on top. */
@keyframes sh { to { background-position:-200% 0; } }
.skel{ background:linear-gradient(90deg,var(--track) 25%,var(--soft) 50%,var(--track) 75%); background-size:200% 100%; animation:sh 1.1s infinite; border-radius:12px; }

/* (3) BRANDED LOADING SHIMMER - a soft gold sweep across every .skel placeholder,
 *     site-wide (overrides the base grey gradient above). Reuses the `sh` keyframes +
 *     background-size on .skel; we only re-skin the gradient so the pre-load state
 *     feels alive + on-brand, not mechanical. */
.skel{ background:linear-gradient(90deg, var(--track) 0%, var(--track) 36%, rgba(198,146,0,.20) 50%, var(--track) 64%, var(--track) 100%) !important; background-size:200% 100% !important; }

/* (3b) SKELETON KIT - shared app-shell placeholder shapes so every page paints a
 *      layout-true skeleton on FIRST parse (zero network, zero JS) instead of a blank
 *      screen or a bare "Loading" line. Composable building blocks on top of .skel:
 *        .skelCard  - a card-shaped container (mirrors .card geometry)
 *        .skelHero  - avatar disc + text lines (dossier / venue hero)
 *        .skelKpis  - the KPI tile row (6 tiles desktop, 2-col grid on phones)
 *        .skelPills - a row of filter/tab pills
 *        .skelRows  - stacked table-row bars (leaderboards / scorecards)
 *      Fallback values keep these theme-correct even before a page's own tokens load. */
.skelCard{ background:var(--card,#1d2e4d); border:1px solid var(--line,#304066); border-radius:18px; padding:22px; }
html[data-theme="light"] .skelCard{ background:var(--card,#ffffff); border-color:var(--line,#e8edf5); }
.skelHero{ display:flex; gap:22px; align-items:center; }
.skelHero .skelDisc{ width:96px; height:96px; border-radius:50%; flex:0 0 auto; }
.skelHero .skelLines{ flex:1; min-width:0; }
.skelKpis{ display:flex; gap:12px; margin-top:22px; }
.skelKpis .skel{ height:92px; flex:1; }
.skelKpis.dcProfileKpis{ display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); }
.skelKpis.dcProfileKpis .dcSkelWide{ grid-column:span 2; }
.skelPills{ display:flex; gap:8px; flex-wrap:wrap; }
.skelPills .skel{ height:34px; width:96px; border-radius:999px; }
.skelRows .skel{ height:30px; margin:7px 0; }
/* NoJump (17 Aug 2026): dc-skelwatch.js marks a shimmer that outlived 12s .dcSkelStuck and gives it a
 * 1px dashed border; content-box, that made every stuck row 2px taller and the whole leaderboard
 * skeleton grew 42px in place on a cold Space (staging, CLS 0.116). The border draws inside now. */
.skel.dcSkelStuck{ box-sizing:border-box; }
.skelRows.dcLbRows .skel{ height:38px; margin:6px 0; }
@media (max-width:720px){
  .skelHero .skelDisc{ width:64px; height:64px; }
  .skelKpis{ display:grid; grid-template-columns:1fr 1fr; gap:6px; margin-top:16px; }
  .skelKpis .skel{ height:76px; }
  .skelKpis.dcProfileKpis{ grid-template-columns:1fr 1fr; }
  .skelRows.dcLbRows .skel{ height:44px; margin:5px 0; }
}
.chip.blue{ color:var(--chip-blue-ink) !important; }

/* (4) DEFAULT AVATAR - a neutral person silhouette for any photo-less player avatar.
 *     Site-wide, `.noimg` is added to an avatar whose photo is missing/failed. Interim
 *     (no image-gen): an inline-SVG silhouette on a soft gradient, replacing the bare
 *     initials, so every default avatar reads as a real avatar. This file loads after
 *     each page's inline styles, so it governs. (Generated per-player/venue avatars = later.) */
.noimg{ position:relative; overflow:hidden; background:linear-gradient(180deg,var(--track),var(--soft)) !important; }
.noimg i{ display:none !important; }
.noimg::after{ content:""; position:absolute; inset:0; opacity:.5;
  background:center 60% / 66% no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2394a3b8'%3E%3Cpath d='M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.4 0-8 2.2-8 5v1h16v-1c0-2.8-3.6-5-8-5Z'/%3E%3C/svg%3E"); }

/* (4b) INSTANT avatar placeholder · the silhouette sits BEHIND every .av image, so a
 *      photo-less avatar shows the silhouette immediately instead of waiting for the
 *      image request to 404 and only then flipping to .noimg (the "avatar opens slowly"
 *      report). A real photo (stacked above) simply covers it the moment it loads; on a
 *      404 the img removes itself and the silhouette is already there. One CSS rule fixes
 *      every page's avatar helper - no per-helper JS change. */
.av, .dcRailAv, .potdAvBig { position:relative; }
.av::before, .dcRailAv::before, .potdAvBig::before{ content:""; position:absolute; inset:0; z-index:0; border-radius:inherit; opacity:.5;
  background:center 60% / 66% no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2394a3b8'%3E%3Cpath d='M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.4 0-8 2.2-8 5v1h16v-1c0-2.8-3.6-5-8-5Z'/%3E%3C/svg%3E")
    ,linear-gradient(180deg,var(--track),var(--soft)); }
.av > img, .av > i, .dcRailAv > img, .dcRailAv > i, .potdAvBig > img, .potdAvBig > i { position:relative; z-index:1; }

/* (5) TEAM CREST - coloured monogram badge (DCTeam.badge()); no licensed logos, so the
 *     team's short code on its brand colour stands in as a consistent crest everywhere. */
.dcCrest{ display:inline-flex; align-items:center; justify-content:center; width:34px; height:34px;
  border-radius:50%; font:800 .72rem/1 Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; letter-spacing:.01em; flex:0 0 auto;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22), 0 1px 3px rgba(15,23,42,.18); }

/* (7) #66 · Impact 100 hero badge (dossiers) - DeepCover's proprietary 0-100 rating,
 *     links to the Impact-ranked league table landing on the player's row. */
.impactBadge a{ display:inline-flex; align-items:center; gap:8px; margin-top:10px; padding:7px 14px;
  border-radius:999px; text-decoration:none; border:1px solid #c6920055; background:#c692001a;
  color:inherit; font-weight:800; }
.impactBadge a:hover{ background:#c6920030; }
.impactBadge b{ font-size:1.15rem; line-height:1; color:var(--gold,#c69200); font-family:"Spline Sans Mono",monospace; }
.impactBadge span{ font-size:.64rem; letter-spacing:.07em; text-transform:uppercase; color:var(--muted,#7c8aa0); font-weight:800; }
.impactBadge.hidden{ display:none; }
/* #66 P2 · bat/bowl component bars under the badge (league-normalised 0-100, no formula) */
.impactBadge .impBars{ display:flex; flex-direction:column; gap:3px; margin-top:7px; }
.impactBadge .impBar{ display:flex; align-items:center; gap:6px; font-size:.66rem; }
.impactBadge .impBar i{ font-style:normal; width:16px; text-align:center; }
.impactBadge .impBar u{ flex:0 0 96px; height:5px; background:var(--track,#22304a); border-radius:999px; overflow:hidden; text-decoration:none; display:block; }
.impactBadge .impBar u b{ display:block; height:100%; background:linear-gradient(90deg,#c69200,#e0b13a); border-radius:999px; }
.impactBadge .impBar em{ font-style:normal; font-weight:800; font-family:"Spline Sans Mono",monospace; color:var(--muted,#7c8aa0); }

/* (7c) DELIVERY-LEVEL band · a slim divider that introduces the ball-tracked deep-analytics
 *      as DeepCover's every-delivery deep dive, kept visually distinct from the all-time
 *      career headline above it. The era-chip states the ball-by-ball SPAN as a confident
 *      FACT for cross-era legends (e.g. Muralitharan) so their partial matchup/phase counts
 *      read as the ball-by-ball era, NOT a career undercount - the headline stays the full
 *      match record. Reuses each page's own .kick typography for the title; NO defensive
 *      "since 2002 / partial / below official" language anywhere. */
.dcCovBand{ display:flex; align-items:center; gap:10px 16px; flex-wrap:wrap; margin:26px 0 2px; padding-top:16px; border-top:1px solid var(--line); }
.dcCovBand .kick{ margin:0 auto 0 0; }   /* title hugs the left; the era-chip pushes right */
.dcCovBand .eraChip{ display:inline-flex; align-items:center; gap:6px; border:1px solid var(--gold,#c69200); color:var(--gold,#c69200);
  font:800 .6rem/1 Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; letter-spacing:.12em; text-transform:uppercase; padding:5px 11px; border-radius:999px; white-space:nowrap; }
.dcCovBand .eraChip[hidden]{ display:none; }
.dcCovBand .cbCap{ flex-basis:100%; font:600 .76rem/1.5 Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; color:var(--muted); margin:2px 0 0; }
.dcCovBand .cbCap[hidden]{ display:none; }
.dcCovBand.hidden{ display:none; }

/* (7c-2) COVERAGE NOTE - a small, calm disclosure placed directly under the hero/KPI strip
 * (profile-level, near the top) for cross-era legends (e.g. Tendulkar, Kumble, Ganguly) whose
 * ball-by-ball warehouse covers only part of the full career. Purely descriptive of
 * d.coverage - no numbers change; the career headline in the hero above stays complete and
 * official. Full-coverage players (frac >= .85 or no coverage overlay) render nothing here. */
.dcCoverNote{ display:flex; align-items:flex-start; gap:8px; margin:10px 0 0; padding:9px 14px;
  border:1px solid var(--line); border-radius:10px; background:var(--soft); }
.dcCoverNote .dcCoverMark{ flex:0 0 auto; font:800 .66rem/1 Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; color:var(--muted);
  border:1px solid var(--line); border-radius:50%; width:15px; height:15px; display:flex;
  align-items:center; justify-content:center; margin-top:2px; }
.dcCoverNote p{ margin:0; font:600 .78rem/1.5 Archivo,Manrope-FB,Manrope-FB-R,Manrope-FB-A,sans-serif; color:var(--muted); }
.dcCoverNote.hidden{ display:none; }

/* (6) A11Y - honour the OS "reduce motion" setting site-wide. Users who set it (vestibular
 *     disorders, motion sensitivity) get near-instant transitions instead of animated ones.
 *     Kept last so it wins the cascade; near-zero (not 0) durations so JS transitionend still
 *     fires and skeletons/charts that gate on it don't stall. Complements the view-transition
 *     rule above (line ~42). Audit a11y follow-up 2026-07-02. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* #37 · dossier hero avatar FALLBACK (founder pick: gold action silhouette).
   Only used when a player has NO avatar photo - real ghibli avatars are kept
   untouched. Applied by each dossier's JS via .faceWrap.dcSil. Broadcast-dark
   disc + a brand-gold cover-drive batsman, so a photo-less hero never looks
   empty (and never like the old helmet-grille art the founder flagged). */
.faceWrap.dcSil{
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg fill='%23f0c452'%3E%3Ccircle cx='46' cy='24' r='8'/%3E%3Cpath d='M44 31 Q40 33 39 42 L37 60 Q37 63 40 63 L52 63 Q56 63 56 58 L54 40 Q53 33 48 31 Z'/%3E%3Cpath d='M40 61 Q36 70 34 82 Q33 86 37 87 Q40 87 41 83 L46 66 Z'/%3E%3Cpath d='M50 61 L60 78 Q62 82 66 80 Q69 78 66 74 L55 60 Z'/%3E%3Cpath d='M52 36 Q60 34 66 30 L64 26 Q58 29 51 31 Z'/%3E%3Cpath d='M42 38 Q50 44 60 52 L63 49 Q54 40 45 35 Z'/%3E%3Cpath d='M60 30 L78 10 Q80 8 82 11 L84 15 Q85 17 82 19 L64 37 Z'/%3E%3C/g%3E%3C/svg%3E") center 84%/76% no-repeat,
    radial-gradient(circle at 50% 22%, #1c2a48, #0c1424 72%) !important;
}
.faceWrap.dcSil .face{ display:none !important; }

/* #37 · hero BAND avatar fallback (the band is the real hero avatar; faceWrap is
   hidden when the band is on). No cutout photo -> a subtle ink-coloured batsman
   silhouette (mask + currentColor, so it contrasts on any team-colour band). */
.dcBandSil{ position:absolute; left:14px; bottom:0; height:calc(100% - 10px); width:200px; pointer-events:none;
  background:currentColor; opacity:.4;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg fill='%23000'%3E%3Ccircle cx='46' cy='24' r='8'/%3E%3Cpath d='M44 31 Q40 33 39 42 L37 60 Q37 63 40 63 L52 63 Q56 63 56 58 L54 40 Q53 33 48 31 Z'/%3E%3Cpath d='M40 61 Q36 70 34 82 Q33 86 37 87 Q40 87 41 83 L46 66 Z'/%3E%3Cpath d='M50 61 L60 78 Q62 82 66 80 Q69 78 66 74 L55 60 Z'/%3E%3Cpath d='M52 36 Q60 34 66 30 L64 26 Q58 29 51 31 Z'/%3E%3Cpath d='M42 38 Q50 44 60 52 L63 49 Q54 40 45 35 Z'/%3E%3Cpath d='M60 30 L78 10 Q80 8 82 11 L84 15 Q85 17 82 19 L64 37 Z'/%3E%3C/g%3E%3C/svg%3E") center bottom/86% no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg fill='%23000'%3E%3Ccircle cx='46' cy='24' r='8'/%3E%3Cpath d='M44 31 Q40 33 39 42 L37 60 Q37 63 40 63 L52 63 Q56 63 56 58 L54 40 Q53 33 48 31 Z'/%3E%3Cpath d='M40 61 Q36 70 34 82 Q33 86 37 87 Q40 87 41 83 L46 66 Z'/%3E%3Cpath d='M50 61 L60 78 Q62 82 66 80 Q69 78 66 74 L55 60 Z'/%3E%3Cpath d='M52 36 Q60 34 66 30 L64 26 Q58 29 51 31 Z'/%3E%3Cpath d='M42 38 Q50 44 60 52 L63 49 Q54 40 45 35 Z'/%3E%3Cpath d='M60 30 L78 10 Q80 8 82 11 L84 15 Q85 17 82 19 L64 37 Z'/%3E%3C/g%3E%3C/svg%3E") center bottom/86% no-repeat;
}
@media (max-width:720px){ .dcBandSil{ left:2px; width:39%; height:calc(100% - 6px); } }

/* ============================================================================
 * MOBILE GOLD HERO RING (founder: "the golden border on mobile is not shining").
 * Every hero/feature card wears a 1.5px var(--gold) hairline ring via a per-page
 * inline rule (.card.hero{...!important} on ~17 pages, #page .card.heroRing on the
 * rankings pages). On small high-DPI phone screens that hairline reads near
 * invisible. At phone widths, thicken the ring to 2px and add a soft gold outer
 * glow so it reads as a crisp premium frame; desktop widths are untouched.
 * Theme-aware: the glow tracks each theme's gold (deep #9b6000 in light, bright
 * #c69200 in dark); the depth layer reuses the theme --shadow token above.
 * Cascade: this file is linked LAST, so at >= the per-page specificity these
 * !important rules win (the hover variants out-specify .card.lift:hover too). */
html{ --gold-glow:rgba(198,146,0,.40); }
html[data-theme="light"]{ --gold-glow:rgba(155,96,0,.28); }
@media (max-width:720px){
  .card.hero, .card.hero:hover,
  .heroRing, .heroRing:hover,
  #page .card.heroRing, #page .card.heroRing:hover{
    box-shadow:0 0 0 2px var(--gold,#c69200),
      0 0 12px 1px var(--gold-glow,rgba(198,146,0,.40)),
      var(--shadow,0 10px 30px rgba(15,23,42,.07)) !important;
  }
}

/* --------------------------------------------------------------------------
 * 8610 motion foundation · one restrained language for every page.
 * Motion communicates navigation, arrival, sorting and live change. It never
 * hides content, never loops, and never owns layout.
 * -------------------------------------------------------------------------- */
:root{
  --dc-motion-instant:90ms;
  --dc-motion-fast:160ms;
  --dc-motion-base:240ms;
  --dc-motion-slow:320ms;
  --dc-ease-out:cubic-bezier(.2,.8,.2,1);
  --dc-ease-standard:cubic-bezier(.4,0,.2,1);
}

/* Existing per-page `.rise` classes used a tall 14px/500ms entrance. Keep the
 * editorial lift, but make it feel immediate and consistent. */
.rise{
  animation-duration:var(--dc-motion-slow) !important;
  animation-timing-function:var(--dc-ease-out) !important;
}
@keyframes dcRise{
  from{ opacity:.72; transform:translate3d(0,8px,0) }
  to{ opacity:1; transform:translate3d(0,0,0) }
}

/* Navigation uses the old-frame-preserving view transition above. The legacy
 * route bar stays disabled even if an older cached script tries to create it. */
#dcRouteProgress{ display:none !important; }

/* Desktop cross-document transition: stable chrome, opacity only. */
@keyframes dcPageOld{
  to{ opacity:.94 }
}
@keyframes dcPageNew{
  from{ opacity:.90 }
  to{ opacity:1 }
}
/* NoJump Phase 2: root no longer animates (see the dc-main rules near the top); dcPageOld / dcPageNew
   are kept only so an old inline reference cannot break. */
::view-transition-old(root){ animation:none; }
::view-transition-new(root){ animation:none; }

/* Keyboard focus gets a fast, stable signal on every interactive control. */
:where(a,button,input,select,textarea,[role="button"]):focus-visible{
  outline:2px solid var(--gold,#c69200);
  outline-offset:3px;
}

@media (prefers-reduced-motion:reduce){
  #dcRouteProgress{ display:none !important }
  ::view-transition-old(root),::view-transition-new(root){ animation:none !important }
}


/* ---- PAGE SCROLLBAR, site-wide (founder 2026-07-27: "scrollbar patla sundar bana do") ----
 * Same family as the .dcSide rail bar in dc-shell.js: quiet --line thumb on a
 * transparent track, --muted on hover, one gold moment only while actively dragging.
 * The bar keeps the browser's native 15px gutter so the page's usable width does not
 * change by a single pixel; the THUMB is what gets thin (a 4px transparent border +
 * background-clip leaves a 7px rounded pill floating in the gutter). Firefox cannot
 * do the inset-pill detail, so it deliberately gets its native-width bar with the
 * same theme colours via scrollbar-color (its "thin" would shrink the gutter and
 * change layout width, which we refuse). Scoped to fine pointers: touch devices keep
 * their OS-drawn overlay scrollbars untouched. Applies to EVERY scroll container, so
 * sideways table bars join the same family; .dcSide keeps its own tighter 9px twin. */
@media (hover:hover) and (pointer:fine){
  html{ scrollbar-color: var(--line,#cdd6e4) transparent; }
  ::-webkit-scrollbar{ width:15px; height:15px; }
  ::-webkit-scrollbar-track{ background:transparent; }
  ::-webkit-scrollbar-corner{ background:transparent; }
  ::-webkit-scrollbar-thumb{
    background-color:var(--line,#cdd6e4);
    border-radius:999px;
    border:4px solid transparent;
    background-clip:padding-box;
    min-height:44px; min-width:44px;
  }
  ::-webkit-scrollbar-thumb:hover{ background-color:var(--muted,#94a3b8); }
  ::-webkit-scrollbar-thumb:active{ background-color:var(--gold,#c69200); }
}

/* ============================================================================
 * DARK MODE: the theme toggle must be VISIBLE (founder 2026-08-02: "dark mode
 * me dikhte hi nahi"). It was --card (#141d2e) on --bg (#0a1018) with a --line
 * (#26324a) rim: three near-blacks stacked, so the control disappeared. Lift
 * the surface, brighten the rim, and paint the sun/moon glyph in the brand gold
 * so the eye finds it instantly. Scoped to the toggle only - every other dark
 * surface keeps its calm. Applies at EVERY width (no media query) and to both
 * the header button and the desktop shell's side-row copy (same #themeBtn id).
 * ========================================================================== */
[data-theme="dark"] #themeBtn,
[data-theme="dark"] .themeBtn {
  background: #22304a;
  border-color: #46587c;
  color: #f2c422;
}
[data-theme="dark"] #themeBtn:hover,
[data-theme="dark"] .themeBtn:hover {
  background: #2c3c5c;
  border-color: #f2c422;
}
[data-theme="dark"] #themeBtn svg,
[data-theme="dark"] .themeBtn svg { stroke: currentColor; color: #f2c422; }

/* ============================================================================
 * THE MATCH STATUS CHIP - ONE definition for the whole site.
 * Founder 2026-08-02: "live, upcoming, already played - ye teenon har jagah
 * poore website pe ek hi tarah ka hona chahiye." Home board, live page,
 * Match Centre finder and the schedule each grew their own chip (.mTag on one
 * page, .lvTag on another, a bare word on a third), so the same match state
 * looked different depending on where you met it. The markup stays where it is;
 * the LOOK now comes from here, so no page can drift again.
 *   LIVE     = red, with the pulsing dot the pages already emit
 *   UPCOMING = blue, the site's "future" colour
 *   PLAYED   = green, the site's "settled" colour
 * ========================================================================== */
.mTag, .lvTag, .dcStatus, .mUp {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .58rem; font-weight: 900; letter-spacing: .12em; text-transform: uppercase;
  line-height: 1; white-space: nowrap; border-radius: 999px; padding: 4px 9px;
  border: 1px solid currentColor; background: color-mix(in srgb, currentColor 12%, transparent);
}
/* live is the default state these two classes carry */
.mTag, .lvTag { color: var(--red, #dc2626); }
/* played */
.mCard.fin .mTag, .lvHero.fin .lvTag, .dcStatus.played, .mTag.played { color: var(--green, #16a34a); }
/* upcoming - the schedule/home "Upcoming" word becomes the same chip */
.mCard.fx .mTag, .mUp, .dcStatus.upcoming, .mTag.fxUp { color: #60a5fa; }
.mUp { border-color: currentColor; }
/* the pulsing dot rides inside the chip, sized to it */
.mTag .livedot, .lvTag .livedot { width: 6px; height: 6px; flex: 0 0 auto; }

/* ==========================================================================
 * What Enter will do, shown in the search box.
 * Founder 2026-08-04: "wo dikhna chahiye ki Ask selected hai, nahi to team player
 * ground ya league select rahe". Gold when the selection is Ask, quiet otherwise,
 * so the reader can tell a question from a lookup without pressing anything.
 * ========================================================================== */
.dcSearchMode {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  pointer-events: none; z-index: 3;
  font-size: .58rem; font-weight: 900; letter-spacing: .1em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px; white-space: nowrap; line-height: 1;
  color: var(--dcMuted, #64748b);
  background: var(--dcTrack, #eef2f7);
  border: 1px solid var(--dcLine, #e8edf5);
}
.dcSearchMode.isAsk {
  color: var(--gold, #c69200);
  background: color-mix(in srgb, var(--gold, #c69200) 14%, transparent);
  border-color: color-mix(in srgb, var(--gold, #c69200) 42%, transparent);
}
@media (max-width: 640px) { .dcSearchMode { display: none; } }

/* the CTA pill (Deep replay / Match room / Full schedule) is one shape too */
.mPill, .lvCta, .fReplay {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .64rem; font-weight: 800; letter-spacing: .01em;
  padding: 6px 12px; border-radius: 999px; white-space: nowrap; line-height: 1;
}

/* ==========================================================================
 * THE FINISHED-MATCH CARD, once.
 *
 * Founder, 2026-08-04, on a team-page strip that had grown its own card:
 * "ye chip achha hai par hamare existing played match ke chip jaisa nahi kyu???
 *  har jagah ek hi rahna chaiye". He is right, and the fix is not to copy the
 * markup a fourth time. The Match Centre's .mlCard2 is the card the site already
 * ships for a match that has been played; its LOOK moves here, where every page
 * already loads it, so a page only has to emit the class names.
 *
 * The four semantic tokens the card needs were defined on matchcentre.html alone,
 * so any other page borrowing the card fell back to nothing. They live here now,
 * with the values matchcentre already used, in both themes.
 * ========================================================================== */
:root { --green:#16a34a; --red:#dc2626; --blue:#2563eb; --gold2:#8a6500; }
html[data-theme="dark"] { --green:#22c55e; --red:#f87171; --blue:#60a5fa; --gold2:#f6c750; }

.mlCard2 {
  display: flex; flex-direction: column; text-align: left; width: 100%;
  padding: 11px 12px 12px; border-radius: 14px; color: var(--ink); cursor: pointer;
  font: inherit; position: relative; overflow: hidden;
  background: radial-gradient(circle at 92% 0%, color-mix(in srgb, var(--green) 9%, transparent), transparent 20rem), var(--card);
  border: 1px solid color-mix(in srgb, var(--gold) 34%, var(--line));
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
  text-decoration: none;
}
.mlCard2:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--gold) 64%, var(--line)); box-shadow: 0 10px 22px rgba(2,6,23,.18); }
.mlCard2::before {
  content: ""; position: absolute; inset: 0; opacity: .5; pointer-events: none;
  background-image: linear-gradient(color-mix(in srgb, var(--gold) 12%, transparent) 1px, transparent 1px),
                    linear-gradient(90deg, color-mix(in srgb, var(--gold) 8%, transparent) 1px, transparent 1px);
  background-size: 38px 38px;
  -webkit-mask-image: linear-gradient(115deg, rgba(0,0,0,.5), transparent 60%);
          mask-image: linear-gradient(115deg, rgba(0,0,0,.5), transparent 60%);
}
.mlCard2 > * { position: relative; }
.mlCard2 .fTop { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.mlCard2 .fComp { color: var(--muted); font-size: .63rem; font-weight: 700; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mlCard2 .fTms { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center; margin-top: 8px; }
.mlCard2 .fTCol { display: block; min-width: 0; }
.mlCard2 .fTCol.right { text-align: right; }
.mlCard2 .fTNm { display: flex; align-items: center; gap: 7px; min-width: 0; font-size: .82rem; font-weight: 700; color: var(--ink); }
.mlCard2 .fTNm .nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mlCard2 .fTNm .dcCrest { flex: 0 0 auto; }
.mlCard2 .fTCol.right .fTNm { justify-content: flex-end; }
.mlCard2 .fTCol.won .fTNm { font-weight: 900; }
.mlCard2 .fSc { display: block; font-size: 1.06rem; font-weight: 800; letter-spacing: .01em; line-height: 1.15; margin-top: 2px; color: var(--ink); font-variant-numeric: tabular-nums lining-nums; }
.mlCard2 .fSc small { font-size: .7rem; color: var(--muted); font-weight: 700; }
.mlCard2 .fTCol.won .fSc { color: var(--gold2); }
.mlCard2 .fVs { color: var(--muted); font-weight: 800; font-size: .62rem; }
.mlCard2 .fVerdict {
  display: block; margin-top: 9px; padding: 6px 11px; border-radius: 10px; text-align: center;
  font-size: .77rem; font-weight: 700; line-height: 1.35; color: var(--ink);
  background: color-mix(in srgb, var(--green) 8%, var(--card));
  border: 1px solid color-mix(in srgb, var(--green) 28%, var(--line));
}
.mlCard2 .fVerdict b { color: var(--green); font-weight: 900; }
.mlCard2 .fVerdict.plain { background: var(--soft); border-color: var(--line); color: var(--muted); }
.mlCard2 .fVen { display: block; max-width: 100%; color: var(--muted); font-size: .63rem; font-weight: 600; margin-top: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mlCard2 .fFoot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: auto; padding-top: 10px; }
.mlCard2 .fReplay { background: linear-gradient(135deg, #f5cd60, #c78f06); color: #191104; }
.mlCard2 .fReplay.soft { background: var(--track); color: var(--ink); border: 1px solid var(--line); }
.mlCard2:hover .fReplay { filter: brightness(1.05); }
.mlCard2 .fDate { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: .72rem; font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums lining-nums; }
