/* ==========================================================================
   Kings Hands Boxing Club — wireframe stylesheet
   Shared by all six pages: index · coach · classes · youth · gym · come-in

   SURFACE PASS — done 2026-07-26, calibrated on Brandon's monitor (R006).
   ✅ Multi-value section blacks: the --ink-0…--ink-3 scale below.
   ✅ Canvas texture: ambientCG Fabric030 (CC0), high-passed. See the CANVAS
      TEXTURE block for why the tile is filtered and why `screen`, not `overlay`.
   ========================================================================== */

:root{
  /* SURFACE SCALE — "not one black, several." Five steps, each a few values
     apart, so the page separates without a single divider line. Structure you
     perceive but can't point at. Steps are deliberately tight: at this range the
     eye reads depth, not stripes. */
  --ink-0:#050404;   /* deepest — footer, hero + closing CTA scrims */
  --ink:#0a0908;     /* base page */
  --ink-1:#0e0c0b;   /* raised band — alternating sections */
  --ink-2:#141210;   /* panels: cards, tiers, voices, the proof strip */
  --ink-3:#1a1715;   /* elevated — hover states */
  --paper:#f2efe9;
  --muted:#9b948c;
  /* TEXT LADDER — added 2026-07-26. Surfaces had --ink-0…--ink-3 from the start;
     text never got the same treatment, so its greys were picked one at a time
     (#ddd8d0 here, #cdc7bf there) and the hierarchy came out accidental. Four
     deliberate steps now, same idea as the ink scale: how close a thing sits to
     the fixture. Display is IN the light; meta is furthest back in shadow. */
  --t-0:#f4f1eb;   /* display + emphasis — full light */
  --t-1:#dcd6cd;   /* lede, the bridge from headline to body */
  --t-2:#c3bdb4;   /* body copy — a real step back, not a nudge */
  --t-3:#9b948c;   /* meta: cites, captions, notes (same as --muted) */
  --blood:#c8102e;
  --blood-lo:#8f0b20;
  --gold:#c9a227;
  --edge:rgba(242,239,233,.10);
  /* display: heavy condensed grotesque, matches the flyer's energy */
  --hdr:"Anton",Impact,"Haettenschweiler","Arial Narrow",sans-serif;
  /* labels, eyebrows, buttons, nav — condensed with real weights */
  --cond:"Barlow Condensed","Arial Narrow",system-ui,sans-serif;
  --body:"Inter",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{background:var(--ink);color:var(--paper);font-family:var(--body);line-height:1.65;
  -webkit-font-smoothing:antialiased}

/* THE PAGE DOES NOT SLIDE SIDEWAYS.
   Several things on this site deliberately hang past their box — the poster's
   tape strips, the hex field's light spill, the pasted photos' tilt. On a
   desktop that costs nothing. On a phone it makes the ENTIRE page pannable:
   you swipe and the site slides off-centre into dead black, then sits there.
   `clip`, NOT `hidden`: `hidden` makes the element a scroll container, which
   kills `position:sticky` on the nav — and this nav has already cost one iOS
   bug. `clip` contains the overflow without creating a scroller, so the nav
   stays stuck and there is nothing to swipe into. Set on both html and body
   because either one alone still leaves the other able to pan. */
html,body{overflow-x:clip}

/* Alternate the section bands. No HTML changes and no divider lines — the page
   just breathes. Sections that set their own surface (.youth, .close) opt out. */
section:nth-of-type(even):not(.youth):not(.close){background:var(--ink-1)}

/* ---------- CANVAS TEXTURE ----------
   Real photographic canvas weave (ambientCG Fabric030, CC0), desaturated to the
   weave alone and tiled at 512px. A fixed overlay rather than a body background,
   so every surface — base, bands, cards, scrims — gets the same tooth instead of
   only the ones that don't paint over it.
   Flat #000 is the cheapest surface on the web; this is what gives it material.
   On a boxing gym the referent is real: canvas, leather, wraps.
   TILE IS HIGH-PASSED, which is why --tooth looks high. The raw diffuse map has
   a mid-grey average; on `screen` that lifts the ENTIRE page toward grey and
   kills the red (tried it — the page went muddy). So the tile is filtered to
   keep only where the weave is brighter than its local average: flats go to
   black, thread highlights stay bright, mean drops to ~16/255. `screen` then
   lights the threads and leaves the field alone.

   BLEND NOTE: `overlay` was the original mistake. Overlay pivots around
   mid-grey, so against #0a0908 it barely moves the pixels — at 3% it was
   effectively a no-op that looked like a plain black page.

   Tune --tooth and nothing else. */
:root{--tooth:0}   /* was .20 — the canvas weave is redundant now that the
                      wall is a real photographed surface. Kept, not deleted:
                      set it back above 0 if the wall ever comes off. */
body::after{
  content:"";position:fixed;inset:0;z-index:9999;pointer-events:none;
  background-image:url("assets/canvas-tile.jpg");
  background-size:300px 300px;
  opacity:var(--tooth);
  mix-blend-mode:screen}
img{max-width:100%;display:block}
/* `viewport-fit=cover` (see nav) hands us the whole screen, INCLUDING the parts
   the hardware eats — the notch and rounded corners in landscape, the home
   indicator at the bottom. That is the deal it makes: the nav gets to cover the
   status-bar strip, and in exchange every other edge has to keep itself clear.
   `max()` means the inset only ever widens the existing margin, never narrows
   it, and it is 0 on hardware with no cutouts. */
.wrap{width:min(1180px,92vw);margin-inline:auto;
  padding-left:max(0px, env(safe-area-inset-left));
  padding-right:max(0px, env(safe-area-inset-right))}

/* Anton ships a single weight — never ask for bold or the browser fakes it.
   It's genuinely condensed, so the old scaleX() squeeze is gone too. */
h1,h2,h3,.hdr{font-family:var(--hdr);font-weight:400;text-transform:uppercase;text-wrap:balance;
  letter-spacing:.005em;line-height:.9}
h1{letter-spacing:-.005em}

/* every small-caps label runs Barlow Condensed at 600 */
.kick,.eyebrow,.btn,nav .lnk a,.proof div,.card .who,.pill,.spots,.tier .name,
.tier .per,.tier .when,.voice .who,footer h4,.band .kingsway,.motto .by,
blockquote cite,.ph span,.more,.fact b,.hours dt{font-weight:600}

/* ---------- placeholder blocks: each names the shot we need ---------- */
/* Flat panel, not a hatch. These are scaffolding — when every photo is missing
   they cover most of the page, and a loud diagonal pattern becomes the loudest
   thing on the screen. Quiet frame, readable caption, gets out of the way. */
.ph{position:relative;background:#131110;
  border:1px solid rgba(201,162,39,.22);display:grid;place-items:center;
  text-align:center;padding:1.5rem;min-height:200px;overflow:hidden}
.ph span{font-family:var(--cond);font-size:.78rem;letter-spacing:.16em;color:var(--gold);
  text-transform:uppercase;max-width:26ch;line-height:1.5}
.ph:not(.has-photo)::after{content:"PHOTO";position:absolute;top:.5rem;left:.6rem;font:700 .6rem/1 var(--cond);
  letter-spacing:.2em;color:rgba(201,162,39,.5)}


/* ==========================================================================
   THE ROOM — the site is built out of Ronnie's actual gym.
   Black corrugated metal walls · a red LED strip where wall meets ceiling ·
   hexagonal fixtures throwing light down · everything else in shadow.

   The rule: translate the room's LOGIC, not its appearance. No fake 3D room,
   no hexagons as decoration. Same materials, same light, same rhythm — a
   member should feel at home without being able to say why.

   Two things kept separate on purpose, because a photograph fuses them and
   that's what made the earlier canvas texture feel dead:
     · the MATERIAL is a real scanned texture (ambientCG CorrugatedSteel005, CC0)
     · the LIGHT is built in CSS, where it stays sharp and can follow the layout
   ========================================================================== */
:root{
  --led:#ff2a3d;          /* the LED itself — hotter than --blood, it's a light */
  --led-glow:200,16,46;   /* rgb for the bloom */
  --wall-op:.34;          /* ribbed metal strength */
  --rib-op:.42;           /* strength of the drawn rib profile */
  --wall-rib:820px;      /* rib pitch. SMALLER = tighter ribs, but below ~900px
                             they stop reading as steel and start reading as
                             scanlines. This is the knob that matters. */
}

/* --- the wall: vertical ribbing, barely there --- */
.wall-surface{position:relative;isolation:isolate}
/* Layer 1 — the WEATHERING. Real scan, irregular, no two feet of it alike. */
.wall-surface::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background-image:url("assets/wall-steel.jpg");
  background-size:var(--wall-rib) auto;
  background-repeat:repeat;
  opacity:var(--wall-op);
  mix-blend-mode:screen}

/* Layer 2 RETIRED 2026-07-26 — the ribs used to be drawn with a
   repeating-linear-gradient because my crushed scan had lost them. Replaced by a
   REAL seamless photograph of corrugated metal (Wikimedia Commons, CC0), graded
   to black. The rib shading in it is photographed light on photographed metal,
   which is what the drawn version could only approximate. Nothing here is
   generated by us. */
.wall-surface > *{position:relative;z-index:1}

/* --- the LED strip: where wall meets ceiling. This replaces dead 1px borders.
       Same job in the room and on the page — it marks the edge between two
       surfaces, and it glows. --- */
.led{position:relative;height:0}
.led::before{                       /* the strip */
  content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:linear-gradient(90deg,
    rgba(var(--led-glow),0) 0%, var(--led) 12%, #ff5a68 50%, var(--led) 88%,
    rgba(var(--led-glow),0) 100%);
  box-shadow:0 0 9px rgba(var(--led-glow),.75)}   /* the tube's own halo */
.led::after{                        /* the bloom it throws */
  content:"";position:absolute;left:0;right:0;top:-26px;height:70px;
  pointer-events:none;
  background:linear-gradient(180deg,
    rgba(var(--led-glow),0) 0%,
    rgba(var(--led-glow),.29) 38%,
    rgba(var(--led-glow),.155) 55%,
    rgba(var(--led-glow),0) 100%);
  filter:blur(8px)}

/* --- the hex fixture: a light source, not a shape. Only the bloom survives. --- */
.hexlight{position:absolute;pointer-events:none;z-index:0;
  width:min(760px,90vw);aspect-ratio:1.15/1;
  left:50%;translate:-50% 0;top:-14%;
  background:
    radial-gradient(ellipse 46% 34% at 50% 42%, rgba(255,252,245,.16), transparent 70%),
    radial-gradient(ellipse 62% 46% at 50% 44%, rgba(255,250,240,.07), transparent 72%);
  filter:blur(24px)}

/* --- logo mounted on the wall, the way signage actually hangs --- */
.wall-logo{position:absolute;z-index:0;pointer-events:none;
  width:min(600px,66vw);right:4%;top:7%;opacity:.30;
  filter:grayscale(.15) contrast(1.06) drop-shadow(0 0 34px rgba(0,0,0,.8));
  mix-blend-mode:screen}
@media(max-width:860px){
  .wall-logo{width:78vw;right:auto;left:50%;translate:-50% 0;top:6%;opacity:.22}
}

@media(prefers-reduced-motion:reduce){.hexlight{filter:blur(20px)}}

/* ---------- nav ----------
   Six pages means mobile needs real navigation, not just the CTA. Below
   900px the links drop to their own scrollable row under the logo; above it
   they sit inline. No JS, nothing to open. */
/* 2026-07-27: was rgba(10,9,8,.92) + backdrop-filter:blur(10px) — a glass-nav
   look, but iOS Safari has a known bug where `backdrop-filter` on a
   `position:sticky` element lets scrolled content bleed through into the
   notch/status-bar strip above it during scroll (screenshot: countdown-band
   digits ghosting above the nav). Solid background removes the whole bug
   class — nothing translucent left for content to show through. */
/* THE STRIP ABOVE THE NAV — the bug that got "fixed" twice before this.
   Symptom: on an iPhone you scroll and a live band of page content runs along
   the very top, above the nav, behind the clock and battery. It is not a ghost
   and not a repaint artifact — it is the real page, scrolling, in a gap.
   An earlier pass blamed `backdrop-filter` and made the nav opaque. That was
   the wrong cause, which is why it came back: the nav was already solid black
   in the screenshot that reported it.
   The actual mechanism: iOS Safari (bottom address bar) collapses its top UI
   as you scroll, and the page's visible area grows UPWARD into the status-bar
   strip — but the LAYOUT viewport, which `top:0` is measured against, does not
   grow with it. So the nav faithfully pins to a top edge that is no longer the
   top of what you can see, and the page shows through above it.
   `viewport-fit=cover` (in every page's viewport meta) makes the layout
   viewport cover the whole screen, so `top:0` is the real top again, and the
   safe-area padding below puts the nav's OWN background in the strip while
   keeping the logo and buttons clear of the clock. env() is 0 on hardware
   without an inset, so nothing changes anywhere else. */
nav{position:sticky;top:0;z-index:50;background:var(--ink);
  padding-top:env(safe-area-inset-top);
  border-bottom:1px solid var(--edge)}

/* THE SHIELD — what actually closes the strip.
   `viewport-fit=cover` + `env(safe-area-inset-top)` above is correct and worth
   keeping, but it CANNOT fix this on its own, which is why the first attempt
   changed nothing: when Safari's top toolbar hides mid-scroll, the web view
   grows upward under the status bar, but the layout viewport and every env()
   value stay at their old, smaller box. So the page paints into the new strip
   while `top:0` and `env()` still describe a top edge that is now ~60px down.
   Anything measured in env() is aiming at the wrong box by definition.
   So the nav stops measuring and just carries a slab of its own background
   upward. It is part of the nav's own paint, so it moves with the nav no
   matter why the gap opened — toolbar collapse, momentum-scroll lag, or
   rubber-band overscroll. 200px is far more than any status bar needs; the
   excess only ever sits above the document, where the page is black anyway. */
nav::before{content:"";position:absolute;left:0;right:0;bottom:100%;
  height:200px;background:var(--ink);pointer-events:none}
nav .wrap{display:flex;flex-wrap:wrap;align-items:center;gap:.55rem 1.25rem;padding:.65rem 0}
nav img{height:46px;width:auto}
/* THE CREST IS MOUNTED SIGNAGE, NOT A FAVICON.
   Ronnie's logo is a real tattoo-flash badge — banners, skull, the whole
   figure. At 46px it was a smudge, and the first fix (pairing it with a typed
   wordmark) was solving the wrong problem: it worked AROUND the logo instead
   of with it. THE ROOM's own logic already had the answer — the hero hangs
   this crest on the wall like the signage it is, so the nav does the same, at
   a size where it actually reads.

   It OVERHANGS the nav's bottom edge and casts a shadow onto the page below,
   so it sits proud of the steel like a mounted badge rather than sitting flat
   inside a bar. The nav must never get overflow:hidden or the overhang is
   clipped — that is the one thing that breaks this. */
nav .brand{display:block;text-decoration:none;position:relative;z-index:2;
  /* the overhang comes out of the nav's own box so the bar does not grow */
  margin-bottom:-18px}
nav .brand img{height:74px;display:block;
  filter:drop-shadow(0 6px 10px rgba(0,0,0,.8)) drop-shadow(0 1px 0 rgba(255,255,255,.06))}
