/* --- the shell typeface, self-hosted --------------------------------------
   Loaded from THIS origin, not fonts.googleapis.com. The two free schedule
   sites ship a strict Content-Security-Policy (default-src 'self'), and the
   browser refused the Google Fonts stylesheet outright -- "Loading the
   stylesheet ... violates the following Content Security Policy". Rewriting
   their CSP to allow a third party would trade real security for a typeface.

   Serving it from the router's own origin satisfies 'self', removes a
   third-party DNS + TLS round trip, and works identically on all 24
   properties. Archivo is a VARIABLE font, so one 34KB file covers the whole
   500-700 range the shell uses -- the three "weights" Google serves are the
   same bytes, verified by hash. */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 700;          /* variable range */
  font-display: swap;            /* paint immediately in the fallback */
  src: url("/fonts/archivo.woff2") format("woff2");
}

/* ============================================================================
 * The Sports King — shell.css
 * The three-bar chrome: slate strip / global bar / sport nav.
 *
 * ONE copy. Served from the root project and loaded by every sport prefix.
 * This file exists specifically so the fleet never again grows 19 divergent
 * copies of its own navigation. If you find yourself copying it, stop.
 *
 * Token vocabulary is deliberately the SAME as the fleet's theme.css
 * (--bg, --panel, --border, --text, --muted, --accent) so a sport page and the
 * shell resolve against one palette rather than two.
 * ==========================================================================*/

#tsk-shell {
  --bg:        #080a0e;
  --bg-2:      #0d0f14;
  --panel:     #14161d;
  --panel-2:   #1b1e27;
  --border:    #232733;
  --border-2:  #313644;
  --text:      #f4f5f8;
  --text-2:    #cbcfd9;
  --muted:     #9aa0ad;
  --muted-2:   #8a919e;   /* was #6f7682: 4.19:1 on the slate, 3.64:1 in search results */
  --pos:       #5fd2a0;
  --neg:       #ff7d8c;
  --brand:     #F5C542;
  --accent:    var(--sport-optic, var(--brand));
  --bar-h:     46px;
  --font-ui:   "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-num:  ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

#tsk-shell, #tsk-shell *, #tsk-shell *::before, #tsk-shell *::after { box-sizing: border-box; }

/* display:contents removes #tsk-shell from the box tree, so the sticky bars
   inside it resolve their containing block against the VIEWPORT instead of a
   148px-tall wrapper. Without this the global bar could travel 44px and then
   scrolled away with the page -- measured top:-798 at scrollY:900, i.e. no
   navigation at all on any page once the reader scrolled. Custom properties
   still inherit through a display:contents element, so the token block above
   keeps working. */
/* display:contents removes the box, so the SPACE for the fixed global bar is
   reserved on <body> instead (see html.tsk-shelled body padding-top). The
   scroll-away bars still need their height reserved before shell.js runs, or
   the whole page jumps down when they appear -- measured +91px at t=2.3-8.4s,
   a deterministic +0.168 mobile CLS on every page. shell.css is
   render-blocking, so this lands before first paint. */
#tsk-shell { display: contents; }
html.tsk-shelled body { min-height: 100vh; }
html.tsk-shelled #tsk-shell-space { display: block; height: 94px; }
@media (max-width: 900px) { html.tsk-shelled #tsk-shell-space { height: 92px; } }

/* Deliberately NOT styling <body>. The shell is injected into 24 sites that
   each own their own page styling; a body rule here would silently restyle
   them. The landing page carries its own body rule instead. */
#tsk-shell {
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
  color: var(--text);
}

/* Origin chrome suppression. The sites keep their own headers in the DOM --
   removing them would break the 256 hardcoded a[href="/app/..."] selectors and
   the scripts that query .topbar -- so they are hidden, not deleted. Hidden is
   also correct for screen readers, which skip display:none rather than
   announcing two navigations. */
html.tsk-shelled header.topbar,
html.tsk-shelled header.hrk-header,
html.tsk-shelled #hrk-mobile-drawer,
html.tsk-shelled .topbar-scrim,
html.tsk-shelled nav#sitenav,
html.tsk-shelled header.site-header,
html.tsk-shelled header.lp-head,
html.tsk-shelled nav.drawer-nav { display: none !important; }

/* --- shared ---------------------------------------------------------------*/
.tsk-inner {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 16px;
  display: flex;
  align-items: center;
  height: 100%;
  gap: 4px;
}

/* =========================================================================
   BAR 1 — the slate strip
   ESPN puts scores here. This product is a dated card, not a news site, so
   this strip answers "which sports have a card today, and how big is it".
   It must never claim a sport is quiet when the truth is that no feed arrived.
   ========================================================================= */
.tsk-slate {
  position: relative; z-index: 2147481800;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  height: 58px;
  font-size: 13px;
}
.tsk-slate .tsk-inner { gap: 10px; }

.tsk-slate-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--muted-2);
  white-space: nowrap;
  padding-right: 12px;
  border-right: 1px solid var(--border);
  margin-right: 4px;
}

.tsk-slate-rail {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1;
  align-items: center;
  height: 100%;
}
.tsk-slate-rail::-webkit-scrollbar { display: none; }

