/* Sporting Hub Hong Kong — prototype v2
   Landing page and shared chrome (nav, drawer, footer, buttons, WhatsApp
   float, tweaks panel). Copied from html/mockup-v2/mockup.css (the approved
   mockup) per the brief; booking.html does not load this file, it is
   self-contained in booking.css (v1's own architecture: booking.html only
   ever loaded _shared.css, never index.html's styles). Tokens only from
   tokens.css: every radius is one of the seven --r-* custom properties (or
   50% for circles).
   One fix applied versus the approved mockup: `.wa-float svg path` used a
   literal `fill:#fff`, which breaks the site-wide "no #FFF" rule (DESIGN.md
   Bans, and this brief's Rules section). Changed to `var(--court-cream)`,
   visually indistinguishable on the WhatsApp-green circle. No other change
   from the approved mockup.css. */

/* ============ reset & base ============ */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; }
html,body{ max-width:100%; overflow-x:hidden; }
body{
  margin:0; background:var(--court-cream); color:var(--charcoal);
  font-family:var(--f-body); font-size:var(--fs-body); line-height:1.6;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
}
img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
ul{ margin:0; padding:0; list-style:none; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; }
svg{ display:block; }
p{ margin:0 0 14px; }
p:last-child{ margin-bottom:0; }
:focus-visible{ outline:2px solid var(--sideline-navy); outline-offset:2px; }
.wrap *{ min-width:0; }

/* ============ layout helpers ============ */
.wrap{ max-width:var(--wrap); margin:0 auto; padding:0 var(--gut); }
/* eixo pattern: photo grids break out of the 1200px text column and run near
   full-bleed, inset only by the page gutter. Same edge inset as .wrap, no
   width cap until a very wide screen. Used for the pillar and space grids only. */
.bleed{ max-width:1680px; margin:0 auto; padding:0 20px; }
@media(min-width:760px){ .bleed{ padding:0 32px; } }
/* Review fix 2026-09-09 (Alfred, point 4): sections carried --section-gap on
   both top and bottom, so two adjacent sections stacked 112 + 112 = 224px of
   air on desktop (144px mobile), double what DESIGN.md specifies ("72px
   mobile / 112px desktop between marketing sections"). Top padding only now,
   so the gap between any two sections is exactly one --section-gap; the last
   section keeps a bottom gap before the footer. Hero to first section is
   unchanged at one gap. */
.section{ padding:var(--section-gap) 0 0; }
section.section:last-of-type{ padding-bottom:var(--section-gap); }
.section-cool{ background:var(--cool-panel); }
.overflow-x-auto{ overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; }
.overflow-x-auto::-webkit-scrollbar{ display:none; }

/* ============ typography ============ */
h1,h2,h3,h4{ margin:0; font-family:var(--f-display); color:var(--deep-navy); }

.eyebrow{
  display:block; font-family:var(--f-display); font-weight:600;
  font-size:var(--fs-eyebrow); text-transform:uppercase; letter-spacing:.08em;
  color:var(--sideline-navy); margin:0 0 10px;
}
.eyebrow.on-photo{ color:var(--court-cream); }

.h-hero{
  font-family:var(--f-display); font-weight:800;
  /* DESIGN.md global hero step. The home page no longer uses this class for
     its headline (the headline is the lockup SVG, see .hero-lockup below);
     kept for secondary-page headers (Our Service, Our Team, Corporate
     Event, Media, Contact Us) per DESIGN.md Hero: "The Hero type step
     (84px) is for page headers on secondary pages... where a one or two
     word title fits easily. The home page has no typeset hero headline." */
  font-size:var(--fs-hero);
  line-height:1.0; letter-spacing:-.025em; color:var(--deep-navy); margin:0 0 18px;
  overflow-wrap:break-word;
}
.h-hero.on-photo{ color:var(--court-cream); }

/* ============ hero lockup (home page headline) ============
   Per DESIGN.md Hero: "The headline is the horizontal lockup, not typeset
   words." Same inline SVG markup as the nav lockup, coloured via
   currentColor. Wrapped in an h1 with an aria-label so the page keeps a
   real h1; the SVG itself carries aria-hidden so the brand name is not
   announced twice.

   Round 2 (client review 2026-09-19): the hero block is now centred, so the
   lockup centres in its column (margin-inline auto) instead of sitting
   flush left. The HERO copy of the SVG also tightens the gap between the
   script and "Hong Kong" (see g.lk-sub in index.html), which drops its
   viewBox to 157.51 x 39.27 (ratio .2493) -- about 150px tall at the 600px
   desktop ceiling. The NAV copy and assets/logo/*.svg keep the agency's
   original 40.43 viewBox, untouched. */
.hero-lockup{
  display:block; color:var(--court-cream); width:min(420px, 86vw); line-height:0; margin:0 auto 16px;
}
.hero-lockup svg{ width:100%; height:auto; display:block; }
@media(min-width:760px){ .hero-lockup{ width:min(600px, 82vw); } }

.h-section{
  font-family:var(--f-display); font-weight:700; font-size:var(--fs-section);
  line-height:1.05; letter-spacing:-.02em; color:var(--deep-navy); margin:0 0 14px;
}
.h-section.on-dark{ color:var(--court-cream); }

.h-tile{
  font-family:var(--f-display); font-weight:700; font-size:var(--fs-tile);
  line-height:1.15; color:var(--deep-navy); margin:0 0 4px;
}
.h-tile.on-photo{ color:var(--court-cream); }
/* tighter line-height inside the 42%-tall tile scrim band so three lines
   (label, one-line description, price) reliably clear the gradient */
.tile-text .h-tile{ font-size:20px; line-height:1.1; margin-bottom:3px; }
@media(min-width:760px){ .tile-text .h-tile{ font-size:24px; } }

.lead{ font-family:var(--f-body); font-weight:400; font-size:var(--fs-lead); line-height:1.5; color:var(--charcoal); max-width:var(--text-col); }
.lead.on-photo{ color:var(--court-cream); }

.body-text{ font-size:var(--fs-body); line-height:1.6; color:var(--charcoal); max-width:var(--text-col); }
.body-text.on-photo{ color:var(--court-cream); }

.ui-label{ font-family:var(--f-display); font-weight:600; font-size:var(--fs-ui); }
.meta{ font-family:var(--f-body); font-weight:500; font-size:var(--fs-meta); color:var(--meta-grey); }
.meta.on-photo{ color:rgba(251,243,228,.82); }

.tile-desc{ font-family:var(--f-body); font-weight:400; font-size:14.5px; color:rgba(251,243,228,.9); margin:0 0 6px; max-width:34ch; }
.tile-price{ font-family:var(--f-body); font-weight:600; font-size:14px; color:var(--court-cream); }