/* it lifts off the wall on hover, the same language as every other clickable */
nav .brand{transition:translate .22s cubic-bezier(.2,.7,.3,1)}
nav .brand:hover{translate:0 -2px}
@media(min-width:900px){
  nav .brand{margin-bottom:-26px}
  nav .brand img{height:92px}
}
nav .sp{flex:1}
nav a{color:var(--paper);text-decoration:none;font-size:.9rem}
nav .lnk{order:3;flex-basis:100%;display:flex;gap:1.15rem;overflow-x:auto;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
nav .lnk::-webkit-scrollbar{display:none}
nav .lnk a{color:var(--muted);font-family:var(--cond);letter-spacing:.1em;font-size:.82rem;
  text-transform:uppercase;white-space:nowrap;padding-bottom:.2rem;
  border-bottom:2px solid transparent}
nav .lnk a:hover{color:var(--paper)}
nav .lnk a[aria-current="page"]{color:var(--paper);border-bottom-color:var(--blood)}
@media(min-width:900px){
  nav .lnk{order:0;flex-basis:auto;overflow:visible;gap:1.5rem}
}

/* ---------- mobile menu button ----------
   2026-07-27: the scrollable pill row was a deliberate no-JS call, but it cut
   words off mid-scroll ("THE GYM" → "THE") with nothing telling you it scrolls
   at all — flagged in a mobile audit as reading like broken layout, not a
   nav. Swapped for a real disclosure: hidden hamburger button, closed panel
   by default, JS toggles one class. Same "no library" bar as the LED reveal
   and the countdown — see the script at the bottom of the page. */
.menu-btn{display:none;order:2;width:38px;height:32px;position:relative;
  border:0;background:none;cursor:pointer;padding:0}
/* THE BARS CONVERGE, THEN CROSS — and uncross before they separate.
   Doing both at once (one `transform:translateY() rotate()`) is what makes most
   hamburger-to-X toggles read as a cheap flip: the bars swing through each
   other diagonally instead of behaving like two objects. Split into the
   independent `translate` and `rotate` properties, they can be SEQUENCED, and
   the sequence reverses on the way back — which is the whole trick:
     opening  → slide to the middle, THEN rotate into the X
     closing  → rotate back to flat, THEN slide apart
   The delay lives on whichever state the property is animating TOWARD, which
   is how one pair of rules gets two different orders for free. */
.menu-btn span{position:absolute;left:0;right:0;height:2px;background:var(--paper);
  translate:0 0;rotate:0deg;
  transition:translate .19s cubic-bezier(.3,.7,.4,1) .17s,
             rotate .19s cubic-bezier(.3,.7,.4,1),
             opacity .16s ease .17s}
.menu-btn span:nth-child(1){top:6px}
.menu-btn span:nth-child(2){top:15px}
.menu-btn span:nth-child(3){top:24px}
.menu-btn[aria-expanded="true"] span{
  transition:translate .19s cubic-bezier(.3,.7,.4,1),
             rotate .19s cubic-bezier(.3,.7,.4,1) .17s,
             opacity .16s ease}
.menu-btn[aria-expanded="true"] span:nth-child(1){translate:0 9px;rotate:45deg}
.menu-btn[aria-expanded="true"] span:nth-child(2){opacity:0}
.menu-btn[aria-expanded="true"] span:nth-child(3){translate:0 -9px;rotate:-45deg}
@media(prefers-reduced-motion:reduce){
  .menu-btn span,.menu-btn[aria-expanded="true"] span{transition:none}
}

@media(max-width:899px){
  .menu-btn{display:block}

  /* THE PANEL IS A SHUTTER, NOT A HEIGHT.
     It used to animate `max-height:0 → 70vh`. The menu's real content is about
     320px, so opening hit full height around 45% of the way through and spent
     the rest of the transition growing an invisible cap — while CLOSING spent
     its first half doing nothing visible at all and then dropped. That
     asymmetry, plus a .2s opacity fade running underneath it, is exactly why
     it opened with a gesture and closed by vanishing.
     `max-height` was the wrong property twice over: it can only animate to a
     GUESS at the content height, and the guess has to be too big to be safe.
     Since the panel is absolutely positioned it owes the layout nothing, so
     nothing needs to animate its height — `clip-path:inset()` wipes it down
     over the real content, whatever that content turns out to be, and reverses
     honestly because both ends of the animation are true. It reads as the
     shutter it should have been: the panel comes DOWN out of the nav, and goes
     back UP into it. */
  nav .lnk{position:absolute;top:100%;left:0;right:0;order:0;flex-basis:auto;
    flex-direction:column;gap:0;
    background:var(--ink);border-top:2px solid var(--edge);
    max-height:70vh;overflow-y:auto;
    clip-path:inset(0 0 100% 0);
    translate:0 -10px;
    opacity:0;visibility:hidden;pointer-events:none;
    transition:clip-path .32s cubic-bezier(.4,0,.6,1),
               translate .32s cubic-bezier(.4,0,.6,1),
               opacity .18s ease .06s,
               border-top-color .2s ease,
               visibility 0s linear .32s}
  nav .lnk.open{clip-path:inset(0 0 0 0);translate:0 0;
    opacity:1;visibility:visible;pointer-events:auto;
    border-top-color:var(--blood);
    transition:clip-path .38s cubic-bezier(.16,.84,.44,1),
               translate .38s cubic-bezier(.16,.84,.44,1),
               opacity .16s ease,
               border-top-color .18s ease,
               visibility 0s}

  nav .lnk a{padding:1rem min(1.25rem,4vw);border-bottom:1px solid var(--edge);
    white-space:normal;
    /* each link arrives just behind the shutter edge that reveals it */
    opacity:0;translate:0 -8px;
    transition:opacity .16s ease, translate .16s ease, color .2s ease}
  nav .lnk.open a{opacity:1;translate:0 0;
    transition:opacity .26s ease, translate .3s cubic-bezier(.16,.84,.44,1),
               color .2s ease}
  /* THE STAGGER IS ONE-WAY ON PURPOSE. Opening, the links light in sequence —
     the same "fixture warming" beat the .led strips use, which is this site's
     one motion idea. Closing, every delay is zero (the rule above carries
     none), so the panel retracts as a single object instead of dribbling six
     links away one at a time. Warm up slow, shut off at once — that is how the
     lights in the room actually behave. */
  nav .lnk.open a:nth-child(1){transition-delay:.05s}
  nav .lnk.open a:nth-child(2){transition-delay:.09s}
  nav .lnk.open a:nth-child(3){transition-delay:.13s}
  nav .lnk.open a:nth-child(4){transition-delay:.17s}
  nav .lnk.open a:nth-child(5){transition-delay:.21s}
  nav .lnk.open a:nth-child(6){transition-delay:.25s}

  /* NOTE on the panel's lit top border, set in the two rules above:
     it is the PANEL's border and not a glow on the nav, because the panel is
     opaque and sits flush at `top:100%` — a downward `box-shadow` on the nav is
     painted over completely, which is what the first version of this was.
     On the border it also earns its timing for free: the wipe clips from the
     BOTTOM, so the top edge is at full height from frame one. The red line
     appears first and the menu unrolls beneath it, which is the right causal
     order for a fixture and its light. No halo — `clip-path` crops
     `box-shadow` (the lesson .btn and .hive both learned), so a glow here
     would be sliced off at the panel's own edge. */

  @media(prefers-reduced-motion:reduce){
    nav .lnk,nav .lnk.open,nav .lnk a,nav .lnk.open a{transition:none}
    nav .lnk.open a{transition-delay:0s}
  }
}
/* ---------- BUTTONS — made of the room ----------
   A rectangle that lights up is still a rectangle. These earn their shapes.

   .btn        CUT-CORNER STEEL PLATE. Fabricated metal has chamfered corners;
               a sharp 90 degrees is the tell that something was stamped rather
               than cut. Clipping the corners makes it read as a PART bolted to
               the wall instead of a UI element — same material logic as the
               surface behind it.
               NOTE: clip-path crops box-shadow, so the glow is done with
               filter:drop-shadow(), which follows the clip.

   .ticket     Used ONCE per page, only for "your first class is free", because
               that is literally an admission ticket. Notches and a perforated
               stub are punched with a mask. It is also the only warm, light
               object on a black page — the eye goes straight to it, which is
               the whole point of the one CTA that matters.
   ---------------------------------------------------------------------------- */

.btn{position:relative;display:inline-block;border:0;cursor:pointer;
  font-family:var(--cond);font-weight:600;text-transform:uppercase;
  letter-spacing:.12em;font-size:.92rem;line-height:1;padding:1.05rem 1.7rem;
  color:#fff;text-decoration:none;isolation:isolate;
  --chamfer:14px;                  /* the one cut size — same as every plate */
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  background:linear-gradient(180deg,#e0182f 0%,var(--blood) 48%,#9d0c22 100%);
  filter:drop-shadow(0 5px 16px rgba(var(--led-glow),.55));
  transition:filter .22s ease, transform .18s ease, background .22s ease}
.btn::after{                       /* the cut edges catch light */
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(135deg,rgba(255,255,255,.34) 0 var(--chamfer), transparent calc(var(--chamfer) + 1px)),
    linear-gradient(315deg,rgba(0,0,0,.45) 0 var(--chamfer), transparent calc(var(--chamfer) + 1px))}
/* The red button can't wear a RED lit edge — red on red is invisible, which is
   why its hover read as nothing happening. Its filament is white-hot instead:
   the same dim → lit gesture as the cards, in the only colour that shows here. */
.btn::before{
  content:"";position:absolute;left:var(--chamfer);right:0;top:0;height:2px;z-index:2;
  background:#fff;opacity:.35;
  transition:opacity .3s ease, height .2s ease, box-shadow .3s ease}
.btn:hover::before,.btn:focus-visible::before,.btn.tap-lit::before{
  height:3px;opacity:1;
  box-shadow:0 0 10px 1px rgba(255,255,255,.9),
             0 0 26px 4px rgba(255,215,215,.55)}
/* Lifts like a .card — but a card only ever sits on black, and these buttons also
   sit on PHOTOS. A red glow and a 3px nudge vanish over a bright photo, so the
   hover also grows the plate and drops a hard BLACK shadow, which reads on any
   background. The face runs hotter too: a real colour change, not a brightness
   filter, because a filter on saturated red barely moves. */
.btn:hover,.btn:focus-visible,.btn.tap-lit{transform:translateY(-4px) scale(1.035);
  background:linear-gradient(180deg,#ff3448 0%,#e0182f 48%,#b21027 100%);
  filter:drop-shadow(0 14px 22px rgba(0,0,0,.85))
         drop-shadow(0 0 26px rgba(var(--led-glow),.9))}
.btn:active{transform:translateY(0)}
.btn:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

.btn.ghost{color:var(--gold);
  background:linear-gradient(180deg,#232019 0%,#16130f 52%,#0c0a08 100%);
  filter:drop-shadow(0 3px 10px rgba(0,0,0,.6));
  box-shadow:none;
  outline:1px solid rgba(201,162,39,.40);outline-offset:-1px}
.btn.ghost::after{
  background:
    linear-gradient(135deg,rgba(255,255,255,.16) 0 var(--chamfer), transparent calc(var(--chamfer) + 1px)),
    linear-gradient(315deg,rgba(0,0,0,.55) 0 var(--chamfer), transparent calc(var(--chamfer) + 1px))}
/* the dark button is a dark plate, so it gets the plate's lit top edge —
   in gold, because that is already this button's accent. */
.btn.ghost::before{
  content:"";position:absolute;left:var(--chamfer);right:0;top:0;height:2px;z-index:2;
  background:var(--gold);opacity:.30;
  transition:opacity .3s ease, box-shadow .3s ease}
.btn.ghost:hover::before,.btn.ghost:focus-visible::before,.btn.ghost.tap-lit::before{
  opacity:1;box-shadow:0 0 10px 1px rgba(201,162,39,.8),
                       0 0 26px 3px rgba(201,162,39,.4)}
.btn.ghost:hover,.btn.ghost.tap-lit{color:#f2dd93;
  outline-color:rgba(201,162,39,.95);
  filter:drop-shadow(0 0 18px rgba(201,162,39,.45))}

/* --- THE METAL BUTTONS: bronze / silver / gold ---------------------------
   Before this, the three tier buttons actively fought their own labels — the
   Bronze plate was gold-tinted, the Gold plate was gold-tinted, and SILVER was
   RED. Colour carried no information, so a reader had to read all three
   buttons plus map the hex fixture back onto the cards to work out what cost
   what. Now each button IS its metal and carries its own price, so the row can
   be scanned rather than read, and the hexagons become confirmation instead of
   a lookup table.

   These are real metal faces, not dark plates with a metal outline — a tinted
   edge on black is too quiet to be found at a glance, which was the whole
   complaint. They keep the chamfer, the lit top filament and the lift-on-hover
   from .btn so they still read as this site's buttons.

   Text colour flips per metal: silver and gold faces are light, so their labels
   are near-black; bronze is dark enough to carry white. Bronze is deliberately
   deeper than a "true" #cd7f32 — that shade puts white text at ~3:1, under the
   contrast floor. --focus is white on all three because the usual gold focus
   ring is invisible against gold. */
/* WHY THESE LOOK LIKE METAL AND NOT LIKE COLOURED BUTTONS: a smooth top-to-
   bottom ramp reads as plastic no matter how well the colour is picked — that
   was the "bland" version. Three things do the actual work here, all cheap:

     1. A SPECULAR HORIZON. Real metal reflects the room in two halves — bright
        sky above, dark floor below — meeting at a near-hard edge. Each metal
        ramp has a 3% step at ~44–47% instead of a smooth blend. This single
        detail is most of the effect.
     2. BRUSH LINES. A repeating-linear-gradient a few degrees off-axis, at very
        low alpha. Anisotropic scatter is what separates machined metal from
        painted metal. Kept under 6% or it turns into visible corduroy.
     3. A RAKING SHEEN that travels on hover. Parked off the right edge at rest
        and swept across the face on hover, so the plate reads as catching the
        gym's light rather than changing colour. This is the same gesture as the
        LED filament on the cards, in reflection instead of glow.

   The inset hairlines are a bevel: light on the top edge, shade on the bottom,
   which is what gives the plate thickness. They're clipped by the chamfer, so
   they stop at the cut corners the way a real bevel would.

   All three keep white text on bronze / near-black on silver+gold at 4.5:1 or
   better, INCLUDING against the lightest stop of each ramp — that constraint is
   why bronze starts at #a06a30 rather than a truer, prettier #cd7f32.

   min-width, not content width: left to size themselves the three plates come
   out ragged ("BRONZE $45 / MONTH" is visibly wider than "GOLD $75 / MONTH"),
   which makes them read as three unrelated buttons instead of one set of three
   choices. Equal plates let the eye compare them at a glance. max-width keeps
   that from overflowing the card on narrow phones. */
/* .btn.btn--metal, not .btn--metal: the base .btn:hover rule sets the `background`
   SHORTHAND (red gradient), which at 0-2-0 would out-specify a plain .btn--metal
   background-image and repaint these plates red on hover. Doubling the class ties
   the specificity and lets source order decide — this rule wins because it comes
   after. Same reason the hover rule below is .btn.btn--metal:hover. */
.btn.btn--metal{display:inline-flex;flex-direction:column;align-items:center;
  justify-content:center;gap:.32rem;padding:.95rem 1.5rem;text-align:center;
  min-width:11.5rem;max-width:100%;outline:none;
  background-image:
    /* 3 — the raking sheen, parked off-frame right */
    linear-gradient(100deg,transparent 34%,rgba(255,255,255,.55) 47%,
                    rgba(255,255,255,.14) 54%,transparent 66%),
    /* 2 — brush lines */
    repeating-linear-gradient(96deg,rgba(255,255,255,.05) 0 1px,
                              rgba(0,0,0,.035) 1px 3px),
    /* 1 — the metal itself, set per-variant */
    var(--metal);
  background-size:260% 100%, auto, auto;
  background-position:190% 0, 0 0, 0 0;
  background-repeat:no-repeat, repeat, no-repeat;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5),
             inset 0 -1px 0 rgba(0,0,0,.4);
  /* 1.5s, and a nearly-symmetric curve rather than an ease-out. At .7s with
     ease-out the sheen dumped most of its travel in the first third and read as
     a flash going off, not as light raking across a surface. Real specular
     movement is roughly constant — the highlight crosses at the speed you move
     past it — so this stays close to linear with only soft ends. Slower than it
     sounds on paper: the sheen crosses 2.6x the plate's width. */
  transition:background-position 1.5s cubic-bezier(.4,.05,.45,.98),
             filter .22s ease, transform .18s ease}
.btn.btn--metal:hover,.btn.btn--metal:focus-visible,.btn.btn--metal.tap-lit{
  background-position:-90% 0, 0 0, 0 0}
.btn--metal .b-name{font-size:.98rem;letter-spacing:.18em;line-height:1;
  position:relative;z-index:1}
.btn--metal .b-price{font-size:.75rem;letter-spacing:.08em;line-height:1;
  opacity:.85;font-weight:500;position:relative;z-index:1}
/* the lit filament, in white so it reads on all three faces */
.btn--metal::before{background:#fff;opacity:.45}
.btn--metal:hover::before,.btn--metal:focus-visible::before,
.btn--metal.tap-lit::before{height:3px;opacity:1;
  box-shadow:0 0 10px 1px rgba(255,255,255,.9),0 0 26px 4px rgba(255,255,255,.4)}
.btn--metal:focus-visible{outline:2px solid #fff;outline-offset:3px}

.btn--bronze{color:#fff;
  --metal:linear-gradient(178deg,#a06a30 0%,#8a571f 44%,#79491a 47%,#5e3512 100%);
  text-shadow:0 1px 2px rgba(0,0,0,.45);
  filter:drop-shadow(0 4px 14px rgba(156,93,32,.45))}
.btn--bronze:hover,.btn--bronze:focus-visible,.btn--bronze.tap-lit{
  filter:drop-shadow(0 14px 22px rgba(0,0,0,.85))
         drop-shadow(0 0 26px rgba(192,122,51,.65))}

.btn--silver{color:#14120f;
  --metal:linear-gradient(178deg,#f2f5f8 0%,#d3dae1 44%,#b6bec6 47%,#8e969e 100%);
  filter:drop-shadow(0 4px 14px rgba(195,202,209,.35))}
.btn--silver:hover,.btn--silver:focus-visible,.btn--silver.tap-lit{
  filter:drop-shadow(0 14px 22px rgba(0,0,0,.85))
         drop-shadow(0 0 28px rgba(214,220,226,.7))}

.btn--gold{color:#14120f;
  --metal:linear-gradient(178deg,#f0d885 0%,#dcb943 44%,var(--gold) 47%,#9c7a17 100%);
  filter:drop-shadow(0 4px 14px rgba(201,162,39,.45))}
.btn--gold:hover,.btn--gold:focus-visible,.btn--gold.tap-lit{
  filter:drop-shadow(0 14px 22px rgba(0,0,0,.85))
         drop-shadow(0 0 28px rgba(201,162,39,.75))}

/* Reduced-motion users still get the plate and the hover lift; only the
   travelling sheen is removed, since that's the part that actually moves. */
@media (prefers-reduced-motion:reduce){
  .btn.btn--metal{transition:filter .22s ease}
  .btn.btn--metal:hover,.btn.btn--metal:focus-visible,.btn.btn--metal.tap-lit{
    background-position:190% 0, 0 0, 0 0}
}

/* --- the ticket --- */
.ticket{--n:10px;
  position:relative;display:inline-flex;align-items:stretch;
  text-decoration:none;cursor:pointer;isolation:isolate;
  /* The paper is painted on the two HALVES, not on .ticket itself. That is what
     lets the tear below open a real hole you can see the page through — if the
     background lived here, a gap between the halves would just show more cream
     and read as nothing at all. */
  filter:drop-shadow(0 8px 22px rgba(0,0,0,.65));
  -webkit-mask:
     radial-gradient(circle at left  center, transparent var(--n), #000 calc(var(--n) + .5px)),
     radial-gradient(circle at right center, transparent var(--n), #000 calc(var(--n) + .5px));
  -webkit-mask-composite:source-in;
          mask:
     radial-gradient(circle at left  center, transparent var(--n), #000 calc(var(--n) + .5px)),
     radial-gradient(circle at right center, transparent var(--n), #000 calc(var(--n) + .5px));
          mask-composite:intersect;
  transition:filter .22s ease, transform .18s ease}
/* The ticket is PAPER, so it must not light up like an LED — a red halo behind a
   cream card is nearly invisible anyway, which is why its hover read as dead.
   Paper does two things instead: it catches more of the room's light (it visibly
   brightens) and it lifts off the wall, throwing a harder, longer shadow. */
.ticket:hover,.ticket.torn{transform:translateY(-4px) rotate(-.5deg);
  filter:brightness(1.10) contrast(1.03)
         drop-shadow(0 16px 26px rgba(0,0,0,.8))
         drop-shadow(0 2px 0 rgba(var(--led-glow),.35))}
.ticket:active{transform:translateY(0) rotate(0deg)}
.ticket:focus-visible{outline:2px solid var(--gold);outline-offset:4px}

/* ---- THE TEAR -------------------------------------------------------------
   Hovering starts to rip the stub off along the perforation.

   FOUNDATION — the two halves carry the paper, not .ticket. With the background
   on the parent, a gap in the middle just shows more cream and reads as nothing;
   split it and the gap is a real hole you see the page through.

   Both clip-paths list the SAME NUMBER OF POINTS at rest and torn — a polygon
   only interpolates into another polygon with a matching vertex count — so each
   straight edge is written as fourteen collinear points waiting to become a tear.

   What makes it read as torn rather than cut, in order of how much it matters:

   1. IT IS A WEDGE, NOT A SLOT. A real tear starts at one edge and travels. This
      one opens ~11px at the top and closes to nothing at the bottom, so the stub
      is still attached down there. A gap of even width everywhere is the single
      biggest tell that you are looking at a graphic. This also means the tear
      costs NO layout change — the halves never resize, the gap is carved out of
      their facing edges, so nothing on the page reflows.
   2. THE EDGE IS FIBROUS, NOT ZIGZAG. Fourteen irregular depths rather than a
      handful of even 45° notches. Paper tears along fibres; it does not pink.
   3. THE TWO SIDES DO NOT INTERLOCK. Each has its own rhythm of depths. Matched
      zigzags read as pinking shears — torn paper never lines back up.
   4. THE TORN EDGE SHOWS THE PAPER'S CORE. A hairline lighter rim along each
      torn edge, because the inside of paper is whiter than its printed face.
      Done with drop-shadow, which follows a clip-path where a border cannot.
   5. IT RESISTS, THEN GIVES. Paper does not tear linearly. The easing sits
      almost still for the first fifth, then rips.

   The stub never MOVES: the ticket's notch mask is anchored to its own box, so
   anything sliding sideways gets clipped. The wedge does the hinging instead. */
/* TICKET STOCK — 2026-07-27, revised same day. First pass used a CSS
   repeating-linear-gradient hatch to fake a print texture — called out
   (correctly) as a shortcut: every other material on this site is a real
   scanned/photographed surface (wall-steel.jpg, canvas-tile.jpg), so a
   drawn hatch was the one thing on the page not actually made of anything.
   Replaced with a real photograph: ambientCG Paper001 (CC0), the
   Displacement channel rather than Color — the fibrous grain map, not the
   near-flat diffuse — cropped to drop a scan-edge defect on the source's
   right border, contrast/brightness lifted slightly so the grain still
   reads at the small size a ticket renders at. `background-blend-mode:
   multiply` prints the grayscale grain onto the cream gradient the same way
   the wall's `screen` blend prints weathering onto steel — one real surface
   moving light through another, nothing drawn. */
.ticket .t-main{padding:1.05rem 1.5rem 1.05rem 1.7rem;
  font-family:var(--cond);font-weight:700;text-transform:uppercase;
  letter-spacing:.10em;font-size:1rem;line-height:1;color:#8f0b20;
  display:flex;align-items:center;
  background-image:url("assets/paper-grain.jpg"),
    linear-gradient(180deg,#f6f1e4 0%,#e6dcc6 100%);
  background-size:260px auto, cover;
  background-blend-mode:multiply;
  clip-path:polygon(0 0,
    100% 0, 100% 7%, 100% 15%, 100% 23%, 100% 31%, 100% 39%, 100% 47%,
    100% 55%, 100% 63%, 100% 71%, 100% 79%, 100% 87%, 100% 94%, 100% 100%,
    0 100%);
  transition:clip-path .55s cubic-bezier(.6,.02,.2,1), color .3s ease,
             filter .55s ease}
.ticket .t-stub{padding:1.05rem 1.15rem;display:flex;align-items:center;
  font-family:var(--cond);font-weight:600;text-transform:uppercase;
  letter-spacing:.16em;font-size:.7rem;line-height:1;color:#a8452f;
  background-image:url("assets/paper-grain.jpg"),
    linear-gradient(180deg,#f6f1e4 0%,#e6dcc6 100%);
  background-position:right 0 top 0, 0 0;
  background-size:260px auto, cover;
  background-blend-mode:multiply;
  clip-path:polygon(0 0,
    100% 0, 100% 100%,
    0 100%, 0 94%, 0 87%, 0 79%, 0 71%, 0 63%, 0 55%, 0 47%, 0 39%, 0 31%,
    0 23%, 0 15%, 0 7%);
  transition:clip-path .55s cubic-bezier(.6,.02,.2,1), color .3s ease,
             filter .55s ease}
/* The perforation itself stays 0 wide — the gap is carved, not spaced. Was a
   native `border-left:dashed`, which most browsers render as near-solid at
   2px (the dash/gap ratio isn't controllable), so it read as a faint stripe
   rather than the tear-along-here line real stubs print. Rebuilt as a crisp
   repeating-linear-gradient so the dash length and gap are exact, and dark
   enough on the cream to actually read as "tear here" before you even hover. */
.ticket .t-perf{width:2px;margin:.55rem 0;flex:none;
  background-image:repeating-linear-gradient(to bottom,
    rgba(143,11,32,.65) 0 5px, transparent 5px 10px);
  transition:opacity .45s ease}
.ticket:hover .t-perf,.ticket.torn .t-perf{opacity:0}
.ticket:hover .t-stub,.ticket.torn .t-stub{color:#8f0b20}

/* torn edge of the big half — deepest at the top, gone by the bottom */
.ticket:hover .t-main,.ticket.torn .t-main{
  filter:drop-shadow(1px 0 0 rgba(255,253,246,.55));
  clip-path:polygon(0 0,
    calc(100% - 6px) 0,   calc(100% - 4px) 7%,  calc(100% - 7px) 15%,
    calc(100% - 5px) 23%, calc(100% - 6px) 31%, calc(100% - 3px) 39%,
    calc(100% - 4px) 47%, calc(100% - 2px) 55%, calc(100% - 3px) 63%,
    calc(100% - 1px) 71%, calc(100% - 1px) 79%, 100% 87%, 100% 94%, 100% 100%,
    0 100%)}
/* and the stub's edge, tearing on its own rhythm so the two never interlock */
.ticket:hover .t-stub,.ticket.torn .t-stub{
  filter:drop-shadow(-1px 0 0 rgba(255,253,246,.55));
  clip-path:polygon(5px 0,
    100% 0, 100% 100%,
    0 100%, 0 94%, 0 87%, 1px 79%, 2px 71%, 2px 63%, 4px 55%, 2px 47%,
    5px 39%, 3px 31%, 6px 23%, 4px 15%, 7px 7%)}

@media(prefers-reduced-motion:reduce){
  .ticket .t-main,.ticket .t-stub,.ticket .t-perf{transition:none}
}
/* PHONES — no hover to start the tear, so `.torn` (set by touchstart, see the
   ticket-tap script) drives the identical animation instead of a dumbed-down
   substitute. The stub and perforation stay on-screen; the same rip that
   plays on desktop just now plays on finger-down rather than mouse-over. */
@media(max-width:520px){
  .ticket .t-main{padding:1rem 1.4rem;font-size:.94rem}
}

/* ---------- hero (home only) ---------- */
.hero{position:relative;min-height:88vh;display:grid;align-items:end}
.hero .ph{position:absolute;inset:0;min-height:0;border:0;place-items:start end;
  padding:5.5rem 1.4rem 0}
.hero .ph span{max-width:22ch;text-align:right;opacity:.75}
.hero{background:var(--ink-0)}
.hero .scrim{position:absolute;inset:0;z-index:0;
  background:linear-gradient(180deg,rgba(5,4,4,.30) 0%,rgba(5,4,4,.10) 30%,rgba(5,4,4,.92) 100%)}
/* the top padding is CLEARANCE FOR THE OVERHANGING CREST, not decoration.
   The hero is align-items:end, so this does nothing at tall viewports — it
   only bites on short/landscape ones, where the bottom-aligned content would
   otherwise ride up under the badge and get its kick line covered. The five
   inner pages need no equivalent: .phead already opens with 3.25rem minimum. */
.hero .wrap{position:relative;padding:2.5rem 0 5rem}
.hero h1{font-size:clamp(3.2rem,12vw,9rem)}
.hero h1 .r{color:var(--blood)}
.kick{font-family:var(--cond);letter-spacing:.28em;font-size:.8rem;color:var(--gold);
  text-transform:uppercase;margin-bottom:1.1rem}
.hero p.lede{font-size:clamp(1.05rem,2vw,1.3rem);max-width:46ch;margin:1.4rem 0 2rem;
  color:var(--t-1);text-wrap:pretty}
.cta-row{display:flex;flex-wrap:wrap;gap:1rem;align-items:center}
.cta-note{font-size:.85rem;color:var(--muted)}

/* ---------- page header (the five inner pages) ----------
   These were the ONE place on the site where THE ROOM stopped: no wall, no
   light, type floating on pure black. index.html was the only page carrying
   `wall-surface` at all. They are on the same wall as everything else now.

   NOT a copy of the hero, though — if every page opened at hero intensity the
   home page would stop being special, and it is carrying the first impression.
   This is the same wall seen DIMMER and CLOSER:

     · --wall-op halved. Present, not announced.
     · --wall-rib LARGER, not smaller. Standing nearer a wall makes its ribs
       look WIDER — and it dodges the documented trap in THE ROOM, where a
       tighter pitch stops reading as steel and starts reading as scanlines.
     · NO wall logo. That is the home page's signature.
     · The 1px bottom border is gone. Every other section boundary on this site
       is a `.led`, and these headers already have one directly beneath them —
       so the border was a dead rule sitting on top of a live one.

   2026-07-27: prototyped on coach.html alone, then approved and rolled to all
   five inner-page headers (classes, greater-purpose, gym, come-in, coach).

   2026-07-27 (mobile parity session): a stray comment-close used to sit at the
   end of the line above this paragraph. It ended the comment EARLY, dumping
   these two sentences into the stylesheet as raw CSS. The parser then read them
   as the start of a selector, ran on until the next open brace, and DISCARDED
   THE WHOLE `.phead` RULE — so all five inner-page headers shipped with
   padding:0 against a clamp(3.25rem,10vw,6rem) that never applied. Caught by a
   brace/comment balance check before commit, not by eye: the pages look
   plausible without it, just cramped. This is the exact trap already written up
   in ../DESIGN_CATALOGUE.md section 8 — it had quietly claimed a rule in the
   very file that documents it.
   DO NOT write a literal comment-close sequence inside a CSS comment when
   describing this bug. Doing so reintroduces it verbatim, which is what
   happened on the first attempt at writing this note. */
.phead{padding:clamp(3.25rem,10vw,6rem) 0 clamp(2.5rem,7vw,4rem);
  border-bottom:1px solid var(--edge)}
.phead.wall-surface{--wall-op:.17;--wall-rib:1180px;border-bottom:0}
.phead h1{font-size:clamp(2.6rem,9.5vw,6rem)}
.phead h1 .r{color:var(--blood)}
.phead p.lede{font-size:clamp(1.02rem,2vw,1.22rem);max-width:54ch;margin-top:1.3rem;
  color:var(--t-1);text-wrap:pretty}

/* ---------- motto ---------- */
.motto{padding:clamp(5rem,12vw,9rem) 0;text-align:center;border-block:1px solid var(--edge)}
.motto p{font-family:var(--hdr);font-size:clamp(1.6rem,5vw,3.4rem);line-height:1.08;
  max-width:20ch;margin-inline:auto;text-transform:uppercase}
.motto p em{color:var(--blood);font-style:normal}
.motto .by{margin-top:1.8rem;font-size:.82rem;letter-spacing:.18em;color:var(--muted);
  font-family:var(--cond);text-transform:uppercase}

/* ---------- generic section ---------- */
section{padding:clamp(4rem,9vw,7rem) 0}
.eyebrow{font-family:var(--cond);letter-spacing:.24em;font-size:.74rem;color:var(--gold);
  text-transform:uppercase;margin-bottom:1rem}
h2{font-size:clamp(2.1rem,6vw,4rem);margin-bottom:1.4rem}
h2 .r{color:var(--blood)}
.two{display:grid;gap:clamp(2rem,5vw,4rem);align-items:center}
@media(min-width:860px){.two{grid-template-columns:1fr 1fr}}
/* photo-first on desktop, prose-first on mobile — the photo shouldn't be the
   first thing between a reader and the copy on a 375px screen */
@media(max-width:859px){.two.flip .ph{order:2}}
.prose p{margin-bottom:1.1rem;color:var(--t-2);max-width:60ch;
  text-wrap:pretty}          /* no single-word last lines */
.prose p.big{font-size:1.18rem;color:var(--t-0);max-width:52ch}
blockquote{border-left:3px solid var(--blood);padding-left:1.3rem;margin:2rem 0;
  font-size:1.15rem;font-style:italic;color:var(--t-0);text-wrap:pretty}
blockquote cite{display:block;margin-top:.7rem;font-style:normal;font-size:.8rem;
  letter-spacing:.14em;color:var(--t-3);font-family:var(--cond);text-transform:uppercase}

/* cross-page link — the seam that makes six pages read as one site */
.more{display:inline-block;font-family:var(--cond);text-transform:uppercase;
  letter-spacing:.14em;font-size:.82rem;color:var(--gold);text-decoration:none;
  border-bottom:1px solid rgba(201,162,39,.45);padding-bottom:.25rem;margin-top:.6rem;
  transition:color .15s,border-color .15s}
.more:hover{color:var(--paper);border-color:var(--paper)}

/* ---------- proof strip ---------- */
.proof{background:var(--ink-2);border-block:1px solid var(--edge);padding:2.2rem 0}
/* The same steel as the hero, read as a wall further back: dimmer and a tighter
   rib pitch. Don't push --wall-rib much under 600px — see the note on the knob
   at :root, tighter than that and it shimmers as scanlines on a phone. */
.proof.wall-surface{--wall-op:.14;--wall-rib:600px}
.proof .wrap{display:flex;flex-wrap:wrap;gap:1rem 2.5rem;align-items:center;
  justify-content:center;text-align:center}
.proof div{font-family:var(--cond);text-transform:uppercase;letter-spacing:.1em;
  font-size:.82rem;color:var(--muted)}
.proof b{color:var(--gold);display:block;font-size:1.05rem;letter-spacing:.06em}

/* ---------- photo wall ---------- */
.wall{display:grid;gap:.7rem;grid-template-columns:repeat(2,1fr)}
@media(min-width:820px){.wall{grid-template-columns:repeat(4,1fr)}
  .wall .tall{grid-row:span 2}.wall .wide{grid-column:span 2}}
.wall .ph{min-height:210px}
.wall .tall .ph,.wall .tall{min-height:100%}

/* ==========================================================================
   THE PLATE — the shared box treatment
   ==========================================================================
   Five components used to share one recipe: `background:var(--ink-2);
   border:1px solid var(--edge); padding`. That flat bordered rectangle is what
   Brandon objected to, and because they all shared the recipe they all share
   the fix — .card, .fact, .tier, .pillar, .voice are styled together here and
   keep only their own padding and typography below.

   The look is NOT new invention. `.btn` is already a cut-corner steel plate, so
   giving the cards the same chamfer makes a card and a button read as the same
   material — the site gains a vocabulary instead of a second style.

   Hover lights the top edge like an LED strip: the same light logic that runs
   every section boundary, now acknowledging the pointer. LIFT is deliberately
   NOT part of this — a .fact is not clickable, and a static box that rises when
   you brush past it is a gimmick. Only genuinely interactive plates lift, via
   `.plate-act` below.

   `clip-path` crops `box-shadow` — the lesson .btn and .hive both learned — so
   every glow here is `filter:drop-shadow()`.
   ========================================================================== */
.card,.fact,.tier,.pillar,.voice,.who-band{
  --chamfer:14px;position:relative;isolation:isolate;
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  background:linear-gradient(165deg,#1a1613 0%,#131010 40%,#0e0c0b 100%);
  transition:filter .25s ease, transform .18s ease}

/* the LED strip along the top edge — dim at rest, lit on hover/focus/open.
   It starts at --chamfer so it follows the cut corner instead of floating past it. */
.card::before,.fact::before,.tier::before,.pillar::before,.voice::before,
.who-band::before{
  content:"";position:absolute;left:var(--chamfer);right:0;top:0;height:2px;z-index:2;
  background:var(--blood);opacity:.22;
  transition:opacity .3s ease, box-shadow .3s ease}
.card:hover::before,.fact:hover::before,.tier:hover::before,
.pillar:hover::before,.voice:hover::before,.who-band:hover::before,
.card.tap-lit::before,.fact.tap-lit::before,.tier.tap-lit::before,
.pillar.tap-lit::before,.voice.tap-lit::before,.who-band.tap-lit::before,
.card:has([aria-expanded="true"])::before{
  opacity:1;box-shadow:0 0 10px 1px rgba(200,32,42,.9),
                       0 0 26px 3px rgba(200,32,42,.45)}

/* the cut corners catching light — same treatment as .btn::after */
.card::after,.fact::after,.tier::after,.pillar::after,.voice::after,
.who-band::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
  background:linear-gradient(135deg,rgba(255,255,255,.13) 0 14px,transparent 15px),
             linear-gradient(315deg,rgba(0,0,0,.6) 0 14px,transparent 15px)}

/* only plates you can actually click lift under the pointer */
.card:hover,.card.tap-lit{transform:translateY(-3px);filter:drop-shadow(0 10px 26px rgba(0,0,0,.7))}
@media(prefers-reduced-motion:reduce){
  .card,.fact,.tier,.pillar,.voice,.who-band,.btn,.ticket,.lb-btn{transition:none}
  .card:hover,.btn:hover,.lb-btn:hover,.lb-btn:focus-visible{transform:none}
  .ticket:hover{transform:none}
  .card.tap-lit,.btn.tap-lit,.btn.ghost.tap-lit{transform:none}
}

/* ---------- classes ---------- */
.cards{display:grid;gap:1.2rem}
@media(min-width:820px){.cards{grid-template-columns:repeat(3,1fr)}}
/* A CARD IS A DISCLOSURE. Brandon: "I don't like seeing all this text on the
   page" — headline visible, body revealed on click. The whole header is a real
   <button> with aria-expanded, so keyboard and screen readers work with no
   extra plumbing, and the body is a plain [hidden] div.
   Padding lives on the button and the body, not the card, so the hit area of
   the header runs the full width of the plate. */
.card{padding:0}
.card-btn{all:unset;box-sizing:border-box;display:flex;width:100%;cursor:pointer;
  align-items:center;gap:1rem;padding:1.6rem 1.8rem}
.card-btn:focus-visible{outline:2px solid var(--gold);outline-offset:-4px}
.card-ttl{display:block;font-family:var(--hdr);font-weight:400;font-size:1.5rem;
  line-height:1;color:var(--paper);margin-bottom:.45rem}
.card-who{display:block;font-size:.72rem;letter-spacing:.14em;color:var(--gold);
  font-family:var(--cond);text-transform:uppercase}
/* the +/- is drawn, not a glyph — stock characters break craft rubric R001,
   the same rule that removed the ★★★★★ from the testimonials */
.card-sign{margin-left:auto;flex:none;position:relative;width:14px;height:14px}
.card-sign::before,.card-sign::after{content:"";position:absolute;left:50%;top:50%;
  translate:-50% -50%;background:var(--blood);transition:transform .25s ease}
.card-sign::before{width:14px;height:2px}
.card-sign::after{width:2px;height:14px}
[aria-expanded="true"] .card-sign::after{transform:scaleY(0)}
.card-body{padding:0 1.8rem 1.7rem;color:var(--t-2);font-size:.96rem}
.card-body[hidden]{display:none}
@media(prefers-reduced-motion:reduce){.card-sign::before,.card-sign::after{transition:none}}

/* ---------- youth ---------- */
.youth{background:
   linear-gradient(180deg,rgba(200,16,46,.10),transparent 60%),var(--ink-2);
  border-block:1px solid rgba(200,16,46,.3)}
.pill{display:inline-block;background:var(--blood);color:#fff;font-family:var(--cond);
  font-size:.72rem;letter-spacing:.16em;padding:.35rem .8rem;text-transform:uppercase;
  margin-bottom:1rem}
.spots{font-family:var(--cond);color:var(--gold);letter-spacing:.1em;font-size:.9rem;
  text-transform:uppercase;margin-top:1.2rem}

/* the four curriculum pillars — youth page */
.pillars{display:grid;gap:1.2rem;margin-top:2.5rem}
@media(min-width:760px){.pillars{grid-template-columns:repeat(2,1fr)}}
.pillar{padding:1.6rem}
.pillar h3{font-size:1.3rem;margin-bottom:.6rem}
.pillar p{color:#cdc7bf;font-size:.95rem}

/* the hard numbers of the youth program */
.facts{display:grid;gap:1rem;margin-top:2.5rem;grid-template-columns:repeat(2,1fr)}
@media(min-width:760px){.facts{grid-template-columns:repeat(4,1fr)}}
.fact{padding:1.2rem}
.fact b{display:block;font-family:var(--hdr);font-weight:400;font-size:2rem;
  color:var(--gold);line-height:1;margin-bottom:.4rem}
.fact span{font-family:var(--cond);font-weight:600;text-transform:uppercase;
  letter-spacing:.12em;font-size:.74rem;color:var(--muted)}

/* ---------- pricing ---------- */
.tiers{display:grid;gap:1.2rem}
@media(min-width:820px){.tiers{grid-template-columns:repeat(3,1fr)}}
.tier{padding:2rem;display:flex;flex-direction:column}
/* the featured tier reads as gold-lit rather than gold-bordered — a border no
   longer exists on these plates. Its LED edge is gold and lit by default. */
.tier.feat::before{background:var(--gold);opacity:.95;
  box-shadow:0 0 10px 1px rgba(201,162,39,.75), 0 0 26px 3px rgba(201,162,39,.35)}
.tier.feat:hover::before{box-shadow:0 0 12px 2px rgba(201,162,39,.95),
                                    0 0 30px 4px rgba(201,162,39,.5)}
/* WAS a ::before pinned at top:-.7rem — i.e. outside the box. The chamfer clip
   crops anything outside, and ::before is now the LED edge, so the badge became
   real markup in classes.html. That also makes it real text for a screen reader
   instead of CSS `content`, which is not reliably announced. */
.feat-tag{display:inline-block;align-self:flex-start;margin-bottom:.9rem;
  background:var(--gold);color:var(--ink);font:700 .66rem/1 var(--cond);
  letter-spacing:.16em;padding:.4rem .7rem;text-transform:uppercase}
.tier .name{font-family:var(--cond);font-size:.8rem;letter-spacing:.2em;color:var(--gold);
  text-transform:uppercase}
.tier h3{font-size:1.9rem;margin:.35rem 0 1rem}
.tier .price{font-family:var(--hdr);font-size:3.4rem;line-height:1;margin-bottom:.2rem}
.tier .per{font-size:.8rem;color:var(--muted);letter-spacing:.1em;text-transform:uppercase;
  font-family:var(--cond);margin-bottom:1.4rem}
.tier ul{list-style:none;margin-bottom:1.8rem;flex:1}
.tier li{padding:.5rem 0 .5rem 1.4rem;position:relative;color:#cdc7bf;font-size:.94rem;
  border-bottom:1px solid var(--edge)}
.tier li::before{content:"";position:absolute;left:0;top:1.05rem;width:7px;height:7px;
  background:var(--blood);transform:rotate(45deg)}
.tier .when{font-family:var(--cond);letter-spacing:.12em;font-size:.8rem;color:var(--gold);
  text-transform:uppercase;margin-bottom:1.3rem}
/* 2026-07-27 — the tier CTA (.btn) was stretching to the card's full width.
   .tier is a column flexbox with no --align-items set, so its default is
   `stretch`; every text child (h3, ul, .when) already fills the width on its
   own as a block element, so nobody noticed — the .btn was the only child
   whose natural size is its own content, and it was the one thing silently
   inheriting the stretch. Full-width flattens a 14px chamfer to the point it
   reads as a plain rectangle — every OTHER .btn on the site sits at its own
   content width. `align-self` on just the button fixes it without touching
   the text children that actually want the full column width. */
.tier .btn{align-self:flex-start}

/* Secondary checkout path. Every tier has TWO Stripe links at the same price —
   a recurring subscription and a one-time "single month". The subscription is
   the .btn (it's the actual pitch, and .cta-note already says "cancel
   anytime"); this is the escape hatch for someone not ready to auto-renew.
   Deliberately NOT a .btn — a second chamfered plate per card reads as two
   competing offers rather than one offer with a softer door. Muted until
   hover so it never pulls weight off the primary. */
.tier-alt{align-self:flex-start;margin-top:.85rem;font-family:var(--cond);
  text-transform:uppercase;letter-spacing:.14em;font-size:.72rem;
  color:var(--muted);text-decoration:none;border-bottom:1px solid transparent;
  transition:color .25s ease,border-color .25s ease}
.tier-alt:hover,.tier-alt:focus-visible{color:var(--gold);border-color:var(--gold)}
.tier-alt:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

/* ---- THE DAY PASS ---------------------------------------------------------
   It IS the site's .ticket — not a bespoke object. Two earlier attempts (a
   laminated card, then a Tyvek wristband) failed the same way: both invented a
   new object to compete for attention on a page that already has a strong one,
   and the card shared the buttons' silhouette closely enough that it just read
   as another checkout button. A day pass is a single admission, which is what a
   ticket already is, so reuse beats invention here.

   --sm because this page ALSO carries the "your first class is free" ticket
   further down. The same object at the same size twice splits the eye between
   them, and the free one has to stay louder — a free first class converts
   better than a $15 drop-in, so it keeps the full size. Same paper, same tear,
   less voice. --n is the notch radius, scaled to match. */
.daypass-wrap{text-align:center}
.ticket--sm{--n:7px}
.ticket--sm .t-main{padding:.78rem 1.15rem .78rem 1.3rem;font-size:.9rem}
.ticket--sm .t-stub{padding:.78rem .88rem;font-size:.66rem}
.ticket--sm .t-perf{margin:.4rem 0}

/* ---------- testimonials ---------- */
/* NOTE: the ★★★★★ glyphs are gone on purpose — DESIGN_DIRECTION.md kills them,
   stock characters violate craft rubric R001 (all iconography custom SVG). */
.voices{display:grid;gap:1.2rem}
@media(min-width:820px){.voices{grid-template-columns:repeat(3,1fr)}}
.voice{padding:1.7rem;display:flex;flex-direction:column}
.voice p{color:#d8d2ca;font-size:.97rem;flex:1}
.voice .who{margin-top:1.2rem;font-family:var(--cond);letter-spacing:.14em;font-size:.78rem;
  color:var(--gold);text-transform:uppercase}

/* ---------- hours / contact list (Come In) ---------- */
.hours{display:grid;gap:0;margin-top:1.4rem;border-top:1px solid var(--edge)}
.hours div{display:flex;flex-wrap:wrap;gap:.3rem 1rem;justify-content:space-between;
  padding:.95rem 0;border-bottom:1px solid var(--edge)}
.hours dt{font-family:var(--cond);text-transform:uppercase;letter-spacing:.12em;
  font-size:.86rem;color:var(--paper)}
.hours dd{color:var(--gold);font-family:var(--cond);font-weight:600;letter-spacing:.08em;
  font-size:.9rem;text-transform:uppercase}

/* ---------- final cta ---------- */
.close{position:relative;text-align:center;padding:clamp(5rem,12vw,9rem) 0}
.close .ph{position:absolute;inset:0;min-height:0;border:0}
.close .scrim{position:absolute;inset:0;background:rgba(5,4,4,.84)}
.close .wrap{position:relative}
.close h2{margin-inline:auto;max-width:16ch}
.close p{max-width:48ch;margin:0 auto 2rem;color:#ddd8d0;font-size:1.1rem}

/* ---------- footer ---------- */
footer{border-top:1px solid var(--edge);background:var(--ink-0);
  padding:3.5rem 0 calc(2rem + env(safe-area-inset-bottom))}
.fgrid{display:grid;gap:2.5rem}
@media(min-width:820px){.fgrid{grid-template-columns:1.4fr 1fr 1fr}}
footer img{height:96px;margin-bottom:1rem}
footer h4{font-family:var(--cond);letter-spacing:.18em;font-size:.78rem;color:var(--gold);
  text-transform:uppercase;margin-bottom:1rem}
footer a{color:#cdc7bf;text-decoration:none;display:block;padding:.22rem 0;font-size:.95rem}
footer a:hover{color:var(--paper)}
footer .tag{color:var(--muted);font-size:.9rem;max-width:34ch}
.band{margin-top:3rem;border-top:1px solid var(--edge);padding-top:1.4rem;
  display:flex;flex-wrap:wrap;gap:1rem;justify-content:space-between;align-items:center}
.band .kingsway{font-family:var(--cond);letter-spacing:.2em;font-size:.78rem;color:var(--gold);
  text-transform:uppercase}
.band small{color:#6d675f;font-size:.78rem}

/* ---------- Axon credit mark ----------
   The small "A2E" monogram + cursor is Axon's own mark (see axon-agentic.com
   nav/footer lockup) shrunk to footer-credit scale, so every client site's
   credit line quietly points back to axon-agentic.com. Axon's own stylesheet
   deliberately HIDES the caret's blink at this scale ("flickers like a stray
   clipped letter" below hero size) so it's rendered static here, not animated
   -- a nod to the mark, not a flourish. */
.legal{display:flex;flex-direction:column;align-items:flex-end;gap:.55rem}
.axon-credit{color:inherit;text-decoration:none;display:inline-flex;align-items:center;gap:.6em;
  transition:opacity .18s ease;opacity:.85}
.axon-credit:hover,.axon-credit:focus-visible{opacity:1}
.axon-credit-label{font-family:var(--cond);letter-spacing:.1em;font-size:.78rem;
  text-transform:uppercase;color:var(--muted)}
.axon-mark-img{height:2.4em;width:auto;border-radius:.18em}
.axon-wordmark-img{height:2.9em;width:auto}

/* ---------- social badges ----------
   Same plate recipe as .lb-btn (chamfer, dim->lit top filament, hover lift,
   gold focus ring), sized up and filled with the real crest photo (not just
   a bare platform glyph) so it reads as "here's what you'll actually see,"
   the way an Instagram profile photo does -- with a small platform-icon
   chip in the corner so it's still clear which network it opens. */
.socials{display:flex;gap:1rem;margin-top:1.2rem}
.social{--chamfer:16px;position:relative;display:block;overflow:hidden;
  width:84px;height:84px;color:var(--t-1);
  background:linear-gradient(165deg,#1a1613 0%,#131010 40%,#0e0c0b 100%);
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  transition:filter .2s, transform .18s ease}
.social-avatar{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:center 38%;z-index:1}
.social::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
  background:linear-gradient(135deg,rgba(255,255,255,.13) 0 var(--chamfer),transparent calc(var(--chamfer) + 1px)),
             linear-gradient(315deg,rgba(0,0,0,.6) 0 var(--chamfer),transparent calc(var(--chamfer) + 1px))}
.social::before{content:"";position:absolute;left:var(--chamfer);right:0;top:0;height:2px;z-index:3;
  background:var(--blood);opacity:.3;transition:opacity .3s ease, box-shadow .3s ease}
.social:hover::before,.social:focus-visible::before{
  opacity:1;box-shadow:0 0 10px 1px rgba(200,32,42,.9),0 0 26px 3px rgba(200,32,42,.45)}
.social:hover,.social:focus-visible{transform:translateY(-3px);
  filter:drop-shadow(0 10px 26px rgba(0,0,0,.7)) drop-shadow(0 0 8px rgba(200,32,42,.55))}
.social:hover .social-avatar,.social:focus-visible .social-avatar{transform:scale(1.06)}
.social-avatar{transition:transform .3s ease}
.social:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
.social:active{transform:translateY(0)}
.social-icon{position:absolute;right:6px;bottom:6px;z-index:4;
  width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:#0e0c0b;border:2px solid var(--ink-0);box-shadow:0 2px 8px rgba(0,0,0,.6)}
.social-icon svg{width:15px;height:15px;fill:#fff;position:relative}
@media(prefers-reduced-motion:reduce){
  .social,.social-avatar{transition:none}
  .social:hover,.social:hover .social-avatar{transform:none}
}

/* internal build note — wireframe only, does not ship */
.note{background:#1a1005;border:1px solid rgba(201,162,39,.35);padding:1rem 1.2rem;
  font-size:.86rem;color:#d9c99a;margin-top:1.2rem}
.note b{color:var(--gold)}

/* ---------- TYPE, LIT ----------
   The hex fixture is overhead, so display type catches a little light on top and
   sits in its own shadow. Barely there — it stops the headline looking pasted on. */
/* Strengthened 2026-07-26. These were at .06/.07 — technically present, visually
   flat. If a fixture is overhead then the top edge of a letter CATCHES it and the
   letter throws a shadow DOWN; at .06 neither read, so the type looked pasted on
   rather than lit. Three stops now: the lit top edge, a tight contact shadow that
   sets the letter ON the surface, then the long soft cast. */
h1,h2,.motto p{text-shadow:
  0 1px 0 rgba(255,255,255,.13),
  0 2px 1px rgba(0,0,0,.55),
  0 18px 40px rgba(0,0,0,.7)}
.hero h1 .r,h2 .r,.phead h1 .r{text-shadow:
  0 1px 0 rgba(255,255,255,.15),
  0 2px 1px rgba(0,0,0,.5),
  0 0 42px rgba(var(--led-glow),.5),
  0 18px 40px rgba(0,0,0,.7)}
/* Display-scale body copy only. Deliberately NOT every <p> — a shadow under
   small text at this contrast reads as blur, not depth. */
.lede,.big,.big-q{text-shadow:0 10px 26px rgba(0,0,0,.5)}

/* ---------- LIGHT ON A DARK WALL ----------
   Added 2026-07-26. The plain-black sections were reading as EMPTY rather than
   dark — type sat in a void with nothing beneath it. That never happens in the
   room: a fixture overhead throws a pool of light on the wall, and anything
   standing in that pool is attached to a surface.

   So this is the .hexlight bloom moved behind content. Same light source, same
   falloff, nothing new invented — it just means every section is lit from above
   the way the hero already is. Alpha is very low on purpose: you should not be
   able to point at it, only notice that the type stopped floating.

   .close opts out — it already has its own photo + scrim doing this job.

   TUNE --pool AND NOTHING ELSE.

   GEOMETRY NOTE — the focal point was originally `at 50% 0%`, the top edge of
   the pseudo-element. Every section carries ~100px of top padding, so the
   brightest part of the pool landed in empty space above the headline and had
   faded out before it reached any type. It painted correctly and did nothing.
   The focal point now sits at 26%, which puts the peak ON the headline. */
:root{--pool:.18}
section:not(.close),.motto{position:relative;isolation:isolate}
/* .phead uses ::after — .wall-surface::before is already the steel */
section:not(.close)::before,.motto::before,.phead.wall-surface::after{
  content:"";position:absolute;pointer-events:none;z-index:0;
  left:50%;translate:-50% 0;top:0;
  width:min(1100px,96vw);height:min(620px,80%);
  background:radial-gradient(ellipse 58% 52% at 50% 26%,
    rgba(255,250,240,var(--pool)),
    rgba(255,250,240,calc(var(--pool) * .4)) 48%, transparent 74%);
  filter:blur(30px)}
section:not(.close) > *,.motto > *{position:relative;z-index:1}

/* Eyebrows get a lit tick — a one-inch offcut of the LED strip. */
.eyebrow{display:flex;align-items:center;gap:.7rem}
.eyebrow::before{content:"";width:26px;height:2px;flex:none;
  background:linear-gradient(90deg,var(--led),rgba(var(--led-glow),0));
  box-shadow:0 0 10px rgba(var(--led-glow),.9)}

/* Inline links light up on hover instead of just changing colour. */
.more{position:relative;display:inline-block;color:var(--gold);text-decoration:none;
  font-family:var(--cond);letter-spacing:.12em;text-transform:uppercase;font-size:.84rem;
  padding-bottom:.35rem}
.more::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:var(--gold);opacity:.45;transition:opacity .2s, box-shadow .2s}
.more:hover::after{opacity:1;box-shadow:0 0 12px rgba(201,162,39,.8)}

/* Nav: the active page is marked with a lit segment, not a flat underline. */
nav .lnk a[aria-current="page"]{border-bottom-color:transparent;
  box-shadow:0 2px 0 var(--led), 0 6px 14px -2px rgba(var(--led-glow),.9)}


/* ---------- PHOTOS IN PLACE ----------
   The two-class treatment from DESIGN_DIRECTION.md:
     SUBJECT (default) — untouched. Full brightness. These are the point, and
       Ronnie's #1 stated visitor action is "look at pictures and sign up".
     ENVIRONMENT (.env) — treated hard. Hero/closing backgrounds are the ROOM,
       not something to look at, so they sit down near the page value.
   Guardrail: a stranger must still be able to see PEOPLE in the dim ones.
   Faces have to survive the filter. */
.ph.has-photo{padding:0;border:0;background:#0c0a09;overflow:hidden}
.ph.has-photo img{width:100%;height:100%;object-fit:cover;display:block;
  /* one common grade so mismatched phone photos read as one set */
  filter:contrast(1.06) saturate(.95) brightness(.98)}
/* GRAPHICS ARE NOT PHOTOGRAPHS. `object-fit:cover` fills a box by cropping the
   overflow, which is correct for a photo — you lose some wall — and catastrophic
   for artwork made of TYPE, where the crop eats the words. The Greater Purpose
   graphic is 2000x1050 (1.90 wide); in a near-square box, cover was hiding 43%
   of it and chopping "…D YOUR …RPOSE." at both edges.
   So: size the box to the artwork's own ratio and CONTAIN it. Nothing is cropped,
   ever. Use this for any image that is type, a logo, a poster or a diagram. */
.ph.has-photo.graphic{aspect-ratio:2000/1050;min-height:0;align-self:center;
  background:#0c0a09}
.ph.has-photo.graphic img{object-fit:contain}

.ph.env{border:0}
.ph.env img{filter:brightness(.42) contrast(.92) saturate(.7)}
.wall .ph.has-photo,.wall .tall .ph.has-photo{height:100%}


/* ---------- LESS TEXT, MORE VOICE ----------
   Diagnosis: the long paragraphs on these pages were OUR connective prose; the
   short punchy ones are Ronnie's. His voice is better than our writing about his
   voice, so the filler gets cut and his lines get promoted to display type.

   .big-q     a quote that stops being a paragraph and becomes an object
   .who-band  three short facts where a 49-word paragraph used to be */
/* REWRITTEN 2026-07-26. This was set in Anton, uppercase, at max-width:26ch.
   Anton is a DISPLAY face — it works at 3–10 words. The only .big-q on the site
   is 35 words, so on a phone it rendered as eight lines of giant all-caps: no
   longer a sentence you read, just a texture you decode. It dominated the
   screen and was the single worst thing on the page.

   Now it runs in Barlow Condensed at a readable size. That also gives --cond a
   real job in the content column instead of only labels, so the page switches
   between fewer faces, not more. The LED tick above it is unchanged — that's
   what marks it as Ronnie speaking.

   .punch restores the Anton treatment for genuinely SHORT lines. Use it only
   under ~10 words; past that it becomes the wall this replaced. */
blockquote.big-q{border-left:0;padding:0;margin:1.9rem 0;font-style:normal;
  font-family:var(--cond);font-weight:600;text-transform:none;
  font-size:clamp(1.28rem,4.2vw,1.8rem);line-height:1.3;letter-spacing:.005em;
  max-width:40ch;color:var(--t-0);text-wrap:pretty;
  text-shadow:0 1px 0 rgba(255,255,255,.07),0 12px 30px rgba(0,0,0,.6)}
blockquote.big-q.punch{font-family:var(--hdr);font-weight:400;
  text-transform:uppercase;font-size:clamp(1.35rem,3.1vw,2.15rem);
  line-height:1.02;max-width:26ch}
blockquote.big-q::before{content:"";display:block;width:44px;height:2px;margin-bottom:1.1rem;
  background:linear-gradient(90deg,var(--led),rgba(var(--led-glow),0));
  box-shadow:0 0 12px rgba(var(--led-glow),.9)}

/* MOBILE TYPE RHYTHM — added 2026-07-26. On a phone the copy column is the
   whole screen, so the desktop line-height and margins read as drift: every
   block floats with no relationship to the one above it. Tighter leading and
   smaller gaps make the stack read as one column of text. Size is left alone —
   the problem was spacing, not scale. */
@media(max-width:700px){
  .prose p{line-height:1.58;margin-bottom:.95rem}
  .prose p.big{line-height:1.45}
  .hero p.lede,.phead p.lede{line-height:1.5}
  blockquote.big-q{margin:1.5rem 0}
  h2{margin-bottom:1.1rem}
}

/* ONE plate, three compartments — this is a joined strip, not three separate
   boxes, so the chamfer goes on the container and the cells are divided by
   hairlines inside it. It used to fake those hairlines with `gap:1px` over a
   coloured background; on a plate that gap shows the plate's own gradient
   instead of a divider, so the rules are explicit now. */
.who-band{display:grid;gap:0;margin:1.6rem 0}
.who-band span{background:transparent;padding:.85rem 1.1rem;position:relative;z-index:1;
  font-family:var(--cond);font-weight:600;text-transform:uppercase;letter-spacing:.1em;
  font-size:.82rem;color:#cdc7bf}
.who-band span + span{border-top:1px solid var(--edge)}
.who-band span:last-child{color:var(--gold)}
@media(min-width:700px){.who-band{grid-template-columns:1fr 1fr 1fr}
  /* side by side the divider is vertical, so swap which edge draws it */
  .who-band span + span{border-top:0;border-left:1px solid var(--edge)}}

/* ---------- THE STEPPER (greater-purpose) ----------
   The program is a sequence, so the page lets you walk it. The rail connecting
   the steps IS the LED strip — the same light that marks every wall/ceiling edge
   on this site now traces progress through the program. That's why interaction
   belongs here and nowhere else: the content's own shape asked for it. */
.stepper{margin-top:2.4rem;--at:0}
.steps{position:relative;display:grid;grid-template-columns:repeat(5,1fr);gap:0;
  padding-top:1.9rem}
.steps::before{                       /* the dead rail */
  content:"";position:absolute;left:6%;right:6%;top:1.9rem;height:2px;
  background:rgba(242,239,233,.13)}
.steps::after{                        /* the lit run, up to the active step */
  content:"";position:absolute;left:6%;top:1.9rem;height:2px;
  width:calc((88% / 5) * (var(--at) + .5));
  background:linear-gradient(90deg,rgba(var(--led-glow),.35),var(--led));
  box-shadow:0 0 14px rgba(var(--led-glow),.85);
  transition:width .34s cubic-bezier(.4,0,.2,1)}
.step{position:relative;background:none;border:0;cursor:pointer;padding:2.1rem .4rem .9rem;
  display:flex;flex-direction:column;align-items:center;gap:.45rem;
  font-family:var(--cond);text-transform:uppercase;color:var(--muted);
  transition:color .2s}
.step::before{                        /* the node */
  content:"";position:absolute;top:-.55rem;left:50%;translate:-50% 0;
  width:13px;height:13px;border-radius:50%;
  background:var(--ink-2);box-shadow:inset 0 0 0 2px rgba(242,239,233,.22);
  transition:background .25s, box-shadow .25s}
.step[aria-selected="true"]{color:var(--paper)}
.step[aria-selected="true"]::before{background:var(--led);
  box-shadow:inset 0 0 0 2px var(--led), 0 0 16px rgba(var(--led-glow),1)}
.step:hover{color:var(--paper)}
.step:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
.s-num{font-size:.72rem;letter-spacing:.2em;color:var(--gold)}
.s-name{font-weight:600;letter-spacing:.09em;font-size:.9rem;line-height:1.1;text-align:center}

.laws{margin-top:2.2rem;min-height:9.5rem}
.law .l-steps{font-family:var(--cond);font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;font-size:.74rem;color:var(--gold);margin-bottom:.8rem}
.law h3{font-size:clamp(1.5rem,3.4vw,2.4rem);max-width:22ch;margin-bottom:.9rem;
  text-shadow:0 1px 0 rgba(255,255,255,.06),0 12px 30px rgba(0,0,0,.6)}
.law p{color:#cdc7bf;max-width:52ch}
@media(prefers-reduced-motion:no-preference){
  .law:not([hidden]){animation:lawIn .3s ease both}
  @keyframes lawIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
}
@media(max-width:700px){
  .steps{grid-template-columns:repeat(5,1fr)}
  .s-name{font-size:.66rem;letter-spacing:.04em}
  .s-num{font-size:.62rem}
}

/* ---------- MOTION: THE LIGHTS COME ON ----------
   The only motion on the site. Each LED strip is dark until its section enters
   view, then it lights and blooms — walking into a room and the strip flickering
   on ahead of you. It extends the room metaphor instead of decorating the page,
   which is the bar craft rubric R006 sets.
   No library. IntersectionObserver, one class, ~20 lines of JS.
   Fully off under prefers-reduced-motion: the LEDs are simply already lit. */
@media(prefers-reduced-motion:no-preference){
  /* 2026-07-27: transitions moved onto .arm (the base/off state) too, not just
     .lit — a transition only fires while it's declared on the rule that's
     ACTIVE at the moment a class changes. With the transition living only on
     .led.lit, scrolling a section back out (removing .lit, falling back to
     .arm's un-transitioned scaleX(.06)/opacity:0) snapped dark instantly
     instead of dimming. Both rules now carry the same timing so the strip
     lights walking in and fades walking away, symmetrically. */
  .led.arm::before{transform:scaleX(.06);opacity:0;transform-origin:left center;
    transition:transform 1.45s cubic-bezier(.22,.61,.36,1), opacity .44s ease}
  .led.arm::after {opacity:0;transition:opacity .5s ease}
  /* Half speed (Brandon, 2026-07-26) — the strip should feel like a long fixture
     warming along its length, not a wipe. Everything scales together so the bloom
     still trails the strip rather than racing it. */
  .led.lit::before{transform:scaleX(1);opacity:1;
    transition:transform 1.45s cubic-bezier(.22,.61,.36,1), opacity .44s ease}
  .led.lit::after {opacity:1;transition:opacity 1.6s ease .36s}
}


/* ==========================================================================
   THE LIGHTBOX — gym.html
   Ronnie's #1 stated visitor action is "look at pictures, then sign up." The
   wall grid shows them small; this is where you actually LOOK at one.

   THE FIXTURE IS THE ONLY LITERAL HEXAGON ON THE SITE, and it earns the shape
   because it is a light. In the room, the hex fixture is the thing that lets
   you see; here it is doing that same job, hanging above the photo it reveals.
   That is the rule DESIGN_DIRECTION.md set — translate the room's logic — and
   a fixture that is actually lighting something is logic, not decoration.

   The photo underneath stays RECTANGULAR and uncropped, deliberately.
   Hex-cropping a photo cuts the corners off heads and shoulders, and these
   photos are almost entirely people. The light is hexagonal. The picture is not.
   ========================================================================== */
.lb{position:fixed;inset:0;z-index:200;display:none;
  background:rgba(5,4,4,.96);
  flex-direction:column;align-items:center;justify-content:center;
  padding:clamp(3.5rem,7vw,4.5rem) clamp(1rem,4vw,2.5rem) clamp(1rem,4vw,2rem)}
.lb.is-open{display:flex}

/* --- the fixture: a flat-top hexagon panel, throwing light DOWN onto the photo.
       Two parts, same split the rest of the site uses — the panel itself, then
       the bloom it casts. The bloom is what actually sells it. --- */
/* CORRECTED after seeing it against a real photo of his ceiling. The first
   version was a SOLID hexagon panel — it read as a big beige shape sitting on
   the page, exactly the decoration this site is not allowed to do. Ronnie's
   actual fixtures are hollow hexagon TUBES in an overlapping cluster, so that
   is what this is: three hexagon outlines, drawn as light, much smaller. The
   photo behind it stays the brightest thing on screen, which is the point. */
.lb-fixture{position:absolute;top:clamp(.4rem,2vw,1.2rem);left:50%;translate:-50% 0;
  width:min(280px,58vw);aspect-ratio:220/120;pointer-events:none;z-index:0}
.lb-fixture::before{                     /* the tubes */
  content:"";position:absolute;inset:0;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 120'%3E%3Cg fill='none' stroke='%23fffdf2' stroke-width='5' stroke-linejoin='round'%3E%3Cpolygon points='60,8 96,29 96,71 60,92 24,71 24,29'/%3E%3Cpolygon points='118,20 154,41 154,83 118,104 82,83 82,41'/%3E%3Cpolygon points='172,6 208,27 208,69 172,90 136,69 136,27'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat;
  opacity:.92;
  filter:drop-shadow(0 0 10px rgba(255,248,228,.9)) drop-shadow(0 0 30px rgba(255,242,205,.45))}
.lb-fixture::after{                      /* the light it throws */
  content:"";position:absolute;left:50%;top:62%;translate:-50% 0;
  width:340%;height:520%;
  background:radial-gradient(ellipse 50% 46% at 50% 0%,
    rgba(255,250,235,.28), rgba(255,250,235,.10) 42%, transparent 74%);
  filter:blur(26px)}

.lb-stage{position:relative;z-index:1;display:flex;flex-direction:column;
  align-items:center;gap:.9rem;max-width:min(1100px,94vw)}
.lb-img{max-width:100%;max-height:min(70vh,780px);width:auto;object-fit:contain;
  border:1px solid var(--edge);
  box-shadow:0 34px 90px rgba(0,0,0,.85), 0 0 0 1px rgba(0,0,0,.6)}

/* the LED under the photo — same strip that marks every other edge on the site */
.lb-led{width:100%;height:2px;flex:none;
  background:linear-gradient(90deg,rgba(var(--led-glow),0),var(--led) 14%,
    #ff5a68 50%,var(--led) 86%,rgba(var(--led-glow),0));
  box-shadow:0 0 12px rgba(var(--led-glow),.8)}

.lb-bar{display:flex;align-items:center;gap:1.2rem;font-family:var(--cond);
  text-transform:uppercase;letter-spacing:.18em;font-size:.78rem;color:var(--t-3)}

/* controls — cut-corner plates, the same shape language as .btn */
/* small plates. These already had a chamfer, but at 8px and with a flat fill —
   close enough to look like a mistake rather than a relation. Same 14px cut and
   the same lit/shadowed corners as every other plate now. */
.lb-btn{--chamfer:14px;position:relative;color:var(--t-1);border:0;
  background:linear-gradient(165deg,#1a1613 0%,#131010 40%,#0e0c0b 100%);
  font-family:var(--cond);font-weight:600;text-transform:uppercase;
  letter-spacing:.14em;font-size:.78rem;padding:.65rem 1.2rem;cursor:pointer;
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  transition:filter .2s, color .18s, transform .18s ease}
.lb-btn::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
  background:linear-gradient(135deg,rgba(255,255,255,.13) 0 var(--chamfer),transparent calc(var(--chamfer) + 1px)),
             linear-gradient(315deg,rgba(0,0,0,.6) 0 var(--chamfer),transparent calc(var(--chamfer) + 1px))}
.lb-btn::before{                   /* same dim → lit top edge as the cards */
  content:"";position:absolute;left:var(--chamfer);right:0;top:0;height:2px;z-index:2;
  background:var(--blood);opacity:.22;
  transition:opacity .3s ease, box-shadow .3s ease}
.lb-btn:hover::before,.lb-btn:focus-visible::before{
  opacity:1;box-shadow:0 0 10px 1px rgba(200,32,42,.9),
                       0 0 26px 3px rgba(200,32,42,.45)}
.lb-btn:hover,.lb-btn:focus-visible{color:var(--t-0);transform:translateY(-3px);
  filter:drop-shadow(0 10px 26px rgba(0,0,0,.7)) drop-shadow(0 0 8px rgba(200,32,42,.55))}
/* clip-path crops box-shadow, so the focus ring has to be an outline */
.lb-btn:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
.lb-btn:active{transform:translateY(0)}
.lb-close{position:absolute;top:clamp(.8rem,2.5vw,1.4rem);right:clamp(.8rem,2.5vw,1.4rem);z-index:2}

/* ---------- THE FLIGHT: a tapped hex travels to centre and opens ----------
   Added 2026-07-27. Before this the lightbox just appeared, so the connection
   between the cell you tapped and the photo you got was left to inference.

   A clone of the cell flies from its place on the wall to where the full photo
   will sit, and morphs from hexagon to rectangle on the way. The hexagon is what
   travels; the rectangle is what arrives — which is the same bargain the hive
   already makes (hex to browse, rectangle to look), just made visible.

   THE ONE RULE THAT MATTERS: a clip-path polygon only animates into another
   polygon with the SAME VERTEX COUNT. The hexagon is 6 points, so the rectangle
   is written as 6 points too — its top and bottom edges each carry a redundant
   midpoint (50% 0%, 50% 100%) that exists purely to receive the hexagon's apex.
   Drop it to 4 points and the morph snaps instead of animating. Same trick the
   ticket's tear uses. */
/* --fly is the single source of truth for the flight length — the JS reads it
   off the element to know when to bin the clone, so changing it here is enough.
   THE MORPH IS DELIBERATELY LATE (--morph-in). Size and shape used to run over
   the same window, which meant the clone was already half-rectangular the
   instant it left the wall and you never actually saw a hexagon fly. Now it
   travels as a hexagon and opens on arrival. Most visible on a phone, where
   there is barely any distance to cross and the shape is all you have. */
.hexfly{--fly:.52s;--morph-in:.2s;
  position:fixed;z-index:210;pointer-events:none;
  filter:drop-shadow(0 0 10px rgba(255,255,255,.95))
         drop-shadow(0 0 34px rgba(206,231,255,.7))
         drop-shadow(0 0 70px rgba(170,205,255,.4));
  transition:left var(--fly), top var(--fly), width var(--fly), height var(--fly);
  transition-timing-function:cubic-bezier(.32,.72,.24,1)}
.hexfly-in{position:absolute;inset:0;overflow:hidden;background:#0c0a09;
  clip-path:polygon(50% 0%,100% 25%,100% 75%,50% 100%,0% 75%,0% 25%);
  /* the tube, as an inset ring — clip-path crops it to whatever shape we're in,
     so the white edge follows the hexagon and then squares off by itself */
  box-shadow:inset 0 0 0 4px rgba(255,255,255,.95);
  transition:clip-path calc(var(--fly) - var(--morph-in)) cubic-bezier(.4,0,.2,1) var(--morph-in),
             box-shadow calc(var(--fly) - var(--morph-in)) ease var(--morph-in)}
.hexfly-in img{width:100%;height:100%;object-fit:cover;
  filter:contrast(1.06) saturate(.95) brightness(.98)}
.hexfly.is-open .hexfly-in{
  clip-path:polygon(50% 0%,100% 0%,100% 100%,50% 100%,0% 100%,0% 0%);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.3)}
/* while the clone is in the air the real stage is held back, or you'd see the
   photo twice — once flying, once already landed */
.lb.is-flying .lb-stage{opacity:0}
.lb-stage{transition:opacity .2s ease}

/* PHONES. Two things are different here and both needed handling:
   1. There is almost no distance to cross — the cell starts near centre and the
      photo lands at ~90vw, so the flight is nearly all scale. Same duration over
      a shorter path reads as a flash, so it runs LONGER here, not shorter.
   2. Three stacked drop-shadows on a moving element at 3x DPR is a lot of blur
      to recompute every frame; iOS drops frames and the whole thing looks like
      it simply appeared. One shadow, and the bloom is carried by the backdrop
      instead. */
@media(max-width:700px){
  .hexfly{--fly:.74s;--morph-in:.3s;
    filter:drop-shadow(0 0 14px rgba(255,255,255,.9))}
}

/* the wall photos become obviously touchable */
.wall .ph.has-photo{cursor:zoom-in}
.wall .ph.has-photo:focus-visible{outline:2px solid var(--led);outline-offset:3px}

@media(max-width:700px){
  .lb-fixture{width:min(190px,44vw)}
  .lb-img{max-height:58vh}
  .lb-bar{gap:.7rem;font-size:.7rem}
  .lb-btn{padding:.55rem .8rem}
}

/* Motion matches the rest of the site: the fixture WARMS UP rather than snapping
   on, same idea as the .led reveal. Off entirely under reduced-motion. */
@media(prefers-reduced-motion:no-preference){
  .lb.is-open .lb-fixture{animation:lb-warm .5s ease both}
  .lb.is-open .lb-stage{animation:lb-rise .42s cubic-bezier(.2,.8,.3,1.18) both .06s}
}
@keyframes lb-warm{from{opacity:0}to{opacity:1}}
/* 2026-07-27 — was translateY(10px) + fade, which read as drifting in rather
   than arriving. The scale is what makes it POP to centre; the ease overshoots
   slightly past 1 and settles, so it lands under the fixture instead of easing
   to a stop. Deliberately small (.9, not .6) — the photo comes forward, it does
   not zoom. The hexagon light above it is already doing the framing. */
@keyframes lb-rise{from{opacity:0;transform:translateY(14px) scale(.9)}to{opacity:1;transform:none}}


/* ==========================================================================
   THE HIVE — photos INSIDE lit hexagon fixtures.
   This is the gym's ceiling, brought onto the page: hexagon tubes of light,
   interlocking in a cluster, except each one is a window onto a photo.

   Build: the outer hexagon IS the tube — a bright warm fill, clipped to a
   hexagon. The inner hexagon is inset by the tube thickness and holds the
   photo. So the glow is the frame, not something drawn near it.

   Glow uses filter:drop-shadow(), NOT box-shadow — clip-path crops box-shadow,
   which is the same lesson the .btn steel plates already learned.

   CROPPING: a hexagon does clip corners, so photos are object-position tuned
   toward the subject and the fixture is a THUMBNAIL — tapping it opens the
   full rectangular photo in the lightbox, uncropped. Hexagon to browse,
   rectangle to look.
   ========================================================================== */
/* INVERTED 2026-07-26. First version gave every hexagon its own bright rim, so
   where two cells met you saw TWO tubes, and the space between the cluster and
   the page stayed black. Real ceiling fixtures don't work that way: the tube is
   a SHARED edge between neighbouring cells, and the light it throws lands in
   the space around them. So the brightness moved off the individual cell and
   into the lattice and the air around it — the glow is now behind everything
   (.hive::before), the photos mask it, and what survives between them reads as
   continuous tube. Packing is tight (gap:0, exact -0.2887 row overlap) so
   neighbouring tubes coincide instead of doubling. */
.hive{--hw:clamp(116px,25vw,208px);
  position:relative;isolation:isolate;
  display:flex;flex-direction:column;align-items:center;
  padding:clamp(2rem,6vw,3.5rem) 0}
/* THE LIGHT — sits behind the whole cluster and spills past it. This is the
   part that was missing: the fixtures illuminate the room, not just themselves. */
.hive::before{content:"";position:absolute;z-index:0;pointer-events:none;
  left:50%;top:50%;translate:-50% -50%;
  width:min(900px,130%);height:140%;
  background:radial-gradient(ellipse 44% 42% at 50% 50%,
    rgba(226,240,255,.34), rgba(198,224,255,.15) 44%,
    rgba(180,212,255,.05) 62%, transparent 78%);
  filter:blur(34px)}
.hive-row{display:flex;justify-content:center;gap:0;position:relative;z-index:1}
/* pointy-top hexes: rows overlap by exactly a quarter of their height */
.hive-row + .hive-row{margin-top:calc(var(--hw) * -.2887)}

/* COLOUR CORRECTED 2026-07-26 after actually looking these fixtures up. Real
   hex garage/gym LEDs run 6500K COOL WHITE at 25,000–86,000 lumens (Litezone,
   VEVOR, Hyperlite spec sheets). Mine were warm cream at low intensity, which
   is why they read as a decorative gold outline instead of a light — the shape
   was right and literally nothing else was. A real one photographs BLOWN OUT:
   a pure-white core with a cool blue-white halo, never beige. */
/* --tube and --hexclip live HERE, on the cell, not on .hive — otherwise any
   other container (.hexfield on the home page) inherits nothing, clip-path and
   inset both fall back to invalid, and the hexes render as bare rectangles
   with the tube gradient spilling below. Learned the hard way 2026-07-26. */
.hex{--tube:4px;
     --hexclip:polygon(50% 0%,100% 25%,100% 75%,50% 100%,0% 75%,0% 25%);
  position:relative;width:var(--hw);aspect-ratio:1/1.1547;flex:none;
  cursor:zoom-in;background:none;border:0;padding:0;
  filter:drop-shadow(0 0 6px rgba(255,255,255,.85))
         drop-shadow(0 0 18px rgba(206,231,255,.6))
         drop-shadow(0 0 42px rgba(170,205,255,.3));
  transition:filter .22s ease, transform .22s ease}
.hex::before{                      /* THE TUBE — a blown-out diffuser, not a rim */
  content:"";position:absolute;inset:0;clip-path:var(--hexclip);
  background:linear-gradient(165deg,#ffffff,#f6fbff 38%,#dceaf8)}
.hex-in{position:absolute;inset:var(--tube);clip-path:var(--hexclip);
  overflow:hidden;background:#0c0a09}
/* PER-PHOTO FRAMING. `--op` is set inline on each cell in gym.html; the value
   here is only the fallback. Every photo used to share `center 38%`, which was
   guesswork applied seven times: a hexagon is TALLER than it is wide (1:1.1547),
   so a landscape photo cover-fitted into one keeps ~65% of its width and loses
   the rest off BOTH SIDES. The horizontal value is therefore the one that
   decides whether you get the boxer or the wall behind him — and it was the one
   nobody was setting. Each cell is now framed against the actual photograph.
   If you swap a photo, look at it and set `--op`; don't inherit the old one. */
.hex-in img{width:100%;height:100%;object-fit:cover;object-position:var(--op,50% 45%);
  filter:contrast(1.06) saturate(.95) brightness(.98)}

.hex:hover,.hex:focus-visible{outline:none;transform:translateY(-3px);
  filter:drop-shadow(0 0 10px rgba(255,255,255,1))
         drop-shadow(0 0 28px rgba(216,236,255,.85))
         drop-shadow(0 0 60px rgba(170,205,255,.45))}
.hex:focus-visible .hex-in{outline:2px solid var(--led);outline-offset:-2px}

@media(prefers-reduced-motion:reduce){.hex{transition:none}.hex:hover{transform:none}}

/* ==========================================================================
   THE HEX FIELD — a layout SYSTEM, not a fixed arrangement
   ==========================================================================
   These fixtures are modular: cells snap edge to edge and whoever installs
   them picks the shape. THE SHAPE IS ARBITRARY. Vendors sell named kits —
   line/run, cluster or bloom, full grid, bordered grid, diamond, star
   (Parmida, VEVOR, Hyperlite, HEXLED) — but those are products, not the
   vocabulary. Anything that tiles is buildable and people do get creative
   with them. Invent layouts for this site; don't shop from a catalogue.

   TWO FACTS FROM THE REFERENCE PHOTOS THAT THE FIRST BUILD GOT WRONG:
   1. MOST CELLS ARE EMPTY. The fixture IS the outline — you look through it at
      the ceiling. Filling every cell with a photo is not what these look like.
      `.hex--void` is an empty cell: pure tube, faint spill, no content. Empty
      cells are the majority; photos are the exception that earns attention.
   2. CELLS SHARE EDGES. Neighbouring tubes coincide into one continuous line
      (the lesson already learned on .hive). Positioning here is exact, so any
      two adjacent cells butt perfectly.

   HOW TO USE IT — every cell carries axial coordinates as `--q` (column) and
   `--r` (row); the field does the pointy-top maths. To author a new layout,
   list coordinates, nothing else:
       row r:   x = hw * (q + r/2)     y = hh * .75 * r
   `--cols`/`--rows` size the container so it centres and doesn't clip.
   ========================================================================== */
.hexfield{--hw:clamp(88px,13vw,168px);
  --hh:calc(var(--hw) * 1.1547);
  --cols:4.5; --rows:2;
  position:relative;isolation:isolate;
  width:calc(var(--cols) * var(--hw));
  height:calc((var(--rows) * .75 + .25) * var(--hh));
  margin:clamp(2.5rem,7vw,4.5rem) auto}
/* the light the run throws into the room — sits behind every cell and spills
   well past the field, because a fixture lights the space, not just itself */
.hexfield::before{content:"";position:absolute;z-index:0;pointer-events:none;
  left:50%;top:50%;translate:-50% -50%;
  width:160%;height:190%;
  background:radial-gradient(ellipse 46% 40% at 50% 50%,
    rgba(226,240,255,.26), rgba(198,224,255,.12) 46%,
    rgba(180,212,255,.04) 66%, transparent 80%);
  filter:blur(38px)}
.hexfield .hex{position:absolute;left:0;top:0;z-index:1;display:block;
  translate:calc(var(--hw) * (var(--q) + var(--r) / 2))
            calc(var(--hh) * .75 * var(--r))}
/* THE LAYOUTS. Sizes live here, in a class — never inline on the element, or
   the phone media query below can't override them (inline wins on specificity).
   `--run` = the garage-ceiling classic: two staggered rows of four. */
/* 2026-07-27 — --mrows went 2 -> 4: the run used to be 4 photo + 4 void cells,
   and mobile dropped the voids to re-pack into a tight 2x2. All 8 cells now
   carry a photo, so there is nothing left to drop; mobile re-packs into a
   honeycomb 4 rows deep instead.
   --mcols STAYS 2.5, not 4+: the translate formula is hw*(q + r/2), so naively
   repeating the same --mq per row (0/1, 0/1, 0/1...) drifts the whole run
   further right every single row — confirmed by measuring cell 8's own
   bounding box running ~35px past a 375px phone while the FIELD's own box
   measured fully on-screen, because the field's CSS width only ever accounted
   for the offset to a cell's top-left corner, not the cell's own width sitting
   past it. A real honeycomb doesn't drift, it ALTERNATES: row r's --mq has to
   shift left by a full step every 2 rows to cancel the r/2 term and land back
   where row r-2 was. See the --mq values on each cell in index.html — they
   step 0/1, then 0/1 again (r/2 alone provides that row's nesting offset),
   then -1/0, -1/0 (repeating). */
.hexfield--run{--cols:4.5;--rows:2;--mcols:2.5;--mrows:4}
/* three cells in a linked row — classes.html. Bronze / Silver / Gold as ONE
   fixture: three ways in, same room. Bigger cells because they carry type. */
.hexfield--trio{--hw:clamp(112px,19vw,196px);--cols:3;--rows:1;--mcols:3;--mrows:1}
/* a run that turns a corner — greater-purpose.html. The five laws are a path,
   so the fixture walks: right, right, step down, right, right. */
.hexfield--path{--hw:clamp(92px,15vw,158px);--cols:4.5;--rows:2;--mcols:2.5;--mrows:3}
/* one cell — come-in.html. A single fixture hung over the door. */
.hexfield--one{--hw:clamp(128px,24vw,220px);--cols:1;--rows:1;--mcols:1;--mrows:1}

/* A cell that CARRIES TYPE. Keep it short — a hexagon narrows top and bottom,
   so anything past two lines starts colliding with the clip. The side padding
   is a percentage for that reason: it tracks the cell, not the font size. */
.hex--label .hex-in{display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:.3rem;text-align:center;padding:0 17%;
  background:radial-gradient(ellipse 82% 74% at 50% 50%,
    #0c0a09 46%, rgba(198,224,255,.09) 86%, rgba(214,234,255,.15))}
.hx-name{font-family:var(--cond);text-transform:uppercase;letter-spacing:.18em;
  font-size:.72rem;color:var(--gold)}
.hx-price{font-family:var(--hdr);font-size:clamp(1.5rem,3.6vw,2.5rem);
  line-height:.9;color:var(--paper)}
.hx-per{font-family:var(--cond);text-transform:uppercase;letter-spacing:.14em;
  font-size:.6rem;color:var(--t-3)}

/* A cell that is a TAB (the five laws). Dark until you get to it: a fixture
   that hasn't been switched on yet, which is the point of a path. `.is-lit` is
   set by the stepper JS for every cell up to the active one, so walking the
   laws switches the run on behind you. */
.hex--step{cursor:pointer;filter:drop-shadow(0 0 3px rgba(255,255,255,.16))}
.hex--step::before{background:linear-gradient(165deg,#5c626c,#525863 40%,#454a53)}
.hex--step .hex-in{display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:.35rem;text-align:center;padding:0 14%;
  background:#0c0a09}
.hex--step.is-lit{filter:drop-shadow(0 0 6px rgba(255,255,255,.8))
                         drop-shadow(0 0 18px rgba(206,231,255,.5))}
.hex--step.is-lit::before{background:linear-gradient(165deg,#ffffff,#f6fbff 38%,#dceaf8)}
.hex--step[aria-selected="true"]{filter:drop-shadow(0 0 10px rgba(255,255,255,1))
                                        drop-shadow(0 0 30px rgba(216,236,255,.85))
                                        drop-shadow(0 0 62px rgba(170,205,255,.45))}
.hex--step[aria-selected="true"] .hx-name{color:var(--paper)}
.hex--step:focus-visible{outline:none}
.hex--step:focus-visible .hex-in{outline:2px solid var(--gold);outline-offset:-3px}

/* The stepper's ORIGINAL flat rail is retired when the tab strip is a hex path:
   the shared edges of the cells ARE the rail now, so a 2px line under them is a
   second, contradictory one. `.step::before` was the little round node — same
   pseudo-element the hex needs for its tube, so every property it set has to be
   unset by hand or the tube renders as a 13px dot. Don't delete these; they are
   what lets one page use the path and another still use a plain stepper. */
.steps.hexfield{display:block;padding-top:0}
.steps.hexfield::before,.steps.hexfield::after{display:none}
.step.hex{padding:0;display:block;gap:0;color:var(--muted)}
.step.hex::before{top:0;left:0;width:auto;height:auto;border-radius:0;
  translate:none;box-shadow:none}
.step.hex[aria-selected="true"]::before{box-shadow:none;
  background:linear-gradient(165deg,#ffffff,#f6fbff 38%,#dceaf8)}

/* an EMPTY cell — the normal case. No photo, no link, no hover: it is a light,
   and the faint wash inside is the ceiling catching it.
   NOTE the inner fill must stay DARK. `.hex::before` paints the whole cell
   white and `.hex-in` is what covers it, leaving only the 4px tube — so a void
   with a near-transparent inner reads as a solid white blob, which is the
   beige-blob mistake in a different colour. Dark inside, light on the edge. */
.hex--void{cursor:default;pointer-events:none}
.hex--void .hex-in{
  background:radial-gradient(ellipse 78% 70% at 50% 50%,
    #0c0a09 52%, rgba(198,224,255,.10) 88%, rgba(214,234,255,.16))}

/* PHONES — a wide run does not fit a 390px screen, and shrinking the cells to
   make it fit turns the photos into thumbnails. So every cell also carries a
   phone coordinate (`--mq`/`--mr`) and the field re-snaps to a narrow layout:
   same fixture, re-installed for a narrow ceiling, which is exactly what an
   installer would do. `--mcols`/`--mrows` resize the container to match. */
@media(max-width:760px){
  .hexfield{--hw:clamp(104px,30vw,150px);
    --cols:var(--mcols,2.5); --rows:var(--mrows,4)}
  .hexfield .hex{
    translate:calc(var(--hw) * (var(--mq, var(--q)) + var(--mr, var(--r)) / 2))
              calc(var(--hh) * .75 * var(--mr, var(--r)))}
}

/* ==========================================================================
   FIGHT NIGHT — index.html, a dated band under the hero
   ==========================================================================
   A live card is the best proof a gym is real, so it sits high on the page.
   Almost nothing new is invented here: the layout is `.two`, the numbers are
   `.facts`, the heading is the standard h2. The ONLY new thing is the poster
   frame, and it is deliberately plain — the poster does the shouting.

   The poster is NOT graded down like the environment photos. Everywhere else
   the rule is that photographs get pushed back so type can stand in front of
   them; a fight poster is the exception, because it IS type, and it is already
   yellow-and-black, which is in-brand. Grading it would make it look like a
   mistake rather than a document.
   ========================================================================== */
.fightnight{padding:clamp(3.5rem,8vw,6rem) 0}
.fightnight h2 .r{font-size:.6em;vertical-align:.14em}   /* "vs" is a joint, not a word */
.fightnight .facts{grid-template-columns:repeat(2,1fr);margin-top:2rem}
@media(min-width:760px){.fightnight .facts{grid-template-columns:repeat(4,1fr)}}
/* this row mixes dates, times and a place name, so the bold line is set smaller
   than the default 2rem — at full size "Homestead" wraps and the row goes ragged */
.fightnight .fact b{font-size:1.5rem}

/* ---------- .pasted — ANYTHING TAPED TO THE WALL ----------
   A real flyer stuck to a gym wall, not an image in a frame. Four things sell
   it, and they are all things that are true of actual paper:

     1. It hangs CROOKED. Nobody tapes a poster square. This is the single
        biggest tell — a perfectly level poster always reads as a graphic.
        The angle is `--tilt`, and every sheet MUST get a different one. Two
        posters at identical angles stop reading as paper and start reading as
        a template, which is the whole thing we are avoiding.
     2. It sits TIGHT to the wall. The old shadow was 18px down and 44px soft,
        which is how a framed picture floats; paper stuck flat casts a short,
        dark contact shadow instead.
     3. The room's LIGHT falls across it — cool from the hex fixtures above,
        red spill from the LED strip below, and a soft crease down the middle
        where the sheet doesn't lie perfectly flat.
     4. It's held up by TAPE, and you can see the tape.

   The artwork itself is still un-graded (see the note above) — none of this
   tints it, it only lights it. That is why `.pasted-light` is pure luminance:
   drop it on a black-and-red graphic or a yellow fight poster and it behaves.

   Usage: put `.pasted` on the wrapper, then inside it, after the <img>:
       <span class="pasted-light" aria-hidden="true"></span>
       <i class="tape tape-tl" aria-hidden="true"></i>
       <i class="tape tape-br" aria-hidden="true"></i>
   and set a `--tilt` nobody else on the site is using. */
.pasted{position:relative;--tilt:-1.4deg;
  transform:rotate(var(--tilt));
  filter:drop-shadow(0 3px 5px rgba(0,0,0,.85))      /* contact shadow, tight */
         drop-shadow(0 14px 30px rgba(0,0,0,.55))}   /* the room's ambient falloff */
.pasted img{display:block;width:100%;height:auto}

/* the light on the paper — never a colour cast, only luminance */
.pasted-light{position:absolute;inset:0;pointer-events:none;z-index:1;
  background:
    /* the crease: a soft highlight and its shadow, just off-centre */
    linear-gradient(100deg,transparent 44%,rgba(255,255,255,.10) 49%,
                    rgba(0,0,0,.14) 52%,transparent 58%),
    /* hex fixtures overhead */
    linear-gradient(180deg,rgba(226,240,255,.16) 0%,transparent 34%),
    /* the red LED strip spilling up onto the bottom edge */
    linear-gradient(0deg,rgba(200,32,42,.20) 0%,transparent 22%),
    /* edges sit slightly in shadow where the sheet meets the wall */
    radial-gradient(120% 100% at 50% 45%,transparent 55%,rgba(0,0,0,.38) 100%)}
/* `.pasted-light` is a bare <span>, so it was also matching `.ph span` above
   (written for the placeholder caption text) — its `max-width:26ch` was
   clipping the overlay to a sliver instead of covering the photo. Only
   noticeable as an ugly pale rectangle in the corner; barely visible on a
   wide desktop photo, glaring on a narrow mobile one. Needs `.ph .pasted-light`
   (two classes) to out-specificity `.ph span` (class+type). */
.ph .pasted-light{max-width:none;text-align:left;letter-spacing:normal;
  text-transform:none;font:inherit;color:inherit;line-height:normal}

/* packing tape: cloudy, not clear, and it picks up the ceiling light */
.tape{position:absolute;z-index:2;width:74px;height:23px;pointer-events:none;
  background:linear-gradient(160deg,rgba(255,255,255,.30),rgba(228,236,244,.14) 45%,
                             rgba(255,255,255,.26));
  box-shadow:0 1px 2px rgba(0,0,0,.5);
  /* torn ends, not scissor-cut */
  clip-path:polygon(0 12%, 6% 0, 100% 4%, 96% 96%, 4% 100%)}
.tape-tl{top:-11px;left:-16px;transform:rotate(-24deg)}
.tape-br{bottom:-10px;right:-15px;transform:rotate(-21deg)}

/* ---------- tape PATTERNS — nobody tapes paper the same way twice ----------
   The default corner-to-corner diagonal (tl+br) was applied identically to
   every photo on the site, tilt aside. That's what made it read as a sticker
   template no matter how the tilt varied — real tape jobs differ in WHERE,
   not just at what angle. Three more patterns, put on the `.pasted` wrapper
   alongside its `--tilt`, reusing the same two <i class="tape tape-tl/br">
   elements already in the markup so no HTML changes beyond one class name:

     (default)   corner-to-corner, tl + br — the fight poster's original
     .pin-alt    the other diagonal, tr + bl
     .pin-top    both pieces along the top edge only — paper tacked up in a
                 hurry, bottom corners hanging free
     .pin-single one piece, top-centre — the laziest, most common real one

   Pick whichever reads right for that photo's crop and tilt; just don't let
   two pasted elements near each other on the same page share a pattern. */
.pasted.pin-alt .tape-tl{left:auto;right:-16px;transform:rotate(24deg)}
.pasted.pin-alt .tape-br{right:auto;left:-15px;transform:rotate(-21deg)}
.pasted.pin-top .tape-tl{left:10%;transform:rotate(-13deg)}
.pasted.pin-top .tape-br{top:-10px;bottom:auto;right:10%;left:auto;transform:rotate(12deg)}
.pasted.pin-single .tape-tl{left:50%;transform:translateX(-50%) rotate(-3deg)}
.pasted.pin-single .tape-br{display:none}

/* the fight poster: `.pasted` plus only its own layout */
.poster{justify-self:center;max-width:420px;width:100%}
@media(max-width:859px){.poster{order:2;max-width:340px}}

/* The Greater Purpose graphic, pasted up on its page the same way. It is a
   2000x1050 sheet of TYPE, so it keeps `.graphic`'s contain-at-its-own-ratio
   behaviour (see .ph.has-photo.graphic) — cropping it would eat the words.
   Its own tilt, leaning the other way from the fight poster. */
.ph.has-photo.graphic.pasted{--tilt:1.1deg;overflow:visible;background:none}
.ph.has-photo.graphic.pasted img{height:100%;object-fit:contain}
/* the sheet is wide and short, so the tape sits further in along the long edges */
.ph.has-photo.graphic.pasted .tape-tl{top:-10px;left:6%;transform:rotate(-8deg)}
.ph.has-photo.graphic.pasted .tape-br{bottom:-9px;right:8%;transform:rotate(-6deg)}

/* Any ordinary cropped photo, pasted to the wall like a real 4x6 print —
   Coach Ronnie's portrait first, then every other standalone `.ph.has-photo`
   that isn't part of the hex-lit `.wall` grid or a full-bleed `.env`
   background. Those two already have their own light/room treatment; a
   plain framed photo floating on bare wall was the odd one out. Lower
   specificity than `.ph.has-photo.graphic.pasted` above, so that rule (its
   own object-fit:contain, its own tilt) still wins for the Greater Purpose
   sheet. Needs overflow visible for the tape, which .ph.has-photo normally
   clips. Every instance sets its own `--tilt` inline — see the comment on
   `.pasted` above for why. */
.ph.has-photo.pasted{overflow:visible;background:none}

/* ---------- the countdown: a seven-segment ring clock ----------
   Brandon asked for a retro game clock. This is built rather than imported.
   The known open-source option is DSEG (SIL OFL seven-segment webfont), but a
   font cannot render UNLIT segments — the dim ghost strokes a real LED panel
   shows where a digit isn't using them. That detail is most of what sells the
   effect, and in CSS it is free. It also honours this site's own rule:
   material is photographed, LIGHT IS CSS. A segment display is pure light.

   Each digit is 7 <i> elements named a-g (the standard segment names: a top
   bar, b/c right verticals, d bottom, e/f left verticals, g the middle). The
   JS sets `data-off` to the segments that should be dark for that numeral;
   everything else stays lit. Geometry is driven by ONE variable, --dw.

   COLOUR: amber-red, not the cool white used elsewhere. The hex fixtures are
   6500K daylight, but a scoreboard is not a ceiling light — every ring clock
   and arcade cabinet worth copying is red or amber, and red is the brand
   colour. Correct here precisely because it does NOT match the hex lights.

   NO SECONDS, ticks once a minute: the only motion on this site is the LED
   warming over 1.45s, and a seconds digit flickering forever fights that. On
   a card days out, seconds are noise rather than urgency. */
.countdown{margin-top:2.2rem}

/* the case the display is set into — a dark recess, not a panel sitting on top */
/* Chamfered like every other plate rather than a rounded rectangle — it was the
   last rounded RECTANGLE on the site, which read as a different material. (The
   remaining border-radius values are all 50%: the colon dots and the stepper
   node, which are meant to be circles.)
   It keeps its INSET shadow though: a display is recessed into its
   case, where a card sits proud of the wall. Same cut, opposite depth. */
.cd-panel{--chamfer:14px;position:relative;display:inline-block;
  padding:clamp(1rem,3vw,1.6rem) clamp(1.1rem,3.5vw,2rem);background:#0a0807;
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  box-shadow:inset 0 2px 18px rgba(0,0,0,.9), inset 0 0 0 1px rgba(0,0,0,.6)}
.cd-panel::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(135deg,rgba(255,255,255,.10) 0 14px,transparent 15px),
             linear-gradient(315deg,rgba(0,0,0,.6) 0 14px,transparent 15px)}
.cd-digits{display:flex;align-items:flex-start;
  gap:calc(var(--dw) * .22);          /* between a pair and the colon */
  /* Four pairs now, not three — the panel is ~10.7 x --dw wide, up from ~7.8,
     so --dw has to come down or the clock runs off a phone. */
  --dw:clamp(20px,5.6vw,40px)}
/* one unit: its two digits and its label stacked, so the label centres under
   its own pair structurally. The first pass sized labels with a calc off --dw
   and they drifted further left with each column — structure beats arithmetic. */
.cd-group{display:flex;flex-direction:column;align-items:center}
.cd-pair{display:flex;gap:calc(var(--dw) * .12)}   /* tighter INSIDE a pair than
                                                      between pairs, so the eye
                                                      groups them as one number */
.cd-label{margin-top:.7rem;font-family:var(--cond);font-weight:600;
  text-transform:uppercase;letter-spacing:.18em;font-size:.62rem;color:var(--t-3)}

/* one digit: --dw wide, 1.8x tall. Every segment position below is a fraction
   of --dw, so the whole clock scales from that single value. */
.cd-digit{position:relative;display:block;
  width:var(--dw);height:calc(var(--dw) * 1.8);
  --t:calc(var(--dw) * .15);          /* segment thickness */
  --gap:calc(var(--dw) * .04)}        /* breathing room where segments meet */
.cd-digit i{position:absolute;display:block;background:var(--blood);
  /* lit by default; `data-off` dims individual segments below */
  filter:drop-shadow(0 0 4px rgba(200,32,42,.9)) drop-shadow(0 0 14px rgba(200,32,42,.45))}

/* the bevelled ends are what make these read as segments rather than bars */
.cd-digit .s-a,.cd-digit .s-d,.cd-digit .s-g{
  left:calc(var(--t) * .55);width:calc(100% - var(--t) * 1.1);height:var(--t);
  clip-path:polygon(0% 50%,14% 0%,86% 0%,100% 50%,86% 100%,14% 100%)}
.cd-digit .s-b,.cd-digit .s-c,.cd-digit .s-e,.cd-digit .s-f{
  width:var(--t);height:calc(50% - var(--t) * .8 - var(--gap));
  clip-path:polygon(50% 0%,100% 14%,100% 86%,50% 100%,0% 86%,0% 14%)}
.cd-digit .s-a{top:0}
.cd-digit .s-g{top:calc(50% - var(--t) / 2)}
.cd-digit .s-d{bottom:0}
.cd-digit .s-f{top:calc(var(--t) * .55 + var(--gap));left:0}
.cd-digit .s-b{top:calc(var(--t) * .55 + var(--gap));right:0}
.cd-digit .s-e{bottom:calc(var(--t) * .55 + var(--gap));left:0}
.cd-digit .s-c{bottom:calc(var(--t) * .55 + var(--gap));right:0}

/* THE GHOST SEGMENTS. An unlit segment on a real display is not invisible —
   you can see the dark element sitting there. Dropping these to 0 is what
   makes a CSS seven-segment look like clip-art instead of hardware. */
.cd-digit[data-off*="a"] .s-a,.cd-digit[data-off*="b"] .s-b,
.cd-digit[data-off*="c"] .s-c,.cd-digit[data-off*="d"] .s-d,
.cd-digit[data-off*="e"] .s-e,.cd-digit[data-off*="f"] .s-f,
.cd-digit[data-off*="g"] .s-g{background:rgba(200,32,42,.11);filter:none}

/* the colon between pairs — two dots on the same optical rhythm as the bars */
.cd-colon{position:relative;display:block;width:calc(var(--dw) * .3);
  height:calc(var(--dw) * 1.8)}
.cd-colon::before,.cd-colon::after{content:"";position:absolute;left:50%;
  width:calc(var(--dw) * .15);height:calc(var(--dw) * .15);translate:-50% 0;
  border-radius:50%;background:var(--blood);
  filter:drop-shadow(0 0 4px rgba(200,32,42,.9))}
.cd-colon::before{top:32%}
.cd-colon::after{top:60%}

/* the value in plain text, for screen readers — the segments are aria-hidden
   and read as nothing at all */
.cd-read{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}

/* fight-day state — the countdown is done, so this is a statement, not a clock */
.cd-live{margin-top:2.2rem;font-family:var(--hdr);
  font-size:clamp(1.4rem,4vw,2.2rem);line-height:1;color:var(--blood);
  text-shadow:0 0 20px rgba(200,32,42,.5)}
/* the facts row is supporting detail once a countdown is above it */
.fightnight .facts{margin-top:1.2rem}

/* PHONES — THE POSTER COMES BEFORE ITS OWN TRANSCRIPTION.
   Everything in the prose column except the first three lines is a reading of
   the poster: the countdown counts to the time printed on it, and the four
   plates ARE its text re-set in the site's type. Side by side on a desktop
   that works — the plates are a legible index beside their source. Stacked on
   a phone it inverts: you read the whole index, then finally meet the document
   it came from, by which point the plates have already spent the information
   and the poster lands as repetition instead of proof.
   So the poster moves up, and the plates become what they should have been —
   the clean version of small, angled, photographed text.
   2026-07-28: the h2 and the paragraph moved BELOW the poster too. The h2 says
   "Hall vs Wadsworth" — which is exactly what the poster shouts, in far bigger
   type, six inches lower. Stacked on a phone you therefore read the matchup as
   a heading, then immediately meet the poster saying the same words, and the
   heading reads as a caption for something you have not seen yet. Below it,
   the same two elements become what they actually are: the poster's own claim,
   re-set in the site's voice, plus the one fact the poster does NOT carry —
   that Elijah is Ronnie's fighter, which is what makes a generic fight card
   into this gym's.
   The EYEBROW stays on top, alone. It is not white type and it is not a
   restatement of the poster — it is the section's label, and it is the only
   thing that tells you what the clock beneath it is counting down to. Without
   it the section opens on a bare seven-segment display with no referent.
   `display:contents` on .prose is what allows this without touching the
   markup: it drops the wrapper's box so its children become grid items in
   their own right and can be ordered against the poster. .prose has no box of
   its own (only `.prose p` descendant rules), so nothing is lost with it.
   The gap goes to 0 because the children carry their own margins — the two
   columns' 2rem+ gutter between every element would tear the section apart. */
@media(max-width:859px){
  .fightnight .two{gap:0}
  .fightnight .two > .prose{display:contents}
  /* LABEL, CLOCK, POSTER — then everything the poster is the source of.
     The clock is the one thing in this section that is not a reading of the
     poster — it is live, it moves, and it says "four days" in a way a printed
     sheet never can. Running it before the poster gives the section a pulse to
     arrive on, so the poster reads as the thing being counted down TO rather
     than an illustration filed after the facts. Everything from order:4 down
     is derived FROM the poster, so it all sits after it. */
  .fightnight .two > .prose > .eyebrow{order:1}
  .fightnight .two > .prose > .countdown{order:2;text-align:center}
  .fightnight .two > .prose > .cd-live{order:3;text-align:center}
  .fightnight .poster{order:4;margin:1.6rem 0 1rem}
  .fightnight .two > .prose > h2{order:5}
  .fightnight .two > .prose > p.big{order:6}
  .fightnight .two > .prose > .facts{order:7}
  .fightnight .two > .prose > .cta-note{order:8}
}
