/* ============================================================================
   sg-shell.css — v2 site shell (header / footer / theme switcher) ported to v1
   ----------------------------------------------------------------------------
   Source of truth: ppmu_v2_2026.05.26 — app/globals.css, components/site/
   site-header.tsx, site-footer.tsx, components/theme-switcher.tsx.

   Why hand-written rather than generated: v1 has no build step, so a Tailwind
   output file would have to be regenerated by hand every time markup changed —
   a trap in a repo where files are copied to prod over RDP. Everything below is
   plain CSS with no toolchain.

   Theme mechanism matches v2 exactly: next-themes runs with attribute="class",
   so dark mode is the class `dark` on <html>, NOT a data-theme attribute. That
   also sidesteps the data-theme selector collision documented in the global
   CLAUDE.md (palette blocks matching the switcher's own buttons).

   Load AFTER sg-bridge.css — the last section re-points the existing sg-*
   surfaces at these tokens.
   ========================================================================== */

/* --- Tokens -----------------------------------------------------------------
   v2 builds its light palette from the Radix scales (@radix-ui/colors), which
   are npm packages. Those are resolved to their hex values here; the display-p3
   variants Radix also ships are deliberately dropped — the hex fallbacks render
   identically everywhere v1 is used and keep this file dependency-free.
   Mapping: slate-1/2/3/4/6/11/12, indigo-8/9/10, red/amber/green-3/9/11. */
:root {
  /* Tells the browser which scheme to paint its OWN parts in: Chrome's
     autofill fill (light blue here, a muted dark blue under dark), scrollbars,
     <select> popups, date pickers, native checkboxes. Without it they all stay
     light in dark mode -- autofilled sign-in fields showed pale blue on the
     dark panel. Keyed to html.dark (below), not the media query, so it follows
     the theme switcher. Same as mfg v2's globals.css (Jason, 2026-11-11). */
  color-scheme: light;
  --slate-1: #fcfcfd;
  --slate-2: #f9f9fb;
  --slate-3: #f0f0f3;
  --slate-4: #e8e8ec;
  --slate-6: #d9d9e0;
  --slate-11: #60646c;
  --slate-12: #1c2024;

  --indigo-8: #8da4ef;
  --indigo-9: #3e63dd;
  --indigo-10: #3358d4;

  --background: var(--slate-1);
  --foreground: var(--slate-12);

  --surface-1: var(--slate-1);
  --surface-2: var(--slate-2);
  --surface-3: var(--slate-3);

  /* Bookend bands — header top row + footer bottom row. */
  --bookend: #dbdbdb;
  --footer-fg: var(--slate-11);

  --browse-bg: #ebebeb;

  --card: var(--slate-1);
  --card-foreground: var(--slate-12);

  --primary: var(--indigo-9);
  --primary-hover: var(--indigo-10);
  --primary-foreground: #ffffff;

  --secondary: var(--slate-3);
  --secondary-foreground: var(--slate-12);

  --muted: var(--slate-3);
  --muted-foreground: var(--slate-11);

  --accent: var(--slate-4);
  --accent-foreground: var(--slate-12);

  --destructive: #e5484d;
  --destructive-foreground: #ffffff;
  --destructive-bg: #feebec;
  --destructive-fg: #ce2c31;

  --warn-bg: #fff7c2;
  --warn-fg: #ab6400;

  --success-bg: #e6f6eb;
  --success-fg: #218358;

  --border: var(--slate-6);
  --input: var(--slate-6);
  --ring: var(--indigo-8);

  --radius: 8px;

  /* Hover fill for the full-height nav buttons. A translucent black/white wash
     rather than a solid, so it reads the same over both bookend bands. */
  --nav-hover-bg: rgba(0, 0, 0, 0.10);
}

html.dark {
  color-scheme: dark;
  --background: #181818;
  --foreground: #ebebeb;

  --surface-1: #181818;
  --surface-2: #1f1f1f;
  --surface-3: #2a2a2a;

  --bookend: #000000;
  --footer-fg: #b7b7b7;

  --browse-bg: #121212;

  --card: #1f1f1f;
  --card-foreground: #ebebeb;

  --secondary: #2a2a2a;
  --secondary-foreground: #ebebeb;

  --muted: #2a2a2a;
  --muted-foreground: #b7b7b7;

  --accent: #2a2a2a;
  --accent-foreground: #ebebeb;

  --destructive-bg: #3b1219;
  --destructive-fg: #ff9592;

  --warn-bg: #302008;
  --warn-fg: #ffca16;

  --success-bg: #132d21;
  --success-fg: #3dd68c;

  --border: #2a2a2a;
  --input: #2a2a2a;

  --nav-hover-bg: rgba(255, 255, 255, 0.06);
}

/* --- Base ---------------------------------------------------------------- */

html {
  height: 100%;
  /* Always render the vertical scrollbar so layout width doesn't shift between
     pages that do and don't overflow. Same reason as v2's globals.css. */
  overflow-y: scroll;
  /* Paired with the scroll lock below: while the drawer is open <html> goes
     overflow:hidden, which would otherwise drop the scrollbar and shift the
     page ~15px sideways. `stable` keeps the gutter reserved through it. */
  scrollbar-gutter: stable;

  /* Kill the browser's tap/press highlight -- the translucent wash Chrome and
     friends paint over whatever you press, which lingers a few hundred ms and
     reads as a coloured flash around the hamburger. v2 and every other Tailwind
     port get this for free from Preflight (`html { -webkit-tap-highlight-color:
     transparent }`); this shell has no reset, so it has to be declared. It is
     an inherited property, so declaring it on <html> covers the whole page.

     This does NOT touch the keyboard focus ring -- that's `outline`, a separate
     property, and it stays. */
  -webkit-tap-highlight-color: transparent;

  /* The default text size and line height, 18px / 1.5, as mfg v2's (Jason,
     2026-11-13). It was 16px, set on body. Text with no size of its own
     inherits this, and so does every em size in the content. The header and
     footer are pinned back to their old 16px just below. In px rather than a
     percentage: nothing in these stylesheets uses rem, so a percentage would
     scale only the unsized text and leave every px size behind. */
  font-size: 18px;
  line-height: 1.5;
}

body.sg-page {
  /* Sticky footer: the body must be at least a full viewport tall so
     .sgs-main's `flex: 1 0 auto` has spare space to absorb, pushing .sgs-footer
     to the bottom on short pages. It still gets pushed DOWN normally when
     content is long -- this is a sticky footer, not a fixed one.

     `min-height: 100%` was wrong: a percentage resolves against the parent's
     height, so it depends on the html chain holding up and quietly collapses to
     content height when it doesn't -- which is why the footer sat mid-viewport
     with dead space below. Viewport units don't depend on any ancestor.
     100dvh is declared second so supporting browsers use the dynamic viewport
     (mobile URL bar shrink/grow) and older ones keep the 100vh above. */
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  display: flex;
  flex-direction: column;
  background: var(--background);
  color: var(--foreground);
  font-family: "Google Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* No font-size / line-height: both come from html (18px / 1.5). */
}

/* ASP.NET Web Forms wraps the ENTIRE page in <form id="form1" runat="server">,
   so the body's only flex child is that form -- header/main/footer are its
   grandchildren. Without this rule .sgs-main's `flex: 1 0 auto` is inert (its
   parent is the form, not the flex container), the form sizes to its content,
   and the footer lands mid-viewport no matter what min-height the body has.
   Make the form a full-height flex column too, so the chain reaches <main>. */