/* ============ buttons / pills ============ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  height:52px; padding:0 26px; border-radius:var(--r-pill); border:2px solid transparent;
  font-family:var(--f-display); font-weight:600; font-size:17px; white-space:nowrap;
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
@media(min-width:760px){ .btn{ height:56px; padding:0 30px; font-size:18px; } }

.btn-primary{ background:var(--sideline-navy); color:var(--court-cream); }
.btn-primary:hover{ background:var(--deep-navy); }

.btn-secondary{ background:transparent; border-color:currentColor; color:var(--sideline-navy); }
.btn-secondary:hover{ background:rgba(30,68,137,.08); }

.btn-on-dark.btn-primary{ background:var(--court-cream); color:var(--sideline-navy); }
.btn-on-dark.btn-primary:hover{ background:var(--raised-cream); }
.btn-on-dark.btn-secondary{ border-color:var(--court-cream); color:var(--court-cream); }
.btn-on-dark.btn-secondary:hover{ background:rgba(251,243,228,.14); }

.btn[disabled],.btn.is-disabled{ opacity:.45; cursor:not-allowed; }

.btn .wa-glyph{ width:18px; height:18px; flex:none; }
.btn .wa-glyph path{ fill:var(--wa-green); }

.text-link{
  display:inline-flex; align-items:center; gap:6px; min-height:44px;
  font-family:var(--f-display); font-weight:600; font-size:var(--fs-ui); color:var(--sideline-navy);
}
.text-link .arrow{ width:16px; height:16px; transition:transform var(--dur) var(--ease); }
.text-link:hover{ text-decoration:underline; text-underline-offset:3px; }
.text-link:hover .arrow{ transform:translateX(3px); }
.text-link.on-photo{ color:var(--court-cream); }
/* .on-dark: same cream text colour as .on-photo but for text sitting on a
   FLAT dark surface (book band, footer) with no photo/scrim beneath it, so
   QA check 7 ("every .on-photo sits inside a .scrim") does not flag it. */
.text-link.on-dark{ color:var(--court-cream); }
.text-link .out{ width:12px; height:12px; }

/* ============ language toggle ============ */
/* Component spec calls for 40px; raised to 44px so every segment clears the
   site-wide 44px tap-target rule tested in QA. Documented as a deviation. */
.lang-toggle{ display:inline-flex; align-items:center; border:1px solid var(--hairline); border-radius:var(--r-pill); padding:3px; gap:2px; }
.lang-toggle button{
  font-family:var(--f-display); font-weight:600; font-size:15px; height:44px; padding:0 14px;
  border-radius:var(--r-pill); color:var(--deep-navy); display:flex; align-items:center; justify-content:center;
}
.lang-toggle button[aria-pressed="true"]{ background:var(--sideline-navy); color:var(--court-cream); }
.nav-transparent .lang-toggle{ border-color:rgba(251,243,228,.5); }
.nav-transparent .lang-toggle button{ color:var(--court-cream); }
.nav-transparent .lang-toggle button[aria-pressed="true"]{ background:var(--court-cream); color:var(--sideline-navy); }

/* ============ dev annotations ============ */
.tbc{
  display:inline-flex; align-items:center; font-family:var(--f-body); font-weight:600; font-size:12px;
  color:var(--meta-grey); background:var(--hairline); border-radius:var(--r-pill); padding:3px 10px; white-space:nowrap;
}
/* .photo-slot: the dev-only spec caption overlaid on an image container, per
   DESIGN.md "Dev annotations". Hidden in client view alongside .tbc. */
.photo-slot{
  position:absolute; z-index:3; left:12px; top:12px;
  font-family:var(--f-body); font-size:11.5px; font-weight:600; line-height:1.3;
  color:var(--court-cream); background:rgba(17,37,72,.62); border-radius:var(--r-pill); padding:5px 11px; max-width:calc(100% - 24px);
}
/* Was: inside .arch containers, --r-arch's top corners are full
   semicircles, so a pill 12px from the top-left got clipped and had to
   move to the bottom (fix 2026-09-07, defect 2). Round 2 replaced both
   arches with 14px rectangles (.photo-rect), which clip nothing at the top
   left, so the caption goes back to the default top-left position shared
   with every other photo slot. The rule stays as a no-op guard against a
   caption sitting on top of a corner radius. */
.photo-rect .photo-slot{ top:12px; bottom:auto; }
html[data-view="client"] .tbc,
html[data-view="client"] .photo-slot{ display:none !important; }

.price-tbc{ color:var(--meta-grey); }
html[data-view="dev"] .price-tbc{ text-decoration:underline dotted var(--meta-grey); text-underline-offset:3px; }
.price-tbc.on-photo,.price-tbc.on-dark{ color:var(--court-line-blue); }
html[data-view="dev"] .price-tbc.on-photo,html[data-view="dev"] .price-tbc.on-dark{ text-decoration-color:var(--court-line-blue); }

.dev-note{
  background:var(--cool-panel); border:1px solid var(--hairline); border-radius:var(--r-card);
  padding:12px 16px; font-size:13.5px; color:var(--meta-grey); margin-bottom:20px;
}
html[data-view="client"] .dev-note{ display:none !important; }

/* ============ scrim / on-photo ============ */
.photo-frame{ position:relative; overflow:hidden; background:var(--cool-panel); }
.photo-frame > img{ width:100%; height:100%; object-fit:cover; display:block; }
/* Hero scrim: DESIGN.md's full recipe, gradient spans the lower part of
   the whole frame because hero text is tall (eyebrow+lockup+lead+pills).
   z-index required: the active .hero-slide carries z-index:1, so the scrim
   must sit above that or the photo paints over the text (regardless of DOM order).
   flex-direction:column + justify-content:flex-end: several tiles reuse
   .scrim itself as the content box (class="tile-text scrim" on one element,
   see pillar/space/shop tiles) so its own children must STACK vertically and
   pack to the bottom, not lay out as a flex row (the flex default), or a
   label/description/price group renders as three items side by side. */
.scrim{
  position:absolute; inset:0; z-index:2; display:flex; flex-direction:column;
  align-items:stretch; justify-content:flex-end;
  background:var(--scrim-bottom); pointer-events:none;
}
.scrim > *{ pointer-events:auto; width:100%; }
/* Hero-only: confine the scrim to a bottom-anchored band instead of the
   full inset:0 frame, so it never darkens the whole photo (bans list:
   "Cream or white text on a photograph without the scrim" is about
   coverage, not full-frame darkening). Scoped to .hero-a so the shared
   .scrim rule above (still inset:0) is untouched for every tile that
   reuses .scrim as its flex/justify-end content wrapper.

   Review fix 2026-09-09 (Alfred, point 1): the lockup was hard to read on
   the brighter slides. Measured before the fix: the band started at 50% of
   the hero and the lockup's top edge sat only 43px inside it, where the
   shared --scrim-bottom gradient (clear until 35%, .66 at the bottom) is
   still almost transparent: alpha 0 at the lockup's top edge, .15 at its
   bottom. The hero now carries its own recipe: the band is taller (66%
   mobile, 62% desktop) so it starts well above the eyebrow, and the ramp is
   steep early so the eyebrow sits at about .5 and the lockup at .58 to .77
   (desktop) / .64 to .76 (mobile), measured by qa.py check 14. The photo's
   upper 40% stays untouched. Tile scrims (.scrim-tile) are not changed.

   Round 2 (client review 2026-09-19, item 1): the hero block moved from
   bottom-anchored to vertically and horizontally centred, so a band pinned
   to the bottom no longer sits under the text at all. The scrim now spans
   the whole frame (inset:0) with a gradient that stays near-clear across
   the top quarter (the hall roof and skylight read through it), ramps hard
   into a plateau across the middle where the eyebrow / lockup / lead /
   pills sit, then eases off again at the bottom so the court floor is not
   flattened. padding-top equal to the fixed nav's height keeps the block
   optically centred in the area BELOW the nav rather than in the raw box.
   Measured by qa.py check 14: alpha at the lockup's top edge stays above
   the .5 floor Alfred set on 2026-09-09. */
.hero-a .scrim{
  inset:0; height:auto; justify-content:center; padding-top:64px;
  background:linear-gradient(180deg,
    rgba(17,37,72,0) 0%,
    rgba(17,37,72,.08) 14%,
    rgba(17,37,72,.46) 28%,
    rgba(17,37,72,.66) 40%,
    rgba(17,37,72,.66) 64%,
    rgba(17,37,72,.48) 84%,
    rgba(17,37,72,.30) 100%);
}
@media(min-width:1000px){ .hero-a .scrim{ padding-top:72px; } }
/* Tile scrim (pillar / space / shop tiles): eixo pattern, confine the
   darkening to the caption band only so photos stay bright. Hard cap at 40%
   of the tile height; text content inside is sized to fit that band. */
