/*
 * The HBVB design system — dark-first.
 *
 * Tokens are lifted verbatim from the design canvas (HBVB Screens.dc.html),
 * so a value here can be diffed against the source of truth rather than
 * eyeballed. Dark is the DEFAULT, not a media-query branch: the cards are
 * foil and holo over a near-black ground, and a light default would make the
 * product's main object look wrong on first paint. Light is a deliberate
 * toggle, stamped as data-theme="light" on <html>.
 *
 * This file deliberately does NOT replace assets/site.css. The old pages are
 * migrating one at a time, and swapping the shared stylesheet would leave
 * every un-migrated page in a half-designed state. site.css goes when the
 * last page stops importing it.
 */
:root {
  /* Surfaces */
  --bg: #05070c;
  --panel: rgba(255, 255, 255, .05);
  --panel-2: rgba(255, 255, 255, .08);
  --br: rgba(255, 255, 255, .10);
  --topbar: rgba(5, 7, 12, .82);
  --sheet: #0B1E42;

  /* Text */
  --txt: #F3E7CE;
  --txt-2: #ffffff;
  --faint: #8EA1BF;
  /* Idle nav text and labels. Dimmer than --faint on purpose: the design uses
     a second, quieter grey for anything that is not the current section. */
  --faint-2: #6E80A0;

  /* Accents. Gold is the brand's primary action colour. */
  --gold: #F5C542;
  /* Gold as TEXT. The brand gold is a 1.6:1 whisper on the light sand ground,
     so light mode needs a darker one - same hue, readable. Surfaces and fills
     keep --gold in both themes. */
  --gold-txt: #F5C542;
  --teal: #37E1D3;
  --coral: #FF6B4A;
  --green: #27C58C;
  --navy: #0B1E42;
  --navy-2: #16376B;
  --navy-deep: #060F22;

  /* Badge tiers, for the skill ladders. */
  --tier-bronze: #D9A56E;
  --tier-silver: #E3E9F2;
  --tier-gold: #F5C542;
  --tier-elite: #37E1D3;

  /* Type. Oswald labels, Rajdhani numerals, Inter prose, Caveat signatures. */
  --label: 'Oswald', 'Helvetica Neue', Arial, sans-serif;
  --display: 'Rajdhani', 'Helvetica Neue', Arial, sans-serif;
  --sans: 'Inter', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --script: 'Caveat', 'Segoe Script', cursive;

  --wrap: 1160px;
  /* The dark ground's light streaks. Zero in light mode. */
  --streak: 1;
  color-scheme: dark;
}

:root[data-theme="light"] {
  --bg: #EAE3D6;
  --panel: #ffffff;
  --panel-2: #F3EEE3;
  --br: rgba(11, 30, 66, .13);
  --topbar: rgba(238, 231, 219, .9);
  --sheet: #ffffff;
  --txt: #1B2E4E;
  --txt-2: #0B1E42;
  --faint: #5E6E88;
  --faint-2: #8494AC;
  --streak: 0;
  --gold-txt: #8A5A08;
  color-scheme: light;
}

* { box-sizing: border-box }
html, body { margin: 0; padding: 0 }

body {
  background: var(--bg);
  color: var(--txt);
  font-family: var(--sans);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

/* The streak wash: sunset over the pier at 88% 8%, deep water bottom-left,
   and two raking light bands. Screen-blended, so it lifts the near-black
   without touching text contrast. Hidden entirely in light mode. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* --wash lets a page dial the brand wash down. Full strength is right on
     the entry pages, where it IS the brand; on an app page - a roster, an
     account table - a warm haze across the whole viewport just makes the
     content look muddy, and the canvas gets away with it only because its
     panels cover most of the frame. `body class="app"` turns it down. */
  opacity: calc(var(--streak) * var(--wash, 1));
  mix-blend-mode: screen;
  /* Softer than the canvas original, deliberately. In the design this sits
     inside a 390x840 phone frame where the two raking bands read as a thin
     sheen across a card; stretched over a full viewport the same stops become
     a rainbow stripe through the headline. The radial glows - sunset over the
     pier, deep water bottom-left - do the work and are kept near full
     strength; the bands are widened and roughly halved so they stay a sheen. */
  background:
    radial-gradient(120% 80% at 88% 8%, rgba(255, 150, 60, .26), transparent 55%),
    radial-gradient(90% 70% at 10% 104%, rgba(20, 45, 100, .45), transparent 62%),
    linear-gradient(116deg, transparent 22%, rgba(245, 197, 66, .06) 40%, rgba(255, 107, 74, .13) 48%, rgba(245, 197, 66, .05) 56%, transparent 74%),
    linear-gradient(116deg, transparent 52%, rgba(55, 225, 211, .06) 67%, transparent 82%);
}
body > * { position: relative; z-index: 1 }
body.app { --wash: .3 }

img { max-width: 100%; display: block }

