/* ============================================================
   Fullrack — the front door.

   The gym/PT switcher plus the content that sits under it. Tokens are
   lifted from site.css so this page and /gyms/ are the same design
   system, not two that happen to be dark.

   No inline styles or scripts anywhere: netlify.toml sets
   style-src 'self'; script-src 'self' with no 'unsafe-inline', which
   silently drops both. If you need a one-off, add a class.
   ============================================================ */

:root{
  --bg:#0B0F0E; --bg-raised:#111614; --card:#151B18; --card2:#1C2320;
  --line:rgba(255,255,255,.09); --line-hi:rgba(43,217,169,.45);
  --accent:#2BD9A9; --accent-soft:#6FEACB; --accent-ink:#062018;
  --gym:#4AC2F0; --pt:#2BD9A9;
  --text:#F4F7F6; --text-body:#C9D1CE; --muted:#8B9490;
  --sans:Geist,system-ui,-apple-system,"Segoe UI",sans-serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --r:16px; --r-lg:22px;
  --max:1120px;
}
*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--bg);color:var(--text);font-family:var(--sans);line-height:1.55;
  -webkit-font-smoothing:antialiased}
a{color:inherit}
h1,h2,h3,p{margin:0 0 14px}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.wrap{max-width:var(--max);margin:0 auto;padding:0 clamp(20px,4vw,40px)}

/* Skip link. /gyms/ has had one all along (site.css) and these two pages did
   not, which is the worst version of the situation: a keyboard user learns the
   site has one, then loses it on the page where the first tab stop is a
   full-screen chooser. Off-screen until focused, then it lands in the corner. */
.skip{position:absolute;left:1rem;top:-4rem;z-index:100;
  background:var(--accent);color:var(--accent-ink);
  padding:10px 16px;border-radius:0 0 10px 10px;font-weight:600;font-size:14px;
  text-decoration:none;transition:top .18s ease}
.skip:focus{top:0}
.kicker{font-family:var(--mono);font-size:12px;letter-spacing:.16em;
  text-transform:uppercase;margin:0 0 12px}

/* ============ the stage ============ */
/* 100vh at the top of a normal page. Not a fixed overlay: a chooser that
   blocks the page gives crawlers a splash screen and gives anyone who
   doesn't want to choose nowhere to go. Scrolling past it is a feature. */
#stage{position:relative;width:100%;height:100vh;height:100svh;min-height:560px;
  overflow:hidden;isolation:isolate;perspective:1600px;
  background:
    radial-gradient(80% 60% at 22% 30%, rgba(74,194,240,.14), transparent 62%),
    radial-gradient(80% 60% at 78% 70%, rgba(43,217,169,.14), transparent 62%),
    #080c0b;
  border-bottom:1px solid var(--line)}

#stage:before{content:"";position:absolute;inset:-20%;pointer-events:none;
  background:
    radial-gradient(38% 34% at 28% 34%, rgba(74,194,240,.10), transparent 70%),
    radial-gradient(38% 34% at 72% 66%, rgba(43,217,169,.10), transparent 70%);
  animation:drift 36s ease-in-out infinite alternate}
@keyframes drift{to{transform:translate3d(3%,-4%,0) scale(1.08)}}

.brand{position:absolute;top:24px;left:0;right:0;text-align:center;margin:0;z-index:5;
  font-weight:700;letter-spacing:.22em;font-size:13px;color:#93a79f;
  opacity:0;transform:translateY(-8px);transition:opacity .6s ease,transform .6s ease}
body.ready .brand{opacity:1;transform:none}

.halves{position:absolute;inset:0;display:flex;z-index:2}
.side{flex:1 1 50%;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(14px,2.4vh,26px);padding:clamp(60px,8vh,92px) clamp(16px,3vw,40px) clamp(56px,8vh,80px);
  text-decoration:none;text-align:center;position:relative;
  transition:opacity .5s ease,transform .5s cubic-bezier(.2,.8,.2,1)}
/* The non-favoured side stays legible. Emphasis is carried by what the
   winning side GAINS — the glow, the lift, the arrow — not by erasing the
   other one. At .34 the second product was effectively invisible, which
   defeats the entire point of showing two. */
.side.dim{opacity:.78;transform:scale(.985)}