.scrim.scrim-tile{
  inset:auto 0 0 0; height:42%; z-index:2;
  background:linear-gradient(180deg, rgba(17,37,72,0) 0%, rgba(17,37,72,.22) 30%, rgba(17,37,72,.8) 100%);
}
.scrim-top{ position:absolute; top:0; left:0; right:0; height:45%; z-index:2; background:var(--scrim-top); pointer-events:none; }
/* No independent `display` here on purpose: this element is either (a)
   combined with .scrim ("tile-text scrim"), where .scrim's flex-column
   must win without a same-specificity fight, or (b) alone as a single-child
   wrapper (recovery panel tile), where the parent's flex layout is enough. */
.tile-text{ padding:14px; width:100%; }
@media(min-width:760px){ .tile-text{ padding:20px; } }

/* Round 2 (client review 2026-09-19, item 4): "No curve, bigger image" on
   the brand-intro photo. Both arches (brand intro and corporate) become
   plain rectangles at the --r-input 14px token. WITHDRAWS DESIGN.md
   Graphic language's arch rule ("Signature photo shape... border-radius:
   999px 999px 24px 24px... Use it on the one feature photo in a section:
   the brand-intro portrait, the corporate-event photo") for this page:
   the page now carries zero arches, so --r-arch is unused here. */
.photo-rect{ border-radius:var(--r-input); overflow:hidden; position:relative; background:var(--cool-panel); }

/* ============ court diagram ============ */
.court-dia{ color:var(--court-cream); }
.court-dia svg{ width:100%; height:auto; display:block; }

