/* site.css — the complete visual layer of blitzmate.co. A class missing from this file ships
 * unstyled. Provenance, the design values, and the compliance rules this file owns (accent as
 * text, focus rings): notes/site-css.md#file-header. Phase 34 relocated the prose; nothing lost. */

/* --- Font stacks · notes/site-css.md#font-stacks ------------------------- */
:root {
  --ff: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --fd: 'Space Grotesk', var(--ff);
  --fb: 'Hanken Grotesk', var(--ff);
}

/* --- Base ---------------------------------------------------------------- */
a { color: inherit; text-decoration: none }
img { display: block; max-width: 100% }
/* .only-mob is the exception at wide widths; .only-desk is simply the default. */
.only-mob { display: none }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth } }

/* --- Header (design 86-89) ----------------------------------------------- */
.site-header { display: flex; align-items: center; justify-content: space-between; padding: 22px 48px; border-bottom: 1px solid var(--border-soft) }
.brand { font: 700 21px var(--fd); letter-spacing: .02em }
/* THE one place the accent may be a text colour — the wordmark is 21px/700. */
.brand span { color: var(--accent) }
.header-cta { display: inline-flex; align-items: center; min-height: 44px; font: 700 14px var(--fd); background: var(--surface-raised); border: 1px solid var(--border); padding: 11px 20px; border-radius: 13px }
.age-badge { font: 600 12px var(--fb); color: var(--text-muted) }

/* --- Hero (design 91-112) ------------------------------------------------ */
.hero { position: relative; display: grid; grid-template-columns: 1.02fr .98fr; gap: 24px; align-items: center; padding: 64px 48px 72px; min-height: 680px }
.hero-copy { position: relative; z-index: 2 }
.tc-chips { display: flex; align-items: center; gap: 8px; margin-bottom: 22px }
.tc-chips .age-badge { margin-left: 6px }
.chip { font: 500 13px var(--fd); font-variant-numeric: tabular-nums; color: var(--text-dim); background: var(--surface); border: 1px solid var(--border-soft); padding: 5px 11px; border-radius: 24px }
.chip--on { color: var(--accent-hot); background: var(--accent-tint); border-color: rgba(255, 90, 77, .4) }
.hero-title { margin-bottom: 20px; font: 700 68px/1.02 var(--fd); letter-spacing: -.02em; text-wrap: balance }
.hero-sub { margin-bottom: 34px; max-width: 440px; font: 400 18px/1.55 var(--fb); color: var(--text-dim) }
.hero-fine { font: 400 12px var(--fb); color: var(--text-muted) }

/* --- Store badges · notes/site-css.md#store-badges — the 86.31/-14.155 pair is arithmetic, not taste --- */
.badge-row { display: flex; gap: 14px; margin-bottom: 16px }
.badge { display: inline-flex; align-items: center; min-height: 58px; border-radius: 13px } .badge--play { border-radius: 7.3px }
.badge-art { display: block; height: 58px; width: auto; max-width: none } .badge--play .badge-art { height: 86.31px; margin: -14.155px }
/* NO GLOW. Removed at the owner's request 2026-08-06; check-website-badges.cjs asserts no accent box-shadow
 * reaches a badge, here or inline. Do not reinstate it. Ordering is all that is left: notes/site-css.md#no-glow */
html.platform-android .badge--play { order: -1 } html.platform-ios .badge--ios { order: -1 }

/* --- Play-in-browser button · the one LIVE action in .badge-row -----------
 * Deliberately NOT .badge: that class carries rules written for the two
 * placeholder STORE badges (check-website.cjs asserts there are exactly four
 * and that none is interactive while unpublished). This button is a real <a>
 * and must stay one, so it takes its own class rather than weakening those
 * guards. It duplicates .badge sizing so it still sits as a peer in the row.
 * No artwork, so no vendor rules, and a real <a> because
 * play.blitzmate.co exists (the #store-badges-not-links reasoning is about
 * links that would 404). order:-2 keeps it ahead of BOTH store badges even
 * after the platform reordering above promotes one of them — while the stores
 * say "coming soon", burying the only working action under a placeholder would
 * be the same dishonesty that rule exists to prevent.
 * NO box-shadow: check-website-badges.cjs bans an accent glow reaching a badge
 * (notes/site-css.md#no-glow). The accent here is a FILL, which is what makes
 * it read as the primary action without reintroducing the lit-rectangle. */