body.sg-page > form {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* The header and footer keep the 16px they had before the default went to 18px
   (Jason, 2026-11-13). Their line height is unchanged too: still html's 1.5.
   ⚠ `> form > header` is the SHELL's header. The sg pages carry their own
   <header class="sg-header"> inside <main>, which must not match this. */
body.sg-page > form > header,
.sgs-footer {
  font-size: 16px;
}

/* The page body grows so the footer is pushed to the bottom on short pages. */
.sgs-main {
  flex: 1 0 auto;
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
  padding: 30px;
  box-sizing: border-box;
}

/* --- Content type scale (Jason, 2026-11-13) --------------------------------
   One rule each for every v2 page in this shell, so the pages cannot drift
   apart. The per-page rules that used to size these (sg-bridge.css
   .sg-header h1, sub-tournament-new.css .stn h1 / .stn h2, pool-player-perks'
   .ppp-page h1) now set only margins and colours.

   ⚠ font-weight 400 has to be stated on h1/h2: browsers make headings bold by
   default, so leaving it out would not give 400. */
.sgs-main h1 { font-size: 32px; font-weight: 400; line-height: 42px; }
.sgs-main h2 { font-size: 22px; font-weight: 400; line-height: 32px; }

/* Every table in the content is 16px, under the 18px default. Cells sized in
   em (sg-bridge.css's 1em guard on .sg-tbl-scroll cells) follow it. */
.sgs-main table { font-size: 16px; }

/* --- Informational panel (.infoContent) ------------------------------------
   For content that informs rather than warns, e.g. the FargoRate data
   submission requirements card. Caution-appropriate content keeps
   .cautionNotice (Jason, 2026-11-13).

   Dark is Jason's scheme (2026-11-13): light-blue text and left rule
   (#9cd7ef) on a dark grey panel (#343537), about 7.8:1, which clears WCAG AA
   for normal text. Light is a grey panel with near-black text and a blue left
   rule: #0c191e on #e3e5e8 is about 14:1, and the rule #2f7fa6 is about 3.5:1
   against the panel. */
.infoContent {
  background: #e3e5e8;
  color: #0c191e;
  border-left: 1px solid #2f7fa6;
  border-radius: var(--radius);
  padding: 14px 20px;
  font-size: 18px;
  text-align: left;
}
html.dark .infoContent {
  background: #343537;
  color: #9cd7ef;
  border-left-color: #9cd7ef;
}
.infoContent ol { margin: 6px 0 0 22px; padding: 0; }
.infoContent ol li { margin: 3px 0; }

/* Matches v2: anchors get a colour + background transition. Declared once here
   rather than per-link, because `transition` is a shorthand and a later
   per-element rule would reset transition-property for the others. */
a {
  transition: color 0.16s cubic-bezier(0.42, 0, 0.58, 1),
              background-color 0.16s cubic-bezier(0.42, 0, 0.58, 1);
}

button:not(:disabled) {
  cursor: pointer;
}

/* bfcache flicker prevention — see global CLAUDE.md. sg-theme.js adds this
   class on pageshow when the page comes back from the bfcache, so the restored
   theme state doesn't animate. */
html.no-transitions *,
html.no-transitions *::before,
html.no-transitions *::after {
  transition: none !important;
  animation: none !important;
}

/* --- Header -------------------------------------------------------------- */

.sgs-bookend {
  background: var(--bookend);
}

.sgs-topbar {
  margin: 0 auto;
  display: flex;
  height: 80px;
  max-width: 1600px;
  align-items: center;
  /* 30px left padding at >= 800px, reset to 0 in the 799.98px block below.
     Two knock-on effects, both deliberate as of 2026-08-27 and both visible:
     the logo is a full-height button whose hover fill now starts 30px in
     rather than running flush to the edge, and because .sgs-logo carries its
     own `padding: 0 30px`, the wordmark now sits at 60px -- no longer aligned
     with the browse row and main content, which stay at 30px. */
  padding-left: 30px;
  padding-right: 30px;
  box-sizing: border-box;
}

/* Full-height button, same block-hover behaviour as .sgs-navbtn. align-self
   stretch overrides the row's align-items:center so the box spans all 80px
   while the wordmark stays optically centred. */
.sgs-logo {
  display: flex;
  align-items: center;
  align-self: stretch;
  padding: 0 30px;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: #555555;
  text-decoration: none;
}

.sgs-logo:hover {
  background: var(--nav-hover-bg);
  color: var(--foreground);
}

html.dark .sgs-logo {
  color: #b7b7b7;
}

html.dark .sgs-logo:hover {
  color: var(--foreground);
}

/* Centre slot: the Ctrl+K search pill (added 2026-11-10; it was an empty
   spacer before). v2's flex-1 justify-center.

   NO left margin, where v2 has ml-[30px]: v2's logo has no padding, but this
   shell's logo is a full-height hover block with 30px padding on BOTH sides,
   and that right-hand 30px already is v2's gap. With a margin on top the pill
   sat ~60px from the wordmark and ~30px from the hamburger -- visibly off-
   centre (Jason, 2026-11-10). Now 30 / 30 at every width, as on v2.

   `min-width: 0` lets it shrink below the pill's content, and it is a size
   CONTAINER so the pill can pick its label from its own width (see below).
   Containment also means its contents never widen the header -- the overflow
   that squeezed the page on a phone came from the header's fixed pieces. */
.sgs-topbar-centre {
  flex: 1;
  min-width: 0;
  display: flex;
  justify-content: center;
  container-type: inline-size;
  container-name: sgsearch;
}

/* v2's trigger: h-10, rounded-full, 2px border, px-4, gap-3, surface-2 fill,
   max-w-xl. A link (to v1's search page) that sg-search.js turns into the
   popup's opener. */
.sgs-search-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 576px;
  height: 40px;
  box-sizing: border-box;
  padding: 0 16px;
  border: 2px solid #858585;
  border-radius: 9999px;
  background: var(--surface-2);
  color: #8b8b8b;
  /* 18px, the page default, where the rest of the header is 16px (Jason,
     2026-11-13). Weight 400 and line height 1.5 are inherited. */
  font-size: 18px;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.2s cubic-bezier(0.42, 0, 0.58, 1);
}
.sgs-search-btn:hover { background: var(--surface-3); }
html.dark .sgs-search-btn { border-color: #393939; }

.sgs-search-btnicon { width: 16px; height: 16px; flex-shrink: 0; color: #858585; }
html.dark .sgs-search-btnicon { color: #b7b7b7; }

.sgs-search-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The shortcut hint: v2's bold "⌘" + "K". */
.sgs-search-kbd {
  display: inline-flex;
  align-items: baseline;
  flex-shrink: 0;
  color: #b7b7b7;
}
html.dark .sgs-search-kbd { color: rgba(183, 183, 183, 0.45); }
.sgs-search-kbd b { font-size: 14px; font-weight: 700; }
.sgs-search-kbd b + b { margin-left: 3px; font-size: 16px; }

/* Exactly one label shows. v2 swaps whole strings at viewport breakpoints
   rather than letting one truncate mid-word; here the swap keys off the PILL's
   own width, which also tracks the cluster beside it (Sign in vs initials) for
   free. Each cutoff is the MEASURED width the pill needs for that label
   (label + icon + hint + 32px padding + 4px border + two 12px gaps; measured
   2026-11-13 with the label at 18px: 161 / 294 / 365 / 477px; at 16px they
   were 156 / 274 / 338 / 437) plus a few px of margin. Re-measure if the
   wording, font, size or padding changes -- a cutoff below the real width
   shows the label cut off mid-word, which is what the swap exists to prevent.
   Below the first one the hint goes too, and the pill is just "Search". */
.sgs-sl { display: none; }
.sgs-sl-1 { display: inline; }
.sgs-search-kbd { display: none; }
@container sgsearch (min-width: 167px) {
  .sgs-search-kbd { display: inline-flex; }
}
@container sgsearch (min-width: 300px) {
  .sgs-sl-1 { display: none; }
  .sgs-sl-2 { display: inline; }
}
@container sgsearch (min-width: 371px) {
  .sgs-sl-2 { display: none; }
  .sgs-sl-3 { display: inline; }
}
@container sgsearch (min-width: 484px) {
  .sgs-sl-3 { display: none; }
  .sgs-sl-4 { display: inline; }
}
/* A keyboard shortcut means nothing on a phone or tablet. */
@media (hover: none) and (pointer: coarse) {
  .sgs-search-kbd { display: none !important; }
}

/* Desktop cluster. `align-self: stretch` gives it the row's full 80px so the
   nav inside can run edge to edge vertically, while `align-items: center`
   keeps the theme switcher optically centred. */
.sgs-cluster {
  margin-left: 30px;
  display: flex;
  align-items: center;
  gap: 24px;
  align-self: stretch;
}

.sgs-nav {
  display: flex;
  align-items: stretch;
  align-self: stretch;
}

/* Full-height nav button. The horizontal padding is doing double duty — it also
   replaces the row's gap, so adjacent hover blocks meet edge to edge instead of
   leaving dead strips between them. That edge-to-edge fill IS the look. */
.sgs-navbtn {
  display: flex;
  align-items: center;
  padding: 0 16px;
  color: var(--footer-fg);
  text-decoration: none;
  white-space: nowrap;
}

.sgs-navbtn:hover {
  background: var(--nav-hover-bg);
  color: var(--foreground);
}

.sgs-browse {
  display: block;
  background: var(--browse-bg);
}

.sgs-browse-inner {
  margin: 0 auto;
  display: flex;
  height: 50px;
  max-width: 1600px;
  align-items: stretch;
  justify-content: center;
  padding: 0 30px;
  box-sizing: border-box;
}


/* --- Theme switcher ------------------------------------------------------
   Geometry is pinned in px on purpose: 34px square buttons, 18px icons, 3px gap
   and padding, 1px border -> 116 x 42 overall. Square buttons make the active
   pill a true circle, and gap == padding spaces it evenly from rim and
   neighbour. See the global CLAUDE.md table — every ppmu-family site uses these
   exact numbers, and rem-based sizing is what made them drift apart before. */

.sgs-theme {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  border-radius: 9999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  padding: 3px;
}

.sgs-theme-btn {
  display: inline-flex;
  width: 34px;
  height: 34px;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--muted-foreground);
  transition: color 0.16s cubic-bezier(0.42, 0, 0.58, 1),
              background-color 0.16s cubic-bezier(0.42, 0, 0.58, 1);
}

.sgs-theme-btn:hover {
  color: var(--foreground);
}

.sgs-theme-btn[aria-checked="true"] {
  background: #ffffff;
  color: var(--foreground);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

html.dark .sgs-theme-btn[aria-checked="true"] {
  background: #000000;
}

/* currentColor is what lets the container's colour drive hover and active
   states — never hard-code a stroke colour on these icons. */
.sgs-theme-btn svg {
  width: 18px;
  height: 18px;
}

/* --- Mobile drawer -------------------------------------------------------
   Ported from v2's components/site/mobile-nav.tsx, which in turn follows FRC's
   drawer: transform + box-shadow both 400ms, and `visibility` flipped with a
   400ms DELAY on close so the panel stays painted through the slide-out and
   only then leaves the tab order.

   These are visible-at-any-width component styles; the breakpoint that turns
   them on lives in the one media query at the end of this file. Everything
   keys off `html.sg-nav-open`, which sg-nav.js is the only thing that sets. */

/* Hidden at >=800px. The media query at the end flips this to inline-flex. */
.sgs-hamburger {
  position: relative;
  z-index: 50;
  display: none;
  width: 50px;
  height: 50px;
  align-items: center;
  justify-content: center;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--footer-fg);
  transition: transform 0.4s cubic-bezier(0.42, 0, 0.58, 1);
}

.sgs-hamburger:hover {
  color: var(--foreground);
}

html.sg-nav-open .sgs-hamburger {
  transform: rotate(45deg);
}

/* The two outer arms retract into the middle bar to form the X. The dash
   figures come straight from the v2 component -- don't round them. */
.sgs-ham-arm {
  stroke-dasharray: 40 121;
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.4s cubic-bezier(0.42, 0, 0.58, 1);
}

html.sg-nav-open .sgs-ham-arm {
  stroke-dashoffset: -68px;
}

.sgs-overlay {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 30;
  background: #000000;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.4s cubic-bezier(0.42, 0, 0.58, 1),
              visibility 0.4s cubic-bezier(0.42, 0, 0.58, 1);
}

html.sg-nav-open .sgs-overlay {
  opacity: 0.4;
  visibility: visible;
  pointer-events: auto;
}

.sgs-drawer {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  width: 88%;
  max-width: 448px;
  flex-direction: column;
  background: var(--background);
  color: var(--foreground);
  transform: translateX(105%);
  box-shadow: none;
  visibility: hidden;
  /* The 0s/400ms delay is what keeps the panel painted through the slide-out.
     Without it visibility flips immediately and the drawer vanishes instead of
     sliding away. */
  transition: transform 0.4s cubic-bezier(0.42, 0, 0.58, 1),
              box-shadow 0.4s cubic-bezier(0.42, 0, 0.58, 1),
              visibility 0s 0.4s;
}

html.sg-nav-open .sgs-drawer {
  transform: translateX(0);
  box-shadow: -4px 0 10px 0 rgba(0, 0, 0, 0.6);
  visibility: visible;
  transition: transform 0.4s cubic-bezier(0.42, 0, 0.58, 1),
              box-shadow 0.4s cubic-bezier(0.42, 0, 0.58, 1);
}

/* Same 80px as .sgs-topbar, so the drawer's band lines up with the page header
   it slides over -- and so the hamburger, which stays on top of the drawer at
   z-index 50, sits optically centred in both at once. `flex-shrink: 0` keeps it
   at 80 when the link list is long enough to scroll. */
/* The theme switcher sits here (2026-11-13), left-aligned at the 20px inset
   where the wordmark was, with Sign in just right of it when signed out
   (Jason). The close X on the right is the page header's hamburger showing
   through, not part of this band. */
.sgs-drawer-head {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 80px;
  flex-shrink: 0;
  box-sizing: border-box;
  background: var(--bookend);
  padding: 0 20px;
}

.sgs-drawer-logo {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: #555555;
  text-decoration: none;
}

.sgs-drawer-logo:hover {
  color: var(--foreground);
}

html.dark .sgs-drawer-logo {
  color: #b7b7b7;
}

.sgs-drawer-nav {
  flex: 1;
  overflow-y: auto;
  padding: 12px 0;
  /* iOS momentum scrolling inside the panel while the page behind is locked. */
  -webkit-overflow-scrolling: touch;
}

/* Icon + label rows, mfg v2's drawer shape (gap-3, size-5 icon), 2026-11-10.
   The icon is stroked in currentColor, so it follows the row's colour and
   its hover. */
.sgs-drawer-nav a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  color: var(--footer-fg);
  text-decoration: none;
}

.sgs-drawer-nav a:hover {
  background: var(--surface-2);
  color: var(--foreground);
}

html.dark .sgs-drawer-nav a {
  color: #cecece;
}

.sgs-mi { width: 20px; height: 20px; flex-shrink: 0; }

.sgs-drawer-div {
  margin: 8px 20px;
  height: 1px;
  background: var(--border);
}

.sgs-drawer-head .sgs-theme,
.sgs-drawer-head .sgs-signin-btn { flex-shrink: 0; }

/* --- Account control (added 2026-11-09) ----------------------------------
   Ported from v2: components/site/sign-in-popup.tsx (signed out) and
   account-menu.tsx (signed in). One wrapper, .sgs-acct, holds the trigger,
   a full-screen scrim and the panel; scripts/sg-account.js toggles .is-open
   on the wrapper and everything else keys off that class.

   The scrim and panel stay in the DOM when closed (opacity 0, no pointer
   events, `inert`), so opening AND closing both transition, as on v2. */
.sgs-acct { position: relative; display: flex; align-items: center; }

/* Signed out: v2's "Sign in" pill (h-9, rounded-full, px-4, bg-primary). */
.sgs-signin-btn {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: 9999px;
  background: var(--primary);
  color: var(--primary-foreground);
  font: inherit;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color .15s ease-in-out;
}
.sgs-signin-btn:hover { background: var(--primary-hover); }

/* Signed in: v2's initials circle (h-9 w-9, rounded-full, bg-primary). */
.sgs-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 9999px;
  background: var(--primary);
  color: var(--primary-foreground);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background-color .15s ease-in-out;
}
.sgs-avatar:hover { background: var(--primary-hover); }