/* ============ nav ============ */
.nav{
  position:fixed; top:0; left:0; right:0; z-index:50; height:64px; display:flex; align-items:center;
  background:var(--raised-cream); border-bottom:1px solid var(--hairline);
  transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
@media(min-width:1000px){ .nav{ height:72px; } }
.nav .wrap{ display:flex; align-items:center; gap:20px; width:100%; }
/* Round 2 (client review 2026-09-19, item 3): at 1200px the seven labels,
   two dev tag pills, language toggle and Book pill could not share one row,
   so four labels wrapped to two lines. The nav (chrome, not content) gets a
   wider measure than DESIGN.md's 1200px content max-width from 1440px up;
   below that the tag pills are dropped instead (see .nav-item-wrap .tbc). */
@media(min-width:1440px){ .nav .wrap{ max-width:1400px; } }
/* Between the desktop breakpoint and 1280px the row is at its tightest
   (the wrap is still capped at 1200px and the seven labels, the toggle and
   the Book pill all have to fit). One graded step down in label size, row
   gap and Book-pill padding keeps every label on one line there instead of
   letting flex shrink one label into its neighbour. */
@media(min-width:1100px) and (max-width:1279px){
  .nav .wrap{ gap:14px; }
  .nav-items{ gap:13px; }
  .nav-items a{ font-size:14.5px; }
  .nav .book-pill{ padding:0 16px; font-size:14px; }
}
.nav-lockup{ width:150px; flex:none; color:var(--sideline-navy); display:flex; align-items:center; min-height:44px; }
.nav-lockup svg{ width:100%; height:auto; display:block; }

.nav-items{ display:none; gap:20px; align-items:center; flex:0 1 auto; }
/* The inline item row appears at 1100px, not DESIGN.md's 1000px desktop
   breakpoint. Round 2 (item 3) requires every label on one line; seven
   English labels plus the language toggle and the Book pill measure about
   36px wider than a 1000px viewport can hold even at 13.5px type, so
   below 1100px the nav keeps the burger and the existing full-height
   drawer, which carries the same seven items with room to spare. */
@media(min-width:1100px){ .nav-items{ display:flex; } }
/* white-space:nowrap + flex:none: DESIGN.md's Top nav is a single row of
   items, and a label that breaks ("Our / Service") reads as two menu
   entries. flex:none stops the row from shrinking a label into its
   neighbour when the viewport is tight -- if it ever does not fit it
   overflows visibly rather than overlapping silently (qa.py check 15). */
.nav-items a{
  font-family:var(--f-display); font-weight:600; font-size:16px; color:var(--sideline-navy);
  display:inline-flex; align-items:center; gap:5px; min-height:44px;
  white-space:nowrap; flex:none;
}
.nav-items a .out{ width:12px; height:12px; opacity:.75; }
/* Wraps a nav link that carries a dev-only .tbc pill (Shop, Media) so the
   link and its tag sit on one baseline without disturbing the other plain
   nav-items links. Hidden together with the .tbc pill in client view via
   the existing html[data-view="client"] .tbc rule (see Dev annotations). */
.nav-item-wrap{ display:inline-flex; align-items:center; gap:6px; flex:none; }
.nav-item-wrap .tbc{ font-size:11px; padding:2px 8px; }
/* Round 2 (client review 2026-09-19, item 3): the two dev tag pills need
   roughly 210px the nav does not have below 1440px, where they pushed
   "Corporate Event" and "Contact Us" into their neighbours. Rendered only
   where there is room; the same two facts stay tagged in the mobile drawer
   (.drawer-item-wrap .tbc), which has a full-width column for them. */
@media(max-width:1439px){ .nav-item-wrap .tbc{ display:none; } }

.nav-right{ margin-left:auto; display:flex; align-items:center; gap:12px; }
.nav-burger{ display:flex; width:44px; height:44px; align-items:center; justify-content:center; flex-direction:column; gap:4px; flex:none; }
@media(min-width:1100px){ .nav-burger{ display:none; } }
.nav-burger i{ width:20px; height:2px; background:var(--sideline-navy); display:block; font-style:normal; }
.nav .book-pill{ background:var(--sideline-navy); color:var(--court-cream); height:44px; padding:0 20px; font-size:15px; }
@media(min-width:760px){ .nav .book-pill{ height:44px; padding:0 22px; } }
/* Mobile nav per DESIGN.md "Top nav": collapses to lockup, language toggle,
   hamburger. The Book pill moves to the bottom of the drawer instead of
   competing for top-bar width; at 390px, lockup + EN/繁 + Book pill + burger
   together measured wider than the viewport and visibly overlapped. */
@media(max-width:999px){ .nav .book-pill{ display:none; } }

/* transparent state: home page only, only before scroll past 80px */
html:not(.nav-scrolled) .page-home .nav{ background:transparent; border-bottom-color:transparent; }
html:not(.nav-scrolled) .page-home .nav .nav-items a,
html:not(.nav-scrolled) .page-home .nav-burger i{ color:var(--court-cream); }
html:not(.nav-scrolled) .page-home .nav-burger i{ background:var(--court-cream); }
html:not(.nav-scrolled) .page-home .nav .book-pill{ background:var(--court-cream); color:var(--sideline-navy); }
html:not(.nav-scrolled) .page-home .nav .lang-toggle{ border-color:rgba(251,243,228,.5); }
html:not(.nav-scrolled) .page-home .nav .lang-toggle button{ color:var(--court-cream); }
html:not(.nav-scrolled) .page-home .nav .lang-toggle button[aria-pressed="true"]{ background:var(--court-cream); color:var(--sideline-navy); }

/* Nav lockup: always visible, including on the home page over the hero.
   WITHDRAWS DESIGN.md Hero's "Nav lockup hides while the nav is
   transparent on the home page... One lockup on screen at a time."
   Client review round 2 (2026-09-19, item 3) read the missing logo as a
   bug: the top-left of the page was blank until you scrolled. Over the
   photo the lockup renders in Court cream so it is legible on the render
   and matches the nav items beside it; it crossfades to Sideline navy with
   the rest of the nav once the nav goes solid after 80px of scroll. The
   opacity transition is replaced by a colour transition (nothing fades in
   or out any more), and initNavScroll no longer toggles aria-hidden on it,
   since the link is now always exposed. */
.page-home .nav .nav-lockup{ transition:color var(--dur) var(--ease); }
html:not(.nav-scrolled) .page-home .nav .nav-lockup{ color:var(--court-cream); }

/* ============ mobile drawer ============
   Per DESIGN.md Top nav: "Mobile collapses to lockup, language toggle,
   hamburger; drawer slides from the right, full height, Deep navy ground
   with cream text at 22px, Book pill at the bottom of the drawer." Ported
   from v1 _shared.css .drawer/.scrim, retokenised, renamed .scrim to
   .drawer-scrim so it cannot collide with the unrelated photo-tile .scrim
   (position:absolute overlay with pointer-events:none, used throughout the
   hero/pillar/space/shop tiles) that this design system also defines. */
.drawer-scrim{
  position:fixed; inset:0; background:rgba(17,37,72,.55); opacity:0; pointer-events:none;
  transition:opacity var(--dur) var(--ease); z-index:55;
}
.drawer-scrim.is-open{ opacity:1; pointer-events:auto; }
.drawer{
  position:fixed; inset:0 0 0 auto; width:min(320px,86vw); background:var(--deep-navy);
  transform:translateX(100%); transition:transform var(--dur) var(--ease); z-index:60;
  padding:22px var(--gut); display:flex; flex-direction:column; gap:3px;
}
.drawer.is-open{ transform:none; }
.drawer a{
  color:var(--court-cream); font-family:var(--f-display); font-weight:700; font-size:22px;
  padding:13px 0; border-bottom:1px solid var(--hairline-on-navy); display:flex; align-items:center; min-height:44px;
}
.drawer-item-wrap{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:13px 0; border-bottom:1px solid var(--hairline-on-navy); }
.drawer-item-wrap a{ padding:0; border-bottom:0; min-height:auto; }
.drawer-x{
  align-self:flex-end; color:var(--court-line-blue); font-size:28px; line-height:1;
  min-width:44px; min-height:44px; margin-bottom:6px; display:flex; align-items:center; justify-content:center;
}
.drawer-book{ margin-top:16px; }
@media(min-width:1100px){ .drawer,.drawer-scrim{ display:none; } }

/* ============ nav clearance ============ */
/* nav is fixed; every page body clears it, except the home hero which pulls
   its full-bleed photo up underneath the transparent nav */
body{ padding-top:64px; }
@media(min-width:1000px){ body{ padding-top:72px; } }
.hero-a{ margin-top:-64px; }
@media(min-width:1000px){ .hero-a{ margin-top:-72px; } }
/* booking.html does not load this file; its own sticky (not fixed) header
   is styled entirely in booking.css, so no .bk-body override is needed here. */

/* ============ hero shared ============ */
.hero{ position:relative; }
.hero-slideshow{ position:relative; width:100%; height:100%; overflow:hidden; }
.hero-slide{ position:absolute; inset:0; opacity:0; overflow:hidden; transition:opacity 650ms var(--ease); }
.hero-slide.is-active{ opacity:1; z-index:1; }
/* Step 0 fix (BRIEF.md): the venue renders paint "Sporting Hub Hong Kong"
   script lettering on the court floor, starting around 71-74% down the
   frame (measured with vision_analyze on render-1.jpg and render-2.jpg),
   which collided with the hero lead line and lockup in visual QA of the
   mockup. Fix: make the image box taller than its clipped slide and anchor
   it to the top (object-position 50% 0), so only the top portion of the
   render shows. height:130% (visible fraction 1/1.3 = 76.9%) still let the
   top edge of the lettering peek through, confirmed in re-shot QA; raised
   to 145% (the brief's stated ceiling), visible fraction 1/1.45 = 69.0%,
   comfortably above the lettering's 71% start on every slide -- the
   skylight and court stay in frame, the floor logo falls fully below the
   visible crop on all five slides. No inline object-position on the <img>
   tags (see index.html): this single rule governs every hero slide
   uniformly. */
/* Round 6 (N42, 2026-10-06): the rule above was written for the old
   render-1..5. On the client's 2026-10-04 render set it kept only the top
   69% of each frame, so phones saw ceiling and ducts and almost no court
   (Suki: "淨係見到天花"). Each slide now sets its own crop in inline
   custom properties: --hz is the image box height (zoom), --hx / --hy the
   object-position, with -m variants for phones. The box is pinned to the
   slide's bottom edge, so any zoom cuts ceiling, never court. */
.hero-slide img{ position:absolute; left:0; bottom:0; width:100%; object-fit:cover; display:block;
  height:var(--hz-m, var(--hz, 100%)); object-position:var(--hx-m, var(--hx, 50%)) var(--hy-m, var(--hy, 50%)); }
@media(min-width:760px){
  .hero-slide img{ height:var(--hz, 100%); object-position:var(--hx, 50%) var(--hy, 50%); }
}
/* tablets in portrait would otherwise show the full frame, a third of it ceiling */
@media(min-width:760px) and (max-width:999px){
  .hero-slide img{ height:var(--hz-t, 120%); }
}
/* phones: dots centred, clear of the floating WhatsApp button (Suki's screenshot: it hid dot 3) */
@media(max-width:759px){
  .hero-dots{ right:auto; left:50%; transform:translateX(-50%); }
}

.hero-dots{ position:absolute; z-index:4; right:16px; bottom:16px; display:flex; gap:2px; }
@media(min-width:760px){ .hero-dots{ right:24px; bottom:24px; } }
.hero-dot{ width:22px; height:44px; padding:0; background:transparent; display:flex; align-items:center; justify-content:center; flex:none; }
.hero-dot::before{ content:""; width:9px; height:9px; border-radius:50%; background:rgba(251,243,228,.55); transition:all var(--dur) var(--ease); }
.hero-dot.is-active::before{ width:20px; border-radius:var(--r-pill); background:var(--court-cream); }

.hero-arrow{
  position:absolute; z-index:4; top:50%; margin-top:-22px; width:44px; height:44px; border-radius:50%;
  background:rgba(17,37,72,.35); color:var(--court-cream); display:flex; align-items:center; justify-content:center;
  opacity:0; transition:opacity var(--dur) var(--ease),background var(--dur) var(--ease); flex:none;
}
.hero-slideshow:hover .hero-arrow,.hero-arrow:focus-visible{ opacity:1; }
.hero-arrow:hover{ background:rgba(17,37,72,.55); }
.hero-arrow.prev{ left:16px; } .hero-arrow.next{ right:16px; }
@media(min-width:760px){ .hero-arrow.prev{ left:24px; } .hero-arrow.next{ right:24px; } }
.hero-arrow svg{ width:18px; height:18px; }

.hero-actions{ display:flex; flex-wrap:wrap; gap:12px; margin-top:6px; }

/* full-height slideshow for the home hero */
.hero-a .hero-slideshow{ height:78vh; min-height:560px; }
@media(min-width:1000px){
  .hero-a .hero-slideshow{ height:min(88vh,860px); min-height:640px; }
}

/* ---- variant A: full bleed ---- */
.hero-a{ padding-top:0; }
.hero-a .hero-slideshow{ border-radius:0 0 var(--r-hero) var(--r-hero); }
/* Bug fix versus the approved mockup: .hero-text-block is also .wrap
   (markup is class="wrap hero-text-block"), and .wrap's own horizontal
   gutter (padding:0 var(--gut)) was being fully overwritten by this rule's
   shorthand `padding:28px 0 48px` (0 left/right), since both write the
   `padding` shorthand and this rule is declared later in the cascade. The
   approved mockup screenshot (shots/hero-A-mobile.png) shows the same
   defect: the eyebrow and lead text sit flush against the viewport's left
   edge on mobile with no side margin, contradicting DESIGN.md's own
   spacing rule ("side gutter 20px mobile"). Not part of "sections, order,
   photo slots, arch count, scrim rules, radii" that the brief freezes; a
   text-flush-to-the-edge legibility bug is fixed here, adding the gutter
   back via padding-left/right instead of clobbering it. */
/* Round 2 (client review 2026-09-19, item 1): centred, not bottom-left.
   Vertical placement is the parent .hero-a .scrim's justify-content:center;
   here the block only keeps its gutter and centres its own inline content.
   The lead keeps DESIGN.md's 680px text-column cap so a centred paragraph
   does not run the full 1136px. */
.hero-a .hero-text-block{ padding:28px var(--gut); max-width:1136px; text-align:center; }
@media(min-width:760px){ .hero-a .hero-text-block{ padding:40px var(--gut); } }
@media(min-width:1000px){ .hero-a .hero-text-block{ padding:56px var(--gut); } }
.hero-a .hero-text-block .lead{ max-width:var(--text-col); margin-left:auto; margin-right:auto; }
.hero-a .hero-actions{ justify-content:center; }

/* ============ pillars (section 2) ============ */
#pillars{ scroll-margin-top:90px; }
.pillars-grid{ display:grid; gap:14px; }
/* Review fix 2026-09-09 (Alfred, point 2): the three tiles were 1.15fr / 1fr /
   1.15fr with the middle one 12% shorter and the row bottom-aligned, which
   DESIGN.md had asked for ("deliberately not identical in height"). Alfred
   reviewed it and wants three equal, aligned tiles, so the stagger is
   withdrawn and DESIGN.md updated to match. */
@media(min-width:760px){ .pillars-grid{ grid-template-columns:repeat(3,1fr); align-items:stretch; gap:16px; } }
.pillar-tile{
  position:relative; display:block; border-radius:var(--r-tile); overflow:hidden;
  aspect-ratio:4/3; transition:transform var(--dur) var(--ease);
}
.pillar-tile:hover{ transform:translateY(-2px); }
@media(min-width:760px){
  /* tablet: three columns of 221px at 760px would be 2:1 portrait slivers at a
     fixed 440px; let them scale at 3:4 until the desktop breakpoint */
  .pillar-tile{ aspect-ratio:3/4; }
}
@media(min-width:1000px){
  .pillar-tile{ aspect-ratio:auto; height:440px; }
}
.pillar-tile .tile-arrow{
  position:absolute; z-index:3; top:14px; right:14px; width:44px; height:44px; border-radius:50%;
  background:var(--raised-cream); color:var(--sideline-navy); display:flex; align-items:center; justify-content:center;
  transition:background var(--dur) var(--ease);
}
.pillar-tile .tile-arrow svg{ width:18px; height:18px; }
.pillar-tile:hover .tile-arrow{ background:var(--court-cream); }
/* Pillar tile label overrides the generic .tile-text .h-tile band-fit size
   (20/24px) back up to the Tile label step per DESIGN.md Typography table
   and Components > Service pillar tile: "label in Outfit 700 28px in Court
   cream at bottom-left". Higher specificity than .tile-text .h-tile (3
   classes vs 2) so it wins regardless of source order. */
.pillar-tile .tile-text .h-tile{ font-size:var(--fs-tile); line-height:1.15; margin-bottom:4px; }
/* Review fix 2026-09-09 (Alfred, point 2, "the right most box photo is not
   filled in the container"). The photo did reach every edge; what read as
   unfilled was the framing: render-4 at natural scale put exposed ceiling in
   the top half of the tile with the content squashed into the bottom 40%,
   while the two tiles beside it are cropped tight. Six crops were rendered
   and compared; this one won on every criterion: render-3's left third
   (training rig, dark shelving, blue ottomans), zoomed 1.35x and anchored
   bottom-left so no ceiling is in frame and it reads as a training floor.
   Full-resolution source so the zoom does not soften it. Still a placeholder
   until Suki shoots the real S&C floor. */
.pillar-tile .pt-crop{ object-position:0% 50%; transform:scale(1.35); transform-origin:0% 100%; }

/* ============ brand intro (section 3) ============ */
.intro-grid{ display:grid; gap:32px; }
/* Round 2 (client review 2026-09-19, item 4): "bigger image". Mobile goes
   from an 80%-wide 4:5 arch to a full-width 4:3 rectangle; desktop drops
   the 400px cap so the frame fills its grid column, and from 1200px up
   takes a fixed 560px height (the column is 520px there, so the frame is
   both wider and taller than the old 400 x 500 arch, and clears the 520px
   floor qa.py check 15 asserts). */
.intro-photo{ order:-1; width:100%; margin:0; }
@media(min-width:900px){
  .intro-grid{ grid-template-columns:minmax(0,560px) 1fr; align-items:center; gap:56px; }
  .intro-photo{ order:2; width:100%; margin:0; justify-self:stretch; }
}
.intro-photo .photo-rect{ aspect-ratio:4/3; }
@media(min-width:900px){ .intro-photo .photo-rect{ aspect-ratio:4/5; } }
@media(min-width:1200px){ .intro-photo .photo-rect{ aspect-ratio:auto; height:560px; } }
.intro-text{ max-width:560px; }

/* ============ courts and spaces (section 4) ============ */
/* Mobile: two columns, the two wide tiles span both, every row is either one
   wide tile or two standard tiles, so rows are always level. */
.spaces-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:12px; }
.space-tile{ position:relative; border-radius:var(--r-tile); overflow:hidden; aspect-ratio:4/3; }
.space-tile.is-wide{ grid-column:span 2; aspect-ratio:16/9; }
/* Review fix 2026-09-09 (Alfred, point 3). Before: four columns, wide tiles
   span 2 columns at 16:8 (340px tall) next to 4:3 standards (249px tall), so
   every row was as tall as its wide tile and the two standards beside it sat
   over 91px of empty cream; and 8 tiles worth 10 column-units in a 4-column
   grid left a 2-cell hole at the end of the last row. Now a fixed bento: 4
   columns by 6 row-units, standard tile = 1 column by 2 units (4:3), wide
   tile = 2 columns by 3 units (16:9). Left column pair holds the two wides
   (3 + 3 = 6 units), right pair holds six standards (2 + 2 + 2 = 6), so the
   grid is fully covered with no holes and every edge lines up. Tile heights
   come from the grid tracks, not from aspect-ratio: the grid itself carries
   the aspect ratio (1376 / 779 is the exact figure at 1440px, and the
   standard tiles stay within 1% of 4:3 between 760px and the 1680px
   bleed cap). Reading order is unchanged: volleyball, basketball,
   pickleball, badminton, S&C, lounge, retail, recovery. */