/* `hidden` has to win against a class that sets `display`.
   The UA rule is `[hidden] { display: none }` - a bare attribute selector,
   which `.btn { display: inline-flex }` outranks. So every button rendered
   with the `hidden` attribute was VISIBLE: the waitlist page showed "Open the
   app" and "Admin" to an account that could reach neither. Any stylesheet
   that sets display on a class needs this line. */
[hidden] { display: none !important }
a { color: var(--teal); text-decoration: none }
a:hover { color: var(--gold) }

:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px }

/* ── the wordmark ───────────────────────────────────────────────────────── */
.mark { display: inline-flex; align-items: center; gap: 9px }
.mark-glyph {
  width: 26px; height: 26px; border-radius: 7px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--gold), var(--coral));
  color: #05070c;
  font-family: var(--label); font-weight: 700; font-size: 15px; line-height: 1;
}
.mark-name {
  font-family: var(--label); font-weight: 700; font-size: 17px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--txt-2);
}

/* ── small caps label, used for every eyebrow and field label ───────────── */
.eyebrow {
  font-family: var(--label); font-weight: 600; font-size: 10.5px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--faint);
}


/* ── headings ───────────────────────────────────────────────────────────── */
h1 {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(34px, 7vw, 52px); line-height: 1.04;
  letter-spacing: -.01em; margin: 0; color: var(--txt-2);
  text-wrap: balance;
}
h1 em { font-style: normal; color: var(--gold-txt) }

.lede { margin: 0; color: var(--faint); font-size: 15px; text-wrap: pretty }

/* ── panels ─────────────────────────────────────────────────────────────── */
.panel {
  background: var(--panel);
  border: 1px solid var(--br);
  border-radius: 18px;
  padding: 22px;
  backdrop-filter: blur(8px);
}

/* ── fields ─────────────────────────────────────────────────────────────── */
.field { display: grid; gap: 7px }
.field label {
  font-family: var(--label); font-weight: 600; font-size: 10.5px;
  letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
}
.field input {
  width: 100%; padding: 13px 14px;
  background: var(--panel-2);
  border: 1px solid var(--br); border-radius: 11px;
  color: var(--txt-2); font-family: var(--sans); font-size: 15px;
}
/* Components for canvas screens that are not built yet - the credits pill,
   the badge rungs, the desktop rail - are deliberately NOT in this file.
   CSS no page exercises is CSS nobody has looked at, and it rots silently.
   Each one lands with the screen that uses it. A test enforces this. */
.field input::placeholder { color: var(--faint); opacity: .7 }
.field input:focus { outline: none; border-color: var(--gold); background: var(--panel) }

/* ── buttons ────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 20px; border-radius: 11px; cursor: pointer;
  border: 1px solid var(--br); background: var(--panel-2); color: var(--txt);
  font-family: var(--label); font-weight: 600; font-size: 13px;
  letter-spacing: .06em; text-transform: uppercase;
}
.btn:hover { border-color: var(--faint); color: var(--txt-2) }
.btn-primary {
  background: linear-gradient(135deg, var(--gold), #F0A93C);
  border-color: transparent; color: #05070c;
}
.btn-primary:hover { filter: brightness(1.07); color: #05070c }
.btn-block { width: 100% }
.btn[disabled] { opacity: .55; cursor: default }

/* ── the theme toggle ───────────────────────────────────────────────────── */
.theme-btn {
  position: fixed; top: 16px; right: 16px; z-index: 3;
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--br); background: var(--panel-2); color: var(--txt);
  font-size: 15px; line-height: 1;
}
.theme-btn:hover { border-color: var(--gold); color: var(--gold) }

/* ── status line ────────────────────────────────────────────────────────── */
/* Collapsed while empty, so a panel is not padded out around a blank line. */
.msg { margin: 0; font-size: 13.5px; color: var(--faint) }
.msg:empty { display: none }
.msg.ok { color: var(--green) }
.msg.error { color: var(--coral) }

/* ── the one-box page: a single centred panel and nothing else ──────────── */
.solo { min-height: 100dvh; display: grid; place-items: center; padding: 28px 20px }
.solo-box { width: 100%; max-width: 420px; display: grid; gap: 22px }

.foot {
  font-family: var(--label); font-weight: 500; font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--faint);
  text-align: center;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * The shared shell
 *
 * Class names are deliberately the SAME as the ones assets/site.css used.
 * Ten pages carry markup and JavaScript written against them, and rewriting
 * that markup to get a new palette would be a large diff whose failures are
 * all behavioural - a lost event handler, a broken selector - for a change
 * that is entirely visual. So the names stay and the design underneath them
 * changes. Where the canvas introduces something new (the credits pill, the
 * desktop rail, the skill ladders) it gets a new name.
 * ═══════════════════════════════════════════════════════════════════════════ */

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 28px }

/* ── top bar ────────────────────────────────────────────────────────────── */
.site-head {
  position: sticky; top: 0; z-index: 20;
  background: var(--topbar);
  border-bottom: 1px solid var(--br);
  backdrop-filter: blur(14px);
}
.site-head-in { display: flex; align-items: center; justify-content: space-between; gap: 20px; height: 62px }

