/* ============================================================================
   HKPL DESIGN SYSTEM — shared responsive component library (B1).
   Injected into EVERY page's <head> (server.js) so one definition per component
   works at phone/tablet/desktop by design — no device overlay, no !important wall.
   Populated 2026-09-06 per docs/PLAN_template_rearchitecture.md. Add components here;
   as each lands, its shell overrides are deleted.
   ============================================================================ */

/* ---- COMPONENT: segmented / chip-tab strip -------------------------------
   A row of `.group-tab` (or division/filter) chips. Base bug: the segmented
   wrapper is `inline-flex … overflow-hidden; flex-wrap:nowrap` and the chips
   default to `flex-shrink:1; min-width:0`, so with many items (e.g. 10 groups)
   they crush to ~36px and the text clips ("All groups" -> "All C", "Group 1"
   -> "Gro"). Responsive-native fix: chips keep their natural width and the
   strip SCROLLS horizontally when it overflows — correct at every width, on the
   standard site and under every shell. Specificity (0,1,1) beats the Tailwind
   `.overflow-hidden` utility (0,1,0) with no !important. */
:is(div, nav, span):has(> .group-tab),
:is(div, nav, span):has(> .div-tab) {
  overflow-x: auto;
  overflow-y: hidden;
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;            /* Firefox: hide the bar, keep the scroll */
}
:is(div, nav, span):has(> .group-tab)::-webkit-scrollbar,
:is(div, nav, span):has(> .div-tab)::-webkit-scrollbar { height: 0; display: none; }
.group-tab, .div-tab { flex-shrink: 0; }

/* ---- COMPONENT: data table (ranked) — mobile-first --------------------------
   Any table in a .tbl-scroll wrapper FITS the phone and truncates its NAME column
   (2nd) so the figures stay visible on load rather than scrolling off-screen.
   Covers standings + club leaderboard + any future ranked table (family fix). */
