/*
Theme Name: Golden Point
Theme URI: https://goldenpoint.club
Update URI: https://github.com/jonnypez/golden-point
Author: Jonny Pez
Author URI: https://goldenpoint.club
Description: Custom one-purpose theme for Golden Point, Columbia, Missouri's backgammon club. Free to play, chips are score only, prizes are non-cash. Club ladder with FIBS-style ratings and a plain-English explainer, a phone-friendly Score Card, sign-up form stored in wp-admin, live Tournament Director console with projector board, leaderboard, and editable official rules. A soft launch switch keeps the unfinished pages out of sight. All copy editable via Appearance → Customize → Golden Point Settings.
Version: 1.68.0
Requires at least: 6.0
Tested up to: 6.7
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: bb-beergammon
Tags: dark, one-page, custom-background, custom-logo, events
*/

/* =============================================================
   0. TOKENS
   ============================================================= */
:root {
  --ink:        #0d0a07;   /* near-black roasted brown */
  --ink-2:      #16110c;
  --ink-3:      #201812;
  --felt:       #143528;   /* poker felt green */
  --felt-2:     #0d271d;
  --amber:      #f2a33c;   /* draft-beer amber */
  --amber-2:    #d97f1e;
  --gold:       #e8c877;
  --cream:      #f4ead6;
  --cream-dim:  #cdbfa4;
  --oxblood:    #8e2f2f;   /* checker red */
  --line:       rgba(232, 200, 119, .18);
  --font-display: 'Anton', 'Arial Narrow', 'Avenir Next Condensed', 'Impact', sans-serif;
  /* The name itself is set apart from the rest of the display type: a light
     geometric face, wide and open, where --font-display is heavy and packed.
     Only the wordmark and the header brand use it, so the two places the club
     is actually named agree with each other and nothing else moves. */
  --font-wordmark: 'Jost', 'Futura', 'Century Gothic', 'Avenir Next', 'Trebuchet MS', sans-serif;
  --font-accent:  'Fraunces', Georgia, serif;
  --font-body:    'Inter', -apple-system, sans-serif;
  --radius: 14px;
  --shadow-lift: 0 24px 60px -20px rgba(0,0,0,.75);
  --ease: cubic-bezier(.22,.9,.3,1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; overflow-x: hidden; }
body {
  background: var(--ink);
  color: var(--cream);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--amber); text-decoration: none; transition: color .25s var(--ease); }
a:hover { color: var(--gold); }
::selection { background: var(--amber); color: var(--ink); }

h1,h2,h3 { font-family: var(--font-display); letter-spacing: .02em; text-transform: uppercase; line-height: 1.04; font-weight: 400; }
h1 { font-size: clamp(3.4rem, 9vw, 7.5rem); }
h2 { font-size: clamp(2.1rem, 4.5vw, 3.4rem); }
h3 { font-size: clamp(1.2rem, 2vw, 1.5rem); letter-spacing: .06em; }

.wrap { width: min(1180px, 92vw); margin-inline: auto; }
.section { padding: clamp(4.5rem, 9vw, 8rem) 0; position: relative; }

/* Said, but not shown. For headings a sighted reader does not need
   because the page has already made the point visually, and a screen
   reader does need because the outline would otherwise have a hole in
   it. Clipped rather than display:none or visibility:hidden, both of
   which take the text out of the accessibility tree as well. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* kicker, small italic serif label above headings */
.kicker {
  font-family: var(--font-accent);
  font-style: italic;
  font-size: 1.05rem;
  color: var(--amber);
  letter-spacing: .04em;
  display: flex; align-items: center; gap: .8rem;
  margin-bottom: 1.1rem;
}
.kicker::before { content: ""; width: 42px; height: 1px; background: linear-gradient(90deg, var(--amber), transparent); }
.kicker::after  { content: ""; width: 42px; height: 1px; background: linear-gradient(270deg, var(--amber), transparent); }

.lede { font-size: clamp(1.05rem, 1.6vw, 1.3rem); color: var(--cream-dim); max-width: 62ch; }

/* ---- Page transitions ----------------------------------------

   The wait between tapping a link and the next page arriving belongs
   to the server, and a page that sits there unchanged through it reads
   as a link that did not work. So the page being left fades down and
   the page arriving fades up.

   Fading in is here and only here: no script, so it happens even if
   main.js never loads, and there is no way to be left staring at an
   invisible page. Fading out needs the script to hold the navigation
   for a moment, so it is gated behind the class the script adds, and
   the transition is deliberately shorter than the fade in.

   Both are dropped entirely for anyone who has asked for less motion,
   including the script, which does not intercept a single click. */
@keyframes pgIn { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: no-preference) {
  body { animation: pgIn .34s var(--ease); }
  html.has-pt body { transition: opacity .2s var(--ease); }
  html.has-pt.is-leaving body { animation: none; opacity: 0; }
}

/* =============================================================
   1. BACKGAMMON POINT DIVIDER (SVG strip between sections)
   ============================================================= */
.bg-points {
  display: block; width: 100%; height: 34px;
  background-repeat: repeat-x; background-size: 96px 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='34'%3E%3Cpolygon points='0,0 24,0 12,34' fill='%23f2a33c' fill-opacity='.5'/%3E%3Cpolygon points='24,0 48,0 36,34' fill='%238e2f2f' fill-opacity='.55'/%3E%3Cpolygon points='48,0 72,0 60,34' fill='%23f2a33c' fill-opacity='.5'/%3E%3Cpolygon points='72,0 96,0 84,34' fill='%238e2f2f' fill-opacity='.55'/%3E%3C/svg%3E");
  opacity: .8;
}
.bg-points.flip { transform: scaleY(-1); }

/* =============================================================
   2. HEADER / NAV
   ============================================================= */
.site-header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  padding: 1.1rem 0;
  transition: padding .4s var(--ease);
}
/* The scrolled bar's blur lives on a pseudo-element, never on the header
   itself. A backdrop-filter makes its element a containing block for
   position:fixed descendants, which would trap the mobile nav drawer
   inside the header box and crop it to bar height. */
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: rgba(13,10,7,.92);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: 0 6px 30px rgba(0,0,0,.5);
  border-bottom: 1px solid var(--line);
  opacity: 0; pointer-events: none;
  transition: opacity .4s var(--ease);
}
.site-header.scrolled { padding: .6rem 0; }
.site-header.scrolled::before { opacity: 1; }
.header-inner { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 2rem; }
.brand { display: flex; align-items: center; gap: .75rem; color: var(--cream); }
.brand:hover { color: var(--cream); }
/* Follows the hero. Held at 400 rather than 300 because a light weight at
   nav size reads as faint rather than as elegant. */
.brand-name { font-family: var(--font-wordmark); font-weight: 400; font-size: 1.25rem; letter-spacing: .09em; text-transform: uppercase; line-height: 1; }
.brand-name small { display: block; font-family: var(--font-accent); font-style: italic; font-size: .68rem; letter-spacing: .08em; text-transform: none; color: var(--amber); margin-top: .25rem; }

.main-nav ul { display: flex; flex-wrap: nowrap; gap: 1.5rem; list-style: none; align-items: center; }
.main-nav a {
  color: var(--cream-dim); font-size: .78rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; position: relative; padding: .4rem 0;
  white-space: nowrap;
}
.main-nav a::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
  background: var(--amber); transition: width .3s var(--ease);
}
.main-nav a:hover, .main-nav li.current-menu-item a { color: var(--cream); }
.main-nav a:hover::after, .main-nav li.current-menu-item a::after { width: 100%; }
.main-nav li.nav-cta a {
  color: var(--ink); background: var(--amber); padding: .65rem 1.3rem; border-radius: 999px;
  white-space: nowrap;
  transition: transform .25s var(--ease), background .25s;
}
.main-nav li.nav-cta a:hover { background: var(--gold); transform: translateY(-2px); }
.main-nav li.nav-cta a::after { display: none; }

@media (max-width: 1440px) {
  .main-nav ul { gap: 1.15rem; }
  .main-nav a { font-size: .74rem; letter-spacing: .1em; white-space: nowrap; }
  .main-nav li.nav-cta a { padding: .55rem 1rem; white-space: nowrap; }
  .brand-name { font-size: 1.1rem; }
}
@media (max-width: 1180px) {
  .main-nav ul { gap: .85rem; }
  .main-nav a { font-size: .7rem; letter-spacing: .06em; }
  .brand-name small { display: none; }
}

.nav-toggle { display: none; background: none; border: 1px solid var(--line); color: var(--cream); border-radius: 8px; padding: .5rem .7rem; font-size: 1.1rem; cursor: pointer; }

/* The full menu is seven links plus the CTA, so hand off to the drawer
   well before phone width rather than letting it crush. */
@media (max-width: 1040px) {
  .main-nav {
    position: fixed; inset: 0 0 0 auto; height: 100svh;
    width: min(320px, 82vw);
    background: rgba(13,10,7,.98);
    -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
    transform: translateX(100%); transition: transform .4s var(--ease);
    /* Clear the notch at the top and the home indicator at the bottom. */
    padding: calc(5.6rem + env(safe-area-inset-top, 0px)) 2.2rem calc(2.4rem + env(safe-area-inset-bottom, 0px));
    border-left: 1px solid var(--line); z-index: 105;
    overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  }
  .main-nav.open { transform: none; }
  .main-nav ul { flex-direction: column; align-items: flex-start; gap: 1.6rem; }
  .main-nav a { font-size: 1rem; letter-spacing: .16em; }
  .brand-name small { display: block; }
  .nav-toggle { display: block; z-index: 110; position: relative; }

  /* Dim the page behind the open drawer and stop it scrolling underneath. */
  body.nav-open { overflow: hidden; }
  /* Sits below the header (z-index 100), which is what contains the
     drawer and the toggle, so both stay lit and clickable. */
  body.nav-open::after {
    content: ""; position: fixed; inset: 0; z-index: 99;
    background: rgba(6,4,3,.6);
    animation: navScrim .3s var(--ease) both;
  }
}
@keyframes navScrim { from { opacity: 0; } to { opacity: 1; } }

/* =============================================================
   3. HERO
   ============================================================= */
.hero {
  min-height: 100svh;
  display: grid; align-items: center;
  position: relative; isolation: isolate;
  padding: 8rem 0 5rem;
  background:
    linear-gradient(180deg, rgba(13,10,7,.55) 0%, rgba(13,10,7,.82) 62%, var(--ink) 100%),
    radial-gradient(ellipse at 70% 20%, rgba(242,163,60,.22), transparent 55%),
    var(--hero-img, radial-gradient(ellipse at 50% 40%, #2b1e10, var(--ink) 70%));
  background-size: cover; background-position: center;
  background-blend-mode: normal;
}
.hero::after { /* film grain vignette */
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,.55));
}
.hero-badge {
  display: inline-flex; align-items: center; gap: .6rem;
  font-size: .78rem; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
  color: var(--gold); border: 1px solid var(--line); border-radius: 999px;
  padding: .5rem 1.2rem; margin-bottom: 1.6rem;
  background: rgba(13,10,7,.55); backdrop-filter: blur(6px);
}
.hero-badge .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--amber); box-shadow: 0 0 12px var(--amber); animation: pulse 2.2s infinite; }
@keyframes pulse { 50% { opacity: .35; } }

.hero h1 {
  background: linear-gradient(100deg, var(--cream) 30%, var(--gold) 55%, var(--amber) 75%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 10px 40px rgba(0,0,0,.6));
}
.hero .tag {
  font-family: var(--font-accent); font-style: italic;
  font-size: clamp(1.15rem, 2.4vw, 1.7rem); color: var(--cream-dim);
  margin: 1.2rem 0 2.4rem; max-width: 44ch;
}
.hero .tag strong { color: var(--amber); font-weight: 600; }

.hero-meta { display: flex; flex-wrap: wrap; gap: 1rem 2.4rem; margin-bottom: 2.6rem; }
.hero-meta div { display: flex; align-items: center; gap: .65rem; font-size: .95rem; color: var(--cream-dim); }
.hero-meta svg { width: 19px; height: 19px; stroke: var(--amber); flex: none; }
.hero-meta b { color: var(--cream); font-weight: 600; }

.btn-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
.btn {
  display: inline-flex; align-items: center; gap: .6rem;
  font-weight: 700; font-size: .9rem; letter-spacing: .12em; text-transform: uppercase;
  padding: 1.05rem 2.2rem; border-radius: 999px; cursor: pointer;
  transition: transform .25s var(--ease), box-shadow .25s, background .25s; border: none;
}
.btn-primary {
  background: linear-gradient(120deg, var(--amber), var(--amber-2)); color: var(--ink);
  box-shadow: 0 14px 40px -12px rgba(242,163,60,.55);
}
.btn-primary:hover { transform: translateY(-3px) scale(1.02); box-shadow: 0 20px 50px -12px rgba(242,163,60,.7); color: var(--ink); }
.btn-ghost { background: transparent; color: var(--cream); border: 1px solid var(--line); }
.btn-ghost:hover { border-color: var(--amber); color: var(--gold); transform: translateY(-3px); }
/* A label in two lines rather than one long one. The question and the way
   to answer it are two thoughts, and stacking them keeps the button from
   running the width of the column on a phone. Explicit lines, not a
   wrap: left to itself the text would break wherever the box happened
   to run out, which is a different place on every screen. */
.btn-stack {
  flex-direction: column; gap: .18rem;
  padding-block: .85rem; line-height: 1.3; text-align: center;
}