.logo { display: flex; align-items: center; gap: 9px }
.logo:hover { color: inherit }
/* The canvas mark: a rounded gold-to-coral tile with an H. Replaces the old
   sun-over-water circle, which belonged to the previous palette. */
.logo-mark {
  width: 28px; height: 28px; flex: none; border-radius: 8px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--gold), var(--coral));
  color: #05070c; font-family: var(--label); font-weight: 700; font-size: 16px; line-height: 1;
}
.logo-mark::after { content: "H" }
.logo-mark.sm { width: 23px; height: 23px; border-radius: 7px; font-size: 13px }
.logo-name {
  font-family: var(--label); font-weight: 700; font-size: 15px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--txt-2);
}

.head-right { display: flex; align-items: center; gap: 18px }
.head-nav {
  display: flex; align-items: center; gap: 22px;
  font-family: var(--label); font-weight: 600; font-size: 12px;
  letter-spacing: .06em; text-transform: uppercase;
}
.head-nav a { color: var(--faint) }
.head-nav a:hover { color: var(--txt-2) }
.head-nav a[aria-current="page"] { color: var(--gold-txt); box-shadow: inset 0 -2px 0 var(--gold) }

.btn-login {
  font-family: var(--label); font-weight: 600; font-size: 10.5px;
  letter-spacing: .14em; text-transform: uppercase;
  padding: 8px 15px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--br); background: var(--panel-2); color: var(--txt);
}
.btn-login:hover { border-color: var(--gold); color: var(--gold-txt) }


/* ── buttons (extends the base set above) ───────────────────────────────── */
.btn-ghost { background: transparent; border-color: var(--br); color: var(--txt) }
.btn-ghost:hover { border-color: var(--txt-2); background: var(--panel); color: var(--txt-2) }
.btn-sm { padding: 7px 13px; font-size: 11px; border-radius: 9px }

/* ── sections ───────────────────────────────────────────────────────────── */
.section { padding: 52px 0 }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  border-bottom: 1px solid var(--br); padding-bottom: 14px; margin-bottom: 28px;
}
.section-head h2 {
  margin: 0; font-family: var(--label); font-weight: 600; font-size: 12.5px;
  letter-spacing: .2em; text-transform: uppercase; color: var(--txt-2);
}
.section-head p { margin: 0; font-size: 12.5px; color: var(--faint) }



.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px }
.card {
  background: var(--panel); border: 1px solid var(--br); border-radius: 18px;
  padding: 22px; display: flex; flex-direction: column; gap: 9px;
}
.card .num { font-family: var(--display); font-weight: 700; font-size: 12px; letter-spacing: .1em; color: var(--teal) }
.card h3 { margin: 4px 0 0; font-family: var(--display); font-weight: 700; font-size: 19px; color: var(--txt-2) }
.card p { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--faint); text-wrap: pretty }





/* ── chips ──────────────────────────────────────────────────────────────── */
.chips { display: flex; flex-wrap: wrap; gap: 6px }
.chip {
  font-family: var(--label); font-weight: 600; font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--br); background: var(--panel-2); color: var(--faint);
}
.chip.none { border-style: dashed }
.chip.on { border-color: var(--gold); background: rgba(245, 197, 66, .12); color: var(--gold-txt) }
.chip.public { border-color: rgba(55, 225, 211, .35); color: var(--teal) }
/* One selector, not two. A class name in a world-readable stylesheet is a
   hint about the roles that exist, so roles that are not meant to be public
   knowledge borrow this styling instead of getting a selector of their own. */
.chip.admin { border-color: transparent; background: var(--gold); color: #05070c }
.chip.expired { border-style: dashed; color: var(--coral); border-color: color-mix(in srgb, var(--coral) 45%, transparent) }

/* The public site-map renderer and its styles are gone with
   /future-home-page/, the last page from the pre-canvas design. The
   machine-readable map is still generated (public/sitemap.json and
   sitemap.xml), and the human one an operator needs is /admin/pages/. */

/* ── footer ─────────────────────────────────────────────────────────────── */
/*
 * ONE footer, everywhere it appears.
 *
 * There used to be three: a columned `.site-foot` with a copyright line, a
 * colour stripe and a couple of links; `.solo-foot` on the entry pages; and
 * `.pv-foot` inside the preview's phone frame. Three rules, one sentence's
 * worth of content between them, and they had already drifted - the old
 * footer's "Privacy" link went to the home page.
 *
 * The line is the canvas's own sign-off and the only thing a footer on this
 * site has to say. It is the same class in both shapes: a page-level
 * <footer> takes the rule above it and the room a footer needs, a <p> inside
 * a box is just the line.
 */
.page-foot {
  font-family: var(--label); font-weight: 500; font-size: 9.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--faint);
  text-align: center; margin: 10px 0 0;
}
footer.page-foot { border-top: 1px solid var(--br); margin: 0; padding: 26px 20px 24px }