.tsk-slate-card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 104px;
  padding: 5px 12px 5px 10px;
  border-right: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
.tsk-slate-card:hover .tsk-slate-name { color: var(--brand); }
.tsk-slate-name {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  display: flex;
  align-items: center;
  gap: 5px;
}
.tsk-slate-meta {
  font-size: 11px;
  color: var(--muted-2);
  font-family: var(--font-num);
}

/* The honest empty state. Not a blank strip — a sentence that says why. */
.tsk-slate-unknown {
  font-size: 12px;
  color: var(--muted-2);
  font-style: italic;
}

/* =========================================================================
   BAR 2 — the global bar. Fixed on every page of every sport.
   ========================================================================= */
/* FIXED, not sticky.
   MLB's body computes overflow:hidden and NFL's hidden auto, which makes BODY
   the sticky element's scrolling ancestor -- so the bar was confined to the
   body scroll box and scrolled away regardless of display:contents on the
   mount. Measured top:-850 at scrollY:900 on both. position:fixed is immune to
   scrolling ancestors (only a transformed/filtered ancestor would break it,
   and there are none), so it holds on all 24 origins whatever their CSS does.
   The DOM order is global -> slate -> sport, so the two scroll-away bars sit
   below the pinned one and the spacer only has to cover the global bar. */
.tsk-global {
  background: #0b0d12;
  border-bottom: 1px solid #000;
  height: var(--bar-h);
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 2147482000;
}
/* The spacer. Set on <html> so it cannot be clobbered by an origin body rule,
   and it doubles as the CLS reservation -- content was jumping 142px when the
   shell injected (measured CLS 0.17-0.31, "poor" is >0.25).
   
   --bar-h MUST be defined on :root, not only on #tsk-shell. body is not a
   descendant of #tsk-shell, so var(--bar-h) did not resolve here and the
   padding fell to 0 -- the fixed top bar then painted straight over the slate
   strip ("TODAY | MLB 126 plays"), hiding it on every DESKTOP page. Phones were
   fine only by accident: the <=900px media query happens to set --bar-h on
   :root as well, which is what made this look like a mobile-only success. */
:root { --bar-h: 46px; }
html.tsk-shelled body { padding-top: var(--bar-h, 46px); }

.tsk-burger {
  display: none;
  background: none;
  border: 0;
  color: var(--text);
  font-size: 19px;
  cursor: pointer;
  padding: 6px 10px 6px 0;
  line-height: 1;
}

.tsk-wordmark {
  display: flex;
  align-items: center;
  gap: 7px;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -.015em;
  color: var(--text);
  text-decoration: none;
  padding-right: 18px;
  white-space: nowrap;
}
/* Namespaced, and defensively reset. The fleet's shared stylesheet defines a
   bare .crown as a 34x34 gradient chip in the sport's colour with a lime glow,
   and a bare .name as Bricolage Grotesque 18px/800 -- both of which were
   landing on the shell's own wordmark and sport chip, so the unified masthead
   rendered as a different coloured object on 17 of 21 sports. Never use an
   unnamespaced class inside #tsk-shell. */
.tsk-wordmark .tsk-crown {
  all: unset;
  color: var(--brand); font-size: 16px; line-height: 1;
  font-family: var(--font-ui);
}
.tsk-chip .tsk-chip-name {
  all: unset;
  color: var(--accent); font-family: var(--font-ui);
  font-size: 14px; font-weight: 700; letter-spacing: -.01em;
}