/* v2's scrim: fixed, 60% black, click to close. Below the drawer's hamburger
   (50) is irrelevant -- the drawer is always shut before this opens. */
.sgs-acct-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity .4s ease-in-out;
}
.sgs-acct.is-open .sgs-acct-scrim { opacity: 1; pointer-events: auto; }

.sgs-acct-panel {
  position: absolute;
  right: 0;
  z-index: 61;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--card-foreground);
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity .4s ease-in-out, transform .4s ease-in-out;
}
.sgs-acct.is-open .sgs-acct-panel { opacity: 1; transform: translateY(0); pointer-events: auto; }

/* Account menu: v2's w-56, sitting just below the 80px header band
   (top-[70px] from a wrapper centred in that band). */
.sgs-acct-menu { top: 70px; width: 224px; }
/* Icon + label rows, mfg v2's profile-menu shape (18px icon, gap-3),
   2026-11-10. Sign out is an asp:LinkButton, which renders an <a>, so this
   covers it too. Direct children only, so a control nested in the menu
   later doesn't pick up the rows' padding and hover fill. */
.sgs-acct-menu > a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  color: var(--foreground);
  text-decoration: none;
}
.sgs-acct-menu > a:hover { background: rgba(0, 0, 0, 0.05); }
html.dark .sgs-acct-menu > a:hover { background: rgba(255, 255, 255, 0.05); }
.sgs-acct-menu .sgs-mi { width: 18px; height: 18px; }

/* The menu gained the drawer's link groups on 2026-11-10, so it can now be
   taller than a short laptop window: cap it at the space below its top edge
   and let it scroll rather than run off the bottom. */
.sgs-acct-menu { max-height: calc(100vh - 86px); overflow-y: auto; }

/* Dividers between the groups. Full width, edge to edge of the menu, as mfg
   v2's profile menu draws them (Jason, 2026-11-13); they were inset 16px like
   the drawer's .sgs-drawer-div, which keeps its inset. */
.sgs-acct-div {
  height: 1px;
  margin: 6px 0;
  background: var(--border);
}

/* --- Admin / Player view toggle (added 2026-11-10) ------------------------
   Markup and rules in sg.master / sg.master.cs. Styled as a small version of
   the theme switcher: same pill, same "selected segment is a raised chip".
   24px tall overall (20 + 1px padding + 1px border, twice), which is exactly the
   breadcrumbs' line-height, so floated right it sits ON their first line. */
.sgs-viewtoggle {
  float: right;
  display: inline-flex;
  gap: 1px;
  margin: 0 0 8px 16px;
  padding: 1px;
  border: 1px solid var(--border);
  border-radius: 9999px;
  background: var(--surface-2);
  font-size: 14px;
  line-height: 20px;
}
.sgs-viewtoggle-btn {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 10px;
  border-radius: 9999px;
  color: var(--muted-foreground);
  text-decoration: none;
}
a.sgs-viewtoggle-btn[href]:hover { color: var(--foreground); }
/* The current view: not a link (no href), so no pointer. */
/* A stronger shadow and 500 weight than the theme switcher's chip: there the
   icons carry the state, here it is two words, and white-on-near-white alone
   barely read in light mode. */