@media(min-width:760px){
  .spaces-grid{
    grid-template-columns:repeat(4,1fr); grid-template-rows:repeat(6,minmax(0,1fr));
    grid-auto-flow:row dense; gap:16px; aspect-ratio:1376/779;
  }
  .space-tile{ aspect-ratio:auto; grid-row:span 2; min-height:0; }
  .space-tile.is-wide{ aspect-ratio:auto; grid-column:span 2; grid-row:span 3; }
}
.space-tile .court-dia{ position:absolute; z-index:3; right:14px; bottom:14px; width:64px; }
@media(min-width:760px){ .space-tile .court-dia{ width:80px; } }
@media(min-width:1000px){ .space-tile .court-dia{ width:96px; } }
.space-tile.is-panel{
  display:flex; align-items:flex-end; background:var(--cool-panel); border:1px solid var(--hairline);
}
.space-tile.is-panel .tile-text .h-tile{ color:var(--deep-navy); }

/* ============ just performance (section 5) ============ */
.jp-panel{ background:var(--cool-panel); border-radius:var(--r-panel); padding:28px var(--card-pad); }
@media(min-width:1000px){ .jp-panel{ padding:56px; } }
.jp-grid{ display:grid; gap:28px; }
@media(min-width:1000px){ .jp-grid{ grid-template-columns:minmax(0,400px) 1fr; gap:48px; align-items:start; } }
.jp-text{ max-width:420px; }
.jp-coaches{ display:flex; gap:16px; }
@media(min-width:1000px){
  /* CSS Grid, not flex-wrap: with three flex:1 items and a min-width, the
     available row width did not clear 3*min-width, so the third card
     wrapped alone and stretched full-width instead of matching its
     siblings. A fixed 3-column grid guarantees three equal columns. */
  .jp-coaches{ display:grid; grid-template-columns:repeat(3, 1fr); }
}
/* Round 2 (client review 2026-09-19, item 6): the third card's title
   ("Volleyball coach") wraps to two lines, which pushed its Book pill 27px
   below the other two. Column flex + margin-top:auto on the pill pins all
   three buttons to the same bottom edge whatever the title does. */