/* the 7-slot rail */
.tsk-rail {
  display: flex;
  align-items: center;
  height: 100%;
  gap: 1px;
  flex: 1;
  min-width: 0;
}
.tsk-rail a, .tsk-more-btn {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 11px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  text-decoration: none;
  background: none;
  border: 0;
  border-bottom: 3px solid transparent;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
}
.tsk-rail a:hover, .tsk-more-btn:hover { color: #fff; }

/* Tools lead the rail and must not read as a 22nd sport: gold, with its glyph
   kept, so the eye separates "cross-sport tool" from "pick a sport". */
.tsk-rail a.tsk-rail-tool { color: var(--accent); gap: 6px; font-weight: 700; }
.tsk-rail a.tsk-rail-tool:hover { color: #ffd970; background: rgba(245,197,66,.08); }
.tsk-rail a.tsk-rail-tool[aria-current="true"] { color: #fff; border-bottom-color: var(--accent); }
.tsk-rail a.tsk-rail-tool .glyph { font-size: 13px; }
.tsk-rail-sep {
  width: 1px; height: 20px; background: var(--line);
  margin: 0 7px; flex: none;
}
.tsk-drawer a.tsk-drawer-tool { color: var(--accent); font-weight: 700; }
.tsk-drawer a.tsk-drawer-tool .tsk-tag {
  margin-left: auto; font-size: 10.5px; font-weight: 600; letter-spacing: .04em;
  color: var(--text-3); background: rgba(245,197,66,.10);
  border: 1px solid rgba(245,197,66,.22); border-radius: 999px; padding: 2px 7px;
}
.tsk-rail a[aria-current="true"] {
  color: #fff;
  border-bottom-color: var(--accent);
}

.tsk-tools {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  padding-left: 12px;
}
.tsk-tool {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  border-radius: 4px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-2);
  text-decoration: none;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
}
.tsk-tool:hover { background: #171a22; color: #fff; }
.tsk-tool.primary {
  background: var(--brand);
  color: #10121a;
}
.tsk-tool.primary:hover { background: #ffd75e; color: #10121a; }

/* the date control — this product is a dated card; ESPN buries date, we do not */
.tsk-date {
  display: flex;
  align-items: center;
  gap: 2px;
  background: #12151c;
  border: 1px solid var(--border);
  border-radius: 4px;
  height: 30px;
  padding: 0 3px;
  margin-right: 6px;
}
.tsk-date button {
  background: none; border: 0; color: var(--muted);
  cursor: pointer; padding: 0 6px; font-size: 13px; line-height: 1;
}
.tsk-date button:hover { color: #fff; }
.tsk-date-val {
  font-family: var(--font-num);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  padding: 0 4px;
  min-width: 62px;
  text-align: center;
}

.tsk-slip-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 17px; height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--accent);
  color: #10121a;
  font-size: 10.5px;
  font-weight: 700;
  font-family: var(--font-num);
}

/* =========================================================================
   BAR 3 — the sport nav. The only bar that swaps between sports.
   ========================================================================= */
/* Scrolls away with the page, by design: it is useful on arrival and dead
   weight once the reader is deep in a board. The pinned global bar keeps the
   rail, search and account reachable at all times. */
.tsk-sport {
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  height: 44px;
  position: relative;
  z-index: 2147481900;
}
.tsk-sport .tsk-inner { gap: 0; }

/* the sport identity chip — with sub-brands retired this is the ONLY
   "where am I" signal left, so it is load-bearing, not decoration. */
.tsk-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 15px;
  margin-right: 13px;
  border-right: 1px solid var(--border);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -.01em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
  height: 26px;
}
.tsk-chip .glyph {
  font-size: 17px;
  line-height: 1;
  filter: saturate(1.1);
}
.tsk-chip .name { color: var(--accent); }

.tsk-spine {
  display: flex;
  align-items: center;
  height: 100%;
  gap: 0;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1;
}
.tsk-spine::-webkit-scrollbar { display: none; }
.tsk-spine a {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 13px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}
.tsk-spine a:hover { color: #fff; }
.tsk-spine a[aria-current="page"] {
  color: #fff;
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* =========================================================================
   More Sports panel — flat, alphabetical, multi-column.
   ESPN does not group its long tail and neither do we; ESPN hand-maintains
   theirs and it has visibly drifted out of order. Ours is generated.
   ========================================================================= */
.tsk-more-wrap { position: relative; height: 100%; display: flex; align-items: center; }
.tsk-more-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background: var(--panel);
  border: 1px solid var(--border-2);
  border-top: 0;
  border-radius: 0 0 6px 6px;
  padding: 14px 8px;
  columns: 3;
  column-gap: 4px;
  min-width: 430px;
  box-shadow: 0 18px 40px -18px rgba(0,0,0,.9);
  z-index: 2147482100;
}
.tsk-more-wrap.open .tsk-more-panel { display: block; }
.tsk-more-panel a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  font-size: 13px;
  color: var(--text-2);
  text-decoration: none;
  border-radius: 4px;
  break-inside: avoid;
}
.tsk-more-panel a:hover { background: var(--panel-2); color: #fff; }
.tsk-more-panel .glyph { font-size: 14px; width: 17px; text-align: center; }

/* =========================================================================
   Mobile drawer
   ========================================================================= */
.tsk-scrim {
  display: none;
  position: fixed; inset: 0;
  background: rgba(0,0,0,.6);
  z-index: 2147482200;
}
/* display:none when closed. Hiding it with transform alone left 23 controls
   focusable at x=-310 on every viewport -- 22 of the 55 tab stops before page
   content on desktop, where the burger that opens it is display:none. The
   transform stays for the slide. */
.tsk-drawer { display: none; }
.tsk-drawer.open { display: block; }
.tsk-drawer {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: min(310px, 84vw);
  background: var(--panel);
  border-right: 1px solid var(--border-2);
  z-index: 2147482300;
  transform: translateX(-100%);
  transition: transform .18s ease;
  overflow-y: auto;
  padding: 14px 0 28px;
}
.tsk-drawer.open { transform: translateX(0); }
.tsk-scrim.open { display: block; }
.tsk-drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 16px 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}
.tsk-drawer-head .tsk-wordmark { padding: 0; }
.tsk-drawer-close {
  background: none; border: 0; color: var(--muted);
  font-size: 21px; cursor: pointer; line-height: 1; padding: 2px 4px;
}
.tsk-drawer-sec {
  font-size: 10px; font-weight: 600; letter-spacing: .13em; text-transform: uppercase;
  color: var(--muted-2); padding: 14px 16px 6px;
}
.tsk-drawer a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px;
  font-size: 14px; font-weight: 500;
  color: var(--text-2); text-decoration: none;
}
.tsk-drawer a:hover { background: var(--panel-2); color: #fff; }
.tsk-drawer .glyph { font-size: 15px; width: 20px; text-align: center; }

/* Native select for the mobile sport picker.
   ESPN ships a bare <select> here despite unlimited budget, and it is the
   right call: OS-native, accessible, zero JS, fine at 21 items. */
.tsk-mobile-picker { display: none; padding: 8px 16px 4px; }
.tsk-mobile-picker select {
  width: 100%;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border-2);
  border-radius: 5px;
  padding: 9px 10px;
  font-size: 14px;
  font-family: inherit;
}

/* =========================================================================
   Skip links — a horizontal nav carousel is a keyboard trap without them.
   ESPN ships two; so do we.
   ========================================================================= */
.tsk-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--brand);
  color: #10121a;
  padding: 9px 15px;
  font-size: 13px;
  font-weight: 600;
  z-index: 2147482600;   /* above the search box; was 200, i.e. under the slate strip */
  text-decoration: none;
}
.tsk-skip:focus { left: 8px; top: 8px; }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
  border-radius: 2px;
}