.sgs-viewtoggle-btn.is-on {
  background: #ffffff;
  color: var(--foreground);
  font-weight: 500;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  cursor: default;
}
html.dark .sgs-viewtoggle-btn.is-on { background: #000000; }

/* Sign-in popup: v2's w-[300px], anchored under the button (top-full mt-2). */
/* 340px wide with 20px body padding, so its fields come out 300px: mfg v2's
   auth modal (components/site/auth-modal.tsx, w-[340px], p-[20px]), set to
   match 2026-11-11. Px in mfg too, so its 18px root doesn't come into it. */
.sgs-signin-panel { top: calc(100% + 8px); width: 340px; transition-duration: .2s; }
.sgs-signin-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 20px;        /* mfg's px-[20px] py-[15px] */
  background: #ebebeb;
  font-weight: 500;
}
html.dark .sgs-signin-head { background: #121212; }
.sgs-signin-head a { color: #47738f; text-decoration: none; font-weight: 400; transition: color .15s ease-in-out; }
.sgs-signin-head a:hover { color: #b78628; }
html.dark .sgs-signin-head a { color: #6a9abf; }
html.dark .sgs-signin-head a:hover { color: #c8a858; }

/* position: relative anchors the hidden username field on the password step. */
.sgs-signin-body { position: relative; padding: 20px; }

/* Two steps (sg-account.js sets data-step). With no data-step, both halves
   render, which only happens if the script never ran -- and then the panel
   cannot be opened anyway. */
.sgs-signin-panel[data-step="user"] .sgs-signin-step2 { display: none; }
.sgs-signin-panel[data-step="pass"] .sgs-signin-step1-actions { display: none; }
/* ⚠ Off-screen by size and opacity, NEVER display:none: Chrome skips fields
   it considers unrendered, and then it won't fill the password step from the
   saved username. absolute keeps it from costing the step any height. */
.sgs-signin-panel[data-step="pass"] .sgs-signin-step1 {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}
.sgs-signin-as { margin: 0 0 14px; font-size: 14px; color: var(--muted-foreground); overflow-wrap: anywhere; }
.sgs-signin-as span { color: var(--foreground); font-weight: 500; }
.sgs-signin-error[hidden] { display: none; }
/* The two fields are floating-label fields (see "Floating-label fields" near
   the end of this file); only their spacing lives here. The top margin is
   room for the floated label above the first field. */
.sgs-signin-body .sg-fl { margin: 10px 0 16px; }

.sgs-signin-forgot { margin: -4px 0 12px; text-align: right; font-size: 14px; }
.sgs-signin-forgot a,
.sgs-signin-cancel { color: #449edb; text-decoration: none; }
html.dark .sgs-signin-forgot a,
html.dark .sgs-signin-cancel { color: #6aafdf; }
.sgs-signin-error { margin: 0 0 20px; font-size: 16px; color: var(--destructive-fg); }

.sgs-signin-actions { display: flex; align-items: center; justify-content: space-between; }
.sgs-signin-cancel {
  height: 40px;
  padding: 0 10px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  font: inherit;
  cursor: pointer;
  transition: background-color .15s ease-in-out;
}
.sgs-signin-cancel:hover { background: rgba(0, 0, 0, 0.1); }
html.dark .sgs-signin-cancel:hover { background: rgba(255, 255, 255, 0.06); }
.sgs-signin-submit {
  height: 40px;
  padding: 0 16px;
  border: 0;
  border-radius: 6px;
  background: var(--primary);
  color: var(--primary-foreground);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background-color .15s ease-in-out;
}
.sgs-signin-submit:hover { background: var(--primary-hover); }

@media (max-width: 799.98px) {
  /* The drawer carries Sign in / Your profile / Sign out at this width, as on
     v2, so the header trigger goes. The wrapper stays: it still holds the
     sign-in panel, which the drawer's Sign in button opens... */
  .sgs-acct > #sgAcctBtn { display: none; }
  /* With the trigger gone the wrapper would be an empty flex item that still
     costs the cluster a 24px gap. `contents` drops the box; the scrim and the
     panel are both position:fixed, so they are out of flow either way, and
     the .is-open selectors still match because the class is still on it. */
  .sgs-acct { display: contents; }

  /* ...as a centred modal rather than a popup anchored to a hidden button. */
  .sgs-signin-panel {
    position: fixed;
    /* sg-account.js sets --sgs-vv-mid to the middle of the VISIBLE viewport
       while the panel is open, so the on-screen keyboard can't cover it
       (50% is of the layout viewport, which the keyboard doesn't shrink).
       50% stays as the fallback for browsers without visualViewport. */
    top: var(--sgs-vv-mid, 50%);
    left: 50%;
    right: auto;
    width: min(340px, calc(100vw - 32px));
    transform: translate(-50%, calc(-50% - 4px));
  }
  .sgs-acct.is-open .sgs-signin-panel { transform: translate(-50%, -50%); }

  /* The page stays put behind the open sign-in modal, as behind the drawer.
     This stops a finger scrolling it; the scroll the BROWSER makes to reveal
     a focused field ignores overflow:hidden, so sg-account.js puts that back. */
  html.sg-acct-open { overflow: hidden; }
}

/* --- Footer -------------------------------------------------------------- */

.sgs-footer {
  flex-shrink: 0;
  color: var(--footer-fg);
}

.sgs-footer a {
  color: inherit;
  text-decoration: none;
}

.sgs-footer a:hover {
  color: var(--foreground);
}

.sgs-footer-row {
  margin: 0 auto;
  height: 70px;
  max-width: 1600px;
  padding: 0 30px;
  box-sizing: border-box;
}

.sgs-footer-3col {
  display: grid;
  height: 100%;
  grid-template-columns: 1fr 1fr 1fr;
  align-items: center;
  gap: 12px;
}

.sgs-footer-3col > :nth-child(1) { justify-self: start; }
.sgs-footer-3col > :nth-child(2) { justify-self: center; }
.sgs-footer-3col > :nth-child(3) { justify-self: end; }

/* Bookend row. Equal thirds starved the centre column and forced the browse
   list onto two lines; auto | 1fr | auto lets the side cells take only what
   they need and gives the remainder to Browse. Matches v2's
   grid-cols-[auto_1fr_auto] on this row. */
.sgs-footer-3col-bookend {
  grid-template-columns: auto 1fr auto;
}

/* Browse must stay on one line -- it is the row's whole point. */
.sgs-footer-links-nowrap {
  flex-wrap: nowrap;
  white-space: nowrap;
}

.sgs-footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 8px;
}

.sgs-sep {
  opacity: 0.6;
}

.sgs-legal {
  display: flex;
  align-items: center;
  gap: 0 8px;
  white-space: nowrap;
}

/* Bookend row, left cell. Holds Terms/Privacy at every width, and below 800px
   also picks up the copyright that lives in the row above on desktop -- so the
   cell itself has to be the flex row, not the <nav> inside it. */
.sgs-footer-legal-cell {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 8px;
}

/* Second copies of the copyright and the secondary links, for the <800px
   layout only. Hidden here, revealed by the media query at the end. */
.sgs-footer-mobile-only {
  display: none;
}

/* Google Sans auto-superscripts the (c) glyph; Roboto renders it baseline
   aligned like a normal character, which is what the footer wants. */
.sgs-copyright-symbol {
  font-family: Roboto, system-ui, sans-serif;
}

/* First of TWO breakpoints on this shell (the other is 800px, at the end of
   the file). Between 800px and 1024px the desktop layout is otherwise intact;
   only the browse list in the middle of the bookend row is hidden, because it
   is the one thing that cannot shrink. The CELL is kept here -- hiding it would
   collapse Levels & Point Values into the middle track instead of the right
   edge. (Below 800px the cell does get hidden, deliberately; see the end of
   the file.) */
@media (max-width: 1023.98px) {
  .sgs-footer-browse-cell > nav { display: none; }
}

/* --- Content bridge ------------------------------------------------------
   sg-bridge.css predates the shell: it makes .sg-page the page container
   (padding + max-width + its own background), and hard-codes 58 colours with no
   custom properties. Two things happen here.

   1. Layout reset (both themes). The bookend bands have to run edge to edge, so
      the body cannot be the 1200px padded container any more — that role moves
      to .sgs-main.

   2. Dark-mode overrides, scoped under html.dark ONLY. Light mode therefore
      renders byte-identically to today, which keeps this change reversible and
      means a bad token can't break the pages EAs already use.

   Follow-up worth doing: fold these into sg-bridge.css as token references so
   there is one source of truth per property instead of two files overriding
   each other. Left separate for now so the shell can be reviewed and backed out
   as a unit. */

body.sg-page {
  padding: 0;
  max-width: none;
}

/* .sg-header no longer has a border-bottom (removed 2026-08-28), so there is
   nothing here to recolour for dark mode. */
html.dark .sg-header h1 { color: var(--foreground); }
/* .sg-definition is deliberately NOT in this muted group any more
   (2026-08-28) -- it is body prose, not metadata, and it now inherits
   var(--foreground) from body.sg-page in both themes. .sg-charter and .sg-meta
   stay muted because they genuinely are secondary. Re-adding .sg-definition
   here would also silently outrank any page-level colour, since this selector
   is (0,2,1). */
html.dark .sg-charter,
html.dark .sg-meta { color: var(--muted-foreground); }

html.dark .sg-instructions {
  background: var(--surface-2);
  border-left-color: var(--primary);
  color: var(--foreground);
}

html.dark .sg-filter-item label { color: var(--foreground); }
html.dark .sg-filter-item select {
  background: var(--surface-2);
  color: var(--foreground);
  border-color: var(--input);
}

html.dark .sg-btn-secondary { background: var(--secondary); color: var(--secondary-foreground); }
/* Hover is one step lighter than the button. It used var(--surface-3), which in
   dark is the SAME #2a2a2a as --secondary, so the hover changed nothing (Jason,
   2026-11-13). No token sits between #2a2a2a and the text, hence a literal. */
html.dark .sg-btn-secondary:hover { background: #363636; }

html.dark .sg-table { background: var(--card); box-shadow: none; }
html.dark .sg-table th, 
html.dark .sg-table td { border-bottom-color: var(--border); }

/* Header row. sg-bridge.css paints it #54688d, which is pitched for a white
   page and reads as a bright band on a dark one. White label text still
   carries on this. */
html.dark .sg-table th { background: #2e3543; }
html.dark .sg-table td { color: var(--foreground); }
html.dark .sg-table tr:nth-child(even) td { background: var(--surface-2); }
html.dark .sg-table tr:hover td { background: var(--surface-3); }
/* indigo-8 rather than indigo-9: the darker step fails contrast on #1f1f1f. */
html.dark .sg-table a { color: var(--indigo-8); }

/* Links inside table cells (added 2026-11-09): the shell DEFAULT for every
   page on this master, so a new table gets it without a page-level rule.
   It is what FargoRate's .fr-action and the player-history page's .plh-link
   already do, by hand: the link FILLS its cell and hover is a background
   change only -- the text colour never changes.

   The fill is a neutral grey at low alpha, so one value works on every row
   state: on the dark hover row (#2a2a2a) it lands at about #3d3d3d, slightly
   LIGHTER than the row; on the light hover row (#ebebeb), slightly darker.

   Scope: a cell whose only child is a link, outside whole-row links
   (tr[data-href], where the row fill is the affordance and a lit-up single
   cell would contradict it). Pages with richer cells (sub-tournament-new's
   DataDisplay tables) apply the same look in their own stylesheet.

   The td rule needs real specificity to beat `.sg-table td`'s padding; the
   link rules are wrapped in :where() so ANY page-level rule (.fr-action's
   centred 8px 14px, .plh-link's 8px 12px) outranks them.
   ⚠ `height: 1px` on the td is the .fr-action trick, not a typo: without a
   definite cell height, `height: 100%` on the link silently computes to auto
   and a wrapped neighbour leaves half the cell unclickable. */
.sg-page :where(table tr:not([data-href])) td:has(> a[href]:only-child) { padding: 0; height: 1px; }
:where(.sg-page table tr:not([data-href]) td) > a[href]:only-child {
  display: flex;
  align-items: center;
  height: 100%;
  box-sizing: border-box;
  padding: 8px 12px;
  transition: background-color 0.12s ease-in-out;
}
:where(.sg-page table tr:not([data-href]) td) > a[href]:only-child:hover { background-color: rgba(127, 127, 127, 0.22); }
/* Same reason -- sg-bridge.css's #2d6cdf is pitched for a white page. */
html.dark .sg-linkbtn { color: var(--indigo-8); }
/* ⚠ This hover rule is NOT redundant with sg-bridge.css's `.sg-linkbtn:hover`.
   `html.dark .sg-linkbtn` is (0,2,1) and `.sg-linkbtn:hover` is only (0,2,0),
   so the dark rule outranks the hover and the links would never change colour
   in dark mode. Dark hovers to #b78628, light stays #e29025 (Jason, 2026-11-14:
   the v2 pages' dark link hover). */
html.dark .sg-linkbtn:hover { color: #b78628; }


/* Frozen-column fills for .sg-tbl-sticky-1 / -2. Must track the three row
   states in sg-bridge.css exactly -- a frozen cell that isn't opaque lets the
   scrolling columns show through it. */
html.dark .sg-tbl-sticky-1 .sg-table td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table td:nth-child(2) { background: var(--card); }
html.dark .sg-tbl-sticky-1 .sg-table tr:nth-child(even) td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table tr:nth-child(even) td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table tr:nth-child(even) td:nth-child(2) { background: var(--surface-2); }
html.dark .sg-tbl-sticky-1 .sg-table tr:hover td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table tr:hover td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table tr:hover td:nth-child(2) { background: var(--surface-3); }
html.dark .sg-tbl-sticky-1 .sg-table th:nth-child(1),
html.dark .sg-tbl-sticky-1 .sg-table td:nth-child(1),
html.dark .sg-tbl-sticky-2 .sg-table th:nth-child(2),
html.dark .sg-tbl-sticky-2 .sg-table td:nth-child(2) { border-right-color: var(--border); }

html.dark .sg-empty {
  background: var(--card);
  color: var(--muted-foreground);
  border-color: var(--border);
}

html.dark .sg-error,
html.dark .sg-modal-warning {
  background: var(--destructive-bg);
  border-left-color: var(--destructive);
  color: var(--destructive-fg);
}

html.dark .sg-warning {
  background: var(--warn-bg);
  color: var(--warn-fg);
}

html.dark .sg-modal {
  background: var(--card);
  color: var(--foreground);
}

html.dark .sg-modal-info { color: var(--muted-foreground); }
html.dark .sg-modal-info dt { color: var(--foreground); }

html.dark .sg-modal-note { color: var(--muted-foreground); }

/* The backdrop's own tint is already dark; it just needs more of it so the
   frosted panel still separates from a dark page behind it. */
html.dark .sg-modal-backdrop { background: rgba(0, 0, 0, 0.6); }

/* --- Added 2026-08-25 with sg-run-setup / sg-special-weeks / sg-review ---
   Everything below is a surface sg-runs never rendered, so none of it was
   reachable until those three pages joined the shell. Grouped separately
   rather than merged above so the two batches stay auditable. */

/* Bare element selectors in sg-bridge.css (h2/h3 are styled globally there,
   not via a class), so these have to be bare too. Scoped under .sg-page to
   keep them off the shell's own headings. */
/* Section sub-titles: #dddddd in dark (Jason, 2026-11-10); light is #55638b in sg-bridge.css. */
html.dark .sg-page h2 { color: #dddddd; border-bottom-color: var(--border); }
html.dark .sg-page h3 { color: var(--foreground); }
/* ...but NOT the modal's own title, which sg-bridge.css deliberately paints red
   (#c62828 -- these modals only ever confirm a destructive action). The bare
   `.sg-modal h3` in sg-bridge.css is (0,1,1) and loses to the (0,2,2) rule
   above, so the red has to be restated here or the title silently turns white
   in dark mode. */
html.dark .sg-page .sg-modal h3 { color: var(--destructive-fg); }
/* ...except the Adjust Run guard, which confirms a NON-destructive action and
   is amber in both themes.

   ⚠ Specificity, not source order, is what settles this. The rule above is
   (0,3,2); writing this one as `html.dark .sg-page .sg-modal-caution h3` would
   also be (0,3,2) and would win only by sitting later in the file -- one
   re-order away from silently turning the title red again. Chaining the two
   classes makes it (0,4,2), which cannot lose. */
html.dark .sg-page .sg-modal.sg-modal-caution h3 { color: var(--warn-fg); }

/* sg-review's league-session picker. The state chips beside it need nothing
   here -- they are built from token pairs that are already theme-aware. */
html.dark .sg-ls-count { color: var(--muted-foreground); }

/* sg-review's per-session blocks. */
html.dark .sg-session-name { color: var(--indigo-8); border-bottom-color: var(--border); }
html.dark .sg-session-meta-name { color: var(--foreground); }
html.dark .sg-session-meta-id,
html.dark .sg-session-meta-dates,
html.dark .sg-session-stats { color: var(--muted-foreground); }
html.dark .sg-week-header { color: var(--foreground); }

/* Bye rows carry !important in sg-bridge.css (it exists to beat the generic
   .sg-table tr:hover rule), so the dark override has to match it -- without
   !important these stay pale yellow on a dark table. */
html.dark .sg-table tr.sg-bye-row td {
  background: var(--warn-bg) !important;
  color: var(--warn-fg);
}
html.dark .sg-table tr.sg-bye-row:hover td { background: #3d2a0a !important; }

/* Special-week callouts on the review page. Same two tints as the editable
   rows on sg-special-weeks, which style their own copies inline. */
html.dark .sg-week-block.kind-position { background: var(--warn-bg); }
html.dark .sg-week-block.kind-position:hover { background: #3d2a0a; }
html.dark .sg-week-block.kind-position .sg-week-header { color: var(--warn-fg); }
html.dark .sg-week-block.kind-exemption { background: var(--destructive-bg); }
html.dark .sg-week-block.kind-exemption:hover { background: #4a1720; }
html.dark .sg-week-block.kind-exemption .sg-week-header { color: var(--destructive-fg); }

/* .sg-error's <pre> hard-codes a white fill, which is the one thing inside the
   red error panel that stays blinding in dark mode. */
html.dark .sg-error pre {
  background: var(--surface-1);
  color: var(--foreground);
}

/* --- Below 800px ----------------------------------------------------------
   The shell's mobile layout. One breakpoint, and everything it does is here:

     header  -- Contact and the whole browse sub-header bar come out; the
                hamburger goes in. The theme switcher STAYS, which is why the
                drawer carries no second copy of it.
     footer  -- top row keeps only the theme switcher, centred; the bookend row
                becomes copyright + Terms + Privacy on the left and Contact /
                Advertise / FAQ on the right, with Browse and Levels & Point
                Values gone (the drawer carries them).

   799.98 rather than 799 so a fractional viewport width can't land between the
   two ranges and match neither. sg-nav.js repeats this number once, in its
   resize handler -- keep the two in sync. */
@media (max-width: 799.98px) {

  /* --- Header --- */

  /* Contact and the theme switcher go (the drawer carries both), as on v2,
     leaving logo + search pill + hamburger. Until 2026-11-10 the switcher
     stayed; the search pill needs that room. The account trigger is hidden in
     its own block above. */
  .sgs-cluster .sgs-nav { display: none; }
  .sgs-cluster > .sgs-theme { display: none; }

  /* v2's ml-[20px] before the hamburger. With only the hamburger left in the
     cluster (the account wrapper is display:contents and its scrim and panel
     are fixed), its 24px gap no longer applies to anything. */
  .sgs-cluster { margin-left: 20px; }

  .sgs-browse { display: none; }

  .sgs-hamburger { display: inline-flex; }
  .sgs-overlay { display: block; }
  .sgs-drawer { display: flex; }

  /* The hamburger glyph is inset ~10px inside its 50px box, so 30px of row
     padding would optically push it 40px from the edge -- 10px further in than
     the logo on the left. 20px lines the two up.

     padding-left back to 0: the 30px in the base rule is desktop-only, and
     below 800 the logo's own 30px is the whole of the left inset. Done here
     rather than with a `min-width: 800px` query so the width breakpoints stay
     max-width only (1024 / 799.98 / 650 / 399.98), layered rather than
     independent. */
  .sgs-topbar { padding-left: 0; padding-right: 20px; }

  /* Scroll lock while the drawer is open. <html> is the scroller (it carries
     overflow-y: scroll), so locking <body> here would do nothing. The
     scrollbar-gutter: stable up in the Base section is what stops this from
     shifting the page sideways as the scrollbar goes away. */
  html.sg-nav-open { overflow: hidden; }

  /* --- Footer --- */

  /* Rows can now wrap on a narrow screen, so 70px becomes a floor rather than
     a fixed height -- otherwise a wrapped second line is clipped. */
  .sgs-footer-row,
  .sgs-footer-3col {
    height: auto;
    min-height: 70px;
  }

  /* Top row: theme switcher alone, centred. Collapsing to a single track (and
     not just hiding the outer cells) matters -- a display:none grid item is
     removed from placement entirely, so the switcher would auto-place into
     column 1 and sit hard left. */
  .sgs-footer-3col-top { grid-template-columns: 1fr; }
  .sgs-footer-3col-top > :nth-child(1),
  .sgs-footer-3col-top > :nth-child(3) { display: none; }
  .sgs-footer-3col-top > :nth-child(2) { justify-self: center; }

  /* Bookend row: legal left, secondary right. The browse CELL goes (not just
     its nav, as at 1024) so the two survivors auto-place into the two tracks;
     the existing nth-child justify-self rules still key off DOM order, so the
     third cell keeps landing at the right edge. */
  .sgs-footer-3col-bookend { grid-template-columns: 1fr auto; }
  .sgs-footer-browse-cell { display: none; }
  .sgs-footer-resources { display: none; }

  .sgs-footer-mobile-only { display: inline-flex; }
}

/* --- Below 650px ----------------------------------------------------------
   The bookend row keeps ONLY the copyright, centred. Terms / Privacy and
   Contact / Advertise / FAQ come out -- the drawer carries all five -- which
   is also what stops the two groups colliding once the row is too narrow to
   hold both. The theme-switcher row above is untouched.

   Nests inside the 800px query's results rather than repeating them: at this
   width the hamburger, the hidden sub-header and the mobile-only copyright are
   all already in effect. */
@media (max-width: 649.98px) {
  /* Terms / Privacy, and the bullet that separated them from the copyright. */
  .sgs-footer-legal-cell > nav,
  .sgs-footer-legal-cell > .sgs-sep { display: none; }

  /* The whole third cell, not just the nav inside it. A grid item with only
     hidden children is still an item: left in place it would take the second
     track and, once the row collapses to one column, wrap onto a second ROW --
     doubling the band's height with an empty line. */
  .sgs-footer-3col-bookend > :nth-child(3) { display: none; }

  /* One track, and the copyright centred in it. The justify-self overrides the
     base `:nth-child(1) { justify-self: start }`. */
  .sgs-footer-3col-bookend { grid-template-columns: 1fr; }
  .sgs-footer-3col-bookend > :nth-child(1) { justify-self: center; }
}

/* --- Below 400px ----------------------------------------------------------
   The search pill leaves the header band: the header stays its usual 80px
   (logo + hamburger) and the pill sits on the PAGE background below it,
   between the header and the breadcrumbs, 30px in from each side like the
   page content (Jason, 2026-11-10). Nests inside the 800px results: the
   switcher and Contact are already gone.

   It is still the same element in the header's DOM, wrapped onto a second
   flex line. That line paints the page background over the bookend band
   behind it, and its -20px right margin cancels the topbar's 20px right
   padding so the fill runs to the edge. Vertical spacing set by eye (Jason,
   2026-11-10): 20px above the pill, and <main>'s top padding cut from 30px to
   22px below it -- at this width only, since only here does the pill sit
   directly above the page content. */
@media (max-width: 399.98px) {
  .sgs-topbar { flex-wrap: wrap; height: auto; }
  .sgs-logo { height: 80px; box-sizing: border-box; }
  .sgs-cluster { height: 80px; margin-left: auto; }
  .sgs-topbar-centre {
    order: 3;
    flex: 0 0 calc(100% + 20px);
    margin-right: -20px;
    box-sizing: border-box;
    padding: 20px 30px 0;
    background: var(--background);
  }
  .sgs-main { padding-top: 22px; }
}

/* Below 340px (Jason, 2026-11-13): the drawer's band is too narrow for the
   theme switcher + Sign in at their usual spacing; at 320px Sign in ran 17px
   into the close X's box. Spacing only, never sizes: band inset 20 -> 12,
   gap 12 -> 8, the switcher's padding and gap 3 -> 2, Sign in's side padding
   16 -> 12. That buys 24px. The switcher's 34px buttons are untouched. */
@media (max-width: 339.98px) {
  .sgs-drawer-head { padding-left: 12px; gap: 8px; }
  .sgs-drawer-head .sgs-theme { padding: 2px; gap: 2px; }
  .sgs-drawer-head .sgs-signin-btn { padding: 0 12px; }
}

/* --- Search popup (sg-search.js builds the markup) -------------------------
   v2's cmdk-search.tsx: a frosted full-screen backdrop (black/55, blur-sm),
   the card 10vh from the top, max-w-xl, rounded-lg, shadow-xl. It stays
   mounted and fades both ways over 400ms, as v2's does.

   z-index 70: above the header and drawer (hamburger 50) and the account
   scrim/panel (60/61). Below the busy overlay (100). */
.sgs-search-modal {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  box-sizing: border-box;
  padding: 10vh 16px 16px;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s cubic-bezier(0.42, 0, 0.58, 1);
}
html.sg-search-open .sgs-search-modal { opacity: 1; pointer-events: auto; }
/* Scroll lock. html is the scroller here -- see the drawer's lock above. */
html.sg-search-open { overflow: hidden; }

.sgs-search-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 576px;
  max-height: 80vh;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--card-foreground);
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}

.sgs-search-inputrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
}
.sgs-search-inputicon { width: 16px; height: 16px; flex-shrink: 0; color: var(--muted-foreground); }

/* 18px, the page default (Jason, 2026-11-13). Never below 16px: iOS Safari
   zooms the whole page when a field under 16px takes focus, and this popup is
   opened on phones too. */
.sgs-search-input {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--foreground);
  font: inherit;
  font-size: 18px;
}
.sgs-search-input::placeholder { color: var(--muted-foreground); opacity: 1; }

.sgs-search-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--muted-foreground);
}
.sgs-search-close:hover { background: var(--surface-3); color: var(--foreground); }
.sgs-search-close svg { width: 14px; height: 14px; }

.sgs-search-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: 420px;
  overflow-y: auto;
  padding: 6px;
  overscroll-behavior: contain;
}
/* A faint dim while a newer query is on its way, so the rows on screen read as
   the previous answer rather than this one. */
.sgs-search-list.is-loading { opacity: 0.6; transition: opacity 0.15s ease-in-out; }

.sgs-search-heading {
  padding: 8px 10px 4px;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

.sgs-search-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border-radius: 6px;
  color: var(--foreground);
  font-size: 16px;
  text-decoration: none;
  cursor: pointer;
  transition: none;   /* the shell's anchor transition would lag the highlight behind the arrow keys */
}
.sgs-search-item[aria-selected="true"] {
  background: var(--primary);
  color: var(--primary-foreground);
}
.sgs-search-icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--muted-foreground); }
.sgs-search-item[aria-selected="true"] .sgs-search-icon { color: var(--primary-foreground); }

.sgs-search-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sgs-search-meta {
  flex: 0 1 auto;
  max-width: 55%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  color: var(--muted-foreground);
}
.sgs-search-item[aria-selected="true"] .sgs-search-meta { color: rgba(255, 255, 255, 0.8); }

/* The "See all results" row sits under a hairline, apart from the matches. */
.sgs-search-all { margin-top: 6px; }
.sgs-search-group:has(.sgs-search-all):not(:first-child) {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.sgs-search-empty {
  padding: 24px 12px;
  text-align: center;
  font-size: 16px;
  color: var(--muted-foreground);
}

.sgs-search-foot {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 11px;
  color: var(--muted-foreground);
}
.sgs-search-foot span { display: inline-flex; align-items: center; gap: 4px; }
.sgs-search-foot kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  box-sizing: border-box;
  padding: 0 4px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--background);
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 11px;
}
/* Keyboard hints are noise on a touch screen. */
@media (hover: none) and (pointer: coarse) {
  .sgs-search-foot { display: none; }
}
/* On a phone the 10vh gap above the box doesn't shrink when the keyboard
   opens, so it costs result rows (Jason, 2026-11-13). The box's height is also
   capped to the space above the keyboard by sg-search.js (fitToKeyboard). */
@media (max-width: 599.98px) {
  .sgs-search-modal { padding-top: 20px; }
  /* No 420px cap on the list here: it fills the box up to the box's own cap
     (80vh, or the space above the keyboard), so a phone shows ~5 more rows. */
  .sgs-search-list { max-height: none; }
}

/* ── Busy overlay (sg-busy.js, markup in sg.master) ──────────────────────────
   Shown during schedule generation, which is a full postback that can run most
   of a minute.

   Built entirely from the shell's THEME TOKENS -- so one rule set is correct in
   both light and dark and there is no html.dark twin to keep in sync. That is
   deliberately unlike sg-bridge.css, which hard-codes its colours and needs a
   matching override for every one of them. */
.sg-busy {
  position: fixed;
  inset: 0;
  z-index: 100;                 /* above the header (50) and the nav drawer */
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}

/* ⚠ Restated because .sg-busy sets display:flex, an AUTHOR rule, which beats
   the UA stylesheet's [hidden] { display: none }. Without this the overlay
   would be permanently visible on every page in the shell. Same trap the
   sg-sessions warning rows and .sg-modal-error hit. */
.sg-busy[hidden] { display: none; }

.sg-busy-box {
  background: var(--card);
  color: var(--card-foreground);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 28px 34px;
  max-width: 420px;
  text-align: center;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
}

.sg-busy-box p { margin: 0 0 8px; }
.sg-busy-box p:last-child { margin-bottom: 0; }

.sg-busy-title { font-size: 22px; }   /* weight 400 and line height 1.5 come from the page (Jason, 2026-11-13) */
.sg-busy-note { color: var(--muted-foreground); }
.sg-busy-timer { color: var(--muted-foreground); font-variant-numeric: tabular-nums; }

.sg-busy-spinner {
  width: 34px;
  height: 34px;
  margin: 0 auto 14px;
  border: 3px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: sg-busy-spin 0.9s linear infinite;
}

@keyframes sg-busy-spin { to { transform: rotate(360deg); } }

/* Respect a reduced-motion preference: keep the ring, drop the rotation. The
   elapsed counter still shows progress, so nothing is lost. */
@media (prefers-reduced-motion: reduce) {
  .sg-busy-spinner { animation: none; }
}

/* html is the scroller on these pages (it carries overflow-y: scroll), so the
   lock goes here and not on body -- the same reason sg-nav.js locks html for
   the drawer. */
html.sg-busy-open { overflow: hidden; }

/* --- Floating-label fields (Jason, 2026-11-11) ---------------------------
   Every form field on the v2 pages EXCEPT the Ctrl+K search box. Ported from
   mfg v2: components/auth/field-styles.ts (INPUT_CLASS, SELECT_CLASS) and
   components/auth/floating-field.tsx (FloatingLabel, SelectLabel).
     field  46px tall, 18px text, 0 18px padding, 1px border that goes to
            3px on focus, 4px corners, transparent fill (a select: the surface)
     label  18px resting inside the empty field (#777 / #676767); 16px floated
            onto the top border (#1a5289 / #6a8aaf); 100ms ease-in-out
   ⚠ All in px on purpose. mfg's root is 18px and this shell's is 16px, so
   copying mfg's rem-based class names would come out 11% smaller here.

   Markup:  <div class="sg-fl"> <input ...> <label for="...">Label</label> </div>
   The label may sit anywhere inside the wrapper (v1's search control wraps
   its input in a div of its own) because every state keys off :has() on the
   wrapper. A text field needs a placeholder so :placeholder-shown tracks
   emptiness; scripts/sg-fields.js gives any field without one placeholder=" ",
   so markup can't forget. A REAL placeholder (an example such as "e.g.
   Christmas Eve") is allowed: it stays invisible until the field is focused,
   by which time the label has floated out of its way. Selects, select2 and
   date fields have no placeholder-shown state, so their labels stay floated --
   what mfg's SelectLabel does.

   --sg-fl-bg is the surface BEHIND the field. The floated label's cut-out and
   a select's fill both use it, so a field on a card or in a modal must sit in
   a container that sets it, or a pale rectangle shows behind the label.

   The html prefix is deliberate: sg-bridge.css and sg-shell.css carry broad
   rules (html.dark .sg-filter-item select, .sg-modal textarea) that would
   otherwise beat these in one theme and not the other. */
:root { --sg-fl-bg: var(--background); }
.sg-modal,
.stn-modal,
.sgs-acct-panel { --sg-fl-bg: var(--card); }
.sg-modal { --sg-fl-bg: #ffffff; }
html.dark .sg-modal { --sg-fl-bg: var(--card); }

.sg-fl { position: relative; display: block; }

html .sg-fl :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], .searchControlsHiddenIDBox, .select2-offscreen, .select2-focusser, .select2-input), select:not(.select2-offscreen), textarea) {
  box-sizing: border-box;
  display: block;
  /* !important: v1's ppmu controls write width/height INLINE from their
     Width/Height properties. The wrapper sets the width instead. */
  width: 100% !important;
  height: 46px !important;
  max-width: 100%;
  margin: 0;
  padding: 0 18px;
  font-family: inherit;
  font-size: 18px;
  font-weight: normal;
  line-height: normal;
  color: var(--foreground);
  background: transparent;
  border: 1px solid rgba(31, 31, 31, 0.57);
  border-radius: 4px;
  box-shadow: none;
  outline: none;
  transition: border-color 100ms ease-in-out, border-width 100ms ease-in-out,
              background-color 100ms ease-in-out, color 100ms ease-in-out;
}
html .sg-fl select:not(.select2-offscreen) { background: var(--sg-fl-bg); cursor: pointer; }
html .sg-fl :is(input, textarea)::placeholder { color: #777777; opacity: 1; }
html.dark .sg-fl :is(input, textarea)::placeholder { color: #676767; }
html .sg-fl :is(input, textarea):not(:focus)::placeholder { color: transparent; }
html .sg-fl textarea { height: auto !important; min-height: 120px; padding: 12px 18px; line-height: 1.4; resize: vertical; }
html .sg-fl :is(input, select, textarea):disabled { opacity: 0.55; cursor: not-allowed; }
html .sg-fl :is(input, select, textarea):hover { border-color: rgba(31, 31, 31, 0.8); }
/* The 3px border IS the focus indicator, as on mfg, so no outline.
   !important because older broad rules (.stn input.input:focus in
   sub-tournament-new.css, .sg-modal input[type="text"]:focus in
   sg-bridge.css) add a 2px outline at higher specificity, which drew a
   second ring around the focused field (found by Jason, 2026-11-11). */
html .sg-fl :is(input, select, textarea):focus { border-width: 3px; border-color: #1a5289; outline: none !important; }
html.dark .sg-fl :is(input:not([type="checkbox"], [type="radio"]), select, textarea) { border-color: rgba(200, 200, 200, 0.3); }
html.dark .sg-fl :is(input:not([type="checkbox"], [type="radio"]), select, textarea):hover { border-color: rgba(200, 200, 200, 0.5); }
html.dark .sg-fl :is(input:not([type="checkbox"], [type="radio"]), select, textarea):focus { border-color: #6a8aaf; }

/* The label. FLOATED is the base state, as in mfg; "resting" overrides it
   while the text field is empty and unfocused. */
.sg-fl > label {
  position: absolute;
  left: 18px;
  top: 0;
  z-index: 21;               /* over v1's .ui-front (z-index 20) */
  transform: translateY(-50%);
  max-width: calc(100% - 36px);
  margin: 0;
  padding: 0 4.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 16px;
  font-weight: normal;
  line-height: 1.3;
  color: #1a5289;
  background: var(--sg-fl-bg);
  pointer-events: none;
  transition: top 100ms ease-in-out, font-size 100ms ease-in-out, padding 100ms ease-in-out,
              color 100ms ease-in-out, background-color 100ms ease-in-out;
}
html.dark .sg-fl > label { color: #6a8aaf; }
/* select2's own inputs (.select2-input, its search box, and .select2-focusser)
   are always empty, so they are kept out of this test or the label would
   never float over a select2. */
.sg-fl:has(:is(input:not(.select2-input, .select2-focusser), textarea):placeholder-shown):not(:has(:focus)) > label {
  top: 23px;                 /* the middle of a 46px field; a textarea's first line */
  padding: 0;
  font-size: 18px;
  color: #777777;
}
html.dark .sg-fl:has(:is(input:not(.select2-input, .select2-focusser), textarea):placeholder-shown):not(:has(:focus)) > label { color: #676767; }

/* select2 (v1's ppmu:DropDownList): the visible box is select2's own anchor,
   not the native select (which select2 hides with .select2-offscreen). */
/* height/line-height: select2 copies the native select's classes onto its
   own container, and v1's .BasicDDL is height: 30px -- which left the 46px
   box overhanging a 30px wrapper and the next row sitting on it. */
html .sg-fl .select2-container { display: block; width: 100% !important; height: auto; line-height: normal; }
html .sg-fl .select2-container .select2-choice {
  box-sizing: border-box;
  height: 46px;
  line-height: 44px;
  padding: 0 0 0 18px;
  font-size: 18px;
  color: var(--foreground);
  background: var(--sg-fl-bg);
  background-image: none;
  border: 1px solid rgba(31, 31, 31, 0.57);
  border-radius: 4px;
  box-shadow: none;
  transition: border-color 100ms ease-in-out, border-width 100ms ease-in-out;
}
html .sg-fl .select2-container .select2-choice:hover { border-color: rgba(31, 31, 31, 0.8); }
html .sg-fl .select2-container-active .select2-choice,
html .sg-fl .select2-dropdown-open .select2-choice { border-width: 3px; border-color: #1a5289; line-height: 40px; padding-left: 16px; }
html .sg-fl .select2-dropdown-open .select2-choice { border-radius: 4px 4px 0 0; }
/* select2.css centres the text and pads it; left-aligned at the field's 18px
   here, like every other field. */
html .sg-fl .select2-container .select2-choice { text-align: left; }
html .sg-fl .select2-container .select2-choice > .select2-chosen { margin-right: 34px; padding: 0; }
/* select2's arrow is a sprite (select2.png) cut for an 18px-wide box; in a
   46px field its neighbouring arrow showed at the edge. A chevron instead,
   in each theme's muted text colour (lucide chevron-down, percent-encoded). */
html .sg-fl .select2-container .select2-choice .select2-arrow { width: 34px; background: transparent; border-left: 0; }
html .sg-fl .select2-container .select2-choice .select2-arrow b {
  background: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22%23555555%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22m6%209%206%206%206-6%22/%3E%3C/svg%3E") no-repeat center / 16px 16px;
}
html.dark .sg-fl .select2-container .select2-choice .select2-arrow b {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22%23b7b7b7%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22m6%209%206%206%206-6%22/%3E%3C/svg%3E");
}
html .sg-fl .select2-default { color: #777777 !important; }
html.dark .sg-fl .select2-container .select2-choice { border-color: rgba(200, 200, 200, 0.3); }
html.dark .sg-fl .select2-container .select2-choice:hover { border-color: rgba(200, 200, 200, 0.5); }
html.dark .sg-fl .select2-container-active .select2-choice,
html.dark .sg-fl .select2-dropdown-open .select2-choice { border-color: #6a8aaf; }
html.dark .sg-fl .select2-default { color: #676767 !important; }

/* A filter row of floating-label fields (sg-runs' Season / Day, sg-review's
   session picker, FargoRate's send bar): room above for the floated labels,
   and its buttons at the fields' 46px, so a button beside a field lines up
   with it -- mfg's rule for every form button. */
.sg-filter-row:has(.sg-fl) { padding-top: 10px; }
.sg-filter-item:has(> .sg-fl) { min-width: 220px; }
.sg-filter-row:has(.sg-fl) .sg-btn { height: 46px; }

/* ── Table pager (v1 DataDisplay) on the v2 pages ─────────────────────────────
   Jason, 2026-11-13. Every button is the same 34px circle (the theme
   switcher's button size): page numbers at the tables' 15px, the current page
   filled and bold, and the first / previous / next / last buttons drawn with
   lucide's chevrons-left / chevron-left / chevron-right / chevrons-right as
   inline SVG instead of dataTables.css's PNG sprite. Hover fills the circle
   grey, like v1's grey box, with NO colour change (v1 turned it orange).
   The arrows are <input type="image"> whose src is a transparent placeholder,
   so the icon is the background. These rules outrank dataTables.css's
   sprite rules (including its :hover and :disabled ones) by specificity.
   The SVGs carry their colour (a background image can't use currentColor):
   --muted-foreground, #60646c light / #b7b7b7 dark. Fully percent-encoded. */
.sg-page .paginationRow {
  --sgpg-first: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%2360646c%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m11%2017-5-5%205-5%27%2F%3E%3Cpath%20d%3D%27m18%2017-5-5%205-5%27%2F%3E%3C%2Fsvg%3E");
  --sgpg-prev: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%2360646c%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m15%2018-6-6%206-6%27%2F%3E%3C%2Fsvg%3E");
  --sgpg-next: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%2360646c%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m9%2018%206-6-6-6%27%2F%3E%3C%2Fsvg%3E");
  --sgpg-last: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%2360646c%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m6%2017%205-5-5-5%27%2F%3E%3Cpath%20d%3D%27m13%2017%205-5-5-5%27%2F%3E%3C%2Fsvg%3E");
  height: auto;
  margin: 0;
}
html.dark .sg-page .paginationRow {
  --sgpg-first: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%23b7b7b7%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m11%2017-5-5%205-5%27%2F%3E%3Cpath%20d%3D%27m18%2017-5-5%205-5%27%2F%3E%3C%2Fsvg%3E");
  --sgpg-prev: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%23b7b7b7%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m15%2018-6-6%206-6%27%2F%3E%3C%2Fsvg%3E");
  --sgpg-next: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%23b7b7b7%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m9%2018%206-6-6-6%27%2F%3E%3C%2Fsvg%3E");
  --sgpg-last: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%23b7b7b7%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m6%2017%205-5-5-5%27%2F%3E%3Cpath%20d%3D%27m13%2017%205-5-5-5%27%2F%3E%3C%2Fsvg%3E");
}
.sg-page .paginationRow .paginationInner { display: block; height: auto; }
.sg-page .paginationRow .paginationInner > span {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 4px;
}
.sg-page .paginationRow .CurrentPageLabelCSS,
.sg-page .paginationRow a.NumericButtonCSS,
.sg-page .paginationRow input[type="image"] {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0 !important;
  margin: 0 !important;
  border: 0 !important;
  border-radius: 50%;
  font-size: 15px;
  line-height: 1;
  vertical-align: middle;
  text-decoration: none;
  background-color: transparent;
  transition: background-color .15s ease-in-out;
}
.sg-page .paginationRow a.NumericButtonCSS:link,
.sg-page .paginationRow a.NumericButtonCSS:visited,
.sg-page .paginationRow a.NumericButtonCSS:link:hover,
.sg-page .paginationRow a.NumericButtonCSS:visited:hover { color: var(--muted-foreground); }
/* The current page is one step past the hover grey (var(--muted)), so it stays
   distinct while another number is hovered (Jason, 2026-11-13): darker in
   light, lighter in dark, by the same perceived amount (~5.5 L*:
   #f0f0f3 -> #e0e1e6, #2a2a2a -> #363636). */
.sg-page .paginationRow .CurrentPageLabelCSS {
  background-color: #e0e1e6;
  color: var(--foreground);
  font-weight: 600;
}
html.dark .sg-page .paginationRow .CurrentPageLabelCSS { background-color: #363636; }
.sg-page .paginationRow input[type="image"] {
  cursor: pointer;
  object-position: -9999px 0; /* keep the placeholder image out of sight */
  background-repeat: no-repeat;
  background-position: center;
  background-size: 18px 18px;
}
.sg-page .paginationRow input.firstPageButton { background-image: var(--sgpg-first); }
.sg-page .paginationRow input.previousPageButton { background-image: var(--sgpg-prev); }
.sg-page .paginationRow input.nextPageButton { background-image: var(--sgpg-next); }
.sg-page .paginationRow input.lastPageButton { background-image: var(--sgpg-last); }
.sg-page .paginationRow a.NumericButtonCSS:hover,
.sg-page .paginationRow input[type="image"]:not(:disabled):hover { background-color: var(--muted); }
.sg-page .paginationRow input[type="image"]:disabled { opacity: .35; cursor: default; }

/* Add further breakpoints below this line. */