.play-cta { display: inline-flex; align-items: center; min-height: 58px; border-radius: 13px;
  order: -2; gap: 9px; padding: 0 22px; background: var(--accent); color: var(--bg); font: 600 15px var(--fd) }
.play-cta:hover { background: var(--accent-hot) }
.play-cta .badge-web-icon { fill: var(--bg); flex: none }

/* --- Hero visual + the iPhone-framed match screen · notes/site-css.md#hero-visual-phone-mockup
 * WEB-12 makes the APP the specification for this surface, not the design file: every value below
 * traces to a source line tabulated in 34-MOCKUP-SPEC.md. .hero-visual's overflow:hidden stops the
 * horizontal scrollbar; .phone-scale does ALL the scaling — never pre-multiply a value under it. */
.hero-visual { position: relative; height: 664px; overflow: hidden }
/* THE CROP · notes/site-css.md#hero-visual-phone-mockup. height is 560, not the device's 635: it reveals
 * the top 764 of the 866px device and lets the rest fall outside, the way an app site conventionally
 * shows a phone. Nothing is fabricated — the app itself leaves 189px empty under "Your move"
 * (BOARD_MAX caps the board at 350), and that region is simply not in view. overflow:hidden IS the
 * crop; the mask only softens its edge so the last 64px dissolve into --bg rather than ending on a
 * hard line 8px under the status text. The two mask stops are tied to the height on this line — the
 * second one IS it — and [mockup] fails the build if they drift apart or the crop moves. */