/* ── entry pages ────────────────────────────────────────────────────────── */
.solo-mark { display: flex; align-items: center; gap: 10px }
.solo-mark .logo-name { font-size: 13px }
.solo h1 { margin: 20px 0 0; font-size: clamp(27px, 5vw, 33px) }
.solo .lede { margin: 9px 0 0 }
.solo-alt { margin: 18px 0 0; font-size: 12.5px; color: var(--faint); text-align: center }
.solo-alt a { cursor: pointer }

.login-main { min-height: calc(100dvh - 62px); display: grid; place-items: center; padding: 44px 24px 64px }
.login-box { width: 100%; max-width: 400px; display: grid; gap: 0 }
.login-box h1 { margin: 12px 0 0; font-size: clamp(27px, 5vw, 32px) }
.login-box .lede { margin: 9px 0 0 }

/* The field spacing the old stylesheet baked into `.field` itself. Kept as a
   margin rather than a grid gap so a page can drop a field in anywhere. */
.field { margin-top: 18px }
.login-actions { margin-top: 22px }
.login-rule {
  display: flex; align-items: center; gap: 12px; margin: 24px 0 0;
  font-family: var(--label); font-weight: 600; font-size: 9.5px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--faint);
}
.login-rule::before, .login-rule::after { content: ""; flex: 1; height: 1px; background: var(--br) }

/* The old status-line class. auth.js finds `.login-msg, .msg` and only toggles
   .ok/.error, so both spellings work; this one keeps its old top margin. */
.login-msg { margin: 13px 0 0; font-size: 12.5px; color: var(--faint) }
.login-msg.ok { color: var(--green) }
.login-msg.error { color: var(--coral) }
.login-msg:empty { display: none }


/* ── tabs ───────────────────────────────────────────────────────────────── */
.tabs { display: flex; flex-wrap: wrap; gap: 7px }
.tab {
  font-family: var(--label); font-weight: 600; font-size: 11.5px;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 7px 13px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--br); background: var(--panel-2); color: var(--faint);
}
.tab:hover { color: var(--txt-2) }
.tab b { font-family: var(--display); font-weight: 700; margin-left: 5px }
.tab[aria-pressed="true"] { border-color: transparent; background: var(--gold); color: #05070c }

/* ── responsive ─────────────────────────────────────────────────────────── */
@media (max-width: 980px) {
  .cards { grid-template-columns: 1fr 1fr }
  }
@media (max-width: 680px) {
  .wrap { padding: 0 18px }
  .head-nav { display: none }
  .cards, .sitemap,   .section { padding: 40px 0 }
}

/* ═══════════════════════════════════════════════════════════════════════════
 * The app shell — rail on desktop, tab bar on phones
 *
 * One nav, two shapes, built from one list in assets/shell.js. The breakpoint
 * is where the rail stops being affordable rather than a device guess: below
 * 900px the rail would eat a third of the width, so it becomes a bottom bar.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* 236px, padding 22px 16px - the design's own measurements, kept so the rail
   is the same object at the same size rather than an approximation of it. */
.app-shell { display: grid; grid-template-columns: 236px minmax(0, 1fr); min-height: 100dvh }

.rail {
  border-right: 1px solid var(--br);
  background: var(--topbar);
  backdrop-filter: blur(14px);
  padding: 22px 16px;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100dvh;
}
.rail-brand { display: flex; align-items: center; gap: 11px; padding: 0 8px 22px }
.rail-brand .logo-mark { width: 38px; height: 38px; border-radius: 11px; font-size: 20px }
.rail-brand .logo-name { font-size: 19px; letter-spacing: .05em; line-height: 1 }
.rail-brand small {
  display: block; font-family: var(--sans); font-size: 9px;
  letter-spacing: .02em; text-transform: none; color: var(--faint); margin-top: 2px;
}
.rail-nav { display: flex; flex-direction: column; gap: 3px }
.rail-link {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; border-radius: 11px;
  font-family: var(--label); font-weight: 600; font-size: 13px;
  letter-spacing: .03em; text-transform: none; color: var(--faint-2);
  border: 1px solid transparent;
}
.rail-link svg { width: 19px; height: 19px; flex: none }
.rail-link:hover { color: var(--txt-2); background: var(--panel) }
.rail-link.on { color: var(--gold-txt); background: rgba(245, 197, 66, .12); border-color: rgba(245, 197, 66, .3) }

.rail-foot { margin-top: auto; display: flex; flex-direction: column; gap: 10px }

/* The credits pill, gold-tinted, as designed. */
.rail-credits {
  display: flex; align-items: center; justify-content: space-between;
  background: rgba(245, 197, 66, .1); border: 1px solid rgba(245, 197, 66, .3);
  border-radius: 12px; padding: 10px 13px;
}
.rail-credits span:first-child { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--faint) }
.rail-credits i {
  width: 16px; height: 16px; border-radius: 50%;
  background: linear-gradient(135deg, var(--gold), var(--coral));
}
.rail-credits b { font-family: var(--display); font-weight: 700; font-size: 16px; color: var(--gold-txt) }