@media (max-width: 640px) {
  .tbl-scroll > table, .tbl-scroll table { min-width: 0 !important; width: 100%; }
  .tbl-scroll table td:nth-child(2), .tbl-scroll table th:nth-child(2) { max-width: 40vw; }
  .tbl-scroll table td:nth-child(2) a,
  .tbl-scroll table td:nth-child(2) > span:first-child { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
  .tbl-scroll table th, .tbl-scroll table td { padding-left: 6px !important; padding-right: 7px !important; }
}
/* compact the numeric columns (3rd onward) so many-column ranked tables fit the phone without hiding figures */
@media (max-width: 640px) {
  .tbl-scroll table { font-size: 12px; }
  .tbl-scroll table th:nth-child(n+3), .tbl-scroll table td:nth-child(n+3) { padding-left: 2px !important; padding-right: 3px !important; }
  .tbl-scroll table td:nth-child(2) { max-width: 32vw; }
}

/* RANKED-TABLE FILL-V2 (2026-09-07, operator-approved on-device): the name column HUGS its text
   (width:1%) so the P/W/L/PTS columns spread to fill the full-width card instead of leaving a blank
   gutter, and the last column (PTS) is pulled in from the card edge. Supersedes the width:100%-only
   fit which still left a dead band because the capped name + tiny number columns never absorbed the
   extra width. Family: any .tbl-scroll ranked table (standings + club-leaderboard). */
@media (max-width: 640px) {
  .tbl-scroll table td:nth-child(2), .tbl-scroll table th:nth-child(2) { width: 1% !important; max-width: 55vw !important; white-space: nowrap; }
  .tbl-scroll table td:last-child, .tbl-scroll table th:last-child { padding-right: 18px !important; }
}

/* SITE-FOOTER-MINIMAL-MOBILE-V1 (2026-09-07, operator-approved on-device): on phones the full footer
   sitemap duplicates the hamburger menu (verified: all 16 footer nav links are in the mobile menu, 0
   stranded), and the grid left a big empty right cell + 44px tap-target rows wasted vertical space. Hide
   the 3 nav columns (Explore/Rankings/Compete = .fwrap children 2-4; brand+tagline=child 1 and
   Connect/socials=child 5 stay), lay the Connect links in a horizontal wrap row, tighten spacing.
   Desktop footer unchanged. Footer built by site-header.js (#hkpl-sf). */
@media (max-width: 640px) {
  #hkpl-sf { margin-top: 20px; }
  #hkpl-sf .fwrap { padding: 16px 16px 8px !important; }
  #hkpl-sf .fwrap > *:nth-child(n+2):nth-child(-n+4) { display: none !important; }
  #hkpl-sf .fbrand { margin-bottom: 10px !important; }
  #hkpl-sf .fwrap > *:last-child h4 { margin: 0 0 6px !important; }
  #hkpl-sf .fwrap > *:last-child ul { display: flex !important; flex-wrap: wrap; gap: 2px 20px !important; }
  #hkpl-sf .fwrap > *:last-child li { margin: 0 !important; }
  #hkpl-sf .fwrap > *:last-child a { min-height: 34px !important; }
}

/* ROOT-RESERVE-V1 (2026-09-07): JS-rendered pages inject into #root late (empty->tall) which shifted visible
   content below it (CLS). Reserve a viewport so the footer starts below the fold and the late render does
   not push visible content down. Content taller than 100vh still grows (only below-fold). */
#root, #content, #board { min-height: 100vh; }
.feed-grid .main { min-height: 100vh; }
#sponsor-tiers { min-height: 60vh; }
#hk-map { min-height: 245px; }
#climbers { min-height: 305px; }
/* FILTER-ROW-RESERVE-0927: the shared league tabs row (js/league-nav.js fills #league-nav after first paint) pushed the
   page below it down 44 px on every page that carries it (results, schedule, standings, club rankings, stats, team
   stats; measured on hkpl.com.hk 27 Sep at 412 and 390 px: a 44 px row plus the 10 px bottom margin of its .lnav, which
   stops collapsing through once the box has a min-height, so 54 px is held). On a phone the row never wraps
   (flex-wrap:nowrap + sideways scroll), so its height is known before it is drawn. The desktop row wraps by language
   and width, so it is not reserved there. */
@media (max-width: 639px) { #league-nav { min-height: 54px; } }

/* ============================================================================
   DS-LAYER-RESTORE-0924-V1 (2026-09-24): commit 0fed9a3a (2026-09-07) rewrote this file and dropped every DS-1/DS-3
   component rule, so the classes below have painted nothing since: the loading skeletons on standings / results /
   schedule / topten were a 1px dot, the home next-match card had no surface, the error state box (my-lineup) and
   the ds-states.js buttons were browser defaults. Restored from 0fed9a3a^ - only classes still used in public/ - with
   colours from the current tokens (design-tokens.css / design-tokens-vars.css), never literals, so light, dark and
   the 7 Sep redesign keep their own values. Not restored (no longer used anywhere): .ds-card, .ds-btn--primary,
   .ds-btn--ghost. Standing check: scripts/ds-classes-have-rules.test.cjs (every .ds-* class used in public/ has a rule).
   ============================================================================ */

/* ---- button (ds-states.js retry/action buttons + the home next-match buttons) ---- */
.ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:0 var(--space-5,20px);border-radius:var(--r-md,10px);font-family:inherit;font-size:var(--fs-sm,.9rem);font-weight:800;line-height:1;text-decoration:none;box-sizing:border-box;border:1.5px solid transparent;cursor:pointer;-webkit-appearance:none;appearance:none;transition:background var(--dur-fast,120ms) ease,border-color var(--dur-fast,120ms) ease,box-shadow var(--dur-fast,120ms) ease,transform var(--dur-fast,120ms) ease}
.ds-btn:active{transform:translateY(1px)}
.ds-btn:focus-visible{outline:2px solid var(--focus,var(--brand,#008034));outline-offset:2px}
.ds-btn--secondary{background:var(--card,#fff);color:var(--brand-strong,#006c2c);border-color:var(--brand,#008034)}
.ds-btn--secondary:hover{background:var(--brand-soft,#ecfdf5)}
.ds-btn--danger{background:var(--danger,#dc2626);color:var(--on-danger,#fff);border-color:var(--danger,#dc2626);box-shadow:var(--shadow-sm,0 1px 2px rgba(10,14,12,.06))}
.ds-btn--danger:hover{filter:brightness(.94)}
/* inverse = a button on a brand-coloured surface (old rule used #fff / rgba(255,255,255,..) literals; --on-brand is white
   in the light theme, so light is unchanged). The dark-theme .ds-btn--inverse override in design-tokens.css still wins. */
.ds-btn--inverse{background:var(--on-brand,#fff);color:var(--brand-700,#026b34);border-color:var(--on-brand,#fff)}
.ds-btn--inverse:hover{background:color-mix(in srgb,var(--on-brand,#fff) 90%,transparent)}
.ds-btn--inverse-ghost{background:color-mix(in srgb,var(--on-brand,#fff) 16%,transparent);color:var(--on-brand,#fff);border-color:color-mix(in srgb,var(--on-brand,#fff) 38%,transparent)}
.ds-btn--inverse-ghost:hover{background:color-mix(in srgb,var(--on-brand,#fff) 26%,transparent)}

/* ---- home next-match card + its "next" rows (app.html _spineCard / _spineNextRow) ----
   The old card was a brand-green gradient with white text. SPINE-ON-PAGE-TOKENS-0924-V1 moved its buttons and team
   chips to page-surface colours (brand-filled button, card-filled chips), and in the dark theme --brand-600/700 are
   LIGHT greens, so the old gradient would put white text on light green. Restored as a page card: the old geometry
   (width, radius, padding, shadow) on the card surface with the page ink. */
.ds-spine-card{max-width:760px;margin:0 auto;border-radius:var(--r-lg,14px);padding:var(--space-5,20px);background:var(--card,#fff);color:var(--ink,#111827);border:1px solid var(--line,#e5e7eb);box-shadow:var(--shadow-md,0 6px 18px -8px rgba(10,14,12,.18));box-sizing:border-box}
.ds-next-row{display:flex;align-items:center;gap:var(--space-3,12px);min-height:48px;padding:var(--space-3,12px) var(--space-4,16px);background:var(--card,#fff);border:1px solid var(--line,#e5e7eb);border-radius:var(--r-md,10px);text-decoration:none;color:var(--ink,#111827);transition:box-shadow var(--dur-fast,120ms) ease,border-color var(--dur-fast,120ms) ease}
.ds-next-row:hover{box-shadow:var(--shadow-sm,0 1px 2px rgba(10,14,12,.06));border-color:var(--brand-200,#cdeadb)}
/* .ds-tile is drawn only by app.html renderSimpleHome(), unreachable since SIMPLE-REMOVED-0924-V1; kept while the source uses it. */
.ds-tile{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2,8px);min-height:104px;padding:var(--space-4,16px) var(--space-3,12px);border-radius:var(--r-lg,14px);text-decoration:none;background:var(--card,#fff);border:1px solid var(--line,#e5e7eb);box-shadow:var(--shadow-sm,0 1px 2px rgba(10,14,12,.06));color:var(--ink,#111827);transition:box-shadow var(--dur-fast,120ms) ease,transform var(--dur-fast,120ms) ease,border-color var(--dur-fast,120ms) ease}
.ds-tile:hover{box-shadow:var(--shadow-md,0 6px 18px -8px rgba(10,14,12,.18));border-color:var(--brand-200,#cdeadb)}

/* ---- loading skeleton (ds-states.js skeleton()/skeletonHTML(); boot markup in standings/results/schedule/topten) ---- */
@keyframes ds-shimmer{0%{background-position:-160% 0}100%{background-position:160% 0}}
.ds-skeleton{--ds-sk-base:var(--surface-2,#f1f3f5);--ds-sk-hi:var(--line,#e5e7eb);display:block;border-radius:var(--r-sm,6px);background-color:var(--ds-sk-base);background-image:linear-gradient(100deg,transparent 0,var(--ds-sk-hi) 45%,var(--ds-sk-hi) 55%,transparent 100%);background-size:200% 100%;background-repeat:no-repeat;background-position:-160% 0;animation:1.4s ease-in-out infinite ds-shimmer;opacity:.9}
.ds-skeleton-text{height:.8em;margin:var(--space-2,8px) 0;border-radius:var(--r-sm,6px)}
.ds-skeleton-text:last-child{width:70%}
.ds-skeleton-title{height:1.25em;width:55%;margin:0 0 var(--space-3,12px);border-radius:var(--r-sm,6px)}
.ds-skeleton-avatar{width:44px;height:44px;border-radius:var(--r-pill,999px);flex:0 0 auto}
.ds-skeleton-card{background:var(--card,#fff);border:1px solid var(--line,#e5e7eb);border-radius:var(--r-lg,14px);box-shadow:var(--shadow-sm,0 1px 2px rgba(10,14,12,.06));padding:var(--space-4,16px);box-sizing:border-box}
.ds-skeleton-row{display:flex;align-items:center;gap:var(--space-3,12px)}
.ds-skeleton-row>.ds-skeleton-lines{flex:1 1 auto;min-width:0}

/* ---- empty state (ds-states.js empty()/emptyHTML()) ---- */
.ds-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:var(--space-3,12px);padding:var(--space-6,24px) var(--space-5,20px);border:1px dashed var(--line,#e5e7eb);border-radius:var(--r-lg,14px);background:var(--surface,#fff);color:var(--muted,#6b7280);box-sizing:border-box}
.ds-empty__icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:var(--r-pill,999px);background:var(--surface-2,#f1f3f5);color:var(--muted,#6b7280);font-size:var(--fs-xl,1.6rem);line-height:1}
.ds-empty__icon svg{width:26px;height:26px}
.ds-empty__title{margin:0;font-size:var(--fs-lg,1.15rem);font-weight:800;color:var(--ink,#111827);line-height:1.3}
.ds-empty__msg{margin:0;max-width:42ch;font-size:var(--fs-sm,.9rem);color:var(--muted,#6b7280);line-height:1.5}
.ds-empty__action{margin-top:var(--space-2,8px)}

/* ---- error state (ds-states.js error()/errorHTML(); my-lineup load failure) ---- */
.ds-error{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:var(--space-3,12px);padding:var(--space-6,24px) var(--space-5,20px);border:1px solid var(--err-line,var(--danger,#dc2626));border-radius:var(--r-lg,14px);background:var(--err-bg,var(--danger-soft,#fef2f2));color:var(--err-fg,var(--danger,#991b1b));box-sizing:border-box}
.ds-error__icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:var(--r-pill,999px);background:var(--card,#fff);border:1.5px solid var(--err-line,var(--danger,#dc2626));color:var(--err-strong,var(--danger,#b91c1c));font-size:var(--fs-xl,1.6rem);line-height:1}
.ds-error__icon svg{width:26px;height:26px}
.ds-error__title{margin:0;font-size:var(--fs-lg,1.15rem);font-weight:800;color:var(--err-fg,var(--danger,#991b1b));line-height:1.3}
.ds-error__msg{margin:0;max-width:42ch;font-size:var(--fs-sm,.9rem);color:var(--err-fg,var(--danger,#991b1b));opacity:.92;line-height:1.5}
.ds-error__action{margin-top:var(--space-2,8px)}

@media (prefers-reduced-motion:reduce){
  .ds-btn,.ds-next-row,.ds-tile{transition:none}
  .ds-btn:active,.ds-tile:active{transform:none}
  .ds-skeleton{animation:none;background-image:none;background-color:var(--surface-2,#f1f3f5)}
}