.phone-frame { position: absolute; left: 0; top: 8px; z-index: 1; width: 302px; height: 560px; overflow: hidden; -webkit-mask-image: linear-gradient(180deg, #000 496px, transparent 560px); mask-image: linear-gradient(180deg, #000 496px, transparent 560px) }
.phone-scale { width: 412px; transform: scale(.733); transform-origin: top left }
/* position:relative is the notch's containing block. .phone must NOT get one: it stays a plain
 * seven-child column and the notch is its absolutely positioned SIBLING, which is what keeps the
 * device 412x866. In flow the notch makes it 899, and 899 scaled crops the bottom of the screen. */
.phone-device { position: relative; padding: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: 56px }
.phone-notch { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 1; width: 157px; height: 33px; border-radius: 0 0 20px 20px; background: #000 }
/* GameScreen.tsx:289-299 + spacing.ts:9-15. 61px top = the 47px inset SafeAreaView edges={['top']}
 * consumes, plus 14px of padding; edges={['top']} reserves NOTHING at the bottom, so neither does
 * this. No justify-content and no auto margin anywhere below — the app sets neither, and the pair
 * is exactly what parked the old chip row at the bottom of an over-tall box. No border either:
 * under the border-box reset one would silently render the board at 348px instead of 350. */
.phone { width: 390px; height: 844px; display: flex; flex-direction: column; gap: 12px; align-items: stretch; padding: 61px 20px 14px; background: var(--bg); border-radius: 46px; overflow: hidden }
/* GameTopBar.tsx:83-101. The bar's height is its tallest child, the 32x32 chat button
 * (chat/ChatToggleButton.tsx:21) — which ships whenever the match is active, as here. */
.gtb { display: flex; align-items: center; min-height: 32px }
.gtb-side { flex: 1; display: flex; flex-direction: column; align-items: flex-start }
.gtb-side--r { align-items: flex-end }
.gtb-center { flex: 2; display: flex; flex-direction: column; align-items: center }
.gtb-right { display: flex; align-items: center; gap: 12px }
.gtb-label { font: 600 12px var(--fb); letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim) }
.gtb-label--resign { color: var(--negative) }
.gtb-chat { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; flex: none }
/* The glyph's colour must stay a token, and a presentation fill= attribute cannot be one. */
.gtb-chat path { fill: var(--text-dim) }
/* PlayerRow.tsx:58-70 — the row has NO gap; spacing.ts:69's 8px is a margin on the identity. */
.player-row { display: flex; align-items: center }
.avatar { display: flex; align-items: center; justify-content: center; border-radius: 50%; color: var(--bg); flex: none }
/* spacing.ts:65-67: a rounded SQUARE, overriding the shared 50%. It carries no letter, so no font. */
.phone .avatar { width: 38px; height: 38px; border-radius: 11px }
.p-id { flex: 1; min-width: 0; margin-left: 8px }
.p-name { font: 400 15px var(--fb); color: var(--text) }
.p-rating { font: 600 12px var(--fb); letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim) }
/* ClockPill.tsx:53-68 + spacing.ts:71-73. tabular-nums comes AFTER the shorthand, which resets it. */
.clock { font: 600 25px/25px var(--fd); font-variant-numeric: tabular-nums; padding: 10px 16px; border-radius: 13px }
.clock--idle { background: var(--surface); color: var(--text-dim2) }
.clock--low { background: var(--clock-low); color: #FFFFFF; animation: lowPulse 1s ease-in-out infinite }
/* CapturedStrip.tsx:22,37-42. Both strips are empty at this FEN — that IS the app's empty state,
 * a full-height blank row with no placeholder glyph. StatusLine.tsx:20-30 closes the column. */
.cap { min-height: 15px }
.status { text-align: center; font: 400 15px var(--fb); color: var(--text) }
/* The app's canonical off-centre coin (LobbyHeader.tsx:31-39). Never centred, never an asset. */
.coin { border-radius: 50%; background: radial-gradient(circle at 35% 30%, var(--gold-in), var(--gold-out)); flex: none }

/* --- Board (design 133-137) · notes/site-css.md#board-squares — .sq--hl is ADDITIVE --- */
.board { display: grid; grid-template-columns: repeat(8, 1fr); border-radius: 12px; overflow: hidden; box-shadow: 0 14px 36px rgba(0, 0, 0, .45) }
.sq { aspect-ratio: 1; display: flex; align-items: center; justify-content: center }
.sq--l { background: var(--sq-l) } .sq--d { background: var(--sq-d) }
.sq--l.sq--hl { background: var(--sq-lm-l) } .sq--d.sq--hl { background: var(--sq-lm-d) }
.pc { width: 80%; height: 80%; display: block } .pc--snap { animation: snapIn .55s cubic-bezier(.2, .8, .2, 1) both }
/* The <symbol> library is taken out of flow by zero size + clipping, NOT by
 * hiding the element outright — that breaks <use> rendering in some engines. */
.piece-sprite { position: absolute; width: 0; height: 0; overflow: hidden }

/* --- Colour modifiers (design 241-246) · notes/site-css.md#colour-modifiers --- */
.avatar--a { background: #5B8FB9 } .avatar--b { background: #B95B7A } .avatar--c { background: #7A5BB9 }
.avatar--d { background: #5BB98F } .avatar--self { background: var(--accent) }
/* The unmodified rank colour MUST be declared before its three modifiers: all
 * four selectors have identical specificity, so source order decides. */
.rank-num { color: var(--text-dim) } .rank-num--1 { color: #D69E33 } .rank-num--2 { color: #B9BEC7 } .rank-num--3 { color: #C97A4A }

/* --- Keyframes (design 28-29, verbatim) ---------------------------------- */
@keyframes lowPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(217, 83, 75, .55) } 50% { box-shadow: 0 0 0 7px rgba(217, 83, 75, 0) } }
@keyframes snapIn { 0% { transform: translate(-92%, 138%) scale(1.12) } 62% { transform: translate(4%, -5%) scale(1.05) } 100% { transform: none } }

/* --- Sections, desktop 1280 (design 154-284) ----------------------------- */
#how, #time-controls, #fair-play, #ranking { padding: 72px 48px 44px }
#how { padding-top: 88px } #fair-play, #ranking { display: grid; align-items: center; gap: 64px }
#fair-play { grid-template-columns: 1fr 1.1fr } #ranking { grid-template-columns: 1fr 1fr }
/* A grid item's min-width defaults to auto, so an unbreakable string in the
 * copy column can push these two tracks wider than the section. Pin it to 0. */
.fp-copy, .rank-copy { min-width: 0 }
.eyebrow { font: 700 12px var(--fd); letter-spacing: .16em; color: var(--accent-hot); margin-bottom: 10px }
.section-title { margin-bottom: 16px; font: 700 40px/1.1 var(--fd); letter-spacing: -.01em }
#how .section-title { margin-bottom: 36px } #time-controls .section-title { margin-bottom: 8px }
.section-sub { font: 400 16px/1.6 var(--fb); color: var(--text-dim) } #time-controls .section-sub { line-height: 1.5; margin-bottom: 32px }

/* How it works (design 157-173) */
.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px }
.card { background: linear-gradient(180deg, var(--surface-grad), var(--surface)); border: 1px solid var(--border-soft); border-radius: 18px; padding: 28px }
.card-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 22px }
.card-num { font: 700 13px var(--fd); font-variant-numeric: tabular-nums; color: var(--text-muted) }
.card-title { font: 700 20px var(--fd); margin-bottom: 8px }
.card-desc { font: 400 15px/1.55 var(--fb); color: var(--text-dim) }
#how .coin, #how .avatar { width: 40px; height: 40px }
.avatar-pair { display: flex; flex: none } .avatar-pair .avatar { border: 2px solid var(--surface) }
.avatar-pair .avatar + .avatar { margin-left: -14px }
.coin-stack { position: relative; width: 44px; height: 40px; flex: none }
.coin-disc { position: absolute; left: 2px; width: 40px; height: 14px; border-radius: 50%; background: radial-gradient(circle at 35% 30%, var(--gold-in), var(--gold-out)) }
.coin-disc:nth-child(1) { bottom: 0 } .coin-disc:nth-child(2) { bottom: 9px } .coin-disc:nth-child(3) { bottom: 18px } .coin-disc:nth-child(n+2) { box-shadow: 0 1px 0 rgba(0, 0, 0, .4) }

/* Time controls (design 180-203) */
.tc-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px }
.tc-card { display: flex; flex-wrap: wrap; gap: 10px; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 16px; padding: 24px }
.tc-label { flex-basis: 100%; font: 600 12px var(--fb); letter-spacing: .14em; color: var(--text-muted); margin-bottom: 6px } .tc-label--on { color: var(--accent-hot) }
.tc-grid .chip { font: 700 18px var(--fd); font-variant-numeric: tabular-nums; color: var(--text-dim2); background: none; border: 1px solid rgba(255, 255, 255, .1); padding: 10px 18px; border-radius: 24px }
.tc-grid .chip--on { color: var(--accent-hot); background: var(--accent-tint); border-color: var(--accent) }