/* The person, then the theme toggle, on one row. */
.rail-user { display: flex; align-items: center; gap: 10px; padding: 8px }
.rail-avatar {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: grid; place-items: center; overflow: hidden;
  box-shadow: 0 0 0 2px rgba(245, 197, 66, .5);
  background: var(--panel-2);
  font-family: var(--label); font-weight: 700; font-size: 13px; color: var(--gold-txt);
}
.rail-avatar img { width: 100%; height: 100%; object-fit: cover }
.whoami { flex: 1; min-width: 0 }
.whoami b {
  display: block; font-family: var(--label); font-weight: 600; font-size: 13px;
  text-transform: uppercase; color: var(--txt-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.whoami span { display: block; font-size: 10px; color: var(--faint) }

.tabbar { display: none }

.app-main { min-width: 0; padding: 30px 38px 44px }
.app-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 14px; margin-bottom: 24px }
/* Oswald, uppercase, 30px - the design's page title, not a generic heading. */
.app-head h1 {
  font-family: var(--label); font-weight: 700; font-size: clamp(24px, 3.4vw, 30px);
  text-transform: uppercase; line-height: 1;
}
.app-head .kicker { display: block; font-size: 13px; color: var(--faint); margin-bottom: 3px }

/* The search field. Inert until there is something to search - see the
   comment where it is rendered. */
.app-search {
  display: flex; align-items: center; gap: 8px;
  background: var(--panel); border: 1px solid var(--br); border-radius: 11px;
  padding: 9px 14px; font-size: 13px; color: var(--faint); width: 220px;
}
.app-search input {
  border: 0; background: none; color: var(--txt-2); font-family: var(--sans);
  font-size: 13px; width: 100%; padding: 0;
}
.app-search input::placeholder { color: var(--faint) }
.app-search input:focus { outline: none }

@media (max-width: 900px) {
  .app-shell { grid-template-columns: minmax(0, 1fr) }
  .app-main { padding: 20px 18px calc(80px + env(safe-area-inset-bottom)) }
  .app-search { display: none }

  /* THE RAIL BECOMES A DRAWER rather than disappearing.
     It used to be `display: none`, which meant that on a phone the only
     navigation was the five tab-bar items - and everything in the rail's
     other two groups was unreachable: Welcome, Player card, Membership,
     Badge sheet, Register a role, Create a card, and the whole Admin group.
     Not hidden behind a menu, not harder to find: absent. The tab bar grows
     a "More" entry that opens this. */
  .rail {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 60;
    width: min(290px, 84vw); height: 100dvh; overflow-y: auto;
    background: var(--bg); border-right: 1px solid var(--br);
    /* Clear of the tab bar, which is fixed over the bottom of this. Without
       it the last link in the last group sits underneath it. */
    padding-bottom: calc(92px + env(safe-area-inset-bottom));
    /* visibility, not display: a drawer that is `display: none` cannot
       animate, and one that is only translated is still in the tab order. */
    transform: translateX(-101%); visibility: hidden;
    transition: transform .2s ease, visibility .2s;
  }
  body.nav-open .rail { transform: none; visibility: visible }
  .nav-scrim {
    position: fixed; inset: 0; z-index: 55;
    background: rgba(0, 0, 0, .55); border: 0; padding: 0; width: 100%;
  }
  /* Sixth tab-bar item, and the only one that is a button rather than a link. */
  .tabbar-link.more { background: none; border: 0; cursor: pointer; font: inherit;
    font-family: var(--label); font-weight: 600; font-size: 10px;
    letter-spacing: .08em; text-transform: uppercase; }
}

@media (prefers-reduced-motion: reduce) {
  .rail { transition: none }
}

@media (max-width: 900px) {
  /* The tab bar, to the design's own numbers: a gradient rather than a flat
     panel, so the content fades out under it instead of stopping at a line. */
  .tabbar {
    display: flex; justify-content: space-around; align-items: center;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    padding: 10px 8px calc(10px + env(safe-area-inset-bottom));
    background: linear-gradient(180deg, rgba(5, 7, 12, .3), rgba(5, 7, 12, .98));
    border-top: 1px solid var(--br);
    backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  }
  .tabbar-link {
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    min-width: 52px; padding: 4px 0; color: var(--faint-2);
    font-family: var(--label); font-weight: 600; font-size: 10px;
    letter-spacing: .08em; text-transform: uppercase;
  }
  .tabbar-link svg { width: 24px; height: 24px }
  .tabbar-link.on { color: var(--gold-txt) }
}

:root[data-theme="light"] .tabbar {
  background: linear-gradient(180deg, rgba(251, 248, 241, .4), rgba(251, 248, 241, .98));
}

/* ── panels used across the app screens ─────────────────────────────────── */
.grid { display: grid; gap: 18px }
.grid-2 { grid-template-columns: minmax(0, 380px) minmax(0, 1fr) }
.grid-3 { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)) }
@media (max-width: 900px) { .grid-2 { grid-template-columns: minmax(0, 1fr) } }