.coach-card{
  flex:0 0 250px; scroll-snap-align:start; background:var(--raised-cream); border:1px solid var(--hairline);
  border-radius:var(--r-card); padding:var(--card-pad);
  display:flex; flex-direction:column; align-items:stretch;
}
/* the dev "name and bio to confirm" pill is an inline-flex span in normal
   flow; as a stretched flex item it would run the full card width, so pin
   it back to its own content width. */
.coach-card > .tbc{ align-self:flex-start; }
.coach-card > .btn{ margin-top:auto; }
@media(min-width:1000px){ .coach-card{ flex:none; min-width:0; } }
.coach-avatar{ width:60px; height:60px; border-radius:50%; background:var(--cool-panel); border:1px solid var(--hairline); margin-bottom:14px; }
.coach-card .h-coach{ font-family:var(--f-display); font-weight:700; font-size:17px; color:var(--deep-navy); margin:0 0 6px; }
.coach-card .coach-meta{ font-size:14px; color:var(--meta-grey); margin:0 0 4px; }
.coach-card .chips{ display:flex; flex-wrap:wrap; gap:6px; margin:12px 0; }
.chip{
  border:1px solid var(--court-line-blue); border-radius:var(--r-pill); padding:4px 11px;
  font-family:var(--f-garnish); font-weight:700; text-transform:uppercase; letter-spacing:.05em;
  font-size:12px; color:var(--sideline-navy); display:inline-flex; align-items:center; min-height:26px;
}
.coach-card .coach-size{ font-size:14px; color:var(--charcoal); font-weight:600; margin-bottom:4px; }
.coach-card .coach-price{ font-size:14px; margin-bottom:14px; }
.coach-card .btn{ width:100%; height:44px; font-size:15px; }

/* ============ corporate + shop (section 6) ============ */
.corp-shop-grid{ display:grid; gap:24px; }
@media(min-width:900px){ .corp-shop-grid{ grid-template-columns:7fr 5fr; gap:28px; align-items:stretch; } }

.corp-block{
  background:var(--raised-cream); border:1px solid var(--hairline); border-radius:var(--r-tile);
  padding:var(--card-pad); display:grid; gap:22px; height:100%;
}
@media(min-width:640px){ .corp-block{ grid-template-columns:180px 1fr; gap:26px; align-items:center; } }
@media(min-width:900px){ .corp-block{ grid-template-columns:220px 1fr; } }
.corp-block .photo-rect{ aspect-ratio:4/5; }
.corp-actions{ display:flex; flex-wrap:wrap; gap:10px; margin-top:16px; }

.shop-tile{ position:relative; border-radius:var(--r-tile); overflow:hidden; min-height:320px; }
@media(min-width:900px){ .shop-tile{ min-height:0; height:100%; } }

/* ============ book band (section 7) ============ */
.book-band{
  background:var(--deep-navy); border-radius:var(--r-panel); padding:36px var(--card-pad);
  position:relative; overflow:hidden; color:var(--court-cream);
}
@media(min-width:1000px){ .book-band{ padding:60px; } }
.book-band::after{
  content:""; position:absolute; inset:12px; border-radius:calc(var(--r-panel) - 4px);
  border:1.5px solid rgba(151,174,214,.35); pointer-events:none;
}
/* Round 2 (client review 2026-09-19, item 5): the court diagram was
   invisible. WITHDRAWS DESIGN.md Book band / Dark panel's "Court diagram
   watermark at 8 percent": at Court line blue .08 on Deep navy the drawing
   read as nothing at all. It is now the Court cream token at .45, drawn
   large (440px desktop) and fully inside the panel instead of bleeding off
   the right and bottom edges, so it reads as the white outline drawing the
   client asked for. The panel takes a min-height from 760px up so the
   whole diagram fits beside the copy rather than behind it, and the copy
   block centres against it; on phones the band is too narrow for that, so
   the diagram is small, tucked into the bottom-right corner and held at a
   lower alpha where the sport pills do sit over it. */
.book-band .watermark{
  position:absolute; right:14px; bottom:14px; width:170px; height:auto;
  color:var(--court-cream); opacity:.4; pointer-events:none;
}
@media(min-width:760px){
  .book-band{ min-height:400px; display:flex; align-items:center; }
  .book-band .watermark{ right:24px; bottom:24px; width:320px; opacity:.42; }
}
@media(min-width:1000px){
  .book-band{ min-height:488px; }
  .book-band .watermark{ right:36px; bottom:36px; width:440px; opacity:.45; }
}
.book-band-inner{ position:relative; z-index:1; max-width:640px; }
.book-band-sub{ font-size:16px; color:rgba(251,243,228,.82); margin:0 0 24px; }
.book-band-sports{ display:flex; flex-wrap:wrap; gap:10px; margin-bottom:24px; }
.book-band-links{ display:flex; gap:24px; flex-wrap:wrap; }