/* Fair play (design 206-229) · notes/site-css.md#fair-play — .fp-title is the fifth face's only consumer */
.fp-list { display: flex; flex-direction: column; gap: 14px }
.fp-card { display: flex; gap: 16px; align-items: flex-start; background: var(--surface); border: 1px solid var(--border-soft); border-radius: 18px; padding: 20px 22px }
.fp-card svg { flex: none; margin-top: 2px } .fp-title { font: 600 16px 'Space Grotesk', var(--ff); margin-bottom: 4px }
.fp-desc { font: 400 14.5px/1.5 var(--fb); color: var(--text-dim) }

/* Ranking (design 232-249) */
.rank-card { background: linear-gradient(180deg, var(--surface-grad), var(--surface)); border: 1px solid var(--border-soft); border-radius: 18px; padding: 22px 24px }
.rank-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px }
.rank-head span { font: 600 12px var(--fb); letter-spacing: .14em; color: var(--text-muted) }
.rank-head .only-desk { font: 500 13px var(--fd); letter-spacing: 0; color: var(--text-dim) }
.rank-row { display: flex; align-items: center; gap: 14px; padding: 10px 8px; border-radius: 13px; margin-top: 2px }
.rank-num { font: 700 15px var(--fd); font-variant-numeric: tabular-nums; width: 20px; flex: none }
.rank-row .avatar { width: 32px; height: 32px; font: 700 13px var(--fd) } .rank-name { flex: 1; font: 600 15px var(--fb); color: var(--text) }
.country { font: 400 12px var(--fb); color: var(--text-muted); margin-left: 6px } .rank-elo { font: 700 16px var(--fd); font-variant-numeric: tabular-nums }
.rank-more { text-align: center; font: 700 14px var(--fd); color: var(--text-muted); padding: 2px 0 }
.rank-row--you { background: var(--accent-tint); border: 1px solid rgba(255, 90, 77, .4) }
.rank-row--you .rank-num, .rank-row--you .rank-elo { color: var(--accent-hot) }