/* align-content: start, because a grid whose rows are `auto` still
   STRETCHES them when the container is taller than its contents - and a
   .stack in a .grid-2 column is exactly that whenever the other column
   is longer. Without this the right-hand column of /profile/ spread four
   boxes over 2800px of empty space. */
.stack { display: grid; gap: 12px; align-content: start }
.row { display: flex; align-items: center; justify-content: space-between; gap: 14px }

.list { border: 1px solid var(--br); border-radius: 16px; background: var(--panel); overflow: hidden }
.list-row { display: flex; align-items: center; gap: 14px; padding: 13px 16px; border-bottom: 1px solid var(--br) }
.list-row:last-child { border-bottom: 0 }
.list-row .grow { flex: 1; min-width: 0 }
/* A row of chips next to a label is two items on a desktop and a collision on
   a phone: the chips wrap to five lines while the label stays vertically
   centred, and they end up printed over each other. Wrapping the ROW is the
   fix - the label takes the first line, the chips the rest. */
@media (max-width: 620px) {
  .list-row { flex-wrap: wrap; gap: 8px }
  .list-row .grow { flex: 1 0 100% }
}
.list-row b { display: block; font-size: 14px; color: var(--txt-2) }
.list-row small { display: block; font-size: 12px; color: var(--faint) }
.list-row .num { font-family: var(--display); font-weight: 700; font-size: 19px; color: var(--txt-2) }
.list-row .num small { display: inline; font-family: var(--label); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint) }
.rank { font-family: var(--display); font-weight: 700; font-size: 15px; color: var(--faint); width: 26px; text-align: center }
.list-row.you { background: rgba(245, 197, 66, .07) }

.note {
  border: 1px dashed var(--br); border-radius: 12px; padding: 11px 14px;
  font-size: 12.5px; color: var(--faint);
}
.note b { color: var(--gold-txt); font-family: var(--label); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; font-size: 10px }

/* ── the rail's secondary groups ────────────────────────────────────────── */
/* The design's rail is six entries and stays six. Everything else the canvas
   draws - the card showcase, membership, the badge sheet, the role wizard -
   is reached from inside a screen there, which on a website means reachable
   by URL and nowhere in the nav. These headings keep the six primary and
   still give every screen a link. */
.rail-group {
  font-family: var(--label); font-weight: 600; font-size: 9.5px;
  letter-spacing: .16em; text-transform: uppercase; color: var(--faint-2);
  padding: 16px 12px 6px;
}

/* ── membership tiers ───────────────────────────────────────────────────── */
.tier-card { border: 1px solid var(--br); border-radius: 18px; background: var(--panel); padding: 16px; display: grid; gap: 10px }
.tier-card.current {
  border-color: rgba(245, 197, 66, .5);
  background: linear-gradient(158deg, rgba(245, 197, 66, .14), var(--panel));
}
.tier-top { display: flex; align-items: center; justify-content: space-between; gap: 10px }
.tier-name { display: flex; align-items: center; gap: 10px; font-family: var(--label); font-weight: 700; font-size: 17px; text-transform: uppercase; color: var(--txt-2) }
.tier-swatch { width: 14px; height: 14px; border-radius: 4px; flex: none }
.tier-price { font-family: var(--display); font-weight: 700; font-size: 18px; color: var(--gold-txt) }
/* The slot row is the point of the whole page: a tier buys stat SLOTS, not a
   nicer foil, and drawing the empty ones is what makes that legible. */
.tier-slots { display: flex; align-items: center; gap: 6px }
.tier-slots i { width: 12px; height: 12px; border-radius: 3px; border: 1px solid var(--br); background: var(--panel-2) }
.tier-slots i.on { background: var(--gold); border-color: var(--gold) }
.tier-slots span { font-size: 11px; color: var(--faint); margin-left: 6px }
.tier-desc { margin: 0; font-size: 12px; color: var(--faint); line-height: 1.45 }
.org-card { border: 1px solid var(--br); border-radius: 14px; background: var(--panel); padding: 14px; display: grid; gap: 4px }
.org-card b { font-family: var(--label); font-weight: 700; font-size: 14px; text-transform: uppercase; color: var(--txt-2) }
.org-card .price { font-family: var(--display); font-weight: 700; font-size: 16px }
.org-card.teal .price { color: var(--teal) }
.org-card.coral .price { color: var(--coral) }
.org-card small { font-size: 11px; color: var(--faint) }