/* ============ find us (section 8) ============ */
.find-grid{ display:grid; gap:28px; }
@media(min-width:900px){ .find-grid{ grid-template-columns:1fr 1fr; gap:48px; align-items:start; } }
.badge-card{
  background:var(--raised-cream); border:1px solid var(--hairline); border-radius:var(--r-card);
  padding:32px; display:flex; align-items:center; justify-content:center; height:100%;
}
.badge-card img{ width:340px; max-width:100%; height:auto; }
.facts-row{ display:grid; grid-template-columns:118px 1fr; gap:12px; padding:14px 0; border-bottom:1px solid var(--hairline); }
.facts-row:first-child{ border-top:1px solid var(--hairline); }
.facts-row dt{ margin:0; font-family:var(--f-display); font-weight:600; font-size:13px; text-transform:uppercase; letter-spacing:.06em; color:var(--sideline-navy); }
.facts-row dd{ margin:0; font-size:16px; }
.facts-row dd a{ display:inline-flex; align-items:center; min-height:44px; }
.find-actions{ display:flex; flex-wrap:wrap; gap:12px; margin-top:24px; }
.map-tile{
  margin-top:32px; background:var(--cool-panel); border:1px solid var(--hairline); border-radius:var(--r-tile);
  height:280px; display:flex; align-items:center; justify-content:center; text-align:center; padding:20px;
}
.map-tile .meta{ max-width:36ch; }
/* Round 6: the map itself fills the tile */
.map-tile:has(iframe){ padding:0; overflow:hidden; height:320px; }
.map-tile iframe{ width:100%; height:100%; border:0; display:block; }
/* rows still waiting on the client stay visible, tagged, in dev view only */
html[data-view="client"] .facts-row.is-tbc{ display:none; }
/* Round 6: shop tile while the store is not live */
.shop-tile.is-soon .shop-go, a.is-soon > .out{ display:none; }
.shop-tile:not(.is-soon) .shop-soon{ display:none; }
.shop-soon{ font-family:var(--f-display); font-weight:600; font-size:15px; }
.shop-soon.on-photo{ color:var(--court-cream); }
/* find us: labels sit on the first line of their value, links read as links */
.facts-row{ align-items:baseline; }
.facts-row dd a{ color:var(--sideline-navy); text-decoration:underline; text-underline-offset:3px; }
#find .h-section{ text-wrap:balance; }
/* Chinese: break at spaces and punctuation so 豪景花園, 九巴, 分鐘, 非繁忙時段 stay whole;
   anywhere only if a run would overflow */
html[lang="zh-Hant"] .facts-row dd, html[lang="zh-Hant"] #find .h-section,
html[lang="zh-Hant"] .price-hours{ word-break:keep-all; overflow-wrap:anywhere; }
/* the two note sentences: a space's width in English, none in Chinese */
.soon-note > span + span{ margin-left:.28em; }
html[lang="zh-Hant"] .soon-note > span + span{ margin-left:0; }
#prices, #spaces, #jp, #corporate, #find, #shop{ scroll-margin-top:90px; }

/* ============ footer (section 9) ============ */
.site-footer{ background:var(--deep-navy); color:var(--court-cream); padding:56px 0 28px; }
.footer-grid{ display:grid; gap:32px; }
@media(min-width:760px){ .footer-grid{ grid-template-columns:1.3fr 1fr 1fr 1fr; gap:24px; } }
.footer-lockup{ width:150px; color:var(--court-cream); margin-bottom:14px; display:flex; align-items:center; min-height:44px; }
.footer-blurb{ font-size:14px; color:rgba(251,243,228,.7); max-width:32ch; margin-bottom:18px; }
.footer-col h4{ font-family:var(--f-garnish); font-weight:700; text-transform:uppercase; letter-spacing:.06em; font-size:13px; color:var(--court-line-blue); margin:0 0 14px; }
.footer-col ul{ display:grid; gap:2px; }
.footer-col a{ font-size:15px; color:rgba(251,243,228,.8); display:flex; align-items:center; min-height:44px; }
.footer-lang{ margin-top:6px; }
.footer-lang .lang-toggle{ border-color:rgba(151,174,214,.35); }
.footer-lang .lang-toggle button{ color:var(--court-cream); }
.footer-lang .lang-toggle button[aria-pressed="true"]{ background:var(--court-cream); color:var(--sideline-navy); }
.footer-bottom{
  border-top:1px solid var(--hairline-on-navy); margin-top:40px; padding-top:20px;
  display:flex; justify-content:space-between; flex-wrap:wrap; gap:10px; font-size:13px; color:rgba(251,243,228,.65);
}

/* ============ WhatsApp float ============ */
.wa-float{
  position:fixed; z-index:60; right:16px; bottom:16px; width:56px; height:56px; border-radius:50%;
  background:var(--wa-green); display:flex; align-items:center; justify-content:center; box-shadow:0 3px 12px rgba(49,62,101,.20);
}
@media(min-width:760px){ .wa-float{ right:24px; bottom:24px; width:60px; height:60px; } }
.wa-float svg{ width:26px; height:26px; }
.wa-float svg path{ fill:var(--court-cream); }

/* ============ tweaks panel ============ */
.tweaks-panel{
  position:fixed; z-index:80; left:16px; bottom:16px; width:224px;
  background:var(--raised-cream); border:1px solid var(--hairline); border-radius:var(--r-card);
  padding:14px; box-shadow:var(--shadow-float); display:flex; flex-direction:column; gap:12px;
}
/* The panel belongs to dev view only. Keying it on the view state rather than
   on a query parameter matters since round 2 made client the default: a plain
   URL opens in client view, and before this rule it still showed the panel
   because nothing had added .clean. index.html carries data-view="client"
   pre-boot, so the panel never flashes before initTweaksPanel runs. Dev view
   is reachable at ?view=dev, which brings the panel back. */
html[data-view="client"] .tweaks-panel{ display:none !important; }
html.clean .tweaks-panel{ display:none !important; }
.tweaks-title{ font-family:var(--f-display); font-weight:700; font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--deep-navy); }
.tweaks-row{ display:flex; flex-direction:column; gap:6px; }
.tweaks-label{ font-family:var(--f-body); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.05em; color:var(--meta-grey); }
.tweaks-btns{ display:flex; gap:6px; }
.tweaks-btns button{
  flex:1; min-height:44px; border:1px solid var(--hairline); border-radius:var(--r-pill);
  font-family:var(--f-display); font-weight:600; font-size:13px; color:var(--deep-navy); padding:0 6px;
}
.tweaks-btns button[aria-pressed="true"]{ background:var(--sideline-navy); color:var(--court-cream); border-color:var(--sideline-navy); }

/* ============ responsive overflow guards ============ */
.pillars-grid > *,.spaces-grid > *,.intro-grid > *,.jp-grid > *,.corp-shop-grid > *,
.find-grid > *,.footer-grid > *,.corp-block > *{ min-width:0; }

/* ============ round 5 (2026-10-03): landing page ships before booking ============ */

/* Top menu "beige/cream font hard to read" (2026-09-25 call). Before scroll
   the nav is transparent, and its cream labels sat on the brightest part of
   the hero photo (the hall ceiling) under a wash that was .45 at the very
   top and close to clear by label height. The hero's top wash is now a
   fixed 220px band holding .62 or more behind the whole nav row. */
.hero-a .scrim-top{ height:220px; background:var(--scrim-nav); }
html:not(.nav-scrolled) .page-home .nav .nav-items a{ text-shadow:0 1px 2px rgba(17,37,72,.45); }

/* Main site (index.html, data-booking="open") versus the coming-soon page
   (coming-soon.html, data-booking="soon"), see shared.js wireBooking().
   No attribute reads as the main site. The coming-soon page drops what
   only makes sense once booking is live: the coach cards (role names,
   HK$ TBC, Book buttons) and the HK$ TBC "from" lines on the tiles. */
.soon-note{ font-family:var(--f-body); font-weight:500; font-size:15px; line-height:1.5; color:var(--charcoal); margin:16px 0 0; text-wrap:balance; }
.soon-note.on-photo{ color:rgba(251,243,228,.9); }
html:not([data-booking="soon"]) .soon-only,
html[data-booking="soon"] .open-only,
html[data-booking="soon"] .jp-coaches,
html[data-booking="soon"] .tile-price.price-tbc{ display:none !important; }


/* Prices, per DESIGN.md "Price table": Barlow Condensed uppercase headers,
   Figtree body, rows separated by Hairline, no zebra striping, inside an
   --r-card container. Figures stay in Latin numerals in both locales. */