/* Free coins strip (design 251-256) — .card here overrides the gradient. */
.free-coins { padding: 36px 48px 0 }
.free-coins .card { display: flex; align-items: center; gap: 16px; background: var(--surface); padding: 18px 24px }
.free-coins .coin { width: 28px; height: 28px } .free-coins strong { color: var(--text); font-weight: 600 }
.fc-text { font: 400 14px/1.55 var(--fb); color: var(--text-dim) }

/* Final CTA (design 258-272) */
.final-cta { text-align: center; padding: 110px 48px }
.cta-title { margin-bottom: 14px; font: 700 52px/1.05 var(--fd); letter-spacing: -.02em }
.cta-sub { margin-bottom: 36px; font: 400 17px var(--fb); color: var(--text-dim) }
.final-cta .badge-row { justify-content: center }

/* Footer (design 274-284) · notes/site-css.md#footer — .copyright is a SIBLING of .brand (33-04 D-2) */
.site-footer { border-top: 1px solid var(--border-soft); padding: 30px 48px 36px }
.footer-top { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 20px; margin-bottom: 14px }
.site-footer .brand { font-size: 15px } .copyright { font: 400 12px var(--fb); color: var(--text-muted) }
.footer-links { display: flex; gap: 24px; margin-left: auto }
.footer-links a { display: inline-flex; align-items: center; min-height: 44px; padding: 10px 0; font: 400 13px var(--fb); color: var(--text-dim) }
.legal, .trademarks { font: 400 12px/1.6 var(--fb); color: var(--text-muted) } .trademarks { margin-top: 8px }

/* --- Focus (WEB-07) · notes/site-css.md#focus-web-07 — the ring sets NO border-radius, deliberately --- */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible { outline: 2px solid var(--accent-hot); outline-offset: 3px }
.footer-links a:focus-visible { border-radius: 4px }

/* --- <=880px, the mobile artboard (design 294-443) · notes/site-css.md#mobile-880-breakpoint
 * Three breakpoints are the whole set — 880, 400, 340. Do not add a fourth. */