@media (max-width: 900px) {
  .tsk-burger { display: block; }
  .tsk-rail, .tsk-more-wrap { display: none; }
  .tsk-mobile-picker { display: block; }
  .tsk-date { display: none; }
  .tsk-tool .lbl { display: none; }
  .tsk-slate { height: 54px; }
}

@media (prefers-reduced-motion: reduce) {
  .tsk-drawer { transition: none; }
}

/* --- sport-bar dropdowns, adopted from each site's own nav ----------------
   The 21 sites each have a grouped menu (NBA: 39 links across 4 groups) that
   is real information architecture someone designed per sport. Flattening it
   into generic slots threw that away, so the shell reads the site's own nav
   out of the hidden header and rebuilds it here. Links in it were already
   rewritten to the prefix by the router, so they are correct by construction. */
.tsk-grp { position: relative; height: 100%; display: flex; align-items: center; }
.tsk-grp-btn {
  display: flex; align-items: center; gap: 5px; height: 100%;
  padding: 0 13px; font-size: 13px; font-weight: 500;
  color: var(--text-2); background: none; border: 0;
  border-bottom: 3px solid transparent; cursor: pointer;
  font-family: inherit; white-space: nowrap;
}
.tsk-grp-btn:hover { color: #fff; }
.tsk-grp.open .tsk-grp-btn { color: #fff; border-bottom-color: var(--accent); }
/* A closed dropdown CONTAINING the current page looked identical to every
   other. shell.js computed .has-active and the stylesheet had no rule for it,
   so the work was done and thrown away. With this, chip + lit group + lit link
   act as breadcrumbs without adding a fourth bar. */
.tsk-spine .tsk-grp.has-active .tsk-grp-btn {
  color: #fff; font-weight: 600; border-bottom-color: var(--accent);
}
/* position:FIXED, not absolute. The spine is an overflow-x:auto scroller so
   it can hold 12 items on a narrow window, and an overflow container CLIPS an
   absolutely-positioned child. The button was getting its active underline --
   proof the click fired and .open was set -- while the panel was rendered and
   immediately clipped out of view. Fixed positioning escapes the scroller;
   shell.js sets top/left from the button's rect on open. */
.tsk-grp-panel {
  display: none; position: fixed;
  background: var(--panel); border: 1px solid var(--border-2); border-top: 0;
  border-radius: 0 0 6px 6px; padding: 7px 0; min-width: 216px;
  box-shadow: 0 18px 40px -18px rgba(0,0,0,.9); z-index: 70;
  max-height: 72vh; overflow-y: auto; z-index: 2147482100;
}
.tsk-grp.open .tsk-grp-panel { display: block; }
.tsk-grp-panel a {
  display: block; padding: 7px 15px; font-size: 13px;
  color: var(--text-2); text-decoration: none; white-space: nowrap;
}
.tsk-grp-panel a:hover { background: var(--panel-2); color: #fff; }
.tsk-grp-panel a[aria-current="page"] { color: #fff; font-weight: 600; }
.tsk-grp-panel .tsk-grp-sep {
  height: 1px; background: var(--border); margin: 6px 0;
}

/* --- cross-sport search ---------------------------------------------------
   The rail shows 7 sports and the More panel 14. Neither helps a reader who
   knows what they want ("NRFI", "coursefit", "first basket") but not which
   sport owns it. Search is the affordance that scales past the rail, and the
   one thing 22 separate hostnames could never offer. */
.tsk-search-scrim {
  display: none; position: fixed; inset: 0;
  background: rgba(0,0,0,.62); z-index: 2147482400;
}
.tsk-search-scrim.open { display: block; }
.tsk-search-box {
  position: fixed; top: 9vh; left: 50%; transform: translateX(-50%);
  width: min(660px, 94vw); z-index: 2147482500;
  background: var(--panel); border: 1px solid var(--border-2);
  border-radius: 9px; box-shadow: 0 30px 70px -20px rgba(0,0,0,.92);
  display: none; overflow: hidden;
}
.tsk-search-scrim.open + .tsk-search-box { display: block; }
.tsk-search-box input {
  width: 100%; background: transparent; border: 0;
  border-bottom: 1px solid var(--border);
  color: var(--text); font-family: inherit; font-size: 17px;
  padding: 17px 19px; outline: none;
}
.tsk-search-box input::placeholder { color: var(--muted-2); }
.tsk-search-results { max-height: 58vh; overflow-y: auto; padding: 6px 0; }
.tsk-sr {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 19px; text-decoration: none; color: var(--text-2);
}
.tsk-sr:hover, .tsk-sr.sel { background: var(--panel-2); color: #fff; }
.tsk-sr .g { font-size: 16px; width: 20px; text-align: center; }
.tsk-sr .t { font-size: 14px; font-weight: 600; }
.tsk-sr .s {
  margin-left: auto; font-size: 11px; color: var(--muted-2);
  text-transform: uppercase; letter-spacing: .07em; white-space: nowrap;
}
.tsk-search-empty { padding: 22px 19px; color: var(--muted-2); font-size: 14px; }
.tsk-search-hint {
  padding: 8px 19px; border-top: 1px solid var(--border);
  color: var(--muted-2); font-size: 11.5px;
}

/* ==========================================================================
   MOBILE
   Measured against 3legparlay.com at 390x844: it keeps ONE 53px sticky header
   and puts navigation in a fixed 54px bottom bar. Three stacked sticky bars
   (slate 58 + global 46 + sport 44 = 148px) would eat 18% of the viewport
   before any content, which is what made this feel harder to move through.
   So on mobile: only the global bar stays pinned, the other two scroll away,
   and the primary actions move to a thumb-reachable bottom bar.
   ========================================================================== */
@media (max-width: 900px) {
  /* Let the slate strip and the sport bar scroll away. They are useful on
     arrival and dead weight once the reader is scrolling a board. */
  .tsk-slate { height: 50px; }
  .tsk-sport { height: 42px; }
  .tsk-global { height: 50px; }
  :root, #tsk-shell { --bar-h: 50px; }

  .tsk-inner { padding: 0 12px; }
  .tsk-wordmark { font-size: 14px; padding-right: 10px; }

  /* Room for the bottom bar, plus the iOS home indicator. */
  body { padding-bottom: calc(58px + env(safe-area-inset-bottom, 0px)); }
}

/* --- bottom tab bar (mobile only) ---------------------------------------- */
.tsk-bottom {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0;
  height: calc(56px + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: #0b0d12;
  border-top: 1px solid var(--border-2);
  z-index: 2147483100;   /* above the origin paywall CTA, which covered all 4 tabs */
}
.tsk-bottom-inner {
  /* Auto-flow, NOT a hardcoded count. This was repeat(4, 1fr); adding the Props
     tab made a 5th child that wrapped to an implicit second row and was clipped
     by the 56px height -- the Account tab rendered at left:0 underneath Today
     and was invisible at every width from 320 to 430. Deriving the columns from
     the children means the next tab cannot silently disappear the same way. */
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  height: 56px; max-width: 640px; margin: 0 auto;
}
.tsk-bt {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; min-height: 44px;              /* tap target floor */
  background: none; border: 0; cursor: pointer;
  color: var(--muted); text-decoration: none;
  font-family: inherit; font-size: 10px; font-weight: 600;
  letter-spacing: .03em; position: relative;
}
.tsk-bt .ic { font-size: 18px; line-height: 1; }
.tsk-bt:hover, .tsk-bt:active { color: var(--text); }
.tsk-bt[aria-current="page"] { color: var(--accent); }
.tsk-bt .badge {
  position: absolute; top: 6px; left: 50%; margin-left: 6px;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px;
  background: var(--accent); color: #10121a;
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
}
@media (max-width: 900px) { .tsk-bottom { display: block; } }

/* --- honest empty states -------------------------------------------------
   An empty table with headers and no rows reads as "this is broken". Usually
   it is not: the sport is out of season, or today's build has not run. Both
   are fine answers and neither was being said out loud. Absence is not
   evidence -- a page that is empty must say WHY it is empty. */
.tsk-empty {
  display: flex; align-items: flex-start; gap: 11px;
  background: #14161d; border: 1px solid #232733;
  border-left: 3px solid var(--sport-optic, #F5C542);
  border-radius: 6px; padding: 15px 17px; margin: 14px 0;
  font-family: var(--font-ui);
}
.tsk-empty .ic { font-size: 17px; line-height: 1.3; flex: none; }
.tsk-empty .tx { min-width: 0; }
.tsk-empty .hd { font-size: 14px; font-weight: 600; color: #f4f5f8; margin-bottom: 3px; }
.tsk-empty .bd { font-size: 13px; color: #9aa0ad; line-height: 1.5; }
.tsk-empty .bd a { color: var(--sport-optic, #F5C542); }

/* Real number in place of the two controls that could not work. */
.tsk-count {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--muted); white-space: nowrap;
  padding: 0 10px; margin-right: 2px;
  font-variant-numeric: tabular-nums;
}
.tsk-count b { color: var(--accent); font-weight: 700; font-size: 13px; }
@media (max-width: 900px) { .tsk-count { display: none; } }

/* --- unified footer --------------------------------------------------------
   Compliance was INHERITED, not unified: every origin page carries 21+, a
   1-800-GAMBLER line and links to responsible-gambling/terms/privacy, but the
   two pages the unification itself authored -- the homepage and the 404 -- had
   one link-less sentence between them. Those are the highest-traffic, most-
   linked pages on the network. One derivation, injected everywhere. */
.tsk-footer {
  border-top: 1px solid var(--border);
  margin-top: 48px; padding: 22px 16px 30px;
  font-family: var(--font-ui);
  color: var(--muted-2); font-size: 12.5px; line-height: 1.6;
}
.tsk-footer-inner { max-width: 1180px; margin: 0 auto; }
.tsk-footer p { margin: 0 0 9px; max-width: 92ch; }
.tsk-footer a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.tsk-footer a:hover { color: var(--text-2); }
.tsk-footer .links { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 11px 0 0; }
.tsk-footer .age { color: var(--text-2); font-weight: 700; }
.tsk-footer .brand { margin-top: 11px; color: var(--muted-2); }

/* --- freshness stamp -------------------------------------------------------
   62 of ~122 data pages carried no "when was this generated" stamp at all,
   and the ones that did used 8 different formats across the fleet -- browser
   locale with no timezone, bare dates, ISO with microseconds, "as of 6:30 PM
   ET" with no date. A reader cannot tell a live board from a nine-day-old one.
   One stamp, one format, from one derivation, on every sport. */
.tsk-fresh {
  display: flex; align-items: center; gap: 6px;
  margin-left: auto; padding-left: 14px;
  font-size: 11.5px; color: var(--muted-2); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.tsk-fresh .pip { width: 6px; height: 6px; border-radius: 50%; background: var(--pos); flex: none; }
.tsk-fresh.warn { color: var(--warn); }
.tsk-fresh.warn .pip { background: var(--warn); }
.tsk-fresh .src { color: var(--muted-2); opacity: .85; }
@media (max-width: 900px) { .tsk-fresh .src { display: none; } }
@media (max-width: 560px) { .tsk-fresh { display: none; } }

/* The origin paywall CTA is fixed at z-index 2147483000 and covered every tab
   on 7 of 10 sports -- elementFromPoint at all four tab centres returned the
   subscribe button. Raising the bar alone would hide the CTA behind it, so
   dock the CTA above the bar instead of overlapping it. */
@media (max-width: 900px) {
  html.tsk-shelled #ca-pw-cta {
    bottom: calc(60px + env(safe-area-inset-bottom, 0px)) !important;
  }
  /* MLB ships its OWN bottom nav and a to-top button; the suppression list
     covered headers only. Sweep bottom-anchored origin chrome too. */
  html.tsk-shelled nav#hrk-bottom-nav,
  html.tsk-shelled #hrk-to-top { display: none !important; }
}

/* Landscape phones match max-width:900px, so a 390px-tall viewport was giving
   198px (50.8%) to chrome before any content. Drop the slate strip and the
   bottom bar there -- the rail is perfectly usable at 844px wide. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
  .tsk-slate { display: none; }
  .tsk-bottom { display: none; }
  body { padding-bottom: 0; }
}

/* Tap targets. Measured before: drawer close 22x25, burger 27x31, tools 36x30,
   chip 26 tall, spine links 41. All below the 44px floor, on every page. */
@media (max-width: 900px) {
  .tsk-burger { min-width: 44px; min-height: 44px; padding: 6px 10px; }
  .tsk-drawer-close { padding: 10px 14px; font-size: 22px; }
  .tsk-tool { min-height: 40px; }
  .tsk-sport { height: 48px; }
  .tsk-chip { height: 44px; }
  .tsk-drawer a { padding-top: 12px; padding-bottom: 12px; }
  .tsk-slate-card { padding-top: 9px; padding-bottom: 9px; }
}

/* Scroll containment: swiping the drawer or the scrim scrolled the page
   behind it by up to 323px while the drawer stayed open. */
.tsk-drawer, .tsk-scrim, .tsk-search-box { overscroll-behavior: contain; }

/* --- same view, other sports -----------------------------------------------
   The point of merging 24 sites is that a reader can move between them. Before
   this, going from an MLB cheat sheet to the NBA one took four clicks and a
   wasted page load through the NBA home page -- and the concept even changed
   menu groups en route. Every nav slot is already keyed and link-validated, so
   the equivalent page in every other sport is knowable. */
.tsk-also {
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-ui);
}
.tsk-also-inner {
  max-width: 1440px; margin: 0 auto; padding: 7px 16px;
  display: flex; align-items: center; gap: 10px;
  overflow-x: auto; scrollbar-width: none;
}
.tsk-also-inner::-webkit-scrollbar { display: none; }
.tsk-also-lbl {
  font-size: 10px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted-2); white-space: nowrap; flex: none;
}
.tsk-also a {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px; border-radius: 4px;
  background: var(--panel); border: 1px solid var(--border);
  color: var(--text-2); text-decoration: none;
  font-size: 12px; font-weight: 600; white-space: nowrap;
  min-height: 28px;
}
.tsk-also a:hover { background: var(--panel-2); color: #fff; border-color: var(--border-2); }
.tsk-also a .g { font-size: 13px; line-height: 1; }
@media (max-width: 900px) { .tsk-also-inner { padding: 6px 12px; } }


/* Discord link in the footer -- given the accent so it reads as a destination
   rather than another legal link sitting beside Terms and Privacy. */
.tsk-footer a.tsk-discord {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--accent); font-weight: 700;
  border: 1px solid rgba(245,197,66,.30); border-radius: 999px;
  padding: 5px 14px; text-decoration: none;
}
.tsk-footer a.tsk-discord::before { content: "\25CF"; font-size: 9px; color: #5fd2a0; }
.tsk-footer a.tsk-discord:hover { background: rgba(245,197,66,.10); color: #ffd970; }

.tsk-more-sep { height: 1px; background: var(--line); margin: 6px 0; }
.tsk-more-panel a.tsk-more-discord { color: var(--accent); font-weight: 700; }
.tsk-more-panel a.tsk-more-discord .glyph { color: #5fd2a0; font-size: 9px; }
.tsk-drawer a.tsk-drawer-tool .glyph { font-size: 13px; }


/* --- Prop Explorer: adopt the network accent ------------------------------
   props defines its identity as two custom properties (--green / --green-deep)
   on :root. Re-pointing just those two re-tints the page without touching a
   single line of the origin's markup or layout. `html.tsk-p-props` outranks
   `:root` on specificity, so this holds regardless of stylesheet order.
   Semantic greens elsewhere on the page keep their own literals. */
html.tsk-p-props {
  --green: #F5C542;
  --green-deep: #c8a233;
  /* --green-2 is the LIGHT end of the headline gradient. Overriding only the
     first two left "Any player." green while "Any team." went gold -- a
     half-converted gradient reads worse than either colour on its own. When
     re-tinting via tokens, convert the whole ramp or none of it. */
  --green-2: #ffdc7a;
}


/* The slate rail is a single fixed-height row. Anything inside it must stay on
   ONE line and scroll rather than wrap -- a wrapped child is clipped by the
   bar's height and reads as broken text. */
.tsk-slate .tsk-slate-unknown {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 60vw;
  flex: none;
}


/* --- shell chrome must not inherit typography from the origin --------------
   .tsk-slate-name set font-size but not line-height, so it took the ORIGIN's.
   On /mlb/ that made the element 19px tall against 14px on the root, the slate
   card grew to 55px inside a 50px bar, and the card was clipped. The bar looked
   broken on one sport and fine on another for a reason that lived in somebody
   else's stylesheet.

   Fixed-height chrome therefore states its own line-height. Anything that
   inherits a value from 22 different origins renders 22 different ways. */
#tsk-shell .tsk-slate-name,
#tsk-shell .tsk-slate-meta,
#tsk-shell .tsk-slate-unknown,
#tsk-shell .tsk-rail a,
#tsk-shell .tsk-more-btn,
#tsk-shell .tsk-chip,
#tsk-shell .tsk-tool,
#tsk-shell .tsk-count,
#tsk-shell .tsk-fresh { line-height: 1.2; }

/* A hard ceiling as well as a fix: even if some origin inflates a child, the
   card cannot push the bar out of shape again. */
#tsk-shell .tsk-slate-card { max-height: 100%; overflow: hidden; }


/* A search miss is a routing opportunity, not a full stop. */
#tsk-shell .tsk-search-hint {
  margin-top: 9px; font-size: 12.5px; line-height: 1.5; color: var(--text-3);
}
#tsk-shell a.tsk-search-props {
  color: var(--accent); font-weight: 700; text-decoration: none;
  border-bottom: 1px solid rgba(245,197,66,.4);
}
#tsk-shell a.tsk-search-props:hover { color: #ffd970; }


/* The wordmark is the most-tapped link on a phone -- it is how you get home --
   and it measured 133x22 inside a 50px bar, well under the 40px tap-target
   floor everything else in the shell already meets. Fill the bar's height so
   the hit area matches the target, without changing where the text sits. */
#tsk-shell .tsk-wordmark {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}


/* Free products, shown on a sport's paywall gate. Deliberately quiet -- the
   gate's main job is still to sell the paid card; this exists so a reader who
   is not going to buy today leaves with something instead of nothing. */
.tsk-gatefree {
  max-width: 940px; margin: 26px auto 40px; padding: 0 16px;
  font-family: var(--font-ui, inherit);
}
.tsk-gatefree-h {
  font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: #6f7682; margin: 0 0 11px;
}
.tsk-gatefree-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 9px; }
.tsk-gatefree-card {
  display: flex; align-items: center; gap: 11px;
  background: #14161d; border: 1px solid #232733; border-left: 3px solid #F5C542;
  border-radius: 6px; padding: 12px 14px; text-decoration: none; color: #f4f5f8;
}
.tsk-gatefree-card:hover { background: #1b1e27; }
.tsk-gatefree-card .g { font-size: 19px; line-height: 1; flex: none; }
.tsk-gatefree-card .tx { min-width: 0; display: block; }
.tsk-gatefree-card b { display: block; font-size: 13.5px; font-weight: 700; }
.tsk-gatefree-card .s { display: block; font-size: 11.5px; color: #6f7682; margin-top: 2px; line-height: 1.4; }


/* --- contrast: the sport optic is a BRAND colour, not a text colour --------
   --accent resolves to var(--sport-optic), so any text using it takes the
   sport's own hue. Measured on /mlb/ that is #D9342B: 4.14:1 on the bar and
   3.85:1 on the panel, both under the 4.5:1 needed at these sizes. Several of
   the 21 optics are dark enough to fail the same way.

   Two separate fixes, because these are two different mistakes:

   1. The rail's TOOL entry was tinted per sport, so "Prop Explorer" rendered
      red on MLB, blue on NFL and so on. It is the one CROSS-SPORT product on
      the network -- it should look identical everywhere, and gold is 11.98:1.
      This is a branding fix that happens to resolve the contrast.

   2. The sport chip legitimately wants the sport's colour. Lighten it for TEXT
      use only; the border and underline keep the true optic. color-mix is
      supported everywhere this ships, and the plain declaration before it is
      the fallback for anything that does not. */
#tsk-shell .tsk-rail a.tsk-rail-tool { color: var(--brand); }
#tsk-shell .tsk-rail a.tsk-rail-tool:hover { color: #ffd970; }

#tsk-shell .tsk-chip .tsk-chip-name { color: var(--text); }
@supports (color: color-mix(in srgb, red 50%, white)) {
  #tsk-shell .tsk-chip .tsk-chip-name {
    /* 55%, not 62%: checked against ALL 21 optics, not a sample. Eleven of them
       fail 4.5:1 as raw text -- npb #1A2B6B is 1.38:1, golf #006747 is 2.61:1,
       horse-racing 2.39:1 -- and 62% still left npb at 4.40:1. At 55% the worst
       sport is 5.32:1 and every hue is still recognisably its own. */
    color: color-mix(in srgb, var(--sport-optic, #F5C542) 55%, #ffffff);
  }
}


/* --- one token for accent TEXT, applied everywhere -------------------------
   --accent resolves to the sport's own optic, and 11 of the 21 optics fail
   4.5:1 as text (npb #1A2B6B is 1.38:1). Ten rules in this stylesheet set
   `color: var(--accent)`, so the failure appeared in a different element each
   time I looked -- the chip, then the rail tool, then the active bottom tab.
   Fixing them one at a time was going to keep missing one.

   So: --accent stays the true optic for BORDERS and BACKGROUNDS, where a brand
   colour belongs and contrast rules do not apply. Text takes --accent-text.

   Two kinds of text, deliberately separated:
     sport-specific (the chip, the active tab, the play count) -> lightened
       optic, so it still reads as that sport's colour
     network-brand  (Prop Explorer, Discord, the search handoff) -> gold at
       11.98:1, and identical on every sport, because they are not sport things
*/
#tsk-shell { --accent-text: var(--brand); }
@supports (color: color-mix(in srgb, red 50%, white)) {
  #tsk-shell { --accent-text: color-mix(in srgb, var(--sport-optic, #F5C542) 55%, #ffffff); }
}

/* sport-tinted text */
#tsk-shell .tsk-chip .tsk-chip-name,
#tsk-shell .tsk-chip .name,
#tsk-shell .tsk-bt[aria-current="page"],
#tsk-shell .tsk-count b { color: var(--accent-text); }

/* network-brand text: always gold, never sport-tinted */
#tsk-shell .tsk-rail a.tsk-rail-tool,
#tsk-shell .tsk-drawer a.tsk-drawer-tool,
#tsk-shell .tsk-more-panel a.tsk-more-discord,
#tsk-shell a.tsk-search-props,
.tsk-footer a.tsk-discord { color: var(--brand); }


/* --- no upsell where the upsell makes no sense ----------------------------
   The gate card sits inside a fixed FULL-VIEWPORT backdrop. Hiding only the
   card left an 86%-opaque black layer with pointer-events:auto covering the
   pricing page -- no plan was clickable. Hide the whole layer, and restore the
   scroll the page locked when it opened.

   :has() does the common case in pure CSS so there is no flash of black before
   the script runs; suppressGateOverlay() in shell.js is the fallback for
   engines without it and also unlocks scrolling. */
html.tsk-nogate .hrk-gate-card,
html.tsk-nogate [class*="gate-card"],
html.tsk-nogate [class*="gate-modal"],
html.tsk-nogate [class*="paywall-modal"],
html.tsk-nogate [class*="upsell-modal"] { display: none !important; }

@supports selector(:has(*)) {
  html.tsk-nogate body > div:has(> .hrk-gate-card),
  html.tsk-nogate body > div:has(.hrk-gate-card),
  html.tsk-nogate body > div:has([class*="gate-card"]) { display: none !important; }
}
html.tsk-nogate body { overflow: auto !important; }