/* ── the badge sheet ────────────────────────────────────────────────────── */
.sheet-skill { display: grid; gap: 8px; margin-bottom: 16px }
.sheet-head { display: flex; align-items: baseline; gap: 8px }
.sheet-head b { font-family: var(--label); font-weight: 700; font-size: 14px; text-transform: uppercase; color: var(--txt-2) }
.sheet-head small { font-size: 11px; color: var(--faint) }
.sheet-rungs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px }
.rung { border: 1px solid var(--br); border-radius: 12px; background: var(--panel); padding: 11px 4px; text-align: center }
/* A rotated square lays out at 1x and renders at 1.41x, which is why the mark
   is well inside the cell rather than filling it. */
.rung-mark {
  width: 32px; height: 32px; margin: 0 auto; transform: rotate(45deg);
  border-radius: 9px; border: 1.5px solid currentColor;
  display: grid; place-items: center; background: rgba(6, 15, 34, .35);
}
.rung-mark svg { width: 16px; height: 16px; transform: rotate(-45deg) }
.rung-tier { font-family: var(--label); font-weight: 600; font-size: 9px; letter-spacing: .05em; text-transform: uppercase; margin-top: 9px }
.rung-th { font-family: var(--display); font-weight: 600; font-size: 12px; color: var(--txt); margin-top: 1px }
.rung.bronze { color: var(--tier-bronze) }
.rung.silver { color: var(--tier-silver) }
.rung.gold { color: var(--tier-gold) }
.rung.elite { color: var(--tier-elite) }
/* The rung this athlete actually holds. Everything else on the sheet is the
   chart; this one line is the only claim about a person on the page. */
.rung.held { border-color: currentColor; background: var(--panel-2) }

.caps-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px }
.capstone { border: 1px solid var(--br); border-radius: 16px; background: var(--panel); padding: 16px 10px; text-align: center; color: var(--faint) }
.capstone.gold { color: var(--gold) } .capstone.teal { color: var(--teal) } .capstone.coral { color: var(--coral) }
.capstone.got { border-color: currentColor; background: linear-gradient(158deg, rgba(55, 225, 211, .12), var(--panel)) }
.capstone-mark {
  width: 44px; height: 44px; margin: 0 auto; transform: rotate(45deg);
  border-radius: 12px; border: 2px solid currentColor;
  display: grid; place-items: center; background: rgba(6, 15, 34, .4);
}
.capstone-mark span { transform: rotate(-45deg); font-size: 18px; line-height: 1 }
.capstone b { display: block; font-family: var(--label); font-weight: 700; font-size: 12px; text-transform: uppercase; color: var(--txt-2); margin-top: 11px }
.capstone small { display: block; font-size: 10px; color: var(--faint); margin-top: 3px; line-height: 1.35 }

/* ── the role wizard ────────────────────────────────────────────────────── */
.wiz-bars { display: flex; gap: 6px }
.wiz-bars i { flex: 1; height: 5px; border-radius: 3px; background: var(--panel-2) }
.wiz-bars i.on { background: var(--gold) }
.wiz-step { font-family: var(--label); font-weight: 600; font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--gold-txt) }
.wiz-nav { display: flex; gap: 10px; margin-top: 6px }

/* ── the welcome screen and the card wizard ─────────────────────────────── */
/* These arrived from preview.css when the two design previews became real
   pages. Same rules, renamed off the `pv-` prefix - a class called "preview"
   on a live screen is a lie that outlives the thing it described. */
.why { display: grid; gap: 8px }
.why div { display: flex; align-items: center; gap: 10px; font-size: 13.5px; color: var(--txt) }
.why b {
  width: 22px; height: 22px; flex: none; border-radius: 7px;
  display: grid; place-items: center;
  background: rgba(55, 225, 211, .14); color: var(--teal);
  font-family: var(--label); font-size: 11px;
}

.teaser { border: 1px solid var(--br); border-radius: 16px; background: var(--panel); overflow: hidden }
.teaser > header {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--br);
}
.teaser > footer { padding: 10px 14px; font-size: 12px; color: var(--faint) }
.teaser-row { display: flex; align-items: center; gap: 10px; padding: 9px 14px; border-bottom: 1px solid var(--br) }
.teaser-row:last-of-type { border-bottom: 0 }
.teaser-row .grow { flex: 1; min-width: 0; font-size: 13px; color: var(--txt-2) }
.teaser-row .num { font-family: var(--display); font-weight: 700; font-size: 15px; color: var(--txt-2) }
.teaser-row .num small { font-family: var(--label); font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint) }
.initials {
  width: 26px; height: 26px; border-radius: 50%; flex: none; display: grid; place-items: center;
  background: var(--panel-2); font-family: var(--label); font-weight: 600; font-size: 10px; color: var(--faint);
}

/* The hole where a card is not. Deliberately card-shaped and deliberately
   empty: filling it with someone else's numbers to "show what it looks like"
   is the one thing a card about a person must never do. */
.empty-card {
  aspect-ratio: 5 / 7; display: grid; place-content: center; gap: 6px; text-align: center;
  border: 2px dashed var(--br); border-radius: 18px; background: var(--panel); padding: 24px;
}
.empty-card b { font-family: var(--label); font-weight: 700; font-size: 15px; letter-spacing: .06em; text-transform: uppercase; color: var(--txt-2) }
.empty-card small { font-size: 12px; color: var(--faint) }