/* floating dice */
.dice-float { position: absolute; z-index: 2; pointer-events: none; opacity: .9; filter: drop-shadow(0 18px 30px rgba(0,0,0,.6)); }
.die {
  width: 88px; height: 88px; border-radius: 18px;
  background: linear-gradient(145deg, #fdf6e6, #d9cbaa);
  display: grid; grid-template: repeat(3,1fr)/repeat(3,1fr); padding: 13px; gap: 4px;
  animation: bob 7s ease-in-out infinite;
}
.die span { border-radius: 50%; background: var(--ink); align-self: center; justify-self: center; width: 15px; height: 15px; opacity: 0; }
.die.show-5 span:nth-child(1), .die.show-5 span:nth-child(3), .die.show-5 span:nth-child(5),
.die.show-5 span:nth-child(7), .die.show-5 span:nth-child(9) { opacity: 1; }
.die.show-3 span:nth-child(1), .die.show-3 span:nth-child(5), .die.show-3 span:nth-child(9) { opacity: 1; }
.die.show-1 span:nth-child(5) { opacity: 1; }
.die.two { transform: rotate(14deg) scale(.8); animation-delay: -3s; }
@keyframes bob { 50% { transform: translateY(-18px) rotate(-8deg); } }

.scroll-hint {
  position: absolute; left: 50%; bottom: 2rem; transform: translateX(-50%);
  color: var(--cream-dim); font-size: .72rem; letter-spacing: .3em; text-transform: uppercase;
  display: flex; flex-direction: column; align-items: center; gap: .6rem; opacity: .7;
}
.scroll-hint::after { content: ""; width: 1px; height: 42px; background: linear-gradient(var(--amber), transparent); animation: drip 2s infinite; }
@keyframes drip { 0% { transform: scaleY(0); transform-origin: top; } 60% { transform: scaleY(1); } 100% { opacity: 0; } }

/* On a phone the hero buttons stack, which walked the second one down
   into the Scroll hint. Give the hint its own reserved strip at the
   bottom and let the buttons run full width above it. */
@media (max-width: 720px) {
  .hero { padding: 7rem 0 9rem; }
  .hero .btn-row { gap: .8rem; }
  .hero .btn-row .btn { flex: 1 1 100%; justify-content: center; }
  .scroll-hint { bottom: calc(2rem + env(safe-area-inset-bottom, 0px)); gap: .45rem; }
  .scroll-hint::after { height: 30px; }
}

/* =============================================================
   3b. GOLDEN POINT HERO  (soft launch front page only)
   -------------------------------------------------------------
   A separate class from .hero on purpose. The full marketing front
   page in front-page.php still wants the photograph, the badge, the
   bullets and the floating dice, and none of that belongs here, so
   nothing above is reused and nothing above is disturbed.

   The static gold wash below is what a visitor with JavaScript off
   sees. aurora-mount.js paints over it and adds .has-aurora.
   ============================================================= */
.gp-hero {
  position: relative; isolation: isolate; overflow: hidden;
  min-height: 100svh;
  display: grid; place-items: center; text-align: center;
  /* An auto column sizes to the max-content of the name, which at phone
     sizes is wider than the padded area, so the column grew past the
     padding and the last letter was clipped. Pin it to the box instead. */
  grid-template-columns: minmax(0, 1fr);
  padding: 7rem 1.2rem 6rem;
  background:
    radial-gradient(ellipse 70% 55% at 50% 42%, rgba(242,163,60,.16), transparent 70%),
    radial-gradient(ellipse at 50% 40%, #241809, var(--ink) 72%);
}
.gp-hero .hero-aurora {
  position: absolute; inset: 0; overflow: hidden;
  pointer-events: none; z-index: 0;
}

/* The point. One long black triangle hanging off the top edge, the way
   a point comes off the bar on a real board. Height is a share of the
   hero so it keeps the same proportion on a phone as on a desktop. */
.gp-point {
  position: absolute; z-index: 1; pointer-events: none;
  left: 50%; top: 0; transform: translateX(-50%);
  width: clamp(150px, 17vw, 290px); height: 78%;
  background: linear-gradient(180deg, #100c08 0%, #050403 62%, #000 100%);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  filter: drop-shadow(0 18px 40px rgba(0,0,0,.75));
}

.gp-hero .hero-copy { position: relative; z-index: 2; width: 100%; max-width: 100%; min-width: 0; }
.gp-hero .wm-wrap { position: relative; }
.gp-hero .wordmark {
  font-family: var(--font-wordmark); font-weight: 300; text-transform: uppercase;
  /* Anton could be stacked at .86 because its caps nearly fill the em box.
     Jost sits lower and lighter, so the same figure would collide; .9 keeps
     the two lines locked together without touching. */
  letter-spacing: 0; line-height: .9;
  font-size: clamp(3rem, 9.5vw, 8.4rem);
  color: var(--cream); margin: 0;
  text-shadow: 0 0 60px rgba(13,10,7,.85), 0 2px 0 rgba(0,0,0,.5);
}
.gp-hero .wordmark .one,
.gp-hero .wordmark .two { display: block; }

/* The gold treatment paints the letters instead of filling them: the type
   becomes a window and the aurora shows through it. Both classes are added
   by JavaScript, so the rule above is the whole fallback. */
.gp-hero .wordmark.gold {
  color: transparent; -webkit-text-fill-color: transparent;
  -webkit-background-clip: text; background-clip: text;
  text-shadow: none; background-repeat: no-repeat;
}
/* lighten, not screen: screen is additive and pushes gold to near white,
   lighten takes the per channel maximum and holds the colour. */
.gp-hero .wordmark.gold.inner { background-blend-mode: lighten; }

/* Two blurred copies of the type sitting behind it, both built in JavaScript.

   The dark one first. The sky moves, so sooner or later a bright pool drifts
   under the name, and a letter lit from inside has nothing left to stand
   against. This darkens only the few pixels around the letters themselves,
   which holds the contrast without putting a visible pool over the hero.

   The gold one on top of it. A transparent fill throws no light of its own,
   and without this the name sits flat on the sky. */
.gp-hero .wm-shade,
.gp-hero .wm-glow {
  position: absolute; left: 0; right: 0; top: 0;
  pointer-events: none; user-select: none;
  background: none; text-shadow: none;
}
/* Both blurs were set against Anton's heavy strokes. A 300 weight puts a lot
   less ink on the page, so the same radii spread the same amount of colour
   over thinner letters and the effect washes out. Pulled in and pushed up a
   little to compensate. */
.gp-hero .wm-shade {
  z-index: -2;
  color: rgba(7,5,3,.92); -webkit-text-fill-color: rgba(7,5,3,.92);
  filter: blur(13px);
}
.gp-hero .wm-glow {
  z-index: -1;
  color: #f0b452; -webkit-text-fill-color: #f0b452;
  filter: blur(21px); opacity: .58;
}

.gp-hero .sub {
  margin-top: 1.1rem;
  font-family: var(--font-accent); font-style: italic;
  color: var(--cream-dim);
  font-size: clamp(.95rem, 1.6vw, 1.25rem); letter-spacing: .02em;
}
.gp-hero .btn-row { justify-content: center; margin-top: 2.4rem; }

/* ---- the activity ticker ----
   One line of club news under the buttons, replaced every few seconds by
   assets/js/news.js. The panels further down the page carry the same three
   stories in full, which is why this one is marked aria-hidden in the
   template and carries no links: it is the moving version of something
   already on the page in a form you can read at your own pace.

   Printed hidden and unhidden by the script, so a site with no games
   recorded, or a browser with JavaScript off, keeps the hero exactly as it
   was. The [hidden] rule has to be here: display:flex would otherwise win
   against the attribute. */
.gp-ticker {
  display: flex; align-items: baseline; justify-content: center;
  gap: .7rem; flex-wrap: wrap;
  margin-top: 1.9rem;
  /* Room for two lines held open all the time. The text swaps while it is
     invisible, and without a floor under the height a short line following
     a long one would jog everything below it. */
  min-height: 3.1em;
  text-align: center;
}
.gp-ticker[hidden] { display: none; }
.gp-tick-lbl {
  flex: none;
  font-family: var(--font-accent); font-style: italic;
  font-size: .7rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--amber); opacity: .8;
}
.gp-tick-lbl[hidden] { display: none; }
.gp-tick-line {
  /* Wide enough that the long ones ("took 3 of 5 3pt matches against") land
     on one line, and the box is two lines deep regardless, so the one that
     does wrap moves nothing. */
  margin: 0; max-width: 58ch;
  font-size: .95rem; line-height: 1.45; color: var(--cream-dim);
  opacity: 0; transform: translateY(5px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.gp-ticker.on .gp-tick-line { opacity: 1; transform: none; }
.gp-tick-line b { color: var(--cream); font-weight: 600; }
.gp-tick-line .tk-n { color: var(--gold); font-weight: 600; }

/* Same fix the old hero needed: stacked buttons on a phone walked down
   into the Scroll hint, so the hint gets its own reserved strip. */
@media (max-width: 720px) {
  .gp-hero { padding: 6rem 1.2rem 9.5rem; }
  .gp-hero .btn-row { gap: .8rem; }
  /* Narrower column, so the line runs to three at the worst. */
  .gp-ticker { margin-top: 1.5rem; min-height: 4.4em; gap: .5rem; }
  .gp-tick-line { font-size: .88rem; max-width: 30ch; }
  .gp-hero .btn-row .btn { flex: 1 1 100%; justify-content: center; padding: .95rem 1.6rem; font-size: .82rem; }
  /* The name carries the whole hero on a phone, so it is set roughly twice
     the size the shared clamp would give at this width, and the point runs
     most of the way down the screen behind it rather than stopping short. */
  /* Jost is a good deal wider than Anton per letter, so 24vw no longer fits
     on its own. It is left where it is on purpose: wmFit() measures the real
     face and writes --wm-vw, which lands the name at the widest size that
     still clears the padding, which is what asking for twice the size meant. */
  .gp-hero .wordmark { font-size: clamp(3.6rem, var(--wm-vw, 24vw), 7rem); letter-spacing: 0; }
  .gp-point { width: clamp(120px, 34vw, 200px); height: 78%; }
}

/* =============================================================
   4. STATS STRIP
   ============================================================= */
.stat-strip {
  background: linear-gradient(180deg, var(--ink-2), var(--ink));
  border-block: 1px solid var(--line);
}
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); }
.stat {
  text-align: center; padding: 2.4rem 1rem; position: relative;
}
.stat + .stat::before { content: ""; position: absolute; left: 0; top: 25%; height: 50%; width: 1px; background: var(--line); }
.stat .num { font-family: var(--font-display); font-size: clamp(2.2rem, 4vw, 3.2rem); color: var(--amber); line-height: 1; }
.stat .lbl { font-size: .74rem; letter-spacing: .22em; text-transform: uppercase; color: var(--cream-dim); margin-top: .55rem; }

/* =============================================================
   5. HOW IT WORKS / CARDS
   ============================================================= */
.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; margin-top: 3rem; }
.card {
  background: linear-gradient(160deg, var(--ink-3), var(--ink-2));
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 2.2rem 1.9rem; position: relative; overflow: hidden;
  transition: transform .35s var(--ease), border-color .35s, box-shadow .35s;
}
.card:hover { transform: translateY(-8px); border-color: rgba(242,163,60,.5); box-shadow: var(--shadow-lift); }
.card::before {
  content: attr(data-step);
  position: absolute; right: -.4rem; top: -1.8rem;
  font-family: var(--font-display); font-size: 7rem; line-height: 1;
  color: rgba(242,163,60,.08); pointer-events: none;
}
.card .chip-ico {
  width: 52px; height: 52px; border-radius: 50%; margin-bottom: 1.3rem;
  border: 3px dashed var(--amber);
  display: grid; place-items: center; color: var(--amber);
  background: radial-gradient(circle at 35% 30%, rgba(242,163,60,.18), transparent 70%);
}
.card .chip-ico svg { width: 24px; height: 24px; stroke: var(--amber); }
.card h3 { margin-bottom: .8rem; color: var(--gold); }
.card p { color: var(--cream-dim); font-size: .97rem; }

/* =============================================================
   6. CUBE PROGRESSION (interactive)
   ============================================================= */
.cube-section { background: radial-gradient(ellipse at 50% 0%, var(--felt-2), var(--ink) 70%); }
.cube-track { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1.1rem; margin-top: 3.2rem; }
.cube-stop { text-align: center; cursor: pointer; }
.cube {
  width: min(110px, 100%); aspect-ratio: 1; margin-inline: auto;
  border-radius: 18px; display: grid; place-items: center;
  font-family: var(--font-display); font-size: 2.6rem; color: var(--ink);
  background: linear-gradient(145deg, #fdf6e6, #cbbc98);
  box-shadow: inset 0 -6px 0 rgba(0,0,0,.18), 0 16px 34px -14px rgba(0,0,0,.7);
  transition: transform .35s var(--ease), box-shadow .35s;
  position: relative;
}
.cube-stop:hover .cube, .cube-stop.active .cube {
  transform: translateY(-10px) rotate(-6deg) scale(1.06);
  box-shadow: inset 0 -6px 0 rgba(0,0,0,.18), 0 26px 50px -14px rgba(242,163,60,.45);
}
.cube-stop.active .cube { outline: 3px solid var(--amber); outline-offset: 4px; }
.cube-stop .rd { margin-top: 1rem; font-size: .74rem; letter-spacing: .24em; text-transform: uppercase; color: var(--cream-dim); }
.cube-stop .stake { font-family: var(--font-accent); font-style: italic; color: var(--amber); font-size: .95rem; }
.cube-detail {
  margin-top: 2.6rem; text-align: center; min-height: 3.2em;
  font-family: var(--font-accent); font-style: italic; font-size: 1.2rem; color: var(--cream-dim);
  transition: opacity .3s;
}
.cube-detail b { color: var(--gold); }

/* =============================================================
   7. VENUE
   ============================================================= */
.venue-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.venue-photo {
  border-radius: var(--radius); overflow: hidden; position: relative;
  box-shadow: var(--shadow-lift); transform: rotate(-1.2deg);
  border: 1px solid var(--line);
}
.venue-photo img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4/3; transition: transform 1.2s var(--ease); }
.venue-photo:hover img { transform: scale(1.05); }
.venue-photo figcaption {
  position: absolute; inset: auto 0 0 0; padding: 2.2rem 1.4rem .9rem;
  background: linear-gradient(transparent, rgba(13,10,7,.9));
  font-family: var(--font-accent); font-style: italic; font-size: .85rem; color: var(--cream-dim);
}
.venue-list { list-style: none; margin-top: 1.8rem; display: grid; gap: 1rem; }
.venue-list li { display: flex; gap: .9rem; align-items: flex-start; color: var(--cream-dim); }
.venue-list svg { width: 20px; height: 20px; stroke: var(--amber); flex: none; margin-top: .2rem; }
.venue-list b { color: var(--cream); }

/* =============================================================
   7b. SOFT LAUNCH HOME
   ============================================================= */
/* Card footer link, so each card on the soft home page points at the
   page it is describing rather than being a dead end. */
.card-link { margin-top: 1.1rem; }
.card-link a {
  font-size: .74rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: var(--amber); display: inline-flex; gap: .4rem; align-items: center;
}
.card-link a:hover { color: var(--gold); }

.soon-band {
  text-align: center;
  background: radial-gradient(ellipse at 50% 0%, var(--felt-2), var(--ink) 70%);
  border-block: 1px solid var(--line);
}
.soon-inner h2 { max-width: 22ch; margin-inline: auto; }
.soon-inner .lede { margin: 1.2rem auto 0; }
.soon-list {
  list-style: none; margin: 2.6rem auto 0; display: grid; gap: 1.1rem;
  max-width: 46rem; text-align: left;
}
.soon-list li { display: flex; gap: .9rem; align-items: flex-start; color: var(--cream-dim); }
.soon-list svg { width: 20px; height: 20px; stroke: var(--amber); flex: none; margin-top: .2rem; }
.soon-list b { color: var(--cream); }
.soon-foot {
  margin: 2.4rem auto 0; max-width: 52ch;
  font-family: var(--font-accent); font-style: italic; color: var(--gold); font-size: 1.02rem;
}

/* -------------------------------------------------------------
   CLUB ACTIVITY BAND (home page)
   Three narrow columns of recent news, written by the results
   rather than by hand. Hidden outright if the script finds it has
   nothing to say. See assets/js/news.js.
   ------------------------------------------------------------- */
.news-band { text-align: center; border-block: 1px solid var(--line); background: linear-gradient(180deg, var(--ink-2), var(--ink)); }
.news-band[hidden] { display: none; }
.news-band .lede { margin: 1.1rem auto 0; }
.news-band .kicker { justify-content: center; }
/* ---- the flare strip ----
   One to three loud lines above the panels: a streak still running, a
   night that was not close. The panels are a record and read at a level
   tone; these are the bits somebody would actually say out loud on the
   way in, so they are allowed to be brighter than everything around them.

   Printed hidden by PHP and unhidden by assets/js/news.js only when there
   is something to say, so a quiet week costs the band nothing at all. */
.news-flare {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .9rem; margin-top: 2.4rem;
}
.news-flare[hidden] { display: none; }
.nf-item {
  flex: 1 1 260px; max-width: 380px; text-align: left;
  position: relative; overflow: hidden;
  border: 1px solid rgba(232,200,119,.34); border-radius: var(--radius);
  padding: 1rem 1.15rem;
  background: linear-gradient(150deg, rgba(242,163,60,.13), rgba(232,200,119,.03) 60%, transparent);
}
/* The shine. A slow band of light crossing the card every few seconds,
   at an angle so it does not read as a scrollbar. Pointer-events off so
   it cannot get in the way of anything, and it is decoration through and
   through, so prefers-reduced-motion takes it off entirely. */
.nf-item::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 40%, rgba(244,234,214,.14) 50%, transparent 60%);
  transform: translateX(-120%);
  animation: nfShine 5.5s var(--ease) 1.2s infinite;
  pointer-events: none;
}
@keyframes nfShine { 60%, 100% { transform: translateX(120%); } }
.nf-tag {
  display: inline-block; margin-bottom: .45rem;
  font-family: var(--font-body); font-size: .62rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--amber);
}
.nf-item p { margin: 0; font-size: .95rem; line-height: 1.45; color: var(--cream-dim); }
.nf-item p b { color: var(--cream); font-weight: 700; }
.nf-item p .tk-n { color: var(--gold); font-weight: 700; }
.nf-when {
  display: block; margin-top: .4rem;
  font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--cream-dim); opacity: .6;
}
/* A run of losses gets the same room on the page and none of the gold.
   The club is telling people where to find a game, not putting somebody
   in the stocks. */
.nf-item.nf-cold {
  border-color: var(--line);
  background: linear-gradient(150deg, rgba(244,234,214,.05), transparent 60%);
}
.nf-item.nf-cold::after { animation: none; }
.nf-item.nf-cold .nf-tag { color: var(--cream-dim); }
@media (prefers-reduced-motion: reduce) {
  .nf-item::after { animation: none; }
}
@media (max-width: 720px) {
  .news-flare { margin-top: 1.8rem; gap: .7rem; }
  .nf-item { flex-basis: 100%; padding: .85rem 1rem; }
  .nf-item p { font-size: .88rem; }
}

/* align-items: start so a card with one row stays short instead of
   stretching to match the tallest column. */
.news-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); align-items: start;
  gap: 1.2rem; margin-top: 2.8rem; text-align: left;
}
.news-card {
  background: linear-gradient(160deg, var(--ink-3), var(--ink-2));
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.5rem 1.5rem 1.3rem;
  opacity: 0; transform: translateY(14px);
}
/* Built after main.js has finished looking for .reveal elements, so
   these fade in off their own class instead. */
.news-grid.is-ready .news-card { animation: nwIn .55s var(--ease) forwards; }
.news-grid.is-ready .news-card:nth-child(2) { animation-delay: .09s; }
.news-grid.is-ready .news-card:nth-child(3) { animation-delay: .18s; }
@keyframes nwIn { to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .news-card { opacity: 1; transform: none; }
  .news-grid.is-ready .news-card { animation: none; }
}
.news-card h3 {
  font-family: var(--font-body); font-size: .7rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--amber);
  margin-bottom: .9rem;
}
.nw-list { list-style: none; margin: 0; }
.nw-list li {
  display: flex; align-items: flex-start; justify-content: space-between; gap: .8rem;
  padding: .6rem 0; border-bottom: 1px solid var(--line);
  color: var(--cream-dim); font-size: .92rem;
}
.nw-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.nw-l { display: flex; align-items: center; flex-wrap: wrap; gap: .45rem; min-width: 0; }
.nw-l a { color: var(--cream); font-weight: 600; }
.nw-l a:hover { color: var(--amber); }
.nw-l b { color: var(--cream); font-weight: 700; }
.nw-l i { font-style: normal; opacity: .55; }
/* The sub-line under a news row. Dimmed with a colour rather than opacity,
   so the session score inside it can still come up bright: an opacity group
   would drag its child down with it. */
.nw-k {
  flex-basis: 100%; display: flex; align-items: baseline; gap: .55rem;
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(205,191,164,.55);
}
.nw-s { color: var(--amber); font-weight: 700; font-size: .78rem; letter-spacing: .06em; }
/* Gammons in the same row. Brighter than the sub-line so it registers,
   quieter than the score so it does not compete with it: the score is
   what happened, the gammons are how. */
.nw-g { color: var(--gold); font-weight: 600; opacity: .8; }
.nw-r {
  flex: none; padding-top: .1rem;
  font-size: .66rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--cream-dim); opacity: .65;
}
.news-foot { margin-top: 2.4rem; }

/* =============================================================
   7b. THE CLUB RECORD
   Drawn by assets/js/record.js into #bbpg-record. The score and
   gammon chips are the news band's .nw-s and .nw-g, borrowed on
   purpose: a 5-3 should look the same wherever the site says it.
   ============================================================= */
.rec-bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem 1.4rem; margin: 0 0 2rem;
  padding-bottom: 1.1rem; border-bottom: 1px solid var(--line);
}
.rec-bar[hidden] { display: none; }
.rec-pick { display: flex; align-items: center; gap: .7rem; }
.rec-pick > span {
  font-size: .68rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cream-dim); opacity: .7;
}
.rec-pick select {
  background: var(--ink-2); color: var(--cream);
  border: 1px solid var(--line); border-radius: 8px;
  padding: .5rem .8rem; font: inherit; font-size: .9rem; min-width: 11rem;
}
.rec-pick select:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.rec-count {
  margin: 0; font-size: .72rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--cream-dim); opacity: .65;
}

.rec-day { margin: 0 0 2.4rem; }
/* The night, and how big it was. Sticky so the heading stays overhead
   while you read down a long evening: the whole point of the page is
   knowing which night you are looking at. */
.rec-date {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  margin: 0 0 .8rem; padding: .55rem 0;
  font-size: clamp(1.05rem, 2vw, 1.35rem); color: var(--gold);
  background: linear-gradient(180deg, var(--ink) 70%, transparent);
  border-bottom: 1px solid var(--line);
}
.rec-date .rec-n {
  flex: none; font-size: .66rem; letter-spacing: .14em; text-transform: uppercase;
  font-weight: 600; color: var(--cream-dim); opacity: .6;
}

.rec-list { list-style: none; margin: 0; padding: 0; }
.rec-row {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: .3rem 1.2rem; padding: .62rem .2rem;
  border-bottom: 1px solid rgba(232, 200, 119, .07);
}
.rec-row:last-child { border-bottom: 0; }
.rec-play { font-size: .98rem; }
.rec-play b { color: var(--cream); font-weight: 700; }
.rec-play i { font-style: normal; color: var(--cream-dim); opacity: .55; padding: 0 .18rem; }
.rec-meta {
  display: flex; align-items: baseline; gap: .55rem; flex: none;
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(205, 191, 164, .55);
}

.rec-more-wrap { text-align: center; margin-top: .6rem; }
#rec-more[hidden] { display: none; }
.rec-empty { color: var(--cream-dim); opacity: .75; }

/* On a phone the two halves of a row stack, so the names get the line
   and the detail sits under them rather than being squeezed beside. */
@media (max-width: 560px) {
  .rec-row { flex-direction: column; gap: .22rem; }
  .rec-meta { flex-wrap: wrap; }
}

/* =============================================================
   8. LEADERBOARD
   ============================================================= */
.lb-wrap { margin-top: 2.8rem; }
.lb-tabs { display: flex; gap: .6rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
/* The Ladder/Player Profiles/Head-to-head tabs specifically, centered
   rather than left-hung like the leaderboard's own tabs above. */
.club-tabs { justify-content: center; }
.lb-tab {
  background: var(--ink-3); border: 1px solid var(--line); color: var(--cream-dim);
  padding: .6rem 1.4rem; border-radius: 999px; cursor: pointer;
  font-size: .8rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  transition: all .25s var(--ease);
}
.lb-tab:hover { color: var(--cream); border-color: var(--amber); }
.lb-tab.active { background: var(--amber); color: var(--ink); border-color: var(--amber); }

/* Wrapper for any wide table. The page sets overflow-x: hidden on html and
   body, so an overflowing table would be silently clipped with no way to
   reach the far columns. This gives the table its own scroll axis instead. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  border-radius: var(--radius);
}
.table-scroll > table { min-width: 100%; }
/* A faint edge cue so it reads as draggable rather than truncated. */
.table-scroll::-webkit-scrollbar { height: 6px; }
.table-scroll::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }

.lb-table { width: 100%; border-collapse: collapse; overflow: hidden; border-radius: var(--radius); }
.lb-table thead th {
  text-align: left; font-size: .72rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--amber); padding: 1rem 1.3rem; background: var(--felt-2);
  border-bottom: 2px solid var(--amber-2);
}
.lb-table tbody td { padding: 1.05rem 1.3rem; border-bottom: 1px solid var(--line); color: var(--cream-dim); }
.lb-table tbody tr { background: var(--ink-2); transition: background .25s; }
.lb-table tbody tr:nth-child(even) { background: var(--ink-3); }
.lb-table tbody tr:hover { background: var(--felt-2); }
.lb-table .p-name { color: var(--cream); font-weight: 600; }
.lb-table .rank { font-family: var(--font-display); font-size: 1.25rem; color: var(--cream-dim); width: 3.4rem; }
tr.podium-1 .rank { color: var(--gold); text-shadow: 0 0 18px rgba(232,200,119,.5); }
tr.podium-2 .rank { color: #c9ccd4; }
tr.podium-3 .rank { color: #dd9070; }
.chip-count { font-family: var(--font-display); font-size: 1.1rem; color: var(--amber); }

/* ---- Rank movement over the last seven days -------------------
   A small chip riding alongside the rank number: green caret up,
   red caret down, grey dash for no change, gold for a player who
   was not on the ladder a week ago. The number of places is in the
   tooltip rather than shouted on the row, so a glance down the
   column reads as colour and direction only. ---- */
.rk-wrap { display: inline-flex; align-items: center; gap: .5rem; }
.rk-n { font-family: var(--font-display); }
/* The rank a member has not earned yet. Quieter than a number and set
   in the body face, so a column of figures does not read as though one
   of them is a strange one. It is a placeholder, not a score.

   Dimmed with a colour rather than with opacity, which is not a style
   choice. Opacity on this span would make it a stacking context, and the
   tooltip that hangs off the mark inside it would then be stuck in there:
   painted at half strength, with the player's name showing straight
   through the middle of the sentence. A translucent tooltip is a
   different bug from a dim question mark, and only one of them was
   wanted. */
.rk-q { font-family: var(--font-body); color: rgba(205,191,164,.5); font-size: 1.05rem; }
/* The mark explains itself on hover or tap, so it has to be focusable
   and it has to be the thing the tooltip is positioned against. */
.rk-tip { position: relative; cursor: help; }
.rk-tip:focus { outline: none; }
.rk-tip:focus-visible { outline: none; border-radius: 4px; box-shadow: 0 0 0 2px rgba(242,163,60,.55); }
/* A sentence rather than "Up 2 places", so this one wraps and is given
   a width to wrap inside. */
.rk-tip[data-tip]::after {
  white-space: normal; width: 15rem; font-weight: 500; line-height: 1.35;
}
.mv {
  display: inline-flex; align-items: center; gap: .2rem;
  font-family: var(--font-body); font-size: .68rem; font-weight: 800;
  line-height: 1; padding: .2rem .4rem; border-radius: 999px;
  border: 1px solid transparent; cursor: default; position: relative;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.mv:hover, .mv:focus-visible { transform: translateY(-1px); }
.mv:focus { outline: none; }
.mv:focus-visible { box-shadow: 0 0 0 2px rgba(242,163,60,.55); }
.mv-i { width: 8px; height: 7px; fill: currentColor; flex: none; }
.mv-down .mv-i { transform: rotate(180deg); }
.mv-up    { color: #7dd3a0; background: rgba(125,211,160,.10); border-color: rgba(125,211,160,.30); }
.mv-down  { color: #e08b8b; background: rgba(224,139,139,.10); border-color: rgba(224,139,139,.30); }
.mv-flat  { color: var(--cream-dim); background: rgba(255,255,255,.03); border-color: var(--line); opacity: .7; }
.mv-flat .mv-d { display: block; width: 8px; text-align: center; }
.mv-new {
  color: var(--gold); background: rgba(232,200,119,.12); border-color: rgba(232,200,119,.32);
  font-size: .56rem; letter-spacing: .12em; text-transform: uppercase; padding: .26rem .42rem;
}
/* Newly ranked and moved since. The pair sits closer together than the
   rank cell's own gap, so they read as one statement about one player
   rather than as two things that happen to be next to each other. */
.mv-pair { display: inline-flex; align-items: center; gap: .24rem; }

/* Tooltip. Deliberately not a native title: this one is reachable
   with a tap because the chip is focusable, and it sits beside the
   chip rather than above it, so the table's own scroll box cannot
   clip it off the top of the first row. */
[data-tip]::after {
  content: attr(data-tip);
  position: absolute; left: calc(100% + .5rem); top: 50%;
  transform: translate(-4px, -50%);
  background: #16110c; color: var(--cream);
  border: 1px solid var(--line); border-radius: 8px;
  padding: .42rem .6rem; font-size: .7rem; font-weight: 600;
  letter-spacing: .01em; text-transform: none; white-space: nowrap;
  opacity: 0; pointer-events: none; z-index: 30;
  box-shadow: 0 10px 28px rgba(0,0,0,.55);
  transition: opacity .18s var(--ease), transform .18s var(--ease);
}
[data-tip]:hover::after, [data-tip]:focus::after {
  opacity: 1; transform: translate(0, -50%);
}

.lb-empty {
  border: 1px dashed var(--line); border-radius: var(--radius);
  padding: 4rem 2rem; text-align: center; background: var(--ink-2);
}
.lb-empty .die { margin: 0 auto 1.4rem; animation: none; }
.lb-empty h3 { color: var(--gold); margin-bottom: .6rem; }
.lb-empty p { color: var(--cream-dim); max-width: 46ch; margin-inline: auto; }

/* =============================================================
   9. SIGN-UP FORM
   ============================================================= */
.signup-section { background: radial-gradient(ellipse at 80% 10%, rgba(142,47,47,.16), transparent 55%), var(--ink-2); border-block: 1px solid var(--line); }
.signup-grid { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(2.5rem, 5vw, 5rem); align-items: start; }

/* Report a game, with the sales pitch taken out: one way through to the
   Score Card, then the form for a game already played. One column and a
   readable measure, because a form stretched over the full wrap puts a
   pair of half-width fields a long way apart and makes the box look
   like a page rather than a thing to fill in. */
.report-stack { width: min(620px, 100%); margin-inline: auto; }
.report-cta { text-align: center; margin-bottom: 1.6rem; }
.pg-form {
  background: linear-gradient(160deg, var(--ink-3), #1a130d);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: clamp(1.8rem, 3.5vw, 2.8rem); box-shadow: var(--shadow-lift);
  position: relative; overflow: hidden;
}
.pg-form::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 5px;
  background: repeating-linear-gradient(90deg, var(--amber) 0 24px, var(--oxblood) 24px 48px);
  opacity: .85;
}
.field { margin-bottom: 1.3rem; }
.field label { display: block; font-size: .74rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: var(--cream-dim); margin-bottom: .5rem; }
.field label .req { color: var(--amber); }
.field input, .field select, .field textarea {
  width: 100%; background: var(--ink); border: 1px solid var(--line); border-radius: 10px;
  color: var(--cream); font: inherit; padding: .9rem 1.05rem;
  transition: border-color .25s, box-shadow .25s;
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px rgba(242,163,60,.18);
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
/* A grid item's default minimum size is the width of its content, so a
   field that refuses to be narrow pushes its whole column wider instead
   of shrinking. Nothing in a form ever wants that. */
.field, .field-row > * { min-width: 0; }
/* iOS decides for itself how wide a date field should be, from the UA
   stylesheet rather than from the box it is sitting in, so it comes out
   noticeably wider than every other input in the same form. Turning the
   native appearance off puts it back under the widths above. Kept to
   date fields on the front end: the admin screens use the WordPress
   styling and should keep it. */
.field input[type="date"] {
  -webkit-appearance: none; appearance: none;
  min-width: 0; max-width: 100%;
}
/* Safari also floats the value rather than filling the field, which
   reads as a different control again next to the text inputs. */
.field input[type="date"]::-webkit-date-and-time-value { text-align: left; margin: 0; }
.field input[type="date"]::-webkit-calendar-picker-indicator { opacity: .55; cursor: pointer; }
.hp-field { position: absolute !important; left: -9999px; opacity: 0; height: 0; overflow: hidden; }
.form-note { font-size: .82rem; color: var(--cream-dim); margin-top: 1rem; font-style: italic; font-family: var(--font-accent); }
.form-msg { border-radius: 10px; padding: 1rem 1.3rem; margin-bottom: 1.4rem; font-weight: 600; }
.form-msg.ok { background: rgba(20,53,40,.7); border: 1px solid #2f7d5a; color: #9fe3c0; }
.form-msg.err { background: rgba(142,47,47,.25); border: 1px solid var(--oxblood); color: #f3b8b8; }

/* =============================================================
   10. RULES PAGE
   ============================================================= */
.rules-hero { padding: 11rem 0 4rem; background: radial-gradient(ellipse at 50% -20%, rgba(242,163,60,.18), transparent 60%), var(--ink); text-align: center; }

/* Aurora hero (Club Play only). The class is added by assets/js/aurora-mount.js,
   so with JavaScript off the hero keeps the amber radial above and nothing here
   ever applies. Decoration only: no pointer events, aria-hidden on the node. */
.rules-hero.has-aurora { position: relative; }
.rules-hero .hero-aurora {
  position: absolute; inset: 0; overflow: hidden;
  pointer-events: none; z-index: 0;
  /* Fade the bottom edge out so the hero melts into the ladder below
     instead of ending on a hard line. */
  -webkit-mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
}
/* A soft scrim between the drifting light and the copy. The blobs move, so
   without it a bright one can wander under the headline and eat the contrast. */
.rules-hero.has-aurora::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(ellipse 76% 62% at 50% 52%, rgba(13,10,7,.46), rgba(13,10,7,0) 72%);
}
.rules-hero.has-aurora > .wrap { position: relative; z-index: 1; }
.rules-body { display: grid; grid-template-columns: 240px 1fr; gap: clamp(2rem, 4vw, 4rem); align-items: start; }
.rules-toc { position: sticky; top: 6.5rem; border-left: 2px solid var(--line); padding-left: 1.3rem; }
.rules-toc a { display: block; padding: .45rem 0; color: var(--cream-dim); font-size: .86rem; letter-spacing: .04em; }
.rules-toc a:hover, .rules-toc a.active { color: var(--amber); }
.rule-block { margin-bottom: 3.4rem; scroll-margin-top: 6.5rem; }
.rule-block h2 { color: var(--gold); font-size: clamp(1.5rem, 2.6vw, 2.1rem); margin-bottom: 1.1rem; display: flex; align-items: baseline; gap: .9rem; }
.rule-block h2 .no { font-family: var(--font-accent); font-style: italic; font-size: 1rem; color: var(--amber); }
.rule-block p, .rule-block li { color: var(--cream-dim); }
.rule-block ul { margin: .8rem 0 0 1.2rem; display: grid; gap: .55rem; }
.rule-block li::marker { color: var(--amber); }
.rule-callout {
  border-left: 3px solid var(--amber); background: var(--ink-2);
  border-radius: 0 var(--radius) var(--radius) 0; padding: 1.2rem 1.5rem; margin-top: 1.2rem;
  font-family: var(--font-accent); font-style: italic; color: var(--gold);
}

/* =============================================================
   11. FOOTER + CTA BAND
   ============================================================= */
.cta-band {
  text-align: center;
  background:
    linear-gradient(rgba(13,10,7,.88), rgba(13,10,7,.88)),
    var(--venue-img, linear-gradient(120deg, #241a0e, var(--ink)));
  background-size: cover; background-position: center; background-attachment: fixed;
  border-block: 1px solid var(--line);
}
.cta-band h2 { max-width: 20ch; margin-inline: auto; }
.cta-band .btn { margin-top: 2.2rem; }

.site-footer { padding: 3.5rem 0 2.5rem; background: #090705; border-top: 1px solid var(--line); }
.footer-inner { display: flex; flex-wrap: wrap; gap: 2rem; justify-content: space-between; align-items: center; }
.site-footer p { color: var(--cream-dim); font-size: .85rem; }
/* Its own line, and quieter than the address above it. Somebody checking a
   version wants to find it, not to be shown it. */
.footer-ver {
  display: block; margin-top: .55rem; font-size: .7rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--cream-dim); opacity: .45;
  font-variant-numeric: tabular-nums;
}
.footer-nav { display: flex; flex-wrap: wrap; gap: .8rem 1.4rem; list-style: none; min-width: 0; }
.footer-nav a { font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; color: var(--cream-dim); }
.footer-nav a:hover { color: var(--amber); }

/* =============================================================
   11b. EDITABLE RULES CONTENT (from the WP editor)
   ============================================================= */
.rules-content h2 {
  color: var(--gold); font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  margin: 2.6rem 0 1.1rem; scroll-margin-top: 6.5rem;
}
.rules-content h2:first-child { margin-top: 0; }
.rules-content p { color: var(--cream-dim); margin-bottom: 1rem; }
.rules-content ul { margin: .8rem 0 1.6rem 1.2rem; display: grid; gap: .55rem; }
.rules-content li { color: var(--cream-dim); }
.rules-content li::marker { color: var(--amber); }
.rules-content blockquote {
  border-left: 3px solid var(--amber); background: var(--ink-2);
  border-radius: 0 var(--radius) var(--radius) 0; padding: 1.2rem 1.5rem; margin: 1.2rem 0 1.8rem;
  font-family: var(--font-accent); font-style: italic; color: var(--gold);
}
.rules-content blockquote p { color: var(--gold); margin: 0; }

/* =============================================================
   11bb. ADD TO HOME SCREEN
   Two panels side by side, because the reader is often holding one
   phone and reading the steps out to a different one across the
   table. They stack on a narrow screen in the order iPhone first,
   which is also the fiddlier of the two.
   ============================================================= */
.install-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.4rem; }
/* Grid rows stretch, and the two sets of steps are never the same
   length, so the shorter card would otherwise leave its footnote
   floating in the middle with the space underneath. Column layout with
   the note pushed down sits both notes on the same line. */
.install-card {
  background: linear-gradient(160deg, var(--ink-3), var(--ink-2));
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 2rem 1.9rem 1.7rem;
  display: flex; flex-direction: column;
}
.install-card .install-note { margin-top: auto; }
.install-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.4rem; }
.install-head .chip-ico {
  width: 46px; height: 46px; flex: none; border-radius: 50%;
  border: 3px dashed var(--amber); color: var(--amber);
  display: grid; place-items: center;
  background: radial-gradient(circle at 35% 30%, rgba(242,163,60,.18), transparent 70%);
}
.install-head .chip-ico svg { width: 21px; height: 21px; stroke: var(--amber); }
.install-head h2 { font-size: 1.5rem; margin: 0; }
.install-sub {
  font-size: .72rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cream-dim); margin: .3rem 0 0;
}
/* Numbered by hand rather than by the browser, so the numeral can be
   set in the display face and sit in its own column with the step text
   wrapping under itself instead of under the number. */
.install-steps { list-style: none; margin: 0; padding: 0; counter-reset: instep; display: grid; gap: 1rem; }
.install-steps li {
  counter-increment: instep; position: relative;
  padding-left: 2.6rem; color: var(--cream-dim); line-height: 1.6;
}
.install-steps li::before {
  content: counter(instep);
  position: absolute; left: 0; top: -.15rem;
  width: 1.8rem; height: 1.8rem; border-radius: 50%;
  border: 1px solid rgba(242,163,60,.4); background: rgba(242,163,60,.08);
  font-family: var(--font-display); font-size: .95rem; color: var(--gold);
  display: grid; place-items: center;
}
.install-note {
  margin: 1.4rem 0 0; padding-top: 1.1rem; border-top: 1px solid var(--line);
  font-family: var(--font-accent); font-style: italic;
  font-size: .82rem; color: var(--cream-dim);
}
.install-warn {
  margin-top: 1.6rem; padding: 1rem 1.2rem;
  border: 1px solid rgba(242,163,60,.35); border-radius: var(--radius);
  background: rgba(242,163,60,.06); color: var(--gold); font-size: .9rem;
}
@media (max-width: 760px) {
  .install-grid { grid-template-columns: 1fr; }
}

/* =============================================================
   11c. HOW RATINGS WORK
   ============================================================= */
/* Sub-headings inside a rules-style page. Deliberately quieter than the
   H2s so the auto-built table of contents (which only reads H2s) still
   maps cleanly onto what the eye picks out as a section. */
.rules-content h3 {
  color: var(--cream); font-size: 1.06rem; letter-spacing: .04em;
  margin: 2rem 0 .7rem;
}
.rules-content .table-scroll { margin: 1.4rem 0 1.8rem; }

/* The formula, set as a little stack of labelled lines rather than a
   wall of code, so it reads on a phone without sideways scrolling. */
.formula-box {
  background: var(--ink-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.3rem 1.4rem; margin: 1.4rem 0 1.8rem;
  display: grid; gap: 1rem;
}
/* Each code line gets its own row. A formula that wraps mid-expression is
   worse than no formula at all, so they never share a line. */
.formula-line { display: grid; gap: .3rem; }
.formula-line code { display: block; }
.fl-label {
  font-size: .68rem; font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--amber);
}
.formula-line code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .95rem; color: var(--gold); line-height: 1.5;
  overflow-wrap: anywhere;
}
/* On a narrow phone the upset-factor line is simply longer than the screen.
   Shrink it a step and hang the indent, so if it does turn over, the second
   row is obviously a continuation and not a new statement. */
@media (max-width: 560px) {
  .formula-line code {
    font-size: .8rem; padding-left: 1.2rem; text-indent: -1.2rem;
    overflow-wrap: normal;
  }
}
.fl-note { font-size: .78rem; color: var(--cream-dim); font-style: italic; font-family: var(--font-accent); }
.cap {
  font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--amber); opacity: .8; margin-left: .35rem; white-space: nowrap;
}

/* The interactive calculator. */
.calc {
  background: var(--ink-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.4rem; margin: 1.4rem 0 2rem;
  display: grid; gap: .9rem;
}
.calc-row { display: grid; grid-template-columns: 1fr auto; gap: 1rem; align-items: center; }
.calc-row[hidden] { display: none; }
.calc-row label {
  font-size: .74rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--cream-dim);
}
.calc-row input, .calc-row select {
  background: var(--ink); color: var(--cream); border: 1px solid var(--line);
  border-radius: 10px; padding: .6rem .8rem; font: inherit; font-size: 1rem;
  min-width: 9rem; text-align: right;
}
.calc-row select { text-align: left; }
.calc-row input:focus, .calc-row select:focus { outline: 2px solid var(--amber); outline-offset: 1px; }
.calc-out {
  display: grid; gap: .35rem; margin-top: .4rem;
  border-top: 1px solid var(--line); padding-top: 1.1rem;
}
.co-big {
  font-family: var(--font-display); font-size: clamp(2.2rem, 7vw, 3rem);
  color: var(--gold); line-height: 1;
}
.co-line { color: var(--cream-dim); font-size: .95rem; }
.co-line b { color: var(--cream); }
.co-math {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .78rem; color: var(--amber); margin-top: .4rem; overflow-wrap: anywhere;
}
.co-note {
  font-family: var(--font-accent); font-style: italic;
  color: var(--cream-dim); font-size: .9rem; margin-top: .2rem;
}
/* The "how does this work?" link under the club ladder. */
.td-more { color: var(--amber); border-bottom: 1px solid rgba(212,160,23,.4); }
.td-more:hover { color: var(--gold); }

@media (max-width: 520px) {
  .calc-row { grid-template-columns: 1fr; gap: .35rem; }
  .calc-row input, .calc-row select { width: 100%; text-align: left; }
}

/* =============================================================
   14. TOURNAMENT DIRECTOR CONSOLE
   ============================================================= */
.td-shell { padding: 8.5rem 0 5rem; min-height: 100svh; }
.td-topbar { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.6rem; margin-bottom: 2rem; }
.td-topbar h1 { font-size: clamp(1.9rem, 4vw, 3rem); }
.td-pill {
  font-size: .72rem; font-weight: 700; letter-spacing: .2em; text-transform: uppercase;
  padding: .5rem 1.1rem; border-radius: 999px; border: 1px solid var(--line); color: var(--cream-dim);
  display: inline-flex; align-items: center; gap: .5rem;
}
.td-pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cream-dim); }
.td-pill.is-live { color: var(--gold); border-color: rgba(242,163,60,.5); }
.td-pill.is-live .dot { background: var(--amber); box-shadow: 0 0 10px var(--amber); animation: pulse 2s infinite; }
.td-topbar .spacer { flex: 1; }

.td-grid { display: grid; grid-template-columns: 360px 1fr; gap: 1.5rem; align-items: start; }
.td-panel {
  background: linear-gradient(165deg, var(--ink-3), var(--ink-2));
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem;
}
.td-panel > h3 { color: var(--gold); margin-bottom: 1.1rem; display: flex; align-items: center; justify-content: space-between; gap: .8rem; }
.td-panel > h3 .sub { font-family: var(--font-accent); font-style: italic; font-size: .85rem; color: var(--cream-dim); text-transform: none; letter-spacing: 0; }

.td-btn {
  display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  background: var(--ink); color: var(--cream); border: 1px solid var(--line); border-radius: 9px;
  font: 700 .78rem/1 var(--font-body); letter-spacing: .1em; text-transform: uppercase;
  padding: .65rem 1rem; transition: all .2s var(--ease); white-space: nowrap;
}
.td-btn:hover { border-color: var(--amber); color: var(--gold); transform: translateY(-1px); }
.td-btn.gold { background: linear-gradient(120deg, var(--amber), var(--amber-2)); color: var(--ink); border-color: transparent; }
.td-btn.gold:hover { color: var(--ink); filter: brightness(1.08); }
.td-btn.danger:hover { border-color: var(--oxblood); color: #f3b8b8; }
.td-btn.tiny { padding: .4rem .6rem; font-size: .68rem; border-radius: 7px; }
.td-btn:disabled { opacity: .35; cursor: not-allowed; transform: none; }

.td-add { display: flex; gap: .6rem; margin-bottom: 1.1rem; }
.td-add input {
  flex: 1; background: var(--ink); border: 1px solid var(--line); border-radius: 9px;
  color: var(--cream); font: inherit; padding: .65rem .9rem;
}
.td-add input:focus { outline: none; border-color: var(--amber); }

.td-roster { list-style: none; display: grid; gap: .5rem; }
.td-roster li {
  display: flex; align-items: center; gap: .7rem;
  background: var(--ink); border: 1px solid var(--line); border-radius: 10px; padding: .6rem .8rem;
  transition: border-color .2s;
}
.td-roster li:hover { border-color: rgba(242,163,60,.4); }
.td-roster .rk { font-family: var(--font-display); font-size: 1rem; color: var(--cream-dim); width: 1.7rem; text-align: center; }
.td-roster li.podium-1 .rk { color: var(--gold); }
.td-roster .nm { flex: 1; font-weight: 600; color: var(--cream); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.td-roster .meta { font-size: .68rem; color: var(--cream-dim); letter-spacing: .05em; }
.chipbadge {
  font-family: var(--font-display); font-size: .95rem; color: var(--ink);
  background: linear-gradient(120deg, var(--gold), var(--amber));
  border-radius: 999px; padding: .28rem .75rem; min-width: 3.1rem; text-align: center;
}

.round-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2rem;
  background: radial-gradient(ellipse at 20% 0%, rgba(20,53,40,.9), var(--ink-2) 75%);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.4rem 1.7rem; margin-bottom: 1.5rem;
}
.round-head .rh-round { font-family: var(--font-display); font-size: 2rem; color: var(--cream); }
.round-head .rh-cube {
  width: 62px; height: 62px; border-radius: 12px; display: grid; place-items: center;
  font-family: var(--font-display); font-size: 1.7rem; color: var(--ink);
  background: linear-gradient(145deg, #fdf6e6, #cbbc98);
  box-shadow: inset 0 -4px 0 rgba(0,0,0,.18);
}
.round-head .rh-cube-lbl { font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--cream-dim); }
.td-timer { font-family: var(--font-display); font-size: 2.6rem; color: var(--amber); letter-spacing: .04em; min-width: 4.5ch; }
.td-timer.warn { color: #ff6b5e; animation: pulse 1s infinite; }
.td-timer-lbl { font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--cream-dim); }

.match-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); gap: 1.1rem; }
.match-card {
  background: linear-gradient(165deg, var(--felt-2), var(--ink-2) 80%);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.2rem 1.3rem;
  transition: border-color .25s;
}
.match-card.done { opacity: .75; }
.match-card:hover { border-color: rgba(242,163,60,.45); }
.match-card .tbl { font-size: .66rem; letter-spacing: .24em; text-transform: uppercase; color: var(--amber); margin-bottom: .8rem; display: flex; justify-content: space-between; }
.match-card .who { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: .6rem; margin-bottom: .9rem; }
.match-card .pbtn {
  background: var(--ink); border: 1px solid var(--line); color: var(--cream); border-radius: 10px;
  padding: .7rem .5rem; cursor: pointer; font: 600 .9rem var(--font-body); transition: all .2s;
  display: grid; gap: .2rem; justify-items: center;
}
.match-card .pbtn small { font-family: var(--font-display); color: var(--amber); font-size: .85rem; }
.match-card .pbtn:hover { border-color: var(--amber); }
.match-card .pbtn.sel { border-color: var(--amber); background: rgba(242,163,60,.14); box-shadow: 0 0 0 2px rgba(242,163,60,.25); }
.match-card .vs { font-family: var(--font-accent); font-style: italic; color: var(--cream-dim); }
.match-card .pts { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; }
.match-card .pts .lbl { font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--cream-dim); width: 100%; }
.ptbtn {
  background: var(--ink); border: 1px solid var(--line); color: var(--gold); border-radius: 8px;
  font: 700 .8rem var(--font-body); padding: .45rem .7rem; cursor: pointer; transition: all .2s;
}
.ptbtn:hover { border-color: var(--amber); }
.ptbtn:disabled { opacity: .3; cursor: not-allowed; }
.match-card input.custom-pts { width: 3.6rem; background: var(--ink); border: 1px solid var(--line); border-radius: 8px; color: var(--cream); padding: .42rem .5rem; font: inherit; }
.match-card .games { margin-top: .8rem; font-size: .8rem; color: var(--cream-dim); display: grid; gap: .25rem; }
.match-card .games b { color: var(--gold); }
.bye-card {
  border: 1px dashed var(--line); border-radius: var(--radius); padding: 1.2rem 1.3rem;
  color: var(--cream-dim); font-family: var(--font-accent); font-style: italic;
}