.prices-lead{ margin:0 0 24px; }
.price-grid{ display:grid; gap:20px; }
@media(min-width:1000px){ .price-grid{ grid-template-columns:minmax(0,1.5fr) minmax(0,1fr); gap:28px; align-items:start; } }
.price-card{ background:var(--raised-cream); border:1px solid var(--hairline); border-radius:var(--r-card); padding:6px var(--card-pad) 20px; }
@media(min-width:760px){ .price-card{ padding:10px 28px 24px; } }
.price-table{ width:100%; border-collapse:collapse; font-family:var(--f-body); }
.price-table thead th{
  font-family:var(--f-garnish); font-weight:700; font-size:15px; text-transform:uppercase; letter-spacing:.08em;
  color:var(--sideline-navy); text-align:right; padding:14px 0 10px 16px; border-bottom:1px solid var(--hairline);
}
.price-table thead th:first-child{ text-align:left; padding-left:0; }
.price-table tbody th,.price-table td{ padding:13px 0; border-bottom:1px solid var(--hairline); vertical-align:middle; }
.price-table tbody tr:last-child th,.price-table tbody tr:last-child td{ border-bottom:0; }
.price-table tbody th{ text-align:left; font-weight:400; }
.price-table td{
  text-align:right; padding-left:16px; white-space:nowrap; font-variant-numeric:tabular-nums;
  font-family:var(--f-body); font-weight:600; font-size:17px; color:var(--deep-navy);
}
.rate-sport{ display:block; font-family:var(--f-display); font-weight:600; font-size:17px; line-height:1.25; color:var(--deep-navy); }
.rate-court{ display:block; font-size:14px; font-weight:500; color:var(--meta-grey); margin-top:2px; }
.price-hours{ font-size:var(--fs-meta); line-height:1.55; color:var(--charcoal); margin:14px 0 0; max-width:var(--text-col); }
.price-notes{ display:grid; gap:12px; }
.price-note{ background:var(--raised-cream); border:1px solid var(--hairline); border-radius:var(--r-card); padding:var(--card-pad); }
@media(min-width:760px){ .price-note{ padding:22px 24px; } }
.price-note .body-text{ margin:0; font-size:16px; }
.price-note > .tbc{ margin-top:10px; }
.h-note{ font-family:var(--f-display); font-weight:700; font-size:19px; line-height:1.2; color:var(--deep-navy); margin:0 0 6px; }
.price-figure{ font-family:var(--f-display); font-weight:700; font-size:28px; line-height:1.1; color:var(--sideline-navy); margin:2px 0 6px; }
.price-actions{ display:flex; flex-wrap:wrap; align-items:center; gap:12px; margin-top:12px; }
.price-notes > .soon-note{ margin-top:0; }
.price-actions [hidden]{ display:none !important; }

/* Visual QA 2026-10-03. The opens-soon line and the peak-hours note carry
   real information, so they take Charcoal rather than Meta grey, which sits
   just under 4.5:1 on cream at these sizes. text-wrap:balance stops "now."
   orphaning on phones. */

/* Phones: seven sport buttons wrapped 2/2/2/1 with Badminton alone. Two
   even columns, the odd last button spans both. The court drawing sat on
   top of the last button and the Personal training link; the band now
   reserves room for it below the content, and the content keeps clear of
   the inset court line. */
@media(max-width:759px){
  .book-band{ padding:36px 28px 204px; }
  /* lift the drawing off the inset court line it used to sit on */
  .book-band .watermark{ right:28px; bottom:28px; }
  .book-band-sports{ display:grid; grid-template-columns:1fr 1fr; }
  .book-band-sports > :last-child:nth-child(odd){ grid-column:1 / -1; }
  .book-band-sports .btn{ width:100%; padding:0 12px; }
}

/* Chinese hero line: "匹克 / 球" split across lines at 390px. keep-all
   breaks only at the ideographic commas, so each sport name stays whole;
   anywhere still lets a too-long run wrap rather than overflow. */
html[lang="zh-Hant"] .hero-a .hero-text-block .lead{ word-break:keep-all; overflow-wrap:anywhere; }

/* Visual QA 2026-10-04, third pass. On the coming-soon page the coach cards
   are hidden, which left 58 percent of the Just Performance panel empty at
   desktop. The jp-wide slot (DESIGN.md Photo slot spec) fills that column
   there instead; the main site keeps the cards in it. */
.jp-photo{ aspect-ratio:4/3; }
@media(min-width:1000px){ .jp-photo{ aspect-ratio:auto; height:100%; min-height:320px; } }

/* Balanced wrapping where a short last line read as a mistake: the hero
   line ("in / one place"), the JP heading (zh "內。" alone), the coach
   ratio line ("16" alone). The opens-soon note is two spans that each keep
   to one line where they fit, so the break falls between the sentences. */
.hero-a .hero-text-block .lead,
.jp-text .h-section,
.coach-card .coach-size{ text-wrap:balance; }
.soon-note > span{ display:inline-block; }

/* Coach cards: a title that wraps ("Volleyball / coach") pushed that card's
   chips, ratio and price 27px below its neighbours. Two title lines are
   reserved wherever the cards sit side by side. */
@media(min-width:1000px){ .coach-card .h-coach{ min-height:2.5em; line-height:1.25; } }
.coach-avatar{ display:flex; align-items:center; justify-content:center; color:var(--court-line-blue); }
.coach-avatar svg{ width:30px; height:30px; }

/* Visual QA 2026-10-04, fifth pass: crops for the client's full render set.
   Basketball and pickleball tiles come from the same camera and read as one
   image twice side by side, so each takes its own close crop of the full
   1600px file: the hoop, and the low net with the kiosk and ottomans. */
.space-tile .crop-hoop{ object-position:100% 50%; transform:scale(1.5); transform-origin:80% 40%; }
.space-tile .crop-net{ object-position:0% 50%; transform:scale(1.5); transform-origin:10% 62%; }
/* Intro on phones: a 4:3 frame sliced the neon slogan and showed its garbled
   mirror image in the glass; a square frame at the right edge drops both. */
.intro-photo img.crop-entrance{ object-position:100% 50%; }
@media(max-width:899px){ .intro-photo .photo-rect{ aspect-ratio:1/1; } }
@media(min-width:900px){ .intro-photo img.crop-entrance{ object-position:85% 50%; } }
/* Coming-soon JP photo: the frame matches the render's ratio, so only a
   scale crops the garbled neon panel at its right edge. */
.jp-photo img{ transform:scale(1.14); transform-origin:0 50%; }
/* "HK$ TBC" inside a photo tile inherited Meta grey (2.1 to 3.1:1 on the
   scrim); on photos and dark bands it takes Court line blue. */
.on-photo .price-tbc,.on-dark .price-tbc{ color:var(--court-line-blue); }
.tile-text .body-text{ text-wrap:balance; }
/* Recovery has no render yet; a navy panel reads as deliberate where a pale
   one read as a photo that failed to load. */
.space-tile.is-panel{ background:var(--deep-navy); border-color:var(--deep-navy); }
.space-tile.is-panel .tile-text .h-tile{ color:var(--court-cream); }
/* Phones: the court drawings collided with two-line tile labels, and the
   longest label wrapped to three lines over the pale wall above its scrim. */
@media(max-width:479px){
  .space-tile .court-dia{ display:none; }
  .space-tile .tile-text .h-tile{ font-size:18px; }
}
/* Phones: a three-line tile label ("Strength and / conditioning / floor")
   climbed above the 42% caption band onto the pale wall. Under 480px the
   space tiles' band runs taller and darker, and labels break evenly. */
@media(max-width:479px){
  .space-tile .scrim.scrim-tile{ height:62%; background:linear-gradient(180deg, rgba(17,37,72,0) 0%, rgba(17,37,72,.45) 35%, rgba(17,37,72,.85) 100%); }
}
.space-tile .tile-text .h-tile{ text-wrap:balance; }