.two { display: grid; grid-template-columns: 1fr 1fr; gap: 10px }
.consent {
  display: flex; gap: 10px; align-items: flex-start;
  border: 1px solid var(--br); border-radius: 12px; padding: 12px 13px;
  font-size: 12.5px; color: var(--faint); line-height: 1.5; margin-top: 12px;
}
.consent input { margin: 2px 0 0; flex: none; accent-color: var(--gold) }

/* A row you choose, inside a .list. The checkbox or radio sits at the left
   and the text fills the rest - which is what the seven-skill picker and the
   coach directory both needed, and what `.consent` is NOT: that one is a
   single wide agreement line, and nesting seven of them inside a `.field`
   made them inherit `.field label`'s uppercase 10.5px letter-spaced type and
   collapse into a column. */
.pick-row {
  display: flex; align-items: center; gap: 13px; padding: 12px 16px;
  border-bottom: 1px solid var(--br); cursor: pointer;
  font-family: var(--sans); font-size: 14px; letter-spacing: normal;
  text-transform: none; color: var(--txt-2);
}
.pick-row:last-child { border-bottom: 0 }
.pick-row:hover { background: rgba(245, 197, 66, .05) }
.pick-row.on { background: rgba(245, 197, 66, .09) }
/* so a tick the visitor just made lights up without any JS listening */
.pick-row:has(input:checked) { background: rgba(245, 197, 66, .09) }
/* A .pick-row inside a .field is still a <label>, and `.field label`
   (0,1,1) outranks `.pick-row` (0,1,0) - which is how the beach
   positions came out as narrow uppercase columns even after they were
   moved off `.consent`. Say it again with more specificity. */
.field label.pick-row {
  font-family: var(--sans); font-weight: 400; font-size: 14px;
  letter-spacing: normal; text-transform: none; color: var(--txt-2);
}
.pick-row input { flex: none; margin: 0; width: 16px; height: 16px; accent-color: var(--gold) }
.pick-row .grow { flex: 1; min-width: 0 }
.pick-row b { display: block; font-size: 14px; color: var(--txt-2) }
.pick-row small {
  display: block; font-family: var(--sans); font-size: 12px; font-weight: 400;
  letter-spacing: normal; text-transform: none; color: var(--faint);
}
.list.pick.scroll { max-height: 320px; overflow-y: auto }
.hint { margin: 10px 0 0; font-size: 12px; color: var(--faint) }
.warn {
  border: 1px dashed rgba(224, 162, 60, .5); border-radius: 12px; margin-top: 12px;
  padding: 11px 13px; font-size: 12.5px; color: #F0C070; line-height: 1.5;
}

/* ── the site-settings switch on /admin/ ────────────────────────────────── */
.setting {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-bottom: 22px;
}
.setting .grow { flex: 1; min-width: 220px }
.setting b { display: block; font-family: var(--label); font-weight: 700; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--txt-2) }
.setting small { display: block; font-size: 12.5px; color: var(--faint); margin-top: 3px }
/* A switch that is ON is showing invented data, which is the state worth
   noticing on a page an administrator opens deliberately. */
.setting[data-on="true"] { border-color: rgba(245, 197, 66, .45) }
.setting[data-on="true"] b { color: var(--gold-txt) }
.setting .msg { margin: 6px 0 0; font-size: 12.5px }
.setting .msg:empty { display: none }
/* Unavailable is a state, not a style: the switch is there and cannot be
   used, and it should not look like a button somebody forgot to wire up. */
.setting button[disabled] { opacity: .55; cursor: not-allowed }

/* ── the environment banner ─────────────────────────────────────────────── */
/* Inserted by assets/shell.js, and only for an account that holds env:read.
   It spans both columns of the app shell so it reads as a property of the
   whole window rather than of the page inside it. */
.env-banner {
  grid-column: 1 / -1;
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 8px 18px;
  border-bottom: 1px solid var(--br);
  background: var(--panel);
  font-family: var(--label); font-weight: 500; font-size: 10.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--faint);
}
.env-banner b { color: var(--txt-2); letter-spacing: .16em }
.env-banner em {
  font-style: normal; text-transform: none; letter-spacing: 0;
  font-family: var(--sans); font-size: 12px; color: var(--coral);
}
/* Anything that is NOT production gets the warning colour, because the
   dangerous case is believing you are somewhere safe. Production itself is
   the expected state and stays quiet. */
.env-banner:not(.live) { border-bottom-color: rgba(255, 107, 74, .5) }
.env-banner:not(.live) b { color: var(--coral) }
/* The rail is sticky at 100dvh. With a banner above it that pushes its
   bottom - and the Log out button - below the fold, for exactly the person
   the banner is shown to. `:has()` is already relied on elsewhere in this
   file, so this costs nothing new. */
.app-shell:has(.env-banner) .rail { height: calc(100dvh - 36px) }