.td-log { margin-top: 1.5rem; max-height: 220px; overflow: auto; font-size: .8rem; color: var(--cream-dim); display: grid; gap: .35rem; }
.td-log div { border-bottom: 1px dashed rgba(232,200,119,.1); padding-bottom: .35rem; }
.td-log time { color: var(--amber); font-size: .7rem; margin-right: .5rem; }
.td-empty { border: 1px dashed var(--line); border-radius: var(--radius); padding: 3rem 2rem; text-align: center; color: var(--cream-dim); }
.td-note { font-size: .78rem; color: var(--cream-dim); font-style: italic; font-family: var(--font-accent); margin-top: .8rem; }
.td-gate { min-height: 70svh; display: grid; place-items: center; padding: 8rem 0 4rem; }
.td-gate .card { max-width: 440px; text-align: center; }

/* =============================================================
   15. LIVE PROJECTOR BOARD
   ============================================================= */
.live-body {
  background:
    radial-gradient(ellipse at 50% -10%, rgba(242,163,60,.14), transparent 55%),
    radial-gradient(ellipse at 85% 100%, rgba(20,53,40,.55), transparent 60%),
    var(--ink);
  min-height: 100svh; color: var(--cream); font-family: var(--font-body);
}
.live-shell { width: min(1700px, 96vw); margin-inline: auto; padding: 2.2rem 0 3rem; }
.live-top { display: flex; align-items: center; justify-content: space-between; gap: 2rem; flex-wrap: wrap; border-bottom: 1px solid var(--line); padding-bottom: 1.4rem; margin-bottom: 2rem; }
.live-brand { display: flex; align-items: center; gap: 1rem; }
/* The disc used to sit in the site header too. It comes off the header now,
   and the tournament board is the only place still drawing one, so the whole
   rule lives here rather than in a shared block with one user. */
.live-brand .brand-mark {
  width: 56px; height: 56px; flex: none;
  border-radius: 50%;
  border: 2px dashed var(--amber);
  display: grid; place-items: center;
  font-family: var(--font-display); font-size: 1.3rem; color: var(--amber);
  background: radial-gradient(circle at 35% 30%, #2b2013, var(--ink-2));
}
.live-brand h1 { font-size: clamp(1.6rem, 3vw, 2.6rem); }
.live-brand small { display: block; font-family: var(--font-accent); font-style: italic; color: var(--amber); font-size: .95rem; letter-spacing: .05em; }
.live-roundbox { display: flex; align-items: center; gap: 2.2rem; }
.live-roundbox .lr { text-align: center; }
.live-roundbox .lr .big { font-family: var(--font-display); font-size: clamp(2.2rem, 4vw, 3.6rem); color: var(--cream); line-height: 1; }
.live-roundbox .lr .lbl { font-size: .68rem; letter-spacing: .26em; text-transform: uppercase; color: var(--cream-dim); margin-top: .4rem; }
.live-roundbox .lr.lr-cube .big {
  background: linear-gradient(145deg, #fdf6e6, #cbbc98); color: var(--ink);
  border-radius: 16px; padding: .45rem 1.1rem .55rem; display: block;
  width: fit-content; margin: 0 auto .2rem;
  box-shadow: inset 0 -5px 0 rgba(0,0,0,.18);
}
.live-roundbox .lr.timer .big { color: var(--amber); font-variant-numeric: tabular-nums; }
.live-roundbox .lr.timer.warn .big { color: #ff6b5e; animation: pulse 1s infinite; }

.live-flash {
  display: none; text-align: center; margin: 0 0 1.8rem;
  font-family: var(--font-display); font-size: clamp(1.6rem, 4vw, 3rem); letter-spacing: .1em;
  color: #ff6b5e; text-transform: uppercase; animation: pulse 1s infinite;
}
.live-flash.on { display: block; }

.live-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 2rem; align-items: start; }
.live-h { font-family: var(--font-display); font-size: 1.1rem; letter-spacing: .18em; text-transform: uppercase; color: var(--amber); margin-bottom: 1rem; }
.live-pairings { display: grid; gap: .9rem; }
.live-match {
  display: grid; grid-template-columns: auto 1fr auto 1fr; align-items: center; gap: 1.1rem;
  background: linear-gradient(165deg, var(--felt-2), var(--ink-2) 85%);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem 1.4rem;
}
.live-match .t { font-size: .66rem; letter-spacing: .2em; color: var(--cream-dim); text-transform: uppercase; writing-mode: vertical-rl; transform: rotate(180deg); }
.live-match .pl { display: flex; align-items: baseline; justify-content: space-between; gap: .8rem; min-width: 0; }
.live-match .pl .n { font-size: clamp(1.05rem, 1.6vw, 1.5rem); font-weight: 700; color: var(--cream); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.live-match .pl .c { font-family: var(--font-display); color: var(--amber); font-size: clamp(1rem, 1.4vw, 1.3rem); }
.live-match .x { font-family: var(--font-accent); font-style: italic; color: var(--cream-dim); }
.live-match.done { opacity: .55; }
.live-bye { font-family: var(--font-accent); font-style: italic; color: var(--cream-dim); padding: .4rem .2rem; }

.live-standings { display: grid; gap: .55rem; }
.live-standings .row {
  display: grid; grid-template-columns: 3rem 1fr auto; align-items: center; gap: 1rem;
  background: var(--ink-2); border: 1px solid var(--line); border-radius: 12px; padding: .65rem 1.1rem;
}
.live-standings .row.podium-1 { border-color: rgba(232,200,119,.6); background: linear-gradient(120deg, rgba(232,200,119,.12), var(--ink-2)); }
.live-standings .row.podium-2 { border-color: rgba(201,204,212,.35); }
.live-standings .row.podium-3 { border-color: rgba(221,144,112,.35); }
.live-standings .rk { font-family: var(--font-display); font-size: 1.4rem; color: var(--cream-dim); }
.live-standings .podium-1 .rk { color: var(--gold); }
.live-standings .podium-2 .rk { color: #c9ccd4; }
.live-standings .podium-3 .rk { color: #dd9070; }
.live-standings .n { font-size: clamp(1rem, 1.5vw, 1.35rem); font-weight: 700; }
.live-standings .c { font-family: var(--font-display); font-size: clamp(1.1rem, 1.6vw, 1.4rem); color: var(--amber); }

.live-status-line { margin-top: 1.6rem; font-family: var(--font-accent); font-style: italic; color: var(--cream-dim); font-size: 1.05rem; }
.live-idle { min-height: 80svh; display: grid; place-items: center; text-align: center; }
.live-idle h2 { font-size: clamp(2.4rem, 6vw, 5rem); background: linear-gradient(100deg, var(--cream) 30%, var(--gold) 55%, var(--amber) 75%); -webkit-background-clip: text; background-clip: text; color: transparent; }
.live-champ { text-align: center; padding: 3rem 0 1rem; }
.live-champ .crown { font-size: 3rem; }
.live-champ h2 { font-size: clamp(2.6rem, 6vw, 5rem); color: var(--gold); text-shadow: 0 0 40px rgba(232,200,119,.35); }
.live-champ p { font-family: var(--font-accent); font-style: italic; color: var(--cream-dim); font-size: 1.3rem; margin-top: .6rem; }

@media (max-width: 1100px) {
  .td-grid { grid-template-columns: 1fr; }
  .live-grid { grid-template-columns: 1fr; }
}

/* =============================================================
   11c. LEAGUE PAGE
   ============================================================= */
.league-table .drop-note { display: block; font-family: var(--font-body); font-size: .62rem; color: var(--cream-dim); letter-spacing: .08em; text-transform: uppercase; }
tr.playoff td.rank { color: var(--gold); }
tr.cutline td {
  padding: .35rem 1.3rem; background: transparent; border-bottom: 2px dashed rgba(242,163,60,.55);
  text-align: center;
}
tr.cutline span {
  font-family: var(--font-accent); font-style: italic; font-size: .82rem; color: var(--amber);
  letter-spacing: .06em;
}
.form-dots { display: inline-flex; gap: .3rem; }
.form-dots i {
  font-style: normal; width: 22px; height: 22px; border-radius: 50%;
  display: inline-grid; place-items: center;
  font-size: .68rem; font-weight: 700; color: var(--cream-dim);
  background: var(--ink); border: 1px solid var(--line);
}
.form-dots i.good { color: var(--cream); border-color: rgba(242,163,60,.5); }
.form-dots i.gold { color: var(--ink); background: linear-gradient(120deg, var(--gold), var(--amber)); border-color: transparent; }
.form-dots i.miss { color: var(--cream-dim); opacity: .5; }

.sched-list { list-style: none; margin-top: 2.4rem; display: grid; gap: .6rem; }
.sched-list li {
  display: grid; grid-template-columns: 3rem 1fr auto; align-items: center; gap: 1rem;
  background: var(--ink-2); border: 1px solid var(--line); border-radius: 12px; padding: .8rem 1.2rem;
}
.sched-list .no { font-family: var(--font-display); font-size: 1.2rem; color: var(--cream-dim); text-align: center; }
.sched-list .what { font-weight: 600; color: var(--cream); }
.sched-list .st { font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--cream-dim); }
.sched-list li.done { opacity: .82; }
.sched-list li.done .st { color: var(--amber); }
.sched-list li.finals { border-color: rgba(232,200,119,.55); background: linear-gradient(120deg, rgba(232,200,119,.1), var(--ink-2)); }
.sched-list li.finals .no, .sched-list li.finals .st { color: var(--gold); }

.season-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1.2rem; margin-top: 2.4rem; }
.season-card {
  background: linear-gradient(160deg, var(--ink-3), var(--ink-2));
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.6rem;
  transition: transform .3s var(--ease), border-color .3s;
}
.season-card:hover { transform: translateY(-5px); border-color: rgba(242,163,60,.5); }
.season-card h3 { color: var(--gold); margin-bottom: .7rem; }
.season-card .champ { font-weight: 700; color: var(--cream); font-size: 1.05rem; }
.season-card .meta { font-size: .82rem; color: var(--cream-dim); margin-top: .3rem; }

@media (max-width: 720px) {
  .league-table th:nth-child(6), .league-table td:nth-child(6) { display: none; }
  .sched-list li { grid-template-columns: 2.2rem 1fr; }
  .sched-list .st { grid-column: 2; }
}

/* =============================================================
   11d. CLUB PLAY
   ============================================================= */
.club-ladder .prov {
  font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--cream-dim); border: 1px solid var(--line); border-radius: 999px; padding: .15rem .5rem; margin-left: .5rem;
}
.club-ladder a { color: var(--cream); border-bottom: 1px dashed rgba(242,163,60,.5); }
.club-ladder a:hover { color: var(--gold); }
/* The run and its mark are one reading, so they do not get to break across
   two lines. On a narrow phone the Streak column is tight enough that an
   emoji a touch wider than the box expects will drop below the number it is
   about, and a fire sitting under a W4 looks like a second thing rather than
   a comment on the first. */
.streak-w, .streak-l { color: #7dd3a0; font-weight: 700; white-space: nowrap; }
.streak-l { color: #e08b8b; }

/* The hottest run on the ladder. One of these on the page at a time.
   cursor:help because the emoji is the whole of the statement and the
   title attribute is the rest of it. The breathe is slow and small on
   purpose: the ladder is a table of numbers and a flicker in one cell of
   it pulls the eye off every other row. */
.hot-mark {
  display: inline-block; font-size: .95em; line-height: 1; margin-left: .3rem;
  cursor: help; vertical-align: baseline;
  filter: drop-shadow(0 0 5px rgba(242,163,60,.55));
  animation: hot-breathe 2.8s ease-in-out infinite;
}
@keyframes hot-breathe {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 4px rgba(242,163,60,.42)); }
  50%      { transform: scale(1.12); filter: drop-shadow(0 0 9px rgba(242,163,60,.75)); }
}
/* The coldest run, and the mirror of the above with one difference: it is
   quieter. A losing streak is not an achievement and the mark should not
   read as a pointing finger, so the glow is dimmer, the breathe is slower
   and shallower, and the whole thing sits back a little. Present enough to
   be noticed by anybody reading the row, not enough to be noticed from
   across the page. */
.cold-mark {
  display: inline-block; font-size: .95em; line-height: 1; margin-left: .3rem;
  cursor: help; vertical-align: baseline; opacity: .85;
  filter: drop-shadow(0 0 4px rgba(125,185,232,.4));
  animation: cold-breathe 4.2s ease-in-out infinite;
}
@keyframes cold-breathe {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 3px rgba(125,185,232,.3)); }
  50%      { transform: scale(1.06); filter: drop-shadow(0 0 7px rgba(125,185,232,.55)); }
}
/* Somebody who has asked their phone to stop moving things has asked for
   this too. The glow stays, because it is not motion. */
@media (prefers-reduced-motion: reduce) {
  .hot-mark, .cold-mark { animation: none; }
}
.spark { display: block; }

.club-view { margin-top: .4rem; }
.club-pickers { display: flex; gap: 1.4rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
.club-pickers label { font-size: .74rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--cream-dim); display: grid; gap: .45rem; }
.club-select {
  background: var(--ink-2); border: 1px solid var(--line); border-radius: 10px;
  color: var(--cream); font: 600 1rem var(--font-body); padding: .7rem 2.2rem .7rem 1rem; min-width: 220px;
}
.club-select:focus { outline: none; border-color: var(--amber); }

.club-profile-head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.2rem; }
.club-profile-head h3 { font-size: clamp(1.6rem, 3vw, 2.2rem); color: var(--gold); }
.club-profile-head .prov { font-family: var(--font-accent); font-style: italic; color: var(--cream-dim); font-size: .9rem; }

.stat-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: .8rem; margin-bottom: 1.4rem; }
.tile { background: linear-gradient(160deg, var(--ink-3), var(--ink-2)); border: 1px solid var(--line); border-radius: 12px; padding: 1rem 1.1rem; }
.tile .v { font-family: var(--font-display); font-size: 1.7rem; color: var(--amber); line-height: 1.05; }
.tile .l { font-size: .68rem; letter-spacing: .18em; text-transform: uppercase; color: var(--cream-dim); margin-top: .4rem; }

.chart-box { background: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.2rem 1.3rem 1rem; margin-bottom: 1.2rem; }
.chart-box h4 { font-family: var(--font-display); font-size: .92rem; letter-spacing: .14em; text-transform: uppercase; color: var(--gold); margin-bottom: .8rem; }
.chart { width: 100%; height: auto; display: block; }
.chart .axis-t { fill: var(--cream-dim); font: 10px var(--font-body); }
.chart .line-lbl { fill: var(--cream); font: 700 11px var(--font-body); }
.chart .hovpt:hover { fill: rgba(242,163,60,.25); }
.legend { display: flex; gap: 1.2rem; align-items: center; font-size: .8rem; color: var(--cream-dim); margin-bottom: .5rem; }
.legend i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-right: .4rem; vertical-align: -1px; }

.club-cols { display: grid; grid-template-columns: 1fr 1.4fr; gap: 1.2rem; align-items: start; }
.chart-box .mu { color: var(--cream-dim); line-height: 2; }
.chart-box .mu b { color: var(--cream); }
.lb-table.mini thead th { padding: .6rem .8rem; }
.lb-table.mini tbody td { padding: .55rem .8rem; font-size: .88rem; }

.h2h-card {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 1.4rem;
  background: linear-gradient(165deg, var(--felt-2), var(--ink-2) 85%);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.8rem; margin-bottom: 1.2rem;
  text-align: center;
}
.h2h-side h3 { color: var(--cream); font-size: clamp(1.1rem, 2vw, 1.5rem); margin: .4rem 0 .2rem; }
.h2h-side .big { font-family: var(--font-display); font-size: clamp(2.6rem, 6vw, 4rem); color: var(--amber); line-height: 1; }
.h2h-side .sub { font-size: .74rem; letter-spacing: .14em; text-transform: uppercase; color: var(--cream-dim); margin-top: .3rem; }
.h2h-side .dotk { display: inline-block; width: 14px; height: 14px; border-radius: 4px; }
.h2h-mid { display: grid; gap: .3rem; justify-items: center; }
.h2h-mid span { font-size: .66rem; letter-spacing: .22em; text-transform: uppercase; color: var(--cream-dim); }
.h2h-mid b { font-family: var(--font-accent); font-style: italic; font-size: 1.4rem; color: var(--gold); }
.h2h-mid em { font-family: var(--font-accent); font-style: italic; font-size: .82rem; color: var(--amber); max-width: 16ch; }

@media (max-width: 820px) {
  .club-cols { grid-template-columns: 1fr; }
  .h2h-card { grid-template-columns: 1fr; gap: .8rem; }
  /* Trend on the Club board, and Record on the provisional board: the
     narrowest columns whose loss costs the least. Scoped to the board
     they belong to, because the three tables share .club-ladder but
     not their column order. */
  .main-board th:nth-child(7), .main-board td:nth-child(7) { display: none; }
  .prov-board th:nth-child(6), .prov-board td:nth-child(6) { display: none; }
}

/* ---- Phone portrait: tighten the tables so the ladder fits without
        losing a column, and let anything still too wide scroll. ---- */
@media (max-width: 560px) {
  .lb-table thead th { padding: .62rem .5rem; font-size: .6rem; letter-spacing: .08em; }
  .lb-table tbody td { padding: .7rem .5rem; font-size: .84rem; }
  .lb-table .rank { width: 2.6rem; font-size: 1rem; }
  /* No room beside the number on a phone: put the movement chip
     under it and let the tooltip sit over the name column. */
  .rk-wrap { flex-direction: column; align-items: flex-start; gap: .22rem; }
  .mv { font-size: .62rem; padding: .16rem .32rem; }
  .mv-new { font-size: .5rem; letter-spacing: .1em; }
  .lb-table.mini thead th { padding: .5rem .5rem; }
  .lb-table.mini tbody td { padding: .5rem .5rem; font-size: .8rem; }
  .chip-count { font-size: .95rem; }

  /* Win % is derivable from Record, so it is the first to go. */
  .main-board th:nth-child(5), .main-board td:nth-child(5) { display: none; }
  /* On the provisional board the rating is the least of it: what that
     table is for is how close somebody is. */
  .prov-board th:nth-child(5), .prov-board td:nth-child(5) { display: none; }
  .club-ladder .prov { display: block; width: max-content; margin: .25rem 0 0; font-size: .56rem; letter-spacing: .1em; }

  /* Recent games: drop the League night / Club play source column. */
  .recent-games td:nth-child(5) { display: none; }

  .stat-tiles { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: .6rem; }
  .tile { padding: .8rem .85rem; }
  .tile .v { font-size: 1.4rem; }
  .tile .l { font-size: .6rem; letter-spacing: .12em; }
  .chart-box { padding: 1rem .9rem .8rem; }
  .club-select { min-width: 0; width: 100%; }
  .club-pickers { gap: .9rem; }
  .club-pickers label { width: 100%; }
}

/* =============================================================
   11e. THE THREE BOARDS
   -------------------------------------------------------------
   The ladder used to be one table with the provisionals shaded in
   at the bottom, which read as a single ranking with second-class
   citizens on it. It is three tables now, because they answer
   three different questions: who is best, who is closest to a
   place, and who has gone quiet. Each keeps the leaderboard's own
   table styling and is told apart by its heading and its note.
   ============================================================= */
.boards { display: grid; gap: 3rem; }
.board-h {
  font-family: var(--font-display); color: var(--gold);
  font-size: clamp(1.25rem, 2.4vw, 1.7rem); margin-bottom: .5rem;
}
.board-note {
  color: var(--cream-dim); font-size: .9rem; line-height: 1.65;
  max-width: 68ch; margin-bottom: 1.1rem;
}

/* How sure the rating is, riding along beside it in small type. It
   is deliberately quiet: the number is the headline and the spread
   is the footnote, not the other way round. */
.chip-count .rd {
  font-family: var(--font-body); font-size: .68rem; font-weight: 700;
  letter-spacing: .04em; color: var(--cream-dim); opacity: .75;
  white-space: nowrap;
}

/* Away, on any board. Faded, never struck through: nobody has been
   removed, they have just not been in lately. */
.club-ladder tr.idle-row { background: rgba(255,255,255,.015); }
.club-ladder tr.idle-row td { color: rgba(205,191,164,.55); }
.club-ladder tr.idle-row .p-name a { color: rgba(205,191,164,.62); }
.club-ladder tr.idle-row .chip-count { color: rgba(242,163,60,.5); }
.club-ladder tr.idle-row .rank { color: rgba(205,191,164,.4); }
.club-ladder tr.idle-row .spark path { stroke: rgba(204,125,27,.45); }
.club-ladder tr.idle-row:hover td { color: var(--cream-dim); }

/* Members who have not settled their rating yet: ten games and a
   spread of opponents, the same bar provisionals clear. Deliberately
   nothing like the away treatment, because this is not a fade. They
   are full members and their row reads at full strength; it just sits
   under the settled ones, and the note beside the name says how far
   off it is. The hairline marks where the order changes meaning, so a
   member below it does not read as somebody having a bad week. */
.club-ladder tr.unset-first td { border-top: 1px solid rgba(242,163,60,.28); }
.club-ladder tr.unset-row .rank { color: rgba(205,191,164,.5); }
.club-ladder tr.unset-row .p-name .prov { color: var(--gold); opacity: .8; }