.pitch{display:flex;flex-direction:column;align-items:center;gap:8px;max-width:380px}
.t,.s,.go{opacity:0;transform:translateY(12px);
  transition:opacity .6s ease,transform .7s cubic-bezier(.2,.8,.2,1)}
body.ready .t{opacity:1;transform:none;transition-delay:.10s}
body.ready .s{opacity:1;transform:none;transition-delay:.18s}
.t{font-size:clamp(22px,2.6vw,32px);font-weight:700;line-height:1.1;letter-spacing:-.02em;margin:0}
.s{color:var(--text-body);font-size:15px;line-height:1.45;max-width:330px;margin:0}
.go{font-weight:600;font-size:15px;letter-spacing:.01em;margin:2px 0 0;transition-delay:0s}
.go em{font-style:normal;display:inline-block;transition:transform .35s cubic-bezier(.2,.8,.2,1)}
.side.gym .go{color:var(--gym)}
.side.pt .go{color:var(--pt)}
body.ready .side.lean .go{opacity:1;transform:none}
.side.lean .go em{transform:translateX(6px)}

/* ---------- the device ----------
   Drawn, not photographed. A device render would be a 400KB PNG that dates
   itself the moment a handset changes its corner radius. The aspect matches
   the 320x692 UI viewport plus the 8px bezel each side. */