@media (max-width: 880px) {
  .only-desk { display: none } .only-mob { display: block }
  .site-header { padding: 16px 20px } .brand { font-size: 18px }
  .hero { grid-template-columns: 1fr; gap: 0; padding: 40px 20px 12px; min-height: auto }
  .tc-chips { margin-bottom: 18px } .chip { font-size: 12px; padding: 4px 10px }
  .hero-title { font-size: 42px; line-height: 1.04; margin-bottom: 14px }
  .hero-sub { font-size: 16px; max-width: none; margin-bottom: 26px }
  /* align-items:center, not the old .badge{width:100%} — that stretched the box into a
   * column-wide rectangle. Nothing lost: the badges are not tap targets any more. */
  .badge-row { flex-direction: column; align-items: center; gap: 12px; margin-bottom: 12px }
  .hero-fine { text-align: center; font-size: 11px }
  /* The frame stops being absolute and is centred instead; .hero-visual keeps
   * overflow:hidden so a sub-302px viewport still cannot scroll sideways. */
  .hero-visual { height: auto; display: flex; justify-content: center; padding: 28px 0 8px }
  .phone-frame { position: static }
  #how, #time-controls, #fair-play, #ranking { display: block; padding: 40px 20px 8px }
  #how { padding-top: 44px } .eyebrow { font-size: 11px; margin-bottom: 8px }
  .section-title { font-size: 28px; line-height: 1.12; margin-bottom: 18px } #how .section-title { margin-bottom: 22px }
  /* Cards become icon-beside-text rows: .card-top spans both text rows. */
  .card-grid { display: flex; flex-direction: column; gap: 12px }
  .card { display: grid; grid-template-columns: auto 1fr; column-gap: 16px; align-items: center; padding: 18px 20px }
  .card-top { grid-row: span 2; margin-bottom: 0 }
  .card-title { font-size: 16px; margin-bottom: 0; align-self: end }
  .card-desc { font-size: 14px; line-height: 1.45; align-self: start }
  #how .coin, #how .avatar { width: 32px; height: 32px } .avatar-pair .avatar + .avatar { margin-left: -12px }
  .coin-stack { width: 36px; height: 30px } .coin-disc { left: 0; width: 34px; height: 12px }
  .coin-disc:nth-child(2) { bottom: 8px } .coin-disc:nth-child(3) { bottom: 16px }
  /* All seven chips flow into one wrapping row. display:contents promotes only
   * an element's OWN children, which is why 33-04 made the chips direct
   * children of .tc-card with no intermediate wrapper (W7). */
  .tc-grid { display: flex; flex-wrap: wrap; gap: 8px }
  .tc-card { display: contents } .tc-label { display: none } .tc-grid .chip { font-size: 15px; padding: 9px 15px }
  .fp-list { gap: 10px } .fp-card { gap: 12px; border-radius: 16px; padding: 16px 18px }
  .fp-title { font-size: 15px; margin-bottom: 3px } .fp-desc { font-size: 13.5px; line-height: 1.45 }
  .rank-card { padding: 18px } .rank-head { margin-bottom: 10px } .rank-head span { font-size: 11px }
  .rank-row { gap: 12px; padding: 8px 6px } .rank-num { font-size: 14px; width: 18px }
  .rank-row .avatar { width: 28px; height: 28px; font-size: 12px }
  .rank-name { font-size: 14px } .rank-elo { font-size: 15px }
  .free-coins { padding: 36px 20px 0 }
  .free-coins .card { align-items: flex-start; gap: 12px; border-radius: 16px; padding: 14px 16px }
  .free-coins .coin { width: 22px; height: 22px; margin-top: 2px } .fc-text { font-size: 13px; line-height: 1.5 }
  .final-cta { padding: 72px 20px } .cta-title { font-size: 34px; line-height: 1.08; margin-bottom: 10px }
  .cta-sub { font-size: 15px; margin-bottom: 26px } .site-footer { padding: 24px 20px 30px }
  /* notes/site-css.md#footer-mobile-stacking — display:block keeps .copyright off the wordmark. */
  .footer-top { display: block; margin-bottom: 14px } .copyright { display: block; margin-top: 5px } .legal, .trademarks { font-size: 11px }
  .footer-links { display: flex; flex-direction: column; gap: 0; margin-left: 0 }
}

/* <=400px: the mockup can no longer assume 302px of room (33-CONTEXT §Edge
 * cases). Badges are already full-width stacked and the chips already wrap.
 * Only the FRAME shrinks. .phone is now the 390px device screen inside a scaled
 * frame, and capping its width would break the board's 1:1 aspect ratio. */
@media (max-width: 400px) {
  .phone-frame { max-width: 100% }
}

/* <=340px: the design's own answer at 320 — the headline drops to 34px. */
@media (max-width: 340px) {
  .hero-title { font-size: 34px }
}

/* Reduced motion · notes/site-css.md#reduced-motion — MUST stay the last rule in the file. */
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important } }