/* The row as its own progress bar.

   How far off initiation is, drawn the full height of the row and the
   full width of the table, so the tier reads down the page as a set of
   bars filling towards the hairline. `--fill` arrives on the row from
   club.js and is the only thing that varies.

   A background-image and not a background, because the row already has
   a background of its own: the table stripes even rows and paints
   another colour on hover, and both of those are background-colour.
   Keeping to the image channel leaves them working underneath, so a
   filled row still stripes and still lights up under the cursor.

   The selector carries `tbody` only to win that argument. The striping
   is `.lb-table tbody tr:nth-child(even) { background: ... }`, and
   `background` is the shorthand, so it sets background-image to none
   as well as setting the colour. Written as `.club-ladder tr.unset-row`
   this rule was the weaker selector and lost the image on every second
   row: the bar vanished for half the tier and survived for the other
   half, which looked far more like a bug than no bar at all would
   have. With `tbody` in it the two are equally specific, this one is
   further down the file, and so the striping keeps the colour while
   this keeps the image. Do not shorten it.

   Faint on purpose, and with no bright leading edge. A hard edge was
   tried and it reads as a stray table rule: at 60% it lands in the
   middle of the record column and draws a line through the score. The
   fill instead holds to the end and then fades out over the last few
   per cent, which says roughly this far without pointing at whatever
   text happens to be underneath. The exact figure is already spelled
   out in words next to the name, so the bar only has to carry the
   glance. At 0% every stop collapses and there is no bar at all, which
   is right: a member who has played nobody has none rather than a
   sliver of one. */
.club-ladder tbody tr.unset-row {
  background-image: linear-gradient(to right,
    rgba(232,200,119,.085) 0 calc(var(--fill, 0%) - 5%),
    rgba(232,200,119,0) var(--fill, 0%),
    transparent var(--fill, 0%));
}
/* Away overrides it. Whatever an away member had got to, they are not
   working towards it at the moment, and a bar says they are. */
.club-ladder tbody tr.unset-row.idle-row { background-image: none; }

/* ---- The rows that are worth looking at -------------------------
   Four tints, all of them on the cells and never on the row. That is a
   constraint and not a style choice. The row's background-colour is the
   table's striping and its hover, and the row's background-image is the
   initiation bar above: all three channels are spoken for, and taking one
   would break whichever rule was unlucky. A translucent wash on the cells
   sits in front of all of it and lets every bit of it through, so a
   tinted row still stripes, still lights up under the cursor, and still
   draws its progress bar.

   The order below is the whole of the override rule. Everything here is
   the same specificity on purpose, so the last one to match wins, and the
   podium comes first: gold, silver and bronze say where somebody stands,
   the fire and the ice say what they are doing right now, and right now
   is the more interesting of the two. A leader on a run of five gets the
   warm tide rather than the gold, and keeps the gold rank number, which
   is where standing was always being reported anyway. Do not move the
   podium block below the two that follow it. */

/* A fourth channel, and the last one going spare: the cells' own
   background-image. The colour underneath stays exactly as it was tuned,
   and the image on top is only light and shade, so each row keeps the hue
   it already had and gains a surface.

   Everything laid on this channel is either a vertical gradient or a tile
   the width of a fixed number of pixels, because a table cell is a box
   that starts its own background at its own left edge. A vertical
   gradient looks identical in every column and crosses the boundaries
   invisibly. Anything diagonal or repeating restarts its phase at each
   column, so those layers are kept faint and short-period, where the jump
   is below the threshold at which an eye reading names down a column
   would ever find it. */

/* The podium. Each metal at the strength the rank number already uses, so
   the row and the number it starts with are obviously the same statement.
   Only the top three, and only while they are still turning up, because
   clubBoard() withholds the class from an idle row.

   What makes a flat colour read as metal is not the colour, it is the
   profile of the light down it: a lit top lip, a fast fall into shade,
   deeper shadow through the body, and a thin bounce along the bottom
   where the surface catches the table. That much is fixed, because the
   shape of a thing does not move. The highlight does, and it is the
   sweep further down rather than a band declared here. The grain over
   the top is a brushed finish and is deliberately almost invisible; it
   is there to stop the shine looking like glass. */
.club-ladder tbody tr.podium-1 td {
  background-color: rgba(232,200,119,.1875);
  background-image:
    repeating-linear-gradient(115deg, rgba(255,255,255,.035) 0 1px, rgba(0,0,0,.022) 1px 2px, rgba(255,255,255,0) 2px 5px),
    linear-gradient(to bottom,
      rgba(255,246,214,.34) 0,
      rgba(255,240,190,.11) 7%,
      rgba(120,84,10,.11) 26%,
      rgba(74,50,4,.18) 64%,
      rgba(105,72,8,.12) 88%,
      rgba(255,236,178,.19) 100%);
  box-shadow: inset 0 1px 0 rgba(232,200,119,.52), inset 0 -1px 0 rgba(232,200,119,.52);
}
.club-ladder tbody tr.podium-1 td:first-child {
  box-shadow: inset 3px 0 0 rgba(232,200,119,1), inset 0 1px 0 rgba(232,200,119,.52), inset 0 -1px 0 rgba(232,200,119,.52);
}
/* Silver is the shiniest of the three and the coldest. Its highlights run
   whiter and its shadows run blue, which is the whole of the difference
   between silver and a pale gold. */
.club-ladder tbody tr.podium-2 td {
  background-color: rgba(201,204,212,.1375);
  background-image:
    repeating-linear-gradient(115deg, rgba(255,255,255,.04) 0 1px, rgba(0,0,0,.022) 1px 2px, rgba(255,255,255,0) 2px 5px),
    linear-gradient(to bottom,
      rgba(255,255,255,.36) 0,
      rgba(240,246,255,.12) 7%,
      rgba(38,54,78,.13) 26%,
      rgba(20,30,48,.21) 64%,
      rgba(30,44,66,.14) 88%,
      rgba(226,236,250,.19) 100%);
  box-shadow: inset 0 1px 0 rgba(201,204,212,.44), inset 0 -1px 0 rgba(201,204,212,.44);
}
.club-ladder tbody tr.podium-2 td:first-child {
  box-shadow: inset 3px 0 0 rgba(201,204,212,.95), inset 0 1px 0 rgba(201,204,212,.44), inset 0 -1px 0 rgba(201,204,212,.44);
}
/* Bronze is the dullest metal of the three and the profile says so: a
   softer top lip, a weaker second highlight, and a deeper shadow. Making
   it as bright as the gold would only make it look like gold. */
.club-ladder tbody tr.podium-3 td {
  background-color: rgba(221,144,112,.1375);
  background-image:
    repeating-linear-gradient(115deg, rgba(255,255,255,.03) 0 1px, rgba(0,0,0,.026) 1px 2px, rgba(255,255,255,0) 2px 5px),
    linear-gradient(to bottom,
      rgba(255,230,216,.29) 0,
      rgba(255,210,190,.10) 7%,
      rgba(96,44,34,.13) 26%,
      rgba(58,22,18,.19) 64%,
      rgba(80,36,26,.13) 88%,
      rgba(250,204,180,.17) 100%);
  box-shadow: inset 0 1px 0 rgba(221,144,112,.44), inset 0 -1px 0 rgba(221,144,112,.44);
}
.club-ladder tbody tr.podium-3 td:first-child {
  box-shadow: inset 3px 0 0 rgba(221,144,112,.95), inset 0 1px 0 rgba(221,144,112,.44), inset 0 -1px 0 rgba(221,144,112,.44);
}

/* The shine. The bodies above are shaded metal and nothing more; the
   highlight is this, and it travels.

   It is a single steep band, close to upright and leaning the way a hand
   tilts a medal, and it crosses the row once and then leaves. The rest of
   the cycle is dark, because that is what makes it read as a reflection
   rather than as a pulse: real metal is not lit, it catches the light and
   loses it again. Each metal runs on its own clock and starts at its own
   moment, so the three never sweep together and the podium never looks
   like a barber's pole.

   It has to be one surface the width of the row. Everything else on this
   board is on the `td`s, but a background on a cell starts at that cell's
   own left edge, so a steep band drawn per cell would arrive in seven
   pieces at seven different moments.

   So it goes on the row's background-image, which is the one channel this
   particular row has spare. The block above says all three of the row's
   channels are taken, and for most rows they are: the third is the
   initiation bar. But a podium row is never an unset row. The bar belongs
   to somebody still earning a place, the metal to somebody who already
   holds one, and no player is both at once, so on these three rows and
   only these three the channel is free. That is the whole licence for what
   follows and it does not extend one row further down the table.

   The first attempt at this was an absolutely positioned layer over the
   row, which is the obvious way to do it and works everywhere except the
   one place it had to. WebKit does not honour `position: relative` on a
   table row, so on an iPhone the layer took its size from an ancestor
   instead and the highlight was drawn across the whole board at once.
   There is nothing to position here, which is why this version cannot go
   wrong in the same way.

   The cost of being behind rather than in front is that the cell colour
   and its texture are over the top, so a share of the shine is eaten
   before it arrives. The alphas are raised to pay for it. What is bought
   with the same trade is that the band now passes under the names instead
   of over them, and a name never dims as it goes by. */
.club-ladder tbody tr.podium-1,
.club-ladder tbody tr.podium-2,
.club-ladder tbody tr.podium-3 {
  background-repeat: no-repeat;
  background-size: 38% 100%;
  animation: metal-sweep 7.5s cubic-bezier(.42,0,.58,1) infinite;
}
.club-ladder tbody tr.podium-1 {
  background-image: linear-gradient(112deg,
    rgba(255,248,220,0) 26%, rgba(255,248,220,.22) 42%,
    rgba(255,252,236,.66) 50%, rgba(255,248,220,.22) 58%, rgba(255,248,220,0) 74%);
}
.club-ladder tbody tr.podium-2 {
  background-image: linear-gradient(112deg,
    rgba(255,255,255,0) 26%, rgba(255,255,255,.24) 42%,
    rgba(255,255,255,.72) 50%, rgba(255,255,255,.24) 58%, rgba(255,255,255,0) 74%);
  animation-duration: 8.9s; animation-delay: -3.1s;
}
.club-ladder tbody tr.podium-3 {
  background-image: linear-gradient(112deg,
    rgba(255,232,218,0) 26%, rgba(255,232,218,.20) 42%,
    rgba(255,242,232,.60) 50%, rgba(255,232,218,.20) 58%, rgba(255,232,218,0) 74%);
  animation-duration: 10.3s; animation-delay: -6.4s;
}
/* Percentages, because the row has no width this stylesheet knows. With a
   band 38% of the row wide, -62% parks it just off the left edge and 162%
   just off the right, and the long tail from there back to the start is
   the dark half of the cycle. */
@keyframes metal-sweep {
  0%        { background-position: -62% 0; }
  40%, 100% { background-position: 162% 0; }
}

/* And the sweep stands down when the bars arrive.

   assets/js/bars-mount.js puts .has-bars on the board once it has three
   canvases running, and only then. The two effects are the same idea told
   twice, so they take turns rather than stacking: whoever a visitor gets,
   they get one of them. Written this way round because the sweep is the
   one that needs no JavaScript, so it is what everybody starts with and
   what a reader keeps if the module never arrives, if the browser has no
   canvas, or if they have asked for less movement. */
.club-ladder.main-board.has-bars tbody tr.podium-1,
.club-ladder.main-board.has-bars tbody tr.podium-2,
.club-ladder.main-board.has-bars tbody tr.podium-3 { background-image: none; animation: none; }

/* The bars themselves are deliberately absent from this stylesheet.
   bars-mount.js measures each host onto its row and the effect builds its
   own canvas inside, and a z-index or an isolation here would be the one
   kind of rule that can quietly break a mix-blend-mode: a new stacking
   context changes what the layer is allowed to blend with, and the thing
   it needs to blend with is the row. */

/* The fire's row. A tint the width of the table, so the mark in the
   Streak column has a row to belong to and the eye finds it without
   hunting. Pitched above the gold on purpose: the override hands this row
   to the fire, and a rank-one row that swapped its metal for something
   fainter would read as a demotion rather than as news.

   Animated on the same beat as the emoji it belongs to. Still a tide
   rather than a flash: this is a table somebody reads across, and a row
   that pulses hard enough to notice is a row nobody can read. The swing
   between the two ends stays narrow however far the floor comes up. */

/* What makes the difference between an orange row and a row that is on
   fire is where the light is: hot and dense along the bottom edge, thin
   and yellow going up, gone before the top. That is the bed of coals, the
   last of the three layers, and it does most of the work.

   The two above it are the licks. Each is a soft flame-shaped bloom
   rising out of the bottom of a tile, and the tile repeats across the
   row. They drift in opposite directions at different speeds, so the two
   patterns are never in the same relationship twice and the flame never
   settles into a row of identical blobs. Each layer travels a whole
   number of its own tiles per cycle, so the loop closes on itself with no
   seam and nothing to notice at the moment it restarts.

   The drift is slow enough to be a flicker rather than a march. It is a
   separate animation from the breathing so that the two run on their own
   clocks and the row never repeats itself exactly. */
.club-ladder tbody tr.hot-row td {
  background-color: rgba(242,163,60,.42);
  background-image:
    radial-gradient(ellipse 9px 62% at 38% 104%, rgba(255,236,170,.24) 0, rgba(255,168,52,.11) 48%, rgba(255,120,30,.03) 72%, rgba(255,120,30,0) 100%),
    radial-gradient(ellipse 15px 86% at 62% 108%, rgba(255,206,110,.17) 0, rgba(255,132,36,.08) 52%, rgba(214,84,20,0) 100%),
    linear-gradient(to top, rgba(255,112,26,.50) 0, rgba(255,158,48,.26) 30%, rgba(255,214,120,.08) 66%, rgba(255,214,120,0) 100%);
  background-size: 28px 100%, 46px 100%, 100% 100%;
  background-repeat: repeat, repeat, no-repeat;
  animation: hot-row 2.8s ease-in-out infinite, hot-lick 9s linear infinite;
}
.club-ladder tbody tr.hot-row td:first-child {
  animation-name: hot-row-edge, hot-lick;
}
@keyframes hot-lick {
  from { background-position: 0 0, 0 0, 0 0; }
  to   { background-position: 56px 0, -46px 0, 0 0; }
}
@keyframes hot-row {
  0%, 100% { background-color: rgba(242,163,60,.36);
             box-shadow: inset 0 1px 0 rgba(242,163,60,.55), inset 0 -1px 0 rgba(242,163,60,.55); }
  50%      { background-color: rgba(242,163,60,.50);
             box-shadow: inset 0 1px 0 rgba(242,163,60,.78), inset 0 -1px 0 rgba(242,163,60,.78); }
}
@keyframes hot-row-edge {
  0%, 100% { background-color: rgba(242,163,60,.36);
             box-shadow: inset 3px 0 0 rgba(242,163,60,.85), inset 0 1px 0 rgba(242,163,60,.55), inset 0 -1px 0 rgba(242,163,60,.55); }
  50%      { background-color: rgba(242,163,60,.50);
             box-shadow: inset 3px 0 0 rgba(242,163,60,1), inset 0 1px 0 rgba(242,163,60,.78), inset 0 -1px 0 rgba(242,163,60,.78); }
}

/* The cold row, quieter for the same reason the ice is: a losing streak
   is not an accusation and the row should not look like one. Lower
   throughout, and slower, so the two are never breathing in step and the
   page does not read as though it has a heartbeat. */

/* Ice is the fire upside down and much more patient. The wash is heaviest
   along the top edge, because frost creeps in from the rim of a thing
   rather than rising out of it, and the blooms hang down from that edge
   instead of climbing off the bottom one.

   The two hatch layers are the crystal. They are the only diagonal thing
   in the whole block and they are pitched at the very bottom of what is
   visible on purpose: a diagonal restarts its phase at every column edge,
   and the only way to have crystal at all is to have so little of it that
   the restart cannot be found. Crossed at opposing angles they read as
   frost on glass rather than as stripes.

   The drift is four times slower than the fire's. Ice that hurried would
   be steam. */
.club-ladder tbody tr.cold-row td {
  background-color: rgba(125,185,232,.34);
  background-image:
    radial-gradient(ellipse 8px 54% at 50% -6%, rgba(236,250,255,.22) 0, rgba(150,205,240,.10) 50%, rgba(150,205,240,0) 100%),
    repeating-linear-gradient(62deg, rgba(255,255,255,.038) 0 1px, rgba(255,255,255,0) 1px 7px),
    repeating-linear-gradient(-62deg, rgba(255,255,255,.032) 0 1px, rgba(255,255,255,0) 1px 9px),
    linear-gradient(to bottom, rgba(226,244,255,.34) 0, rgba(150,205,240,.16) 34%, rgba(125,185,232,.04) 70%, rgba(125,185,232,0) 100%);
  background-size: 34px 100%, auto, auto, 100% 100%;
  background-repeat: repeat, repeat, repeat, no-repeat;
  animation: cold-row 4.2s ease-in-out infinite, cold-drift 34s linear infinite;
}
.club-ladder tbody tr.cold-row td:first-child {
  animation-name: cold-row-edge, cold-drift;
}
@keyframes cold-drift {
  from { background-position: 0 0, 0 0, 0 0, 0 0; }
  to   { background-position: 34px 0, 0 0, 0 0, 0 0; }
}
@keyframes cold-row {
  0%, 100% { background-color: rgba(125,185,232,.29);
             box-shadow: inset 0 1px 0 rgba(125,185,232,.46), inset 0 -1px 0 rgba(125,185,232,.46); }
  50%      { background-color: rgba(125,185,232,.42);
             box-shadow: inset 0 1px 0 rgba(125,185,232,.66), inset 0 -1px 0 rgba(125,185,232,.66); }
}
@keyframes cold-row-edge {
  0%, 100% { background-color: rgba(125,185,232,.29);
             box-shadow: inset 3px 0 0 rgba(125,185,232,.75), inset 0 1px 0 rgba(125,185,232,.46), inset 0 -1px 0 rgba(125,185,232,.46); }
  50%      { background-color: rgba(125,185,232,.42);
             box-shadow: inset 3px 0 0 rgba(125,185,232,.92), inset 0 1px 0 rgba(125,185,232,.66), inset 0 -1px 0 rgba(125,185,232,.66); }
}

/* Motion off, colour on. The tint, the surface and the edge are the
   information; the breathing and the drift are only how they arrived. One
   `animation: none` stops both, and the flame and the frost stay exactly
   where they were standing. The podium needs nothing here because it
   never moved in the first place. */
@media (prefers-reduced-motion: reduce) {
  .club-ladder tbody tr.hot-row td, .club-ladder tbody tr.cold-row td { animation: none; }
  /* The shine is nothing but movement, so there is nothing of it to keep.
     Parking it mid-row would leave a bright diagonal stripe across a name
     for good, which is worse than no highlight at all. */
  .club-ladder tbody tr.podium-1,
  .club-ladder tbody tr.podium-2,
  .club-ladder tbody tr.podium-3 { background-image: none; animation: none; }
  .club-ladder tbody tr.hot-row td:first-child { box-shadow: inset 3px 0 0 rgba(242,163,60,.95), inset 0 1px 0 rgba(242,163,60,.62), inset 0 -1px 0 rgba(242,163,60,.62); }
  .club-ladder tbody tr.cold-row td:first-child { box-shadow: inset 3px 0 0 rgba(125,185,232,.88), inset 0 1px 0 rgba(125,185,232,.52), inset 0 -1px 0 rgba(125,185,232,.52); }
}

/* An away row is already faded on purpose, and a tint on top of a fade
   reads as neither. Whoever is hottest among the people who have stopped
   coming keeps the emoji, which is the statement; the row stays quiet. */
.club-ladder tbody tr.idle-row.hot-row td,
.club-ladder tbody tr.idle-row.cold-row td { animation: none; background-color: transparent; background-image: none; box-shadow: none; }

/* And the shine goes with the row it belonged to. A metal that has been
   handed to the fire keeps none of itself, least of all the one part of
   it that moves.

   Every selector here names a metal as well as the thing that took it,
   which looks like belt and braces and is not. The shine is on the row's
   background-image, and so is the initiation bar; the only reason the two
   can share a channel is that a podium row is never an unset row. Written
   as `tr.hot-row` alone this would match the whole table's worth of hot
   rows, and a provisional on a run would lose the bar showing how close
   they are. Named against a metal it can only reach a row that had a
   shine to lose. */
.club-ladder tbody tr.podium-1.hot-row,
.club-ladder tbody tr.podium-2.hot-row,
.club-ladder tbody tr.podium-3.hot-row,
.club-ladder tbody tr.podium-1.cold-row,
.club-ladder tbody tr.podium-2.cold-row,
.club-ladder tbody tr.podium-3.cold-row,
.club-ladder tbody tr.podium-1.idle-row,
.club-ladder tbody tr.podium-2.idle-row,
.club-ladder tbody tr.podium-3.idle-row { background-image: none; animation: none; }

.prov-board td, .away-board td { font-size: .92rem; }
.prov-board .prog-cell { min-width: 190px; }

/* ---- Progress bars ---------------------------------------------
   The same component on the provisional board and on a profile, so
   a player recognises the shape of their own progress wherever it
   turns up. ---- */
.prog { display: grid; gap: .38rem; }
.prog-track {
  height: 7px; border-radius: 999px; overflow: hidden;
  background: rgba(255,255,255,.06); border: 1px solid var(--line);
}
.prog-track span {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--amber), var(--gold));
  transition: width .6s var(--ease);
}
.prog-lbl {
  font-size: .72rem; letter-spacing: .04em; color: var(--cream-dim); line-height: 1.4;
}

/* =============================================================
   11f. WORKING TOWARDS, AND EARNED
   -------------------------------------------------------------
   Quests look forwards and badges look backwards, which is why
   they are two lists and not one. A quest is only worth showing
   while it is still open; a badge cannot be taken away.
   ============================================================= */