.phone{position:relative;width:min(268px,74%);aspect-ratio:336/708;
  border-radius:34px;padding:8px;flex-shrink:1;min-height:0;
  background:linear-gradient(160deg,#2c3633,#121715 42%,#232c29);
  box-shadow:0 2px 0 rgba(255,255,255,.07) inset,
             0 40px 70px -28px rgba(0,0,0,.85),
             0 0 0 1px rgba(255,255,255,.05);
  transform:translate3d(0, calc(var(--ty,0px) + var(--lift,0px) + var(--in,52px)), 0)
            rotateY(var(--ry,0deg)) rotateX(var(--rx,0deg)) scale(var(--sc,1));
  transform-style:preserve-3d;opacity:0;
  transition:box-shadow .5s ease,opacity .7s ease,transform .8s cubic-bezier(.2,.8,.2,1)}
body.ready .phone{opacity:1;--in:0px}
body.ready .side.gym .phone{transition-delay:.18s}
body.ready .side.pt .phone{transition-delay:.30s}
/* Once the entrance is done the bob and tilt need to be frame-accurate, so
   the long transform transition has to come off or every tilt update eases
   over 800ms and the phone lags half a second behind the cursor. */
body.settled .phone{transition:box-shadow .5s ease,opacity .4s ease}

.side.lean .phone{box-shadow:0 2px 0 rgba(255,255,255,.09) inset,
             0 48px 84px -26px rgba(0,0,0,.9),
             0 0 0 1px rgba(255,255,255,.08),
             0 0 100px -28px var(--glow,transparent)}
.side.lean{z-index:3}
.side.gym{--glow:#4AC2F0}
.side.pt{--glow:#2BD9A9}
.side.launch .phone{--sc:1.14;--lift:-26px;transition:transform .42s cubic-bezier(.4,0,.6,1)}
body.leaving #stage{opacity:0;transition:opacity .38s ease}

.screen{position:absolute;inset:8px;border-radius:27px;overflow:hidden;background:#0B0F0E;
  display:flex;align-items:center;justify-content:center}
.phone:after{content:"";position:absolute;top:16px;left:50%;transform:translateX(-50%);
  width:62px;height:5px;border-radius:99px;background:rgba(0,0,0,.55);z-index:3}

/* #54635d measured 3.11:1 on this stage — under the 4.5 AA needs for 11px text,
   and it was the ONLY text on the whole site that failed. A cue nobody can read
   is a cue that does not exist, and this one is carrying the single instruction
   that there is more page below the fold. Same value as .brand above (7.74:1),
   so the stage now speaks in one muted voice instead of two. */
.scrollcue{position:absolute;bottom:20px;left:0;right:0;text-align:center;margin:0;z-index:5;
  color:#93a79f;font-family:var(--mono);font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  opacity:0;transition:opacity .6s ease .9s}
body.ready .scrollcue{opacity:1}
.scrollcue.gone{opacity:0;transition-delay:0s}

/* ============ content under the stage ============ */
/* Real content, in the DOM on first paint, with no scripting required. The
   switcher is the moment; this is the page. */
.two{padding:clamp(56px,9vh,96px) 0 8px}
.two h2{font-size:clamp(24px,3.4vw,34px);letter-spacing:-.02em;max-width:620px}
.two .lede{color:var(--text-body);font-size:17px;max-width:620px;margin-bottom:40px}

.routes{display:grid;gap:20px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  padding-bottom:clamp(56px,9vh,96px)}
.route{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:clamp(22px,3vw,32px);text-decoration:none;display:flex;flex-direction:column;
  transition:border-color .3s ease,transform .3s cubic-bezier(.2,.8,.2,1)}
.route:hover{transform:translateY(-3px)}
.route.gymroute:hover{border-color:rgba(74,194,240,.45)}
.route.ptroute:hover{border-color:var(--line-hi)}
.route .kicker{color:var(--muted)}
.route.gymroute .kicker{color:var(--gym)}
.route.ptroute .kicker{color:var(--pt)}
.route h3{font-size:22px;letter-spacing:-.01em;margin:0 0 10px}
.route p{color:var(--text-body);font-size:15px;margin:0 0 18px}
.route ul{list-style:none;margin:0 0 22px;padding:0;color:var(--muted);font-size:14px}
.route li{padding:7px 0;border-top:1px solid var(--line)}
.route .price{font-family:var(--mono);font-size:13px;color:var(--muted);margin:0 0 18px}
.route .price b{color:var(--text);font-weight:600}
.route .more{margin-top:auto;font-weight:600;font-size:15px}
.route.gymroute .more{color:var(--gym)}
.route.ptroute .more{color:var(--pt)}
.route .more em{font-style:normal;display:inline-block;transition:transform .3s ease}
.route:hover .more em{transform:translateX(5px)}

.shared{border-top:1px solid var(--line);padding:clamp(44px,7vh,72px) 0}
.shared h2{font-size:20px;margin-bottom:20px}
.sgrid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.sbox{background:var(--bg-raised);border:1px solid var(--line);border-radius:var(--r);padding:20px}
.sbox h3{font-size:16px;margin:0 0 6px}
.sbox p{color:var(--muted);font-size:14px;margin:0}

footer.foot{border-top:1px solid var(--line);padding:34px 0;color:var(--muted);font-size:13px}
footer.foot .wrap{display:flex;flex-wrap:wrap;gap:8px 22px;align-items:center;justify-content:space-between}
/* WCAG 2.5.8 (AA) wants 24x24 of target. These links rendered 20px tall, which
   on a phone is the difference between tapping Privacy and tapping Terms. The
   padding buys the height inside the existing gap, so nothing moves visually —
   the hit area grows and the layout does not. */
footer.foot a{color:var(--muted);text-decoration:none;
  display:inline-flex;align-items:center;min-height:24px}
footer.foot a:hover{color:var(--text)}
footer.foot nav{display:flex;flex-wrap:wrap;gap:4px 18px;align-items:center}

/* ============ no scripting ============ */
/* The stage is script-driven. With scripting off it collapses and the page
   below carries the whole message — the failure direction is always
   "visible", never blank. */
.noscript-hero{display:none;padding:clamp(56px,10vh,110px) 0 0}
.noscript-hero h1{font-size:clamp(30px,5vw,46px);line-height:1.05;letter-spacing:-.03em;max-width:660px}
.noscript-hero p{color:var(--text-body);font-size:18px;max-width:620px}
@media (scripting:none){
  #stage{display:none}
  .noscript-hero{display:block}
}

@media (max-width:860px){
  .phone{width:min(216px,64%)}
  .s{font-size:14px}
}
@media (max-width:720px){
  .halves{flex-direction:column}
  .side{padding:clamp(36px,6vh,60px) 20px 18px;gap:12px}
  .phone{width:min(150px,40%)}
  .s{display:none}
  body.ready .go{opacity:1;transform:none}
  .side.dim{opacity:.82;transform:none}
  #stage{height:auto;min-height:100svh}
}
@media (prefers-reduced-motion:reduce){
  #stage:before{animation:none}
  .side,.phone,.go,.t,.s,.brand,.scrollcue,.route{transition:none}
  .phone{transform:none;opacity:1}
  .t,.s,.go,.brand,.scrollcue{opacity:1;transform:none}
  .side.dim{opacity:.85;transform:none}
}