.quests { list-style: none; display: grid; gap: 1rem; margin: 0; padding: 0; }
.quests li { display: grid; gap: .4rem; }
.q-lbl { color: var(--cream); font-size: .88rem; font-weight: 600; line-height: 1.4; }
.quests li.q-done .q-lbl { color: var(--gold); }
.quests li.q-done .prog-track span { background: linear-gradient(90deg, var(--gold), #f6e4b0); }
.quests li.q-done .prog-lbl { color: rgba(232,200,119,.8); }

/* The climb to the next rank, under the quest list rather than inside
   it: a point gap and a list of names are not a bar that fills, so they
   read as their own paragraph group instead of another quest. */
.rank-goal { margin-top: 1rem; }
.rank-goal .mu + .mu { margin-top: .5rem; }
.rank-goal .to-play { margin-top: .5rem; }

.badges {
  list-style: none; margin: .2rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .5rem;
}
.badges li {
  position: relative; cursor: default;
  border: 1px solid var(--line); border-radius: 999px;
  padding: .42rem .8rem; font-size: .74rem; letter-spacing: .04em;
  transition: transform .2s var(--ease), border-color .2s var(--ease);
}
.badges li:focus { outline: none; }
.badges li:focus-visible { box-shadow: 0 0 0 2px rgba(242,163,60,.55); }
.badges li:hover { transform: translateY(-2px); }
.badges li.got {
  color: var(--gold); background: rgba(232,200,119,.1);
  border-color: rgba(232,200,119,.34);
}
.badges li.not {
  color: rgba(205,191,164,.5); background: rgba(255,255,255,.02);
  border-style: dashed;
}
/* Not open yet, as opposed to not done yet. Dimmer than .not and without
   the dashed edge, because a dashed outline reads as an empty slot
   waiting to be filled and these are not waiting on this player at all.
   The tooltip says what unlocks them. */
.badges li.shut {
  color: rgba(205,191,164,.3); background: transparent;
  border-color: rgba(232,200,119,.08);
}
.badges li b { font-weight: 700; }

/* The shared tooltip points sideways, which suits a chip on a table
   row but not a pill in a wrapping grid. A badge note is a sentence,
   so this one sits above the badge and is allowed to wrap. */
.badges [data-tip]::after {
  left: 50%; top: auto; bottom: calc(100% + .5rem);
  white-space: normal; width: max-content; max-width: 24ch;
  font-weight: 500; line-height: 1.45; text-align: center;
  transform: translate(-50%, 4px);
  /* The shared tooltip's ink is the same ink the profile boxes are
     painted in, so on this panel it would read as text with a hairline
     round it rather than as something floating over the page. */
  background: #241a12; border-color: rgba(232,200,119,.28); color: var(--cream);
}
.badges [data-tip]:hover::after, .badges [data-tip]:focus::after {
  transform: translate(-50%, 0);
}

/* The names behind "Face 6 Club members". Links, every one of them, to
   that player's profile, the same jump a name on the board makes.
   Deliberately not shaped like a badge: a badge is a thing already done
   and there is nothing to click. These are the opposite, so they are
   painted as something to go and do. Members who have gone quiet keep
   their place in the list, because they still count towards the bar,
   and they are marked, because they are the likeliest reason the number
   has not moved. */
.to-play {
  list-style: none; margin: .2rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .5rem;
}
.to-play li {
  display: inline-flex; align-items: baseline; gap: .4rem;
  border: 1px solid rgba(232,200,119,.28); border-radius: 999px;
  background: rgba(232,200,119,.06);
  padding: .42rem .8rem; font-size: .74rem; letter-spacing: .04em;
  transition: transform .2s var(--ease), border-color .2s var(--ease), background .2s var(--ease);
}
.to-play li:hover {
  transform: translateY(-2px);
  border-color: rgba(242,163,60,.6); background: rgba(232,200,119,.12);
}
.to-play li a { color: var(--gold); text-decoration: none; font-weight: 600; }
.to-play li a:focus-visible {
  outline: none; border-radius: 4px;
  box-shadow: 0 0 0 2px rgba(242,163,60,.55);
}
.to-play li.away { border-style: dashed; background: rgba(255,255,255,.02); }
.to-play li.away a { color: rgba(205,191,164,.62); }
.tp-away {
  color: rgba(205,191,164,.45); font-size: .64rem;
  letter-spacing: .1em; text-transform: uppercase;
}

@media (max-width: 560px) {
  .boards { gap: 2.2rem; }
  .board-note { font-size: .84rem; }
  .prov-board .prog-cell { min-width: 140px; }
  .badges li { padding: .36rem .65rem; font-size: .68rem; }
  .to-play li { padding: .36rem .65rem; font-size: .68rem; }
}

/* =============================================================
   16. LIVE SCORE CARD
   ============================================================= */
.sc-body {
  background:
    radial-gradient(ellipse 85% 55% at 50% -8%, rgba(20,53,40,.85), transparent 68%),
    radial-gradient(ellipse 85% 55% at 50% 108%, rgba(20,53,40,.85), transparent 68%),
    radial-gradient(ellipse 55% 45% at 50% 50%, rgba(242,163,60,.05), transparent 72%),
    var(--ink);
  color: var(--cream); font-family: var(--font-body);
  min-height: 100svh; margin: 0;
  /* Notched iPhones in standalone mode: keep names and clocks clear of the
     camera housing and the home indicator. Zero everywhere else. */
  --sc-safe-t: env(safe-area-inset-top, 0px);
  --sc-safe-b: env(safe-area-inset-bottom, 0px);
  padding: var(--sc-safe-t) env(safe-area-inset-right, 0px) var(--sc-safe-b) env(safe-area-inset-left, 0px);
  box-sizing: border-box;
  /* No pull-to-refresh, no rubber-band, no double-tap zoom delay: this is
     a scoring surface being poked mid-game, not a document. */
  overscroll-behavior: none;
  touch-action: manipulation;
}
/* Saved to a home screen and opened as an app, the card is laid out edge to
   edge, which on Android means underneath the gesture bar or the
   three-button bar. iPhones report the height of that strip in
   safe-area-inset-bottom and the padding above already handles them.
   Android reports nothing for a system bar, only for a camera cutout, so
   the inset is zero and the bottom of the card goes behind the buttons.
   A floor under the inset is what closes that gap: a real number wins over
   it, and where there is no bar at all the card simply sits a few pixels
   higher. Installed copies only, because a card in an ordinary browser tab
   is laid out above the system bar to begin with. */
@media (display-mode: standalone) and (pointer: coarse) {
  .sc-body { --sc-safe-b: max(env(safe-area-inset-bottom, 0px), 36px); }
}
@media (display-mode: fullscreen) and (pointer: coarse) {
  .sc-body { --sc-safe-b: max(env(safe-area-inset-bottom, 0px), 36px); }
}
@media (display-mode: minimal-ui) and (pointer: coarse) {
  .sc-body { --sc-safe-b: max(env(safe-area-inset-bottom, 0px), 36px); }
}
/* Added by the script while a match is on screen. The card is sized to fit
   one screen, so pinning the document costs nothing and stops the whole
   page sliding when a clock tap lands with a bit of travel in it. */
.sc-body.sc-locked {
  position: fixed; inset: 0; width: 100%; overflow: hidden;
  display: flex; flex-direction: column;
}
/* Once a match is on, the two links out live as icons at the top of the mid
   bar, so the footer would only be dead space under the clocks. Drop it and
   let the card take the whole screen instead of a fixed slice of it. */
.sc-body.sc-locked .sc-foot { display: none; }
.sc-body.sc-locked .sc-shell { flex: 1 1 auto; min-height: 0; }
/* And the header with it. A match wants the whole screen: the two links
   out are icons in the mid bar by then, and a navigation bar sitting over
   a live clock is an invitation to leave in the middle of a game. */
.sc-body.sc-locked .site-header { display: none; }

/* The site header on the setup screen.

   Everywhere else the header is fixed and floats over a scrolling
   document. Here the document does not scroll, the shell does, so the
   header takes its place at the top of the column instead and the form
   scrolls underneath it rather than starting behind it. */
.sc-body .site-header { position: relative; flex: none; padding: .9rem 0; }
.sc-body .site-header::before { opacity: 1; }
/* The card turns every drag into a tap, because on the card a drag is a
   mistap. The drawer is the exception: it is a list and it has to scroll. */
.sc-body .main-nav { touch-action: pan-y; }
/* Landscape-first: the card lies sideways beside the board, one player
   per side with the match column down the middle, like a real paper
   scorecard. A portrait override further down keeps it usable upright. */
.sc-shell {
  display: flex; flex-direction: row; align-items: stretch;
  min-height: calc(100svh - 4.4rem - var(--sc-safe-t, 0px) - var(--sc-safe-b, 0px)); width: 100%; max-width: 1500px; margin-inline: auto;
}
/* The setup screen is a form, not a card. Left as a flex row it is a
   stretched item, so its height is handed down from the shell rather
   than measured from what is inside it, and a short window crops the
   Start button instead of scrolling to it. Dropping to block fixes the
   measuring. The :has() twin covers the frame before the script has
   tagged the shell. */
.sc-shell.sc-idle,
.sc-shell:has(> .sc-setup) { display: block; }
/* Where the scrollbar lives.

   Setting the table runs to two seats, a swap, two dropdowns, two
   checkboxes with their explanations, the stakes panel and the button,
   which is taller than a laptop window long before it is taller than a
   phone. It used to just run past the bottom and borrow the document's
   scrollbar, and that is the part that did not hold up: the rest of this
   page works hard to take the document's scroll away, the body is pinned
   the moment a match starts, and a copy saved to a home screen has no
   browser chrome to rubber-band against. Nothing outside this screen
   wants a scrolling document, so it stops asking for one and carries its
   own scrollbar instead. The footer stays put underneath, which is what
   keeps the version number reachable on a short screen.

   If the script never runs the class is never added, the body is not
   pinned, and the form falls back to running off the bottom of an
   ordinary page: longer, but never cropped. */
.sc-body.sc-setting {
  position: fixed; inset: 0; width: 100%; overflow: hidden;
  display: flex; flex-direction: column;
}
.sc-body.sc-setting .sc-rotate,
.sc-body.sc-setting .site-header,
.sc-body.sc-setting .sc-foot { flex: none; }
.sc-body.sc-setting .sc-shell {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  /* The body turns off anything that is not a tap, because on the card
     itself a drag is a mistap. Here it is a scroll, and has to be. */
  touch-action: pan-y;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
.sc-body.sc-setting .sc-shell::-webkit-scrollbar { width: 6px; }
.sc-body.sc-setting .sc-shell::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }
.sc-panel {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .85rem; padding: 1.2rem; position: relative; cursor: pointer; overflow: hidden;
  border-inline: 1px solid var(--line); user-select: none; -webkit-user-select: none;
  transition: background .35s var(--ease), box-shadow .35s var(--ease);
  /* The panels are the clock. Nothing that starts on one should ever be
     interpreted as a scroll, a swipe or a pinch. */
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
.sc-panel > * { position: relative; z-index: 2; }
/* The light on the player to move. It sits over the panel's background
   and under the score, the name and the clock. The canvas underneath is
   opaque and paints itself black every frame, so it is blended with
   screen: black adds nothing and only the light lands. isolation keeps
   that blend inside the panel instead of reaching the page behind it.
   The layer fades rather than snapping, because the clock changes hands
   every few seconds and a hard cut would flicker. */
.sc-panel { isolation: isolate; }
.sc-light {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  mix-blend-mode: screen; opacity: 0;
  transition: opacity .45s var(--ease);
}
.sc-light.on { opacity: 1; }
/* The idle background, for a match played without the clock. It covers
   the screen and everything else on the card is lifted over it. The
   panels carry no background of their own, so it reads straight through
   them. Opaque, and it fades the same way the turn light does, because
   it replaces the body's own gradients while it is up. */
.sc-goo {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0; transition: opacity .45s var(--ease);
}
/* Half strength, not full. The effect is kept exactly as it was
   generated, but a gold blob drifting under a gold score makes the one
   number on the card that has to be readable hard to read. Fading the
   whole layer into the card's own dark background keeps the colours and
   the movement and takes the fight out of them. */
.sc-goo.on { opacity: .5; }
.sc-body .sc-shell,
.sc-body .sc-foot,
.sc-body .sc-rotate { position: relative; z-index: 1; }
/* The header is lifted clear of the drifting background like the rest of
   the card, but not to 1: the mobile drawer's scrim is at 99 and expects
   the header to be the one thing above it, since the drawer and its
   toggle both live inside the header. At 1 the scrim painted over the
   nav on the Score Card and swallowed its taps. 100 is what the header
   carries everywhere else on the site. */
.sc-body .site-header { position: relative; z-index: 100; }
/* Five taps on this opens the unlock panel. Stop the browser from
   answering the second and third of them by selecting the words, and
   from reading the run of them as a double tap to zoom, which costs a
   tap or two on a phone. */
.sc-setup .kicker {
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
/* The panel behind the five taps. Nothing on the card links to it, so
   this is styled to be plain and quick rather than to belong: it is a
   thing being tried out, and one day it will either be let out to
   everybody or taken away. */
.sc-gate {
  /* Above the header, which is at 100 so the mobile drawer works. */
  position: fixed; inset: 0; z-index: 200; display: flex;
  align-items: center; justify-content: center; padding: 1.2rem;
  background: rgba(6, 12, 4, .72);
}
.sc-gate-box {
  width: 100%; max-width: 20rem; padding: 1.2rem;
  border: 1px solid rgba(196, 133, 33, .35); border-radius: .8rem;
  background: #10180c; text-align: center;
}
.sc-gate-t { margin: 0 0 .35rem; font-weight: 700; letter-spacing: .02em; }
.sc-gate-n { margin: 0 0 .9rem; font-size: .82rem; opacity: .75; }
.sc-gate-b { display: flex; gap: .6rem; justify-content: center; }
/* A strip of backgammon points down the outer edge of each side, so the
   card reads as a board even before a single checker moves. */
.sc-panel::before {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0;
  width: 26px; pointer-events: none; z-index: 0; opacity: .9;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='88'%3E%3Cpath%20d='M0%200%2026%2022%200%2044Z'%20fill='rgba(242,163,60,.14)'/%3E%3Cpath%20d='M0%2044%2026%2066%200%2088Z'%20fill='rgba(244,234,214,.06)'/%3E%3C/svg%3E");
  background-repeat: repeat-y; background-size: 26px 88px;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
}
.sc-panel.side-b::before { inset-inline-start: auto; inset-inline-end: 0; transform: scaleX(-1); }
.sc-panel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1; opacity: 0;
  background: radial-gradient(ellipse 72% 58% at 50% 50%, rgba(var(--sc-tone),.17), transparent 70%);
  transition: opacity .45s var(--ease);
}

/* Gold, silver and bronze.

   The top three on the ladder bring their colour to the card with them, so
   a player sitting down opposite the number one can see it without being
   told. Nothing about the panel changes except the colour: the same strip
   of points down the outer edge, the same glow when the clock is theirs,
   the same fades on the same curves. Every colour on this half of the card
   is mixed from two numbers, so a medal is three lines and not a second
   copy of the panel.

   Only settled members are coloured. A provisional is still earning a
   number, and a panel painted gold for a place nobody holds yet would be
   a lie told in colour rather than in words. */
.sc-panel {
  --sc-tone: 242,163,60;        /* the amber the card has always run on */
  --sc-tone-lit: 232,200,119;   /* its lighter partner, for winning */
  --sc-tone-top: 253,243,214;   /* the highlight at the top of the numeral */
  --sc-tone-deep: 217,127,30;   /* and the shadow at the foot of it */
  --sc-turn: 20,53,40;          /* the wash under the player to move */
}
.sc-panel.sc-medal-1 {
  --sc-tone: 232,192,88;  --sc-tone-lit: 250,228,150;
  --sc-tone-top: 255,250,229; --sc-tone-deep: 197,150,48; --sc-turn: 74,58,16;
}
.sc-panel.sc-medal-2 {
  --sc-tone: 196,203,214; --sc-tone-lit: 230,235,243;
  --sc-tone-top: 252,253,255; --sc-tone-deep: 141,151,167; --sc-turn: 46,52,62;
}
.sc-panel.sc-medal-3 {
  --sc-tone: 199,131,80;  --sc-tone-lit: 228,171,118;
  --sc-tone-top: 250,229,208; --sc-tone-deep: 154,90,44;  --sc-turn: 63,38,20;
}
/* At rest the medal is a wash rather than a colour, because the panel
   spends most of the match at rest and a half-screen of solid gold behind
   a score is a headache, not a distinction. The states below overwrite it,
   which is the right way round: whose turn it is matters more than where
   they stand. */
.sc-panel.sc-medal {
  background: linear-gradient(180deg, rgba(var(--sc-tone),.11), rgba(var(--sc-tone),.03));
}
/* The strip of points, in the same geometry and the same two alternating
   weights, mixed for each medal. */
.sc-panel.sc-medal-1::before {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='88'%3E%3Cpath%20d='M0%200%2026%2022%200%2044Z'%20fill='rgba(232,192,88,.16)'/%3E%3Cpath%20d='M0%2044%2026%2066%200%2088Z'%20fill='rgba(250,228,150,.07)'/%3E%3C/svg%3E");
}
.sc-panel.sc-medal-2::before {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='88'%3E%3Cpath%20d='M0%200%2026%2022%200%2044Z'%20fill='rgba(196,203,214,.16)'/%3E%3Cpath%20d='M0%2044%2026%2066%200%2088Z'%20fill='rgba(230,235,243,.07)'/%3E%3C/svg%3E");
}
.sc-panel.sc-medal-3::before {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='88'%3E%3Cpath%20d='M0%200%2026%2022%200%2044Z'%20fill='rgba(199,131,80,.18)'/%3E%3Cpath%20d='M0%2044%2026%2066%200%2088Z'%20fill='rgba(228,171,118,.07)'/%3E%3C/svg%3E");
}

.sc-panel.turn {
  background: linear-gradient(180deg, rgba(var(--sc-turn),.6), rgba(var(--sc-turn),.18));
  box-shadow: inset 0 0 0 1px rgba(var(--sc-tone),.28);
}
.sc-panel.turn::after { opacity: 1; }
.sc-panel.champ { background: linear-gradient(180deg, rgba(var(--sc-tone-lit),.15), transparent 72%); }
.sc-panel.champ::after { opacity: 1; background: radial-gradient(ellipse 80% 62% at 50% 42%, rgba(var(--sc-tone-lit),.24), transparent 72%); }
.sc-name {
  font-family: var(--font-display); font-size: clamp(1.5rem, 5svh, 3.6rem); line-height: 1.1;
  letter-spacing: .06em; text-transform: uppercase; text-align: center; color: var(--cream);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color .35s var(--ease);
  flex: none;
}
.sc-panel.turn .sc-name, .sc-panel.champ .sc-name { color: rgb(var(--sc-tone-lit)); }
/* With the per-player "Game →" buttons gone the panel holds a name, a
   number and a clock, so the number takes everything that is left. */
.sc-score {
  font-family: var(--font-display); font-size: clamp(5rem, 42svh, 17rem); line-height: .9;
  background: linear-gradient(174deg, rgb(var(--sc-tone-top)) 6%, rgb(var(--sc-tone-lit)) 46%, rgb(var(--sc-tone-deep)) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums;
  filter: drop-shadow(0 8px 22px rgba(var(--sc-tone),.22));
}
.sc-score.bump { animation: scBump .5s var(--ease); }
@keyframes scBump { 0% { transform: scale(1); } 32% { transform: scale(1.17); } 100% { transform: scale(1); } }
.sc-champ-tag { font-family: var(--font-accent); font-style: italic; color: rgb(var(--sc-tone)); }

.sc-clock {
  flex: none;
  display: flex; align-items: center; gap: 1rem; margin-top: .4rem;
  border: 1px solid var(--line); border-radius: 999px; padding: .5rem 1.5rem;
  background: rgba(13,10,7,.55); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  transition: border-color .25s, box-shadow .3s, background .3s;
}
.sc-clock.on {
  border-color: rgba(var(--sc-tone),.75); background: rgba(var(--sc-tone),.08);
  box-shadow: 0 0 0 4px rgba(var(--sc-tone),.1), 0 0 26px -6px rgba(var(--sc-tone),.55);
}
.sc-clock.low.on { border-color: #ff8a5e; box-shadow: 0 0 0 4px rgba(255,138,94,.14); animation: scPulse 1s infinite; }
.sc-clock.flag { border-color: #ff6b5e; box-shadow: 0 0 0 4px rgba(255,107,94,.18); }
@keyframes scPulse { 0%, 100% { opacity: 1; } 50% { opacity: .6; } }
.sc-reserve { font-family: var(--font-display); font-size: 3rem; letter-spacing: .02em; color: var(--cream); font-variant-numeric: tabular-nums; }
.sc-clock.on .sc-reserve { color: rgb(var(--sc-tone)); }
.sc-clock.low.on .sc-reserve { color: #ff9b73; }
.sc-clock.flag .sc-reserve { color: #ff6b5e; }
.sc-delay { font-size: 1.28rem; font-weight: 700; letter-spacing: .06em; color: var(--cream-dim); min-width: 3.4ch; text-align: center; font-variant-numeric: tabular-nums; }
.sc-clock.on .sc-delay { color: rgb(var(--sc-tone-lit)); }

/* The middle column is the board's bar: raised, edge-shadowed, hairlined. */
.sc-mid {
  position: relative; flex: 0 0 clamp(240px, 30vw, 420px);
  padding: 1.2rem 1.2rem; display: flex; flex-direction: column; gap: 1.1rem;
  align-items: center; justify-content: center;
  /* Centred while there is room, and pinned to the top the moment there is
     not. Plain centring overflows a short column at BOTH ends at once, and
     the ends of this column are the match length at the top and the Score
     button at the bottom, so a screen an inch too short loses the only
     button that puts points on the board. "safe" is ignored by anything
     that does not understand it, which leaves the line above standing. */
  justify-content: safe center;
  /* The other half of the same guarantee: if it does overflow anyway, the
     overflow can be reached. Contained, so a flick that runs out of column
     does not go on to drag the card underneath it. */
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
  background: linear-gradient(180deg, var(--ink-3), var(--ink-2) 46%, var(--ink-3));
  border-inline: 1px solid var(--line);
  box-shadow: inset 12px 0 24px -18px #000, inset -12px 0 24px -18px #000, 0 0 46px -12px rgba(0,0,0,.9);
}
.sc-mid::-webkit-scrollbar { width: 4px; }
.sc-mid::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }
.sc-mid::before, .sc-mid::after {
  content: ""; position: absolute; inset-inline: 14%; height: 1px; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(232,200,119,.45), transparent);
}
.sc-mid::before { top: 0; }
.sc-mid::after { bottom: 0; }
/* Ladder on the left, match length in the middle, home on the right. The
   1fr middle column keeps the number dead centre whatever the icons do. */
.sc-top { display: grid; grid-template-columns: 44px 1fr 44px; align-items: center; gap: .5rem; width: 100%; }
.sc-nav-btn {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px;
  border: 1px solid var(--line); color: var(--cream-dim);
  background: linear-gradient(180deg, rgba(32,24,18,.92), rgba(22,17,12,.92));
  transition: border-color .2s, color .2s, transform .12s, box-shadow .3s;
  -webkit-tap-highlight-color: transparent;
}
.sc-nav-btn svg { width: 21px; height: 21px; }
.sc-nav-btn:hover { color: var(--gold); border-color: rgba(242,163,60,.5); box-shadow: 0 8px 20px -12px rgba(0,0,0,.95); }
.sc-nav-btn:active { transform: translateY(1px); }
/* MATCH TO in small caps with the length stacked large underneath, the
   way it is written at the top of a paper scoresheet. */
.sc-meta { display: grid; justify-items: center; gap: .1rem; text-align: center; color: var(--cream-dim); min-width: 0; }
.sc-meta > * { white-space: nowrap; }
.sc-meta-lbl { font-size: .66rem; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; opacity: .8; }
.sc-meta-len {
  color: var(--gold); font-family: var(--font-display); font-size: 3.2rem;
  line-height: 1; letter-spacing: .04em; font-weight: 400; font-variant-numeric: tabular-nums;
}
/* A cube game puts a word in the slot a match puts a number in, so it
   takes the same face at a size that fits across a phone.
   Two class names deep on purpose: the breakpoints further down restyle
   .sc-meta-len, and a word needs to stay smaller than a numeral at every
   one of them. */
.sc-meta-len.sc-meta-word { font-size: clamp(1.05rem, 4.2vw, 1.6rem); letter-spacing: .01em; }
.sc-meta-state { font-size: .84rem; letter-spacing: .14em; text-transform: uppercase; margin-top: .25rem; }
.sc-cube-row { display: flex; align-items: center; gap: 1.1rem; }
.sc-cube {
  width: 86px; height: 86px; border-radius: 18px; border: none; cursor: pointer;
  font-family: var(--font-display); font-size: 2.6rem; color: #2b2117;
  background: radial-gradient(120% 120% at 30% 22%, #fffaf0 0%, #f1e5ca 44%, #ccba94 100%);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.85),
    inset 0 -5px 0 rgba(120,98,60,.35),
    0 10px 20px -8px rgba(0,0,0,.85),
    0 0 0 1px rgba(0,0,0,.35);
  transform: rotate(-3deg);
  transition: transform .2s var(--ease), box-shadow .3s;
}
.sc-cube:hover {
  transform: rotate(0deg) translateY(-2px);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.85), inset 0 -5px 0 rgba(120,98,60,.35),
    0 16px 28px -10px rgba(0,0,0,.9), 0 0 0 1px rgba(0,0,0,.35),
    0 0 32px -6px rgba(242,163,60,.6);
}
.sc-cube:active { transform: rotate(0deg) translateY(2px) scale(.97); }
.sc-cube.bump { animation: scFlip .55s var(--ease); }
@keyframes scFlip {
  0%   { transform: rotate(-3deg) scale(1); }
  45%  { transform: rotate(184deg) scale(1.14); }
  100% { transform: rotate(357deg) scale(1); }
}
.sc-cube.dead { opacity: .3; filter: grayscale(.6); cursor: not-allowed; transform: rotate(-3deg); }
/* 16, 32 and the resting 64 are two digits wide and need to come down a
   size to sit inside the same cube face. */
.sc-cube.wide { font-size: 1.95rem; letter-spacing: -.01em; }
.sc-cube-lbl { font-size: .78rem; letter-spacing: .16em; line-height: 1.5; max-width: 13ch; text-transform: uppercase; color: var(--cream-dim); }
.sc-controls { display: grid; grid-template-columns: 1fr 1fr; gap: .85rem; width: 100%; }
.sc-controls .sc-btn { padding: .95rem .5rem; text-align: center; }
/* Odd button out takes the whole row rather than leaving a hole. The
   markup orders the buttons so that button is always Undo, whether the
   clock is on (five of them) or off (three). */
.sc-controls .sc-btn:last-child:nth-child(odd) { grid-column: 1 / -1; }
.sc-btn {
  background: linear-gradient(180deg, rgba(32,24,18,.92), rgba(22,17,12,.92));
  border: 1px solid var(--line); color: var(--cream);
  border-radius: 12px; padding: .82rem 1.15rem; cursor: pointer;
  font: 700 .86rem var(--font-body); letter-spacing: .12em; text-transform: uppercase;
  transition: border-color .2s, color .2s, transform .12s, box-shadow .3s, filter .25s;
}
.sc-btn:hover { border-color: rgba(242,163,60,.5); color: var(--gold); box-shadow: 0 8px 20px -12px rgba(0,0,0,.95); }
.sc-btn:active { transform: translateY(1px); }
.sc-btn.gold {
  background: linear-gradient(135deg, #f7bb63, var(--amber) 46%, var(--amber-2));
  color: #2a1c08; border-color: transparent; box-shadow: 0 10px 26px -12px rgba(242,163,60,.9);
}
.sc-btn.gold:hover { color: #2a1c08; filter: brightness(1.06); box-shadow: 0 14px 32px -12px rgba(242,163,60,1); }
.sc-btn.danger:hover { border-color: var(--oxblood); color: #f3b8b8; }
.sc-btn.big { padding: 1.15rem 1.6rem; font-size: 1rem; }
/* The one way to put points on the board, so it gets the full width of the
   bar and sits under the housekeeping buttons where a thumb already is. */
.sc-score-btn {
  width: 100%; padding: 1.1rem 1rem; font-size: 1.05rem; letter-spacing: .22em;
}
.sc-log { font-size: .82rem; color: var(--cream-dim); text-align: center; line-height: 1.75; opacity: .78; width: 100%; padding-top: .8rem; border-top: 1px solid var(--line); }

.sc-overlay {
  position: fixed; inset: 0; z-index: 60; display: grid; place-items: center;
  background: rgba(8,6,4,.82); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  padding: 1.2rem; animation: scFade .28s var(--ease);
}
@keyframes scFade { from { opacity: 0; } to { opacity: 1; } }
.sc-sheet {
  position: relative; overflow: hidden;
  background: linear-gradient(160deg, #241b13, var(--ink-2) 62%);
  border: 1px solid rgba(242,163,60,.35); border-radius: 18px;
  padding: 2.2rem 1.9rem; max-width: 520px; width: 100%; text-align: center;
  box-shadow: var(--shadow-lift), 0 0 70px -22px rgba(242,163,60,.45);
  animation: scRise .38s var(--ease);
}
.sc-sheet::before {
  content: ""; position: absolute; top: 0; inset-inline: 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--amber), var(--gold), transparent);
}
@keyframes scRise { from { opacity: 0; transform: translateY(16px) scale(.97); } to { opacity: 1; transform: none; } }
.sc-sheet h3 { font-family: var(--font-display); text-transform: uppercase; color: var(--gold); font-size: 1.9rem; letter-spacing: .04em; margin-bottom: .55rem; }
.sc-sheet p { color: var(--cream-dim); font-size: 1.05rem; margin-bottom: 1.3rem; }
.sc-sheet-row { display: grid; gap: .8rem; }

/* --- the sign-off keypad ---
   One player at a time keys four digits into a shared phone with the other
   player sitting opposite, so the sheet is built for a thumb and gives away
   as little as it can: the digits show as dots, the keys carry no press
   colour that lingers, and the layout is the phone keypad everyone already
   knows so nobody has to hunt for a number in front of an audience. */
.sc-sign { max-width: 380px; }
.sc-sign-step {
  font-family: var(--font-display); font-size: .78rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--cream-dim); opacity: .7;
  margin-bottom: .45rem !important;
}
.sc-sign p { font-size: .98rem; margin-bottom: .9rem; }
.sc-sign-err {
  color: #f3b8b8 !important; font-size: .92rem !important;
  border: 1px solid rgba(142,47,47,.5); border-radius: 10px;
  background: rgba(142,47,47,.16); padding: .55rem .7rem;
  margin-bottom: .9rem !important;
}
.sc-dots { display: flex; gap: .85rem; justify-content: center; margin: 0 0 1.3rem; }
.sc-dot {
  width: 15px; height: 15px; border-radius: 50%;
  border: 1px solid rgba(232,200,119,.45); background: transparent;
  transition: background .12s var(--ease), box-shadow .12s var(--ease);
}
.sc-dot.on {
  background: var(--amber); border-color: var(--amber);
  box-shadow: 0 0 14px -2px rgba(242,163,60,.9);
}
.sc-pad {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: .6rem;
  margin-bottom: 1.1rem;
}
.sc-key {
  font-family: var(--font-display); font-size: 1.6rem; line-height: 1;
  color: var(--cream); background: rgba(255,255,255,.045);
  border: 1px solid var(--line); border-radius: 12px;
  padding: .85rem 0; cursor: pointer;
  /* A code is keyed fast, and a long press on a digit should not pop a
     selection bubble or a magnifier over the number being pressed. */
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  transition: background .12s var(--ease), border-color .12s var(--ease);
}
.sc-key:hover { border-color: rgba(242,163,60,.45); }
.sc-key:active { background: rgba(242,163,60,.2); transform: translateY(1px); }
.sc-key.ghost { background: none; border: 0; cursor: default; }
/* The error line lives in the markup whether or not it has anything to say,
   so that saying something does not shove the keypad down the screen under a
   thumb already on its way to a key. Spelled out rather than left to the
   browser default, because a display rule anywhere else in the theme would
   otherwise win and show an empty red box. */
.sc-sign [hidden] { display: none !important; }

/* Two ways out, stacked. Side by side would squeeze the Skip sentence into
   three lines and cost most of the height it saved, and stacking also puts
   the harmless button last, which is the order every other row of buttons on
   the card already uses: the one nearest the thumb should be the one that
   costs nothing to press by mistake. */
.sc-sign-outs { display: grid; gap: .45rem; }
.sc-sign-outs .sc-btn { margin: 0; }
.sc-sign-outs [data-act="signcancel"] { padding-top: .55rem; padding-bottom: .55rem; opacity: .85; }

/* --- the confirmation ---
   The keypad hands the sheet over to this between the two signatures and
   again at the end. It replaces the pad rather than sitting under it, so
   there is nothing left to key into, and it holds for a beat: the point is
   that a fourth digit is seen to have landed somewhere. */
.sc-sign.done { text-align: center; }
.sc-sign.done h3 { margin-bottom: .4rem; }
.sc-sign.done p { color: var(--cream-dim); margin-bottom: 0; }
.sc-tick {
  width: 52px; height: 52px; margin: .4rem auto 1.1rem; position: relative;
  border-radius: 50%; border: 2px solid var(--amber);
  background: rgba(242,163,60,.12);
  box-shadow: 0 0 22px -4px rgba(242,163,60,.75);
  animation: sc-tick-pop .34s var(--ease) both;
}
.sc-tick::after {
  content: ''; position: absolute; left: 18px; top: 10px;
  width: 12px; height: 22px; border: solid var(--amber);
  border-width: 0 2.5px 2.5px 0; transform: rotate(45deg);
  transform-origin: center;
  /* The ring lands first and the mark drops into it a moment later. Scale
     and opacity rather than a stroke being drawn: a drawn stroke wants
     clip-path or a dash offset, and neither is worth an animation that
     quietly does nothing on a browser that will not take it. */
  animation: sc-tick-draw .24s var(--ease) .1s both;
}
@keyframes sc-tick-pop {
  from { transform: scale(.6); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}
@keyframes sc-tick-draw {
  from { opacity: 0; transform: rotate(45deg) scale(.35); }
  60%  { opacity: 1; transform: rotate(45deg) scale(1.12); }
  to   { opacity: 1; transform: rotate(45deg) scale(1); }
}
/* Anybody who has asked for less movement gets the tick without the flourish,
   which is the part that carries the meaning anyway. */
@media (prefers-reduced-motion: reduce) {
  .sc-tick, .sc-tick::after { animation: none; }
}

/* Twelve keys stacked four rows deep make this the tallest thing the card
   ever puts on the screen, and a small phone in portrait is shorter than
   the roomy version of it. Ground is given in order. First the sheet
   tightens, which is enough for every phone we can name. Then, if the
   window is shorter still, the overlay scrolls rather than quietly cutting
   the bottom row off, because a keypad missing its last row is a keypad
   nobody can finish keying. "safe" is what makes that scroll reachable:
   a centred grid item taller than its container hides its own top edge
   above the scroll origin, and safe centring drops back to the start
   rather than losing it. */
@media (max-height: 740px) {
  .sc-sign { padding: 1.3rem 1.1rem; }
  .sc-sign h3 { font-size: 1.45rem; margin-bottom: .35rem; }
  .sc-sign p { font-size: .9rem; margin-bottom: .6rem; }
  .sc-sign-err { font-size: .85rem !important; margin-bottom: .6rem !important; padding: .45rem .6rem; }
  .sc-sign .sc-dots { gap: .7rem; margin-bottom: .85rem; }
  .sc-sign .sc-dot { width: 13px; height: 13px; }
  .sc-sign .sc-pad { gap: .5rem; margin-bottom: .75rem; }
  /* Still a 44px target at this size, which is the floor a thumb needs. */
  .sc-sign .sc-key { font-size: 1.4rem; padding: .65rem 0; }
}
/* A phone on its side. The instruction stays, quietly, because "look away"
   is the whole etiquette of a shared keypad and is not the line to drop. */
@media (max-height: 560px) {
  .sc-sign { padding: .9rem 1rem; }
  .sc-sign h3 { font-size: 1.2rem; }
  .sc-sign p { font-size: .8rem; margin-bottom: .45rem; }
  .sc-sign .sc-dots { margin-bottom: .6rem; }
  .sc-sign .sc-pad { gap: .4rem; margin-bottom: .6rem; }
  .sc-sign .sc-key { font-size: 1.3rem; padding: .6rem 0; }
}

/* The card is meant to be played sideways, so the keypad has a sideways
   shape of its own rather than a tall one that has to be scrolled. The
   pad moves to its own column and everything that is words stays in the
   other, which puts the whole thing on the glass at once and leaves the
   digits under the thumb that is already holding that side of the phone. */
@media (orientation: landscape) and (max-height: 560px) {
  .sc-sign {
    display: grid; grid-template-columns: 1fr minmax(190px, .85fr);
    column-gap: 1.2rem; align-items: start;
    max-width: 560px; text-align: left;
  }
  .sc-sign > * { grid-column: 1; }
  .sc-sign .sc-pad { grid-column: 2; grid-row: 1 / span 9; align-self: center; margin: 0; }
  .sc-sign .sc-dots { justify-content: flex-start; }
  .sc-sign p { margin-bottom: .55rem; }
  .sc-sign .sc-sign-outs { margin-top: .2rem; }
  /* The confirmation has no keypad, so the second column has nothing to hold
     and the sheet goes back to being one centred column of words. */
  .sc-sign.done { display: block; text-align: center; max-width: 420px; }
  .sc-sign.done .sc-tick { width: 40px; height: 40px; margin: 0 auto .7rem; }
  .sc-sign.done .sc-tick::after { left: 13px; top: 7px; width: 10px; height: 17px; }
}
.sc-overlay { overflow-y: auto; overscroll-behavior: contain; place-items: safe center; }

/* Setting the table is the one screen here that is a form rather than a
   card, so its spacing comes off the window height. A tall desktop keeps
   the roomy version; a laptop window tightens up instead of pushing the
   Start button under the fold. */
.sc-setup { padding: clamp(1.4rem, 4.5vh, 3.2rem) 1.4rem; max-width: 660px; margin-inline: auto; }
.sc-setup h2 { font-size: clamp(2.2rem, 6.5vw, 3.4rem); }
.sc-form { display: grid; gap: clamp(.7rem, 1.7vh, 1.25rem); max-width: 460px; margin: clamp(1rem, 3vh, 2rem) auto 0; }
.sc-form label { display: grid; gap: .45rem; font-size: .82rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--cream-dim); }
.sc-form input[type="text"], .sc-form select {
  background: rgba(13,10,7,.7); border: 1px solid var(--line); border-radius: 11px;
  color: var(--cream); font: 600 1.12rem var(--font-body); padding: .95rem 1.1rem;
  transition: border-color .2s, box-shadow .25s;
}
.sc-form input[type="text"]:focus, .sc-form select:focus {
  outline: none; border-color: rgba(242,163,60,.65); box-shadow: 0 0 0 3px rgba(242,163,60,.12);
}
.sc-form small { font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--cream-dim); font-family: var(--font-accent); font-style: italic; }
.sc-form .sc-check { display: flex; align-items: center; flex-wrap: wrap; gap: .6rem; }
.sc-form .sc-check input { width: 20px; height: 20px; accent-color: var(--amber); }
.sc-form .sc-check small { flex-basis: 100%; margin-top: -.2rem; }
.sc-form .sc-pick { cursor: pointer; }
/* Seat flip: quieter than the real buttons, because it is a
   convenience rather than a step in setting the table. */
.sc-flip {
  justify-self: center; background: none; border: 1px solid var(--line);
  border-radius: 999px; color: var(--cream-dim); cursor: pointer;
  padding: .5rem 1.15rem; font: 700 .7rem var(--font-body);
  letter-spacing: .18em; text-transform: uppercase;
  transition: color .2s, border-color .2s, transform .2s var(--ease);
}
.sc-flip:hover { color: var(--amber); border-color: rgba(242,163,60,.55); transform: translateY(-1px); }
/* When one seat reveals its "new player" box the grid row grows; without
   this the other seat's dropdown stretches to match and looks broken. */
.sc-form label { align-content: start; }
/* The line under the length picker that explains what was just picked.
   Quiet enough to be skipped by anybody who already knows, and there for
   anybody who does not. */
.sc-hint {
  font-size: .86rem; line-height: 1.5; color: var(--cream-dim);
  border-left: 2px solid rgba(242,163,60,.4); padding: .1rem 0 .1rem .8rem;
  margin: -.3rem 0 .1rem;
}
.sc-warn {
  font-family: var(--font-accent); font-style: italic; color: #ffb3a6;
  background: rgba(142,47,47,.22); border: 1px solid rgba(255,107,94,.35);
  border-radius: 10px; padding: .55rem .9rem; font-size: .92rem; text-align: center;
}

/* -------------------------------------------------------------
   What is at stake
   -------------------------------------------------------------
   Two people sitting down already know they want to play. What
   they do not know is what the next hour is worth, and the honest
   answer is usually more interesting than the guess: a 7-pointer
   against the player two rungs up is worth four times a single
   against the player below. The panel is quiet furniture, not a
   scoreboard, so it is drawn as an inset on the setup card rather
   than as another box shouting for the eye. It only exists when
   the ladder knows both names, which means it is simply absent on
   a new player's first night rather than showing them zeroes.
   ------------------------------------------------------------- */
.sc-stakes {
  border: 1px solid var(--line); border-radius: 13px;
  background: rgba(13,10,7,.55);
  padding: .95rem 1rem 1rem;
}
.sc-stk-h {
  display: flex; align-items: baseline; justify-content: space-between; gap: .6rem;
  font: 700 .7rem var(--font-body); letter-spacing: .18em; text-transform: uppercase;
  color: var(--cream-dim); margin-bottom: .8rem;
}
.sc-stk-k { color: var(--amber); letter-spacing: .1em; text-transform: none;
  font-family: var(--font-accent); font-style: italic; font-size: .82rem; }
.sc-stk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .55rem; }
.sc-stk-side {
  border: 1px solid rgba(255,255,255,.05); border-radius: 10px;
  background: rgba(255,255,255,.02); padding: .6rem .7rem; min-width: 0;
}
.sc-stk-name {
  font-family: var(--font-display); color: var(--cream); font-size: 1rem;
  letter-spacing: .01em; line-height: 1.2;
  /* Long names are common and a wrap here would push the two sides out
     of step with each other, so they clip instead. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sc-stk-now {
  font: 700 .95rem var(--font-body); color: var(--gold);
  font-variant-numeric: tabular-nums; margin: .15rem 0 .55rem;
}
.sc-stk-now span { font-weight: 500; font-size: .74rem; color: var(--cream-dim); }
.sc-stk-line {
  display: flex; align-items: baseline; gap: .35rem; flex-wrap: wrap;
  font-size: .74rem; color: var(--cream-dim); line-height: 1.5;
}
.sc-stk-line + .sc-stk-line { margin-top: .15rem; }
.sc-stk-line b { font-variant-numeric: tabular-nums; font-size: .88rem; font-weight: 700; }
.sc-stk-line.win b { color: #9fe3c0; }
.sc-stk-line.lose b { color: #ffb3a6; }
/* The rank note rides on the same line as the delta where it fits and
   drops beneath it where it does not, rather than always taking a row
   of its own: four forced rows per side made two numbers look like a
   report. */
.sc-stk-line i {
  font-style: normal; font-size: .68rem; letter-spacing: .04em;
  color: rgba(205,191,164,.6);
}
.sc-stk-line i.up { color: #9fe3c0; }
.sc-stk-line i.down { color: #ffb3a6; }
.sc-stk-note {
  font-family: var(--font-accent); font-style: italic; font-size: .8rem;
  line-height: 1.55; color: var(--cream-dim); text-align: center;
  margin: .8rem 0 0; letter-spacing: 0; text-transform: none; font-weight: 400;
}
@media (max-width: 380px) {
  .sc-stk-grid { grid-template-columns: 1fr; }
}
.sc-foot { text-align: center; padding: .8rem 0 1.4rem; display: flex; justify-content: center; gap: .8rem; align-items: center; }
.sc-foot a { font-size: .74rem; letter-spacing: .16em; text-transform: uppercase; color: var(--cream-dim); }
.sc-foot a:hover { color: var(--amber); }
.sc-foot span { color: var(--cream-dim); opacity: .4; }
.sc-foot .sc-ver { font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; font-variant-numeric: tabular-nums; }
/* Already saved to a home screen, so the offer to save it has nothing
   left to offer. Pure CSS, so it is right on the first paint rather
   than a link that blinks out once a script gets around to it. */
@media (display-mode: standalone) {
  .sc-foot .sc-install { display: none; }
}
.sc-toast {
  position: fixed; top: 1rem; left: 50%; transform: translateX(-50%); z-index: 70;
  background: rgba(20,53,40,.95); border: 1px solid #2f7d5a; color: #9fe3c0;
  border-radius: 999px; padding: .6rem 1.4rem; font-weight: 600; font-size: .85rem;
  box-shadow: 0 18px 40px -18px rgba(0,0,0,.9);
  /* A toast is news, not furniture. The next match begins on this same
     screen without a page load, so a pill that stays is a pill sitting over
     a fresh 0-0 all evening. It leaves on its own, in one animation rather
     than a fade in plus a fade out, because two animations on the same
     property means the second one's backwards fill quietly eats the first.
     Done in CSS so it still happens on a night when the script does not. */
  animation: scToastLife 5.6s ease forwards;
  pointer-events: none;
}
@keyframes scToastLife {
  0%   { opacity: 0; transform: translate(-50%, -14px); }
  6%   { opacity: 1; transform: translate(-50%, 0); }
  88%  { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, -10px); visibility: hidden; }
}
/* Same shape, opposite news. Wider than the green one because a rejected
   code says why, and a reason has to fit. It also stays about twice as long:
   "logged" is read in a glance, but a reason has to be read and then acted
   on, and the card is already putting that same reason back on the match. */
.sc-toast-bad {
  background: rgba(60,18,18,.95); border-color: #8e2f2f; color: #f3b8b8;
  border-radius: 14px; max-width: min(30rem, 92vw); text-align: center;
  animation-duration: 11s;
}
/* Reduced motion takes away the slide, not the leaving. A pill that never
   goes is the very thing being fixed here. */
@media (prefers-reduced-motion: reduce) {
  .sc-toast { animation-name: scToastFadeOnly; }
}
@keyframes scToastFadeOnly {
  0%   { opacity: 0; }
  6%   { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0; visibility: hidden; }
}

@media (prefers-reduced-motion: reduce) {
  .sc-score.bump, .sc-cube.bump, .sc-clock.low.on, .sc-overlay, .sc-sheet { animation: none; }
}

/* Short landscape (a phone on its side): trim the chrome so the scores
   and the cube keep the room. */
@media (orientation: landscape) and (max-height: 520px) {
  .sc-shell { min-height: calc(100svh - 2.4rem - var(--sc-safe-t, 0px) - var(--sc-safe-b, 0px)); }
  .sc-panel { padding: .55rem .7rem; gap: .5rem; }
  /* A little more room under the Score button than over the match length.
     Sideways it is the last thing in the column and the nearest thing to
     whatever the phone keeps along its bottom edge, so it does not sit
     flush against the end of the card. */
  .sc-mid { flex-basis: clamp(226px, 30vw, 420px); padding: .6rem .8rem 1rem; gap: .55rem; }
  .sc-clock { padding: .3rem 1.15rem; gap: .7rem; margin-top: .15rem; }
  .sc-reserve { font-size: 2.4rem; }
  .sc-delay { font-size: 1.05rem; }
  .sc-panel::before { width: 18px; background-size: 18px 62px; }
  .sc-cube { width: 64px; height: 64px; border-radius: 14px; font-size: 1.95rem; }
  .sc-cube.wide { font-size: 1.45rem; }
  .sc-top { grid-template-columns: 38px 1fr 38px; gap: .4rem; }
  .sc-nav-btn { width: 38px; height: 38px; border-radius: 10px; }
  .sc-nav-btn svg { width: 18px; height: 18px; }
  .sc-meta-len { font-size: 2.3rem; }
  .sc-meta-state { font-size: .74rem; margin-top: .1rem; }
  .sc-controls { gap: .6rem; }
  .sc-controls .sc-btn { padding: .68rem .4rem; }
  .sc-score-btn { padding: .8rem .6rem; font-size: .92rem; }
  .sc-log { display: none; }
  .sc-foot { padding: .3rem 0 .5rem; }
  .sc-setup { padding: 1.2rem 1rem; }
  .sc-setup h2 { font-size: 1.6rem; }
  .sc-setup .kicker { display: none; }
  .sc-form { grid-template-columns: 1fr 1fr; max-width: 760px; margin-top: .9rem; gap: .55rem 1.2rem; }
  .sc-form .sc-full { grid-column: 1 / -1; }
  .sc-form label { gap: .3rem; font-size: .74rem; }
  .sc-form input[type="text"], .sc-form select { font-size: 1rem; padding: .68rem .95rem; }
  /* Sideways on a phone there is barely a screen's worth of height for
     the whole setup card, so the panel keeps its numbers and gives up
     its breathing room and its closing line. */
  .sc-stakes { padding: .6rem .7rem .65rem; }
  .sc-stk-h { margin-bottom: .5rem; }
  .sc-stk-note { display: none; }
  .sc-btn.big { padding: .85rem 1.3rem; font-size: .9rem; }
  .sc-sheet { padding: 1.2rem; max-width: 560px; }
  .sc-sheet h3 { font-size: 1.2rem; }
  .sc-sheet p { margin-bottom: .8rem; }
  .sc-sheet-row { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
}

/* A phone actually lying on its side. Nothing but a phone is this short in
   landscape, and a phone is the one place a system bar can be sitting on
   top of the card, so this tier gives up size everywhere in the middle
   column until the whole of it, Score button included, fits above the bar
   on the shortest S24 display setting with room left over. Measured
   against 694x320, which is what a Galaxy S24 reports sideways with the
   display size turned up. */
@media (orientation: landscape) and (max-height: 420px) {
  .sc-mid { padding: .5rem .7rem .8rem; gap: .3rem; }
  .sc-top { grid-template-columns: 34px 1fr 34px; gap: .35rem; }
  .sc-nav-btn { width: 34px; height: 34px; border-radius: 10px; }
  .sc-nav-btn svg { width: 16px; height: 16px; }
  .sc-meta-lbl { font-size: .58rem; letter-spacing: .22em; }
  .sc-meta-len { font-size: 1.85rem; }
  .sc-meta-state { font-size: .66rem; margin-top: .05rem; }
  .sc-cube { width: 52px; height: 52px; border-radius: 12px; font-size: 1.55rem; }
  .sc-cube.wide { font-size: 1.15rem; }
  .sc-cube-lbl { font-size: .64rem; max-width: 11ch; }
  .sc-cube-row { gap: .75rem; }
  .sc-controls { gap: .32rem; }
  .sc-controls .sc-btn { padding: .46rem .35rem; font-size: .74rem; letter-spacing: .09em; }
  .sc-score-btn { padding: .6rem .5rem; font-size: .84rem; letter-spacing: .16em; }
}

.sc-rotate {
  display: none; align-items: center; justify-content: center; gap: .6rem;
  padding: .5rem 1rem; font-size: .66rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--amber); border-bottom: 1px solid var(--line);
  background: linear-gradient(90deg, transparent, rgba(242,163,60,.13), transparent);
}
.sc-rotate svg { width: 18px; height: 18px; flex: none; animation: scTilt 3.4s var(--ease) infinite; }
@keyframes scTilt { 0%, 62%, 100% { transform: rotate(0); } 74%, 88% { transform: rotate(-90deg); } }
@media (prefers-reduced-motion: reduce) { .sc-rotate svg { animation: none; } }

/* Upright phone: fall back to the stacked card, plus a nudge that the
   full-width version is one rotation away. */
@media (orientation: portrait) and (max-width: 860px) {
  .sc-shell { flex-direction: column; max-width: 560px; min-height: calc(100svh - 7.2rem - var(--sc-safe-t, 0px) - var(--sc-safe-b, 0px)); }
  .sc-foot { padding: .35rem 0 .55rem; }
  .sc-panel { border-inline: none; border-block: 1px solid var(--line); }
  .sc-panel::before { width: 20px; background-size: 20px 68px; }
  .sc-mid { flex: none; padding: .6rem 1rem; gap: .5rem; border-inline: none; border-block: 1px solid var(--line); }
  /* Stacked, the middle bar and two panels share one screen, so the
     numerals come down from their landscape size. */
  .sc-score { font-size: clamp(4rem, 21vw, 7rem); }
  .sc-name { font-size: clamp(1.35rem, 5.4vw, 2rem); }
  .sc-panel { gap: .4rem; padding: .6rem; }
  .sc-reserve { font-size: 2.5rem; }
  .sc-delay { font-size: 1.12rem; }
  .sc-cube { width: 66px; height: 66px; border-radius: 14px; font-size: 2rem; }
  .sc-cube.wide { font-size: 1.5rem; }
  .sc-top { grid-template-columns: 42px 1fr 42px; }
  .sc-nav-btn { width: 42px; height: 42px; }
  .sc-meta-len { font-size: 2.4rem; }
  .sc-meta-state { font-size: .76rem; margin-top: .1rem; }
  .sc-controls { gap: .5rem; }
  .sc-controls .sc-btn { padding: .62rem .4rem; }
  .sc-score-btn { padding: .85rem .6rem; font-size: .98rem; }
  .sc-log { display: none; }
  .sc-rotate { display: flex; }
}

/* Short upright phones (an SE on its end): the stacked card has three
   rows to fit in one screen, so everything comes down a size. */
@media (orientation: portrait) and (max-height: 700px) {
  .sc-panel { gap: .3rem; padding: .45rem; }
  .sc-name { font-size: clamp(1.05rem, 4.4vw, 1.5rem); }
  .sc-score { font-size: clamp(3rem, 15vw, 4.6rem); }
  .sc-clock { padding: .24rem .95rem; gap: .55rem; margin-top: .15rem; }
  .sc-reserve { font-size: 1.85rem; }
  .sc-delay { font-size: .92rem; }
  .sc-mid { padding: .45rem .8rem; gap: .45rem; }
  .sc-meta-lbl { font-size: .6rem; letter-spacing: .24em; }
  .sc-meta-len { font-size: 1.85rem; }
  .sc-meta-state { font-size: .68rem; margin-top: .05rem; }
  .sc-top { grid-template-columns: 36px 1fr 36px; gap: .4rem; }
  .sc-nav-btn { width: 36px; height: 36px; border-radius: 10px; }
  .sc-nav-btn svg { width: 17px; height: 17px; }
  .sc-cube { width: 54px; height: 54px; border-radius: 12px; font-size: 1.6rem; }
  .sc-cube.wide { font-size: 1.2rem; }
  .sc-cube-lbl { font-size: .66rem; }
  .sc-cube-row { gap: .8rem; }
  .sc-controls { gap: .45rem; }
  .sc-controls .sc-btn { padding: .5rem .35rem; font-size: .76rem; }
  .sc-score-btn { padding: .7rem .5rem; font-size: .88rem; letter-spacing: .18em; }
}

/* =============================================================
   12. REVEAL ANIMATION + GENERIC WP CONTENT
   ============================================================= */
.reveal { opacity: 0; transform: translateY(34px); transition: opacity .8s var(--ease), transform .8s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .die, .hero-badge .dot, .scroll-hint::after { animation: none; }
  /* The ticker still changes line, because that is the content and not
     the decoration, but it does so by fading rather than by moving. */
  .gp-tick-line { transform: none; transition: opacity .35s linear; }
}

.entry-content { padding: 9rem 0 5rem; }
.entry-content p, .entry-content li { color: var(--cream-dim); margin-bottom: 1rem; }
.entry-content h2, .entry-content h3 { color: var(--gold); margin: 2rem 0 1rem; }

/* =============================================================
   13. RESPONSIVE
   ============================================================= */
@media (max-width: 940px) {
  .card-grid, .stat-grid, .news-grid { grid-template-columns: repeat(2, 1fr); }
  .venue-grid, .signup-grid, .rules-body { grid-template-columns: 1fr; }
  .rules-toc { position: static; display: flex; flex-wrap: wrap; gap: .3rem 1.2rem; border-left: none; border-bottom: 1px solid var(--line); padding: 0 0 1rem; }
  .cube-track { grid-template-columns: repeat(5, minmax(0,1fr)); gap: .6rem; }
  .cube { font-size: 1.7rem; border-radius: 12px; }
  .dice-float { display: none; }
}
@media (max-width: 720px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .stat + .stat::before { display: none; }
  .field-row { grid-template-columns: 1fr; }
  .card-grid, .news-grid { grid-template-columns: 1fr; }
}

/* ==============================================================
   The Bat Signal
   ============================================================== */

/* The way back to a match in progress.

   Fixed to the bottom of the page while a Score Card is half played, and
   gone the moment it is logged or started over. It carries the score so
   that returning to it is a decision made with the facts, and so that a
   phone picked up an hour later says what it is offering rather than just
   that it is offering something.

   Below the Bat Signal in the stack and out of its corner. Both live at
   the bottom of the screen and on a phone the cube runs the full width,
   so the one page state that can show both lifts the cube clear rather
   than letting them argue over the same inch. */
.bbpg-resume {
  position: fixed; z-index: 54;
  left: 1rem; bottom: 1rem;
  max-width: min(19rem, calc(100vw - 2rem));
  display: flex; align-items: center; gap: .7rem;
  padding: .7rem .95rem;
  background: rgba(13,10,7,.92);
  border: 1px solid rgba(242,163,60,.4);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  backdrop-filter: blur(8px);
  text-decoration: none; color: var(--cream);
  font-family: var(--font-body);
  transition: border-color .25s var(--ease), transform .25s var(--ease);
}
.bbpg-resume:hover { border-color: var(--gold); transform: translateY(-2px); }
.bbpg-resume-i { flex: none; width: 1.5rem; height: 1.5rem; color: var(--gold); }
.bbpg-resume-i svg { width: 100%; height: 100%; display: block; }
.bbpg-resume-t { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.bbpg-resume-t strong {
  font-size: .82rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--gold); font-weight: 600;
}
.bbpg-resume-t em {
  font-style: normal; font-size: .95rem; color: var(--cream);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.has-resume .bat-signal { bottom: calc(1rem + 4.6rem); }
@media (max-width: 460px) {
  .bbpg-resume { left: .75rem; right: .75rem; bottom: .75rem; max-width: none; }
  body.has-resume .bat-signal { bottom: calc(.75rem + 4.6rem); }
}
@media (prefers-reduced-motion: reduce) {
  .bbpg-resume { transition: none; }
  .bbpg-resume:hover { transform: none; }
}

/* A cube in the corner of every page while somebody is at the spot.

   Fixed and low in the stack: above the page, below the mobile nav
   drawer and its scrim (99 and up) and below the Score Card's modals
   (60), because a signal that a game might be going is never more
   important than the thing the visitor opened deliberately. */
.bat-signal {
  position: fixed; z-index: 55;
  right: 1rem; bottom: 1rem;
  max-width: min(22rem, calc(100vw - 2rem));
  display: flex; align-items: stretch; gap: .25rem;
  background: rgba(13,10,7,.92);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  backdrop-filter: blur(8px);
  font-family: var(--font-body);
}
.bat-signal[hidden] { display: none; }

/* Out of the way while the form that lights it is on screen.

   The panel is anchored to the bottom of the viewport and on a phone it
   goes the full width, so on the one page in the site with a Bat Signal
   form it sat squarely over "Put mine out": the button a visitor is
   reaching for is the button the cube is covering. Scrolling clears it,
   but having to scroll a thing off your own thumb is the sort of small
   rudeness nobody reports and everybody notices.

   It also has nothing to say at that moment. The section it is covering
   is a heading, a paragraph and a control, all about the same signal the
   cube is reporting, so the corner is repeating what is already on the
   screen at full size. Slide it down, and bring it back the moment the
   form scrolls away. */
.bat-signal {
  transition: transform .32s var(--ease), opacity .32s var(--ease);
}
.bat-signal.is-tucked {
  transform: translateY(calc(100% + 1.5rem));
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .bat-signal { transition: none; }
}

.bat-signal .bs-body {
  display: flex; align-items: center; gap: .8rem;
  padding: .8rem .5rem .8rem .9rem;
  text-decoration: none; color: inherit; min-width: 0;
}
.bat-signal .bs-mark { flex: 0 0 auto; color: var(--gold); line-height: 0; }
.bat-signal .bs-cube { width: 34px; height: 34px; display: block; }

.bat-signal .bs-text { min-width: 0; }
.bat-signal .bs-who {
  display: block; color: var(--cream); font-size: .95rem;
  font-weight: 600; line-height: 1.25;
}
.bat-signal .bs-sub {
  display: block; margin-top: .15rem;
  color: var(--cream-dim); font-size: .78rem; letter-spacing: .01em;
}

.bat-signal .bs-x {
  flex: 0 0 auto; align-self: flex-start;
  background: none; border: 0; cursor: pointer;
  color: rgba(205,191,164,.55);
  font-size: 1.2rem; line-height: 1; padding: .5rem .6rem;
}
.bat-signal .bs-x:hover, .bat-signal .bs-x:focus { color: var(--cream); }

/* The fade.

   Slow, and only ever on the cube. An earlier pass breathed the whole
   panel, which meant the name faded out too and the thing you actually
   wanted to read spent half of every cycle unreadable. The mark is the
   signal and the words are the caption: the mark can pulse, the caption
   has to stay put.

   Eight seconds because it has to be visibly slower than anything else
   moving on the page. A one second pulse in the corner of the screen is
   a notification badge nagging for a tap, and this is a lamp somebody
   left on. It also never fades to nothing, bottoming out at a third:
   the cube is still there through the whole cycle, so a glance that
   lands at the wrong moment does not find an empty corner. */
@keyframes bbpg-signal-breathe {
  0%, 100% { opacity: .34; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: no-preference) {
  .bat-signal .bs-mark { animation: bbpg-signal-breathe 8s ease-in-out infinite; }
}
/* Asked not to move: the cube simply stays lit. The whole message is
   carried by the words next to it either way, so nothing is lost by
   holding still. */
@media (prefers-reduced-motion: reduce) {
  .bat-signal .bs-mark { opacity: 1; }
}

@media (max-width: 560px) {
  .bat-signal { right: .6rem; bottom: .6rem; left: .6rem; max-width: none; }
  .bat-signal .bs-who { font-size: .9rem; }
}

/* ---- the form on the ladder page ---- */

.bs-form { display: grid; gap: .9rem; max-width: 34rem; margin: 0 auto; }
.bs-form .bs-row { display: grid; grid-template-columns: 1fr 8rem; gap: .7rem; }
.bs-form .bs-actions { display: grid; grid-template-columns: 1fr auto; gap: .7rem; }
.bs-form.is-busy { opacity: .6; pointer-events: none; }

/* The name and the code sit on one line, so they have to be one height.
   Left alone they are not: the theme's text input and its select reach
   their size by different routes, the input from padding and the select
   from the line box the browser gives it, and the pair came out 59 and
   52. Seven pixels is small enough to look like nothing in a spec and
   obvious enough on the page that the row reads as crooked. Setting the
   height on both rather than nudging the padding of one keeps them
   agreeing whatever a browser thinks a select should be. */
.bs-form .bs-row .field select,
.bs-form .bs-row .field input { height: 3.25rem; }

.bs-msg { margin: 0; font-size: .9rem; }
/* No message, no room reserved for one. A blank line under the buttons
   pushes the note about codes away from them and reads as something
   failing to load. */
.bs-msg:empty { display: none; }
.bs-msg.ok  { color: #7fd6a4; }
.bs-msg.err { color: #f0a2a2; }

/* The lower-it button is deliberately the quiet one. Both buttons take
   the same name and the same code, so the difference between them is
   only which way the cube goes, and the one that arrives at the spot is
   the one being reached for nine times out of ten. */
.bs-form .bs-down { white-space: nowrap; }

@media (max-width: 560px) {
  .bs-form .bs-row { grid-template-columns: 1fr; }
  .bs-form .bs-actions { grid-template-columns: 1fr; }
}

/* ---- the notifications switch ----

   Set apart from the form above it by a rule rather than a box, because
   it is a different question. The form asks "are you at the spot"; this
   asks "do you want telling when somebody else is", and somebody who has
   come to the page to light a cube should be able to see at a glance
   that this is not another field they have to fill in. */
.bs-push {
  display: grid; gap: .55rem; justify-items: center;
  max-width: 34rem; margin: 1.6rem auto 0; padding-top: 1.4rem;
  border-top: 1px solid rgba(255, 255, 255, .09);
  text-align: center;
}
.bs-push .bs-bell { width: 100%; }

/* One button with two labels, swapped by a class, so the width never
   jumps between them and there is no moment where it says the wrong
   thing while a request is in the air. */
.bs-push .bs-bell-on { display: none; }
.bs-push.is-on .bs-bell-on { display: inline; }
.bs-push.is-on .bs-bell-off { display: none; }

.bs-push-msg { margin: 0; font-size: .85rem; color: var(--cream-dim); }
.bs-push-msg:empty { display: none; }
.bs-push-msg.ok   { color: #7fd6a4; }
.bs-push-msg.err  { color: #f0a2a2; }
/* The iPhone explanation, which is longer than the rest and is a fact
   about the device rather than a result, so it reads as a footnote. */
.bs-push-msg.note { font-size: .82rem; line-height: 1.5; max-width: 30rem; }
