/* ARBA · The Ember Hour — brand stylesheet
   Ember palette, four brand fonts, mobile-first, mom-friendly (big targets). */

:root {
  /* ARBA is a deliberate single dark theme, not light+dark — every color
     below is a fixed brand value, never tied to the OS setting. Without
     this, a phone in light mode still renders native form chrome (the
     Streakboard/Coach <select> dropdowns' own popup, checkboxes) using the
     browser's light-mode UA styling, which looks broken sitting on this
     dark background even though nothing else on the page changed. This
     tells every browser to use its dark UA styling for those controls
     regardless of the phone's own theme setting. */
  color-scheme: dark;
  --ember:   #ff7e2e;
  --amber:   #ffce6e;
  --red:     #ff4d1f;
  --ink:     #0b0518;
  --ink2:    #080216;
  --panel:   #160a26;
  --panel2:  #1e0f31;
  --line:    rgba(255,206,110,0.14);
  --text:    #f6ecff;
  --muted:   #b59ec9;
  --muted2:  #8a76a6;
  --grad:    linear-gradient(90deg, var(--amber), var(--ember) 45%, var(--red));
  --grad-d:  linear-gradient(135deg, #ffce6e, #ff4d1f);
  --radius:  20px;
  --radius-sm: 14px;
  --tap: 56px;
  --maxw: 480px;
  --sheet: 'Hanken Grotesk', system-ui, -apple-system, sans-serif;
  --display: 'Fraunces', Georgia, serif;
  --label: 'Jost', system-ui, sans-serif;
  --prestige: 'Cinzel', Georgia, serif;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* Real bug, found live-testing the claim-check name-field toggle (2026-09-06):
   the browser's own [hidden]{display:none} is a user-agent rule, which loses
   to ANY author rule of equal specificity (like .field{display:block}) by
   cascade origin, not just specificity - so toggling .hidden on a <label
   class="field"> silently did nothing. Elsewhere in this codebase .hidden
   happened to work by luck (plain <input> elements with no conflicting
   display rule); this makes it actually reliable everywhere. */
[hidden] { display: none !important; }
/* Root stays at the browser/OS default (100%, effectively 16px) on purpose —
   a member who has already turned her phone's own text size up gets that
   preference multiplied through every rem value below instead of overridden.
   Every font-size in this file is rem so it scales with her setting; never
   put a px font-size back on html. */
html { font-size: 100%; }
html, body { margin: 0; padding: 0;
  /* Found 2026-09-04 running the Streakboard e2e suite: a long, unbroken
     name (no spaces) in a single copy string — here the Wake line's
     {name} token — can force horizontal overflow of the whole document,
     which then drags the fixed bottom tab bar sideways with it (any
     ancestor `transform` on .app-frame makes it the containing block for
     position:fixed, not the true viewport). A real first name is short
     enough that this was never hit before, but it's a real robustness
     gap, not just a test artifact — this floor stops it app-wide instead
     of chasing it element by element. */
  overflow-x: hidden; }
body {
  background:
    radial-gradient(900px 500px at 50% -8%, rgba(255,126,46,0.18), transparent 60%),
    radial-gradient(700px 500px at 100% 100%, rgba(255,77,31,0.10), transparent 55%),
    var(--ink);
  color: var(--text);
  font-family: var(--sheet);
  /* App base scaled from 17px to a 21px-equivalent per arba-mom-friendly-ux-sop
     (2026-09-04, Keeno: "larger and easier the better... makes it less
     intimidating"). 1.3125rem = 21px only at the browser default of 16px —
     with html at 100% (see above) this is 21px *times* whatever the member
     has already set, not a fixed 21px. */
  font-size: 1.3125rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
  overscroll-behavior-y: none;
}

#app { min-height: 100dvh; }
.app-frame {
  max-width: var(--maxw);
  margin: 0 auto;
  min-height: 100dvh;
  position: relative;
  display: flex;
  flex-direction: column;
}
.view {
  flex: 1;
  padding: 22px 20px 24px;
  padding-top: max(22px, env(safe-area-inset-top));
}
.app-frame.has-tabs .view { padding-bottom: 96px; }
.fade-in { animation: fade .32s ease; }
@keyframes fade { from { opacity: 0; transform: translateY(6px);} to { opacity:1; transform:none;} }

/* ---------- typography ---------- */
.display { font-family: var(--display); font-weight: 600; font-optical-sizing: auto;
  line-height: 1.04; letter-spacing: -0.01em; font-size: 3.0882rem; margin: 0 0 8px; }
.display.sm { font-size: 2.3162rem; }
.display.wake-big { font-size: 2.625rem; }
.cinzel { font-family: var(--prestige); letter-spacing: .02em; }
.kicker { font-family: var(--label); text-transform: uppercase; letter-spacing: .18em;
  font-size: 1.1581rem; color: var(--ember); font-weight: 600; margin: 0 0 6px; }
.lede { color: #e6d8f7; font-size: 1.3125rem; margin: 0 0 14px; }
.muted { color: var(--muted); }
.muted.small, .small { font-size: 1.1581rem; }
.fine { color: var(--muted2); font-size: 1.1581rem; text-align: center; }
h1,h2,h3 { font-family: var(--display); font-weight: 600; }
h3 { font-size: 1.3897rem; margin: 0; }
.brand-rule { width: 84px; height: 12px; border-radius: 8px; background: var(--grad); margin-bottom: 18px; }

/* ---------- focus (a11y audit finding, 2026-09-03) ----------
   Before this rule, only text inputs/textareas had an explicit focus style
   (line ~112) — every button, icon-button, tab, chip, and card relied on
   the browser's unstyled default outline, which is invisible or clipped on
   several rounded/circular controls (.icon-btn, .arch-card, .sb-cell).
   One brand-consistent :focus-visible ring, app-wide, closes that gap
   without touching mouse/touch interaction (:focus-visible only engages
   for keyboard/switch-control navigation in evergreen browsers). */
button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 2px;
}

/* ---------- buttons ---------- */
.btn { font-family: var(--label); font-weight: 600; font-size: 1.2353rem; border: none;
  border-radius: 999px; padding: 0 22px; min-height: var(--tap); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  color: var(--text); background: rgba(255,255,255,0.08); transition: transform .08s, filter .2s;
  width: 100%; }
.btn:active { transform: scale(.98); }
.btn:disabled { opacity: .4; cursor: default; }
.btn-primary { background: var(--grad-d); color: #2a0a02; box-shadow: 0 8px 24px rgba(255,77,31,0.32); }
.btn-primary:active { filter: brightness(1.05); }
.btn-lg { min-height: 60px; font-size: 1.3125rem; }
.btn-xl { min-height: 84px; font-size: 1.6985rem; flex-direction: column; gap: 2px; }
.btn-xl small { font-weight: 500; font-size: 1.1581rem; opacity: .8; font-family: var(--sheet); }
.btn-ghost { background: transparent; color: var(--muted); border: 1px solid var(--line); }
/* .sm: dense staff-facing contexts (Coach Dashboard row actions) where a
   full-width 44px button would break a table row - the dashboard is already
   exempt from the mom-facing tap-target floor (see .coach-dash comment). */
.btn.sm { width: auto; min-height: 36px; padding: 0 12px; font-size: 0.9rem; }
/* Item 7: the Welcome screen's small "Trainer?" entry point — visually
   de-emphasized (this isn't a member action) but keeps the full 44px tap
   target since real members will still see and could tap it by accident. */
.muted-link { opacity: 0.55; font-size: 0.9rem; border-color: transparent; }
.btn-danger { background: rgba(255,77,31,0.14); color: #ff9a7d; border: 1px solid rgba(255,77,31,0.3); }
/* A real secondary CTA, not a subtle link (btn-ghost) - for the Iron Factory
   quick check-in (2026-09-06, Keeno): "front and center, easy to see,
   enlarge" for a client who wants the fast path, not the tiny/quiet
   treatment btn-ghost gives the "Same as usual" shortcut right above it. */
.btn-outline { background: transparent; color: var(--amber, #ffce6e);
  border: 2px solid var(--ember, #ff7e2e); }
.btn-outline:active { background: rgba(255,126,46,0.12); }
.icon-btn { width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--line);
  background: rgba(255,255,255,0.05); color: var(--text); font-size: 1.8529rem; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  transition: transform .08s ease, background .18s ease; }
.icon-btn:active { transform: scale(.94); }
.icon-btn svg { display: block; }
.icon-btn.ghost { color: var(--muted); }
.xp-tag { font-size: 1.1581rem; background: rgba(0,0,0,0.18); padding: 3px 9px; border-radius: 999px; }
.xp-inline { color: var(--ember); font-style: normal; font-size: 1.1581rem; font-family: var(--label); }

/* ---------- fields ---------- */
.field { display: block; margin: 14px 0; }
.field > span { display: block; font-family: var(--label); font-size: 1.1581rem; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }
.field input, .field textarea, textarea, input[type=number] {
  width: 100%; background: var(--panel); border: 1px solid var(--line); color: var(--text);
  border-radius: var(--radius-sm); padding: 15px 16px; font-size: 1.3125rem; font-family: var(--sheet);
  min-height: var(--tap); }
.field textarea, textarea { min-height: 88px; resize: vertical; line-height: 1.5; }
input:focus, textarea:focus { outline: none; border-color: var(--ember); box-shadow: 0 0 0 3px rgba(255,126,46,0.16); }
.field .warn { color: var(--red); font-style: normal; font-size: 1.1581rem; margin-left: 6px; }
.check, .perm { display: flex; gap: 12px; align-items: flex-start; margin: 12px 0; cursor: pointer; }
.check input, .perm input { width: 24px; height: 24px; min-height: 24px; accent-color: var(--ember); margin-top: 2px; }
.perm span { display: flex; flex-direction: column; }
.perm b { font-family: var(--sheet); }
.perm small { color: var(--muted); }

/* ---------- onboarding ---------- */
.onboard { display: flex; flex-direction: column; min-height: calc(100dvh - 46px); }
.onboard.has-photo { padding: 0; }
.ob-dots { display: flex; gap: 7px; margin-bottom: 22px; }
.ob-dots span { flex: 1; height: 5px; border-radius: 3px; background: rgba(255,255,255,0.1); }
.ob-dots span.on { background: var(--grad); }
.ob-hero { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.ob-hero .display { font-size: 2.9338rem; }
.ob-quote { margin-top: 22px; font-family: var(--display); font-style: italic; font-size: 1.5441rem; color: var(--amber); }
.ob-actions { margin-top: auto; padding-top: 22px; display: flex; flex-direction: column; gap: 10px; }

/* "Continue with Google" (2026-09-12) — additive to the email+password form
   below it, never a replacement. GIS's own rendered button is a fixed-height
   iframe (40px at size:'large') that this app can't resize directly; the
   8px of padding here brings the wrapper's own footprint up near the
   mom-friendly-UX SOP's 44px tap-target floor around it. Centered because
   the button's own `width` option renders it at a fixed pixel size, not a
   responsive 100%. */
.google-btn-wrap { display: flex; justify-content: center; padding: 8px 0; margin-bottom: 4px; }
.google-btn-wrap[hidden] { display: none; }
.ob-divider { display: flex; align-items: center; gap: 10px; margin: 4px 0 14px; color: var(--muted); font-size: 0.9rem; }
.ob-divider[hidden] { display: none; }
.ob-divider::before, .ob-divider::after { content: ''; flex: 1; height: 1px; background: rgba(255,255,255,0.12); }

/* ---------- welcome hero (full-bleed group photo + scrim) ---------- */
/* Landscape group photo (three women mid-workout at sunrise) — full-bleed
   background with a dark ember/ink scrim for legibility, headline/kicker
   overlaid directly on the photo. Market-standard hero treatment, chosen
   over the old portrait cutout because the source photo is landscape and a
   group shot, not a single standing subject (2026-09-03). */
.ob-hero-photo { position: relative; aspect-ratio: 16 / 11; overflow: hidden;
  background: var(--ink); display: flex; align-items: flex-end;
  margin: calc(-1 * max(22px, env(safe-area-inset-top))) -20px 0; }
.ob-hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  object-position: center 42%; animation: heroFadeIn .6s ease both; }
.ob-hero-scrim { position: absolute; inset: 0; background:
  linear-gradient(180deg, rgba(8,2,22,0.32) 0%, rgba(8,2,22,0.28) 40%, rgba(8,2,22,0.72) 78%, var(--ink) 100%); }
.ob-hero-textwrap { position: relative; z-index: 1; padding: 0 20px 22px; width: 100%; }
.ob-hero-textwrap .kicker { color: var(--amber); text-shadow: 0 2px 14px rgba(0,0,0,0.7); }
.ob-hero-title { font-size: 2.4706rem; line-height: 1.12; text-shadow: 0 4px 24px rgba(0,0,0,0.65); margin: 0;
  animation: heroRise .6s .12s ease both; }
.ob-below-hero { padding: 18px 20px 24px; flex: 1; display: flex; flex-direction: column; justify-content: center; }
/* Item 5 (2026-09-06, Keeno's live-URL feedback): "get rid of the awkward
   space between our quote and begin it looks no good." .ob-actions's shared
   margin-top:auto (still correct for the older non-photo onboarding screens
   that use .ob-hero) was pinning Begin to the very bottom of this taller
   photo layout, leaving a big dead gap under the quote instead. Scoped
   override: group lede/quote/actions together and center the whole block,
   so any leftover space lands evenly above and below instead of as one
   ugly hole. */
.ob-below-hero .ob-actions { margin-top: 26px; }
.ob-below-hero .lede { animation: heroRise .5s .26s ease both; }
.ob-below-hero .ob-quote { animation: heroRise .5s .34s ease both; }
.ob-below-hero .ob-actions { animation: heroRise .5s .4s ease both; }
@keyframes heroFadeIn { from { opacity: 0; transform: scale(1.04); } to { opacity: 1; transform: scale(1); } }
@keyframes heroRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .ob-hero-bg, .ob-hero-title, .ob-below-hero .lede, .ob-below-hero .ob-quote, .ob-below-hero .ob-actions {
    animation: none !important; opacity: 1 !important; transform: none !important; }
}

.xp-explain-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 6px 16px; margin: 16px 0; }
.xp-explain-row { display: flex; gap: 14px; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--line); }
.xp-explain-row:last-child { border-bottom: none; }
.xer-emoji { font-size: 2.0074rem; flex: 0 0 auto; }
.xp-explain-row b { display: block; font-family: var(--display); font-size: 1.2353rem; }
.xp-explain-row small { color: var(--muted); font-size: 1.1581rem; }

.arch-grid { display: flex; flex-direction: column; gap: 12px; margin: 8px 0 4px; }
.arch-card { text-align: left; background: var(--panel); border: 1.5px solid var(--line);
  border-radius: var(--radius); padding: 16px; cursor: pointer; display: grid;
  grid-template-columns: 48px 1fr; grid-template-rows: auto auto auto; column-gap: 14px; color: var(--text); }
.arch-card .arch-emoji { font-size: 2.9338rem; grid-row: 1 / span 3; align-self: center; }
.arch-card .arch-name { font-family: var(--display); font-size: 1.6985rem; font-weight: 600; }
.arch-card .arch-sub { font-family: var(--label); font-size: 1.1581rem; text-transform: uppercase;
  letter-spacing: .1em; color: var(--ember); }
.arch-card .arch-blurb { grid-column: 2; color: var(--muted); font-size: 1.1581rem; margin-top: 4px; }
.arch-card.sel { border-color: var(--ember); background: linear-gradient(180deg, rgba(255,126,46,0.14), var(--panel));
  box-shadow: 0 6px 22px rgba(255,77,31,0.18); }

/* ---------- the Hearth ---------- */
.hearth-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; gap: 10px; }
/* Item: "EVENING · ANYTIME WORKS HERE" was breaking mid-phrase on a 390px
   viewport (kicker tracking/size tuned for short words, not this longer
   combined string) — tighten tracking/size here only, so it reads as one
   calm line instead of an awkward two-line wrap next to the pills. */
.hearth-top .kicker { font-size: 1.1581rem; letter-spacing: .1em; max-width: 210px; }
.kicker-nowrap { white-space: nowrap; }
.hearth-pills { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
.flame-pill { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,126,46,0.16);
  border: 1px solid rgba(255,126,46,0.3); color: var(--amber); border-radius: 999px; padding: 8px 14px;
  font-size: 1.3897rem; font-weight: 700; cursor: pointer; }
.xp-pill { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,206,110,0.14);
  border: 1px solid rgba(255,206,110,0.3); color: var(--amber); border-radius: 999px; padding: 6px 14px;
  cursor: pointer; font-family: var(--sheet); }
.xp-pill-emoji { font-size: 1.2353rem; }
.xp-pill-body { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
.xp-pill-body b { font-family: var(--display); font-size: 1.2353rem; color: var(--text); }
.xp-pill-body small { font-size: 1.1581rem; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }

.xp-callout { display: flex; gap: 12px; align-items: flex-start; background: linear-gradient(135deg, rgba(255,206,110,0.14), var(--panel));
  border: 1px solid rgba(255,206,110,0.32); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 16px; }
.xp-callout b { font-family: var(--display); font-size: 1.2353rem; }
.xp-callout p { margin: 4px 0 0; color: var(--muted); font-size: 1.1581rem; }
.xp-callout-x { flex: 0 0 auto; background: rgba(255,126,46,0.2); border: none; color: var(--amber);
  border-radius: 999px; padding: 0 16px; min-height: 44px; font-family: var(--label); font-weight: 600; font-size: 1.1581rem; cursor: pointer; }
.live-card { display: flex; gap: 16px; align-items: center; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; margin-bottom: 16px; }
.live-card.open { background: linear-gradient(135deg, rgba(255,126,46,0.16), var(--panel)); border-color: rgba(255,126,46,0.34); }
/* Two real bugs found rendering this at the new type scale, not one:
   (1) .live-ring is a flex-row child with no flex-shrink:0, so once its rem
   width grew there wasn't room in the row and only its WIDTH got squeezed
   (flex shrinks the main axis, not the cross axis) — a circle became an
   oval/crescent. flex:0 0 auto below fixes that.
   (2) -webkit-mask on a parent clips that parent's ENTIRE painted output,
   descendants included — the count span's own `-webkit-mask: none` only
   means the span has no mask of its OWN, it does not exempt the span from
   the hole already punched in its ancestor. The digit was sitting exactly
   inside that punched-out hole and was invisible at every size, before this
   pass too — this was never actually visible, "breaks into two arcs" just
   made it visually obvious. Real fix: move the ring art (conic-gradient +
   mask) onto a ::before layer so the live span is never inside the masked
   paint region. Ring size stays in rem, matching the text scale, so both
   grow together under a member's own OS text-size preference. */
.live-ring { position: relative; width: 5.8676rem; height: 5.8676rem; flex: 0 0 auto;
  display: grid; place-items: center; }
.live-ring::before { content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--ember), var(--red), var(--amber), var(--ember));
  -webkit-mask: radial-gradient(circle 2.3162rem at center, transparent 0, transparent 2.1618rem, #000 2.239rem); }
.live-ring span { position: relative; font-family: var(--display); font-size: 2.3162rem; font-weight: 600; color: var(--text); }
.live-lead { font-family: var(--display); font-size: 1.5441rem; margin: 0; }
.live-sub { color: var(--muted); margin: 4px 0 0; font-size: 1.1581rem; }
.wake-mini { background: var(--panel2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 16px; }
.wake-label { font-family: var(--label); font-size: 1.1581rem; letter-spacing: .18em; color: var(--ember); margin: 0 0 6px; }
.wake-text { font-family: var(--display); font-size: 1.4669rem; font-style: italic; margin: 0; line-height: 1.3;
  overflow-wrap: anywhere; word-break: break-word; }
.light-up { margin-bottom: 18px; }
.light-up .lu-emoji { font-size: 2.1618rem; }
.log-session-hero { margin-top: 14px; }
.relight-secondary { margin: 10px 0 18px; font-size: 1.1581rem; }
.done-banner { display: flex; gap: 12px; align-items: center; background: rgba(96,200,120,0.1);
  border: 1px solid rgba(120,220,150,0.28); border-radius: var(--radius); padding: 16px; margin-bottom: 18px; }
.done-check { width: 40px; height: 40px; border-radius: 50%; background: #57c97a; color: #06210e;
  display: grid; place-items: center; font-weight: 800; font-size: 1.6985rem; }
.done-banner small { display: block; color: var(--muted); font-size: 1.1581rem; }

.row-head { display: flex; justify-content: space-between; align-items: baseline; margin: 18px 0 10px; }

/* ---------- Hearth composer trigger — a real "share a post" entry point,
   not just an auto-generated Light Up post or a buried Journal share ---- */
.compose-trigger { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 56px;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 999px; padding: 8px 10px 8px 8px;
  cursor: pointer; color: var(--muted); font-family: var(--sheet); font-size: 1.1581rem; text-align: left; }
.compose-trigger:active { transform: scale(.99); }
.ct-avatar { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
  background: var(--grad-d); flex: 0 0 auto; font-size: 1.3897rem; }
.ct-placeholder { flex: 1; }
.ct-camera { font-size: 1.3897rem; flex: 0 0 auto; opacity: .8; }

/* ---------- composer sheet ---------- */
.composer-sheet h3 { margin-bottom: 4px; }
.composer-who { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 1.1581rem; margin-bottom: 14px; }
.composer-who .ct-avatar { width: 28px; height: 28px; font-size: 1.1581rem; }
.composer-photo-zone { display: flex; flex-direction: column; align-items: center; justify-content: center;
  border: 2px dashed rgba(255,126,46,0.4); border-radius: var(--radius); background: rgba(255,126,46,0.05);
  padding: 18px; text-align: center; cursor: pointer; margin: 10px 0 4px; }
.composer-photo-zone.has-photo { padding: 0; border-style: solid; overflow: hidden; }
.composer-photo-zone img { width: 100%; max-height: 220px; object-fit: cover; display: block; border-radius: 14px; }
.composer-photo-remove { margin-top: 8px; }

/* ---------- real community Feed (replaces the old fake seed-crew rows) --- */
.feed-list { display: flex; flex-direction: column; gap: 12px; }
.feed-post { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 14px 16px; transition: transform .12s ease; }
.feed-post:active { transform: scale(.99); }
/* text-only posts (no photo/metrics) get a subtle gradient wash instead of a
   flat panel, so the feed doesn't read as a wall of identical rectangles —
   pattern borrowed from 2ndPrime's community cards, ARBA's own ember tones */
.feed-post.text-only { background: linear-gradient(135deg, rgba(255,126,46,0.09), var(--panel) 60%); }
.fp-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.fp-avatar { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  background: var(--grad-d); flex: 0 0 auto; box-shadow: 0 3px 10px rgba(255,77,31,0.28); }
.fp-emoji { font-size: 1.3125rem; }
.fp-who { flex: 1; display: flex; flex-direction: column; line-height: 1.2; }
.fp-who b { font-family: var(--display); font-size: 1.1581rem; }
.fp-who small { color: var(--muted); font-size: 1.1581rem; }
.fp-time { color: var(--muted2); font-size: 1.1581rem; white-space: nowrap; }
.fp-badge { display: inline-block; background: rgba(255,126,46,0.18); color: var(--amber);
  border-radius: 999px; padding: 3px 10px; font-family: var(--label); font-size: 1.1581rem;
  letter-spacing: .06em; text-transform: uppercase; margin-bottom: 6px; }
.fp-activity { margin: 4px 0; font-size: 1.1581rem; }
.fp-note { margin: 6px 0; color: #e6d8f7; font-size: 1.1581rem; font-style: italic;
  overflow-wrap: anywhere; word-break: break-word; }
.fp-card-btn { display: block; width: 100%; border: none; padding: 0; margin: 8px 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.fp-card-btn .fp-card { margin: 0; }
.fp-card { width: 100%; border-radius: 14px; margin: 8px 0; max-height: 260px; object-fit: cover; }
.fp-metrics { display: flex; gap: 14px; margin: 6px 0; font-size: 1.1581rem; color: var(--muted); }
/* the like button is one of the app's 3 deliberate focal points (with XP and
   journal) — 44px tap-target floor, not the 32px used by the lower-priority
   secondary chips (reply/pin/report) in the same row. */
.fp-like { margin-top: 8px; background: rgba(255,255,255,0.06); border: 1px solid var(--line);
  color: var(--muted); border-radius: 999px; padding: 0 16px; font-size: 1.1581rem; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px; width: auto; min-height: 44px; }
.fp-like.liked { background: rgba(255,126,46,0.18); border-color: rgba(255,126,46,0.4); color: var(--amber); }
.fp-like b { font-variant-numeric: tabular-nums; }

.mini-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 16px; }
.mini-card { display: flex; flex-direction: column; gap: 6px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 14px; color: var(--text); cursor: pointer; align-items: flex-start; }
.mini-card.static { cursor: default; }
.mini-emoji { font-size: 1.8529rem; }
.mini-label { font-size: 1.1581rem; color: var(--muted); }

/* ---------- flow (Light Up) ---------- */
.flow { min-height: calc(100dvh - 46px); display: flex; flex-direction: column; }
/* TOAST-OVER-SHARE-001 (2026-09-12): .flow has no tabbar (lightup isn't in
   TAB_ROUTES, js/app.js), so .flow-actions's margin-top:auto pins the last
   button flush to the true viewport bottom on any short screen. The
   card-result ("finished") screen is short enough to hit exactly that —
   and its own buttons (#share, #postFeed) are the ones that fire .toast,
   which sits fixed at bottom:110px and can run 2-3 lines for real copy
   ("Card saved — ready for Instagram."), reaching ~220px up from the
   viewport bottom. Confirmed live with Playwright: #share's bounding box
   overlapped #toast's after tapping Share. Reserving bottom clearance on
   .finished itself (border-box, so this shrinks the area .flow-actions's
   margin-top:auto anchors within — the same shape as
   .app-frame.has-tabs .view's 96px tabbar clearance) keeps this screen's
   action row above the toast's band by construction, instead of guessing
   at where its content happens to land. Scoped to .finished rather than
   all of .flow: it's the one flow screen whose own buttons are the toast's
   trigger, and the one short enough for margin-top:auto to reach the
   floor at all. */
.finished { padding-bottom: 240px; }
.flow-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.flow-head .kicker { margin: 0; flex: 1; text-align: center; }
.beat-bar { display: flex; gap: 6px; margin-bottom: 22px; }
.beat-bar span { flex: 1; height: 5px; border-radius: 3px; background: rgba(255,255,255,0.1); }
.beat-bar span.on { background: var(--grad); }
.beat-bar span.done { background: rgba(255,126,46,0.5); }
.flow-body { flex: 1; display: flex; flex-direction: column; }
.flow-actions { margin-top: auto; padding-top: 18px; }
.flow-actions.stack { display: flex; flex-direction: column; gap: 10px; }

.wake-screen { flex: 1; display: flex; flex-direction: column; justify-content: center; text-align: left; }
.wake-kicker { font-family: var(--label); text-transform: uppercase; letter-spacing: .14em; font-size: 1.1581rem; color: var(--ember); }
.wake-foot { color: var(--muted); margin-top: 18px; }

.rally-live { text-align: center; margin: 8px 0 18px; }
.rally-num { font-family: var(--display); font-size: 4.3235rem; font-weight: 600; color: var(--amber); display: block; line-height: 1; }
.rally-live p { color: var(--muted); margin: 4px 0 0; }
.mood-row { display: grid; grid-template-columns: repeat(4,1fr); gap: 8px; margin-bottom: 16px; }
.mood { background: var(--panel); border: 1.5px solid var(--line); border-radius: var(--radius-sm); padding: 12px 4px;
  display: flex; flex-direction: column; align-items: center; gap: 4px; color: var(--text); cursor: pointer; }
.mood span { font-size: 2.0074rem; }
.mood small { color: var(--muted); font-size: 1.1581rem; }
.mood.sel { border-color: var(--ember); background: rgba(255,126,46,0.12); }

.act-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; margin: 14px 0; }
.act-card { background: var(--panel); border: 1.5px solid var(--line); border-radius: var(--radius-sm); padding: 16px 6px;
  display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--text); cursor: pointer; font-size: 1.1581rem; }
.act-emoji { font-size: 2.3162rem; }
.act-card.sel { border-color: var(--ember); background: rgba(255,126,46,0.12); box-shadow: 0 4px 16px rgba(255,77,31,0.16); }

.or-divider { text-align: center; color: var(--muted2); font-size: 1.1581rem; text-transform: uppercase;
  letter-spacing: .12em; margin: 14px 0; position: relative; }
.or-divider::before, .or-divider::after { content: ''; position: absolute; top: 50%; width: 40%;
  height: 1px; background: var(--line); }
.or-divider::before { left: 0; } .or-divider::after { right: 0; }
.or-divider span { background: var(--ink); padding: 0 10px; }

.official-toggle { background: rgba(255,126,46,0.08); border: 1px solid rgba(255,126,46,0.24);
  border-radius: var(--radius-sm); padding: 12px 14px; margin: 4px 0 8px; }
.official-toggle span { font-size: 1.1581rem; }

.snap-zone { display: block; border: 2px dashed rgba(255,126,46,0.4); border-radius: var(--radius);
  background: rgba(255,126,46,0.05); padding: 36px 16px; text-align: center; cursor: pointer; margin: 16px 0; }
.snap-inner { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.snap-emoji { font-size: 3.7059rem; }
.snap-inner small { color: var(--muted); font-size: 1.1581rem; }
.venue-thumb, #watchPreviewWrap img { width: 100%; border-radius: var(--radius); margin-top: 8px; max-height: 220px; object-fit: cover; }
.metric-fields { display: flex; flex-direction: column; }

/* EMBER-CARD-BACKGROUNDS (2026-09-11) — proofVenue()'s "or pick a background
   instead" row: three on-brand fields as an alternative to a real photo. */
.proof-or { display: flex; align-items: center; gap: 10px; color: var(--muted2, var(--muted)); text-transform: uppercase;
  letter-spacing: .12em; font-size: 1.1581rem; margin: 18px 0 10px; }
.proof-or::before, .proof-or::after { content: ''; height: 1px; flex: 1; background: var(--line); }
.background-options { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.background-option { display: flex; flex-direction: column; align-items: center; gap: 7px; padding: 8px;
  min-height: 44px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel);
  color: var(--text); cursor: pointer; font: inherit; font-size: 1.1581rem; }
.background-option.selected { border-color: var(--ember); box-shadow: 0 0 0 2px rgba(255,126,46,0.22); }
.background-swatch { display: block; width: 100%; min-height: 64px; border-radius: 10px; }
.higgs-bg-dawn { background: radial-gradient(circle at 55% 25%, rgba(255,214,133,.55), transparent 42%), linear-gradient(145deg, #6d4162, #d16f55 48%, #20142f); }
.higgs-bg-ember { background: radial-gradient(circle at 52% 25%, rgba(255,214,133,.55), transparent 42%), linear-gradient(145deg, #7a2e39, #d35a3c 48%, #180b22); }
.higgs-bg-night { background: radial-gradient(circle at 52% 25%, rgba(146,202,255,.42), transparent 42%), linear-gradient(145deg, #17284a, #304b68 48%, #0b1026); }

/* MULTI-WORKOUT-001 (2026-09-11) — Hearth's "Log another workout today" +
   the History section's private log list. */
.additional-workout-btn { margin-top: 6px; }
.additional-workout-note { margin: 6px 2px 0; }
.history-list { display: flex; flex-direction: column; gap: 10px; }
.history-item { padding: 14px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel); }
.history-kind { color: var(--muted); font-size: 1.1581rem; margin-bottom: 6px; }
.history-title { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.history-title span { color: var(--amber); font-size: 1.1581rem; }
.history-item p { overflow-wrap: anywhere; }
.history-private-note { color: var(--text); font-style: italic; }
.history-xp { color: var(--amber); font-size: 1.1581rem; margin-bottom: 0; }

/* finished */
.finished { text-align: center; }
.finished-top { padding: 12px 0 8px; }
.big-burst { font-size: 4.9412rem; animation: pop .5s ease; }
@keyframes pop { 0%{transform:scale(.4);opacity:0;} 60%{transform:scale(1.15);} 100%{transform:scale(1);opacity:1;} }
.card-reveal { margin: 12px auto; max-width: 240px; }
.card-reveal img { width: 100%; border-radius: 18px; box-shadow: 0 14px 40px rgba(0,0,0,0.5); }
.xp-breakdown { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 8px 16px; margin: 14px 0; text-align: left; }
.xp-line { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--line); }
.xp-line:last-child { border-bottom: none; }
.xp-line.mult { color: var(--amber); }
.xp-line.total { font-family: var(--display); font-size: 1.5441rem; border-top: 2px solid var(--line); }
.xp-line.total b { color: var(--ember); }

/* ---------- You ---------- */
.you-hero { text-align: center; padding: 8px 0 16px; }
.you-emoji { font-size: 5.5588rem; }
.you-identity { font-family: var(--label); text-transform: uppercase; letter-spacing: .1em; color: var(--ember); font-size: 1.1581rem; }
.belt-chip { display: inline-block; margin-top: 10px; background: rgba(255,206,110,0.14);
  border: 1px solid rgba(255,206,110,0.3); color: var(--amber); border-radius: 999px;
  padding: 6px 16px; font-family: var(--label); font-size: 1.1581rem; letter-spacing: .04em; }
.stat-row { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; margin: 14px 0 18px; }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 14px; text-align: center; }
.stat b { font-family: var(--display); font-size: 1.8529rem; display: block; }
.stat small { color: var(--muted); font-size: 1.1581rem; }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 14px; }
.panel-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 12px; }
.bar { height: 14px; border-radius: 8px; background: rgba(255,255,255,0.1); overflow: hidden; }
/* animates transform: scaleX() off a fixed-width base rather than the
   `width` layout property — avoids layout thrash while producing the
   identical visual fill-bar behavior. JS sets --pct as a 0..1 fraction. */
.bar span { display: block; width: 100%; height: 100%; background: var(--grad); border-radius: 8px;
  transform: scaleX(var(--pct, 0)); transform-origin: left center; transition: transform .5s ease; }
.bar.slim { height: 8px; }
.flame-tier { margin-top: 10px; font-size: 1.1581rem; color: var(--muted); }
.flame-tier b { color: var(--amber); }
.gate-row { display: grid; grid-template-columns: 100px 1fr auto; gap: 10px; align-items: center; margin: 10px 0; font-size: 1.1581rem; }
.gate-row b { font-variant-numeric: tabular-nums; }
.prestige-panel { background: linear-gradient(135deg, rgba(255,206,110,0.1), var(--panel2)); border-color: rgba(255,206,110,0.3); }
.mini-stats { display: grid; grid-template-columns: repeat(auto-fit,minmax(66px,1fr)); gap: 8px; text-align: center; }
.mini-stats b { font-family: var(--display); font-size: 1.3897rem; display: block; }
.mini-stats small { color: var(--muted); font-size: 1.1581rem; }
.you-foot { margin-top: 8px; }
.you-foot-global { margin-top: 18px; text-align: center; }

/* Real visual hierarchy in the Overview section: the Flame is the primary
   panel (bigger padding, a warm border glow); Season/Iron-Factory is folded
   into one lighter tertiary panel instead of two more uniform cards. */
.panel-primary.flame-panel { padding: 20px 20px 22px; border-color: rgba(255,126,46,0.35);
  background: linear-gradient(160deg, rgba(255,126,46,0.10), var(--panel)); }
.panel-primary.flame-panel .panel-head h3 { font-size: 1.4669rem; }
.panel-tertiary.season-panel { padding: 14px 16px; background: var(--panel2); }
.panel-tertiary.season-panel .panel-head h3 { font-size: 1.1581rem; color: var(--muted); font-family: var(--label);
  text-transform: uppercase; letter-spacing: .06em; }
.season-panel-iron { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }

/* You hub — in-page segmented control (Overview/Battle Pass/Cards/Journal).
   Nav consolidation: these used to be 3 separate bottom-nav tabs + routes. */
.you-tabs { display: grid; grid-template-columns: repeat(4,1fr); gap: 6px; margin: 18px 0 16px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 5px; }
.you-tab { background: none; border: none; color: var(--muted2); display: flex; flex-direction: column;
  align-items: center; gap: 2px; padding: 8px 2px; border-radius: 12px; cursor: pointer;
  font-family: var(--label); font-size: 1.1581rem; min-height: 52px; }
.you-tab-icon { display: flex; opacity: .75; transition: opacity .18s ease; }
.you-tab-icon svg { display: block; width: 17px; height: 17px; }
.you-tab.active { background: rgba(255,126,46,0.16); color: var(--amber); }
.you-tab.active .you-tab-icon { opacity: 1; }
.you-section-inner { animation: fade .28s ease; }
.you-section-inner .section-sub { margin: -4px 0 14px; }
.you-section-inner .card-grid,
.you-section-inner .quick-vent,
.you-section-inner .journal-list,
.you-section-inner .bp-progress { margin-top: 4px; }

/* ---------- tab screens ---------- */
.tab-head { margin-bottom: 16px; }
.tab-head .muted { margin: 4px 0 0; }
.empty { text-align: center; padding: 50px 20px; }
.empty-emoji { font-size: 4.3235rem; display: block; margin-bottom: 10px; }
.empty .btn { margin-top: 16px; }

.card-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.card-thumb { display: flex; flex-direction: column; gap: 6px; border: none; padding: 0; background: none; cursor: pointer; }
.card-thumb img { width: 100%; display: block; border-radius: var(--radius-sm); }
/* CARD-META-OVERLAP-001 (2026-09-09, full-app visual audit): this used to be
   `position: absolute` on top of the card image. Moved it — bottom-left,
   then top-left, then bottom-right — chasing empty space in the card's own
   canvas render each time, and kept colliding, because the real problem was
   never which corner: this chip's size is fixed in real CSS px while the
   thumbnail scales the 1080x1920 card down ~5.5x, so at list size it is
   nearly as wide as the whole image. No corner is clear of that. Confirmed
   by sampling the rendered image's own pixel data, not by eye, before and
   after two wrong guesses. The only fix that cannot collide with anything a
   future card design draws is to stop drawing it on the image at all — a
   plain caption below the thumbnail, in normal flow. */
.card-thumb-meta { color: var(--muted); font-size: 1.1581rem; padding: 0 2px; }
.card-view img { width: 100%; border-radius: var(--radius-sm); }
.card-view-actions { margin-top: 14px; }

.quick-vent { margin-bottom: 18px; }
.quick-vent .btn { margin-top: 10px; }
.journal-entry { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 14px 16px; margin-bottom: 10px; }
.je-head { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
.je-head b { font-family: var(--label); font-size: 1.1581rem; color: var(--muted); letter-spacing: .04em; }
.journal-entry p { margin: 0 0 10px; }
.journal-entry .je-share { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 14px; }

/* ---------- Coach / Studio attendance view -------------------------------- */
.coach-rows { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.coach-row { display: flex; justify-content: space-between; align-items: center; background: var(--panel);
  border: 1px solid var(--line); border-radius: 14px; padding: 12px 16px; }
.coach-row b { display: block; font-family: var(--display); font-size: 1.1581rem; }
.coach-row small { color: var(--muted); font-size: 1.1581rem; }
.coach-date-group { margin-bottom: 18px; }
.coach-date-heading { font-family: var(--label); font-size: 1.1581rem; letter-spacing: .04em;
  color: var(--amber); margin: 0 0 8px 2px; }

/* ---------- Post-to-Feed sheet -------------------------------------------- */
.post-feed-sheet h3 { font-size: 1.5441rem; margin-bottom: 6px; }

/* ---------- battle pass ---------- */
.bp-progress { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; margin-bottom: 14px; }
.bp-prog-top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.bp-prog-top b { font-family: var(--display); font-size: 1.5441rem; }
.bp-legend { display: flex; flex-direction: column; gap: 6px; font-size: 1.1581rem; color: var(--muted); margin-bottom: 12px; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 6px; }
.dot.community { background: var(--amber); }
.dot.warrior { background: var(--red); }
.bp-grid { display: flex; flex-direction: column; gap: 8px; }
.bp-colhead { display: grid; grid-template-columns: 36px 1fr 1fr; gap: 8px; font-family: var(--label);
  font-size: 1.1581rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); padding: 0 4px; }
.bp-row { display: grid; grid-template-columns: 36px 1fr 1fr; gap: 8px; align-items: stretch; }
.bp-tier { display: grid; place-items: center; font-family: var(--display); font-size: 1.3897rem; color: var(--muted); }
.bp-row.unlocked .bp-tier { color: var(--amber); }
.bp-reward { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px 6px;
  border-radius: 14px; border: 1px solid var(--line); background: var(--panel); color: var(--text);
  cursor: pointer; font-size: 1.1581rem; text-align: center; min-height: 92px; justify-content: center; }
.bp-reward.community { border-color: rgba(255,206,110,0.25); }
.bp-reward.warrior { border-color: rgba(255,77,31,0.25); }
.bp-reward.locked { opacity: .42; cursor: default; }
.bp-reward.claimed { background: rgba(255,126,46,0.12); border-color: var(--ember); }
.bp-emoji { font-size: 1.8529rem; }
.bp-name { line-height: 1.15; }
.bp-state { font-family: var(--label); font-size: 1.1581rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ember); }
.bp-reward.claimed .bp-state { color: #57c97a; }
.bp-foot { margin-top: 14px; }

/* ---------- settings ---------- */
.arch-mini { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; }
.arch-mini-card { background: var(--panel); border: 1.5px solid var(--line); border-radius: var(--radius-sm); padding: 10px 4px;
  display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 1.6985rem; color: var(--text); cursor: pointer; }
.arch-mini-card small { font-size: 1.1581rem; color: var(--muted); }
.arch-mini-card.sel { border-color: var(--ember); background: rgba(255,126,46,0.12); }
.dev-row { display: flex; gap: 10px; margin-bottom: 10px; }
.dev-row .btn { font-size: 1.1581rem; }
.settings-foot { text-align: center; color: var(--muted2); font-size: 1.1581rem; margin-top: 18px; }

/* Settings, reorganized into named groups instead of one flat panel list. */
.settings-group-label { font-family: var(--label); font-size: 1.1581rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted2); margin: 22px 4px 8px; }
.settings-group-label:first-of-type { margin-top: 4px; }
/* The dev-tools/testing panel is real, but shouldn't carry the same visual
   weight as settings a real client actually uses — collapsed by default,
   muted until opened. */
.settings-advanced { opacity: .72; border-style: dashed; }
.settings-advanced[open] { opacity: 1; border-style: solid; }
.settings-advanced summary { display: flex; justify-content: space-between; align-items: baseline;
  cursor: pointer; list-style: none; margin-bottom: 0; }
.settings-advanced summary::-webkit-details-marker { display: none; }
.settings-advanced summary h3 { margin: 0; font-size: 1.1581rem; }
.settings-advanced[open] summary { margin-bottom: 12px; }
.settings-advanced .dev-row, .settings-advanced .btn-danger { margin-top: 12px; }

/* ---------- tab bar ---------- */
.tabbar { position: fixed; left: 50%; transform: translateX(-50%); bottom: 0; width: 100%; max-width: var(--maxw);
  display: grid; grid-template-columns: repeat(3,1fr); background: rgba(12,5,26,0.92);
  backdrop-filter: blur(14px); border-top: 1px solid var(--line); padding: 8px 4px max(8px, env(safe-area-inset-bottom)); z-index: 40; }
.tabbar.hidden { display: none; }
.tab { background: none; border: none; color: var(--muted2); display: flex; flex-direction: column;
  align-items: center; gap: 2px; padding: 6px 0; cursor: pointer; font-family: var(--label); min-width: 0;
  min-height: var(--tap); flex: 1; transition: color .18s ease; }
.tab-icon { display: flex; opacity: .75; transition: opacity .18s ease; }
.tab-icon svg { display: block; }
.tab-label { font-size: 1.1581rem; white-space: nowrap; }
.tab.active { color: var(--amber); }
.tab.active .tab-icon { opacity: 1; }

/* ---------- toast / sheet / celebrate ---------- */
.toast { position: fixed; left: 50%; bottom: 110px; transform: translateX(-50%) translateY(20px);
  background: #2a1530; color: var(--text); border: 1px solid var(--line); padding: 12px 20px;
  border-radius: 999px; font-size: 1.1581rem; opacity: 0; pointer-events: none; transition: all .25s; z-index: 210; max-width: 90%; }
/* TOAST-BEHIND-SHEET-001 (2026-09-12): toast is the app's one universal
   confirmation surface — ui.js's own comment above dismissToast() says the
   whole point is that it "survives whatever's on screen under it." It
   can't survive what it's numerically below. It sat at z-index 80, under
   .sheet-overlay (90), .celebrate-overlay (100), .ember-burst (120), and
   .tour-overlay (200) — any toast fired while a sheet stayed open (e.g.
   Cards > open card > Share/Save, which never closes the card sheet) was
   in the DOM with .show applied but invisible, no matter which CSP-driven
   fallback path fired it. 210 is the highest z-index anywhere in this
   file, keeping the toast above every current overlay by construction
   rather than by chasing today's specific 80-vs-90 pair. */
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* TOAST-SHEET-CONTENT-COLLISION-001 (2026-09-12): TOAST-BEHIND-SHEET-001
   above fixed the toast's z-index so it's visible over an open sheet, but
   visible-and-colliding is still wrong. bottom:110px was tuned to clear the
   fixed .tabbar (~56px + safe-area) on a normal screen — it has nothing to
   do with a .sheet, which is hidden behind the sheet's own opaque backdrop
   anyway. A sheet that's taller than its content (e.g. Cards > open card,
   which never closes for Share/Save — see cards.js openCard()) always
   leaves its own padding-bottom (max(24px, safe-area-inset-bottom), see
   .sheet below) empty beneath its last button, because .sheet-overlay
   anchors the panel to align-items:flex-end — that gap is real screen
   space, clear of both the tabbar (not visible) and the sheet's own
   content. toast() (ui.js) adds this class whenever a .sheet-overlay.open
   exists at fire time, dropping the toast into that gap instead of
   colliding with whatever the sheet drew 110px up — which, for an Ember
   Card, is the card's own XP bar and quote line, the exact content the
   card exists to showcase. pointer-events:none on .toast (above) means
   even the residual overlap with a sheet's own action button, if the sheet
   is short enough that the gap is tighter than the toast's height, never
   blocks that button from being tapped. */
.toast.toast-in-sheet { bottom: max(20px, calc(env(safe-area-inset-bottom, 0px) + 12px)); }

.sheet-overlay { position: fixed; inset: 0; background: rgba(5,2,12,0.6); display: flex; align-items: flex-end;
  justify-content: center; opacity: 0; transition: opacity .24s; z-index: 90; }
.sheet-overlay.open { opacity: 1; }
.sheet { width: 100%; max-width: var(--maxw); background: var(--panel2); border-radius: 24px 24px 0 0;
  padding: 24px 20px max(24px, env(safe-area-inset-bottom)); transform: translateY(30px); transition: transform .26s; border: 1px solid var(--line); }
.sheet-overlay.open .sheet { transform: none; }
.confirm h3 { font-size: 1.6985rem; margin-bottom: 8px; }
.confirm-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }

.celebrate-overlay { position: fixed; inset: 0; background: radial-gradient(circle at 50% 30%, rgba(255,77,31,0.42), rgba(6,1,16,0.985));
  display: grid; place-items: center; z-index: 100; opacity: 0; transition: opacity .3s; padding: 24px; }
.celebrate-overlay.open { opacity: 1; }
/* The overlay's own gradient was only 28% opacity at its brightest (center,
   exactly where this card sits), so the Hearth behind it visibly bled
   through and competed with the celebration text - the app's single
   biggest reward moment looked unfinished. A solid card background fixes
   it regardless of what the overlay gradient is doing underneath. */
.celebrate-card { text-align: center; max-width: 420px; background: var(--panel, #160a26);
  border: 1px solid var(--line, rgba(255,206,110,0.14));
  border-radius: var(--radius); padding: 32px 24px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.6); }
.celebrate-glyph { font-size: 6.7941rem; animation: pop .5s ease; }
.celebrate-card .display { font-size: 3.3971rem; margin: 6px 0; }
.celebrate-quote { font-family: var(--display); font-style: italic; color: var(--amber); margin-top: 14px; }
.celebrate-actions { margin-top: 24px; display: flex; flex-direction: column; gap: 10px; }

/* ---------- ember burst ---------- */
.ember-burst { position: fixed; inset: 0; pointer-events: none; z-index: 120; overflow: hidden; }
.ember-spark { position: absolute; bottom: 30%; width: 10px; height: 10px; border-radius: 50%;
  background: radial-gradient(circle, #ffce6e, #ff4d1f); box-shadow: 0 0 12px #ff7e2e;
  animation: spark 1.4s ease-out forwards; transform: scale(var(--s, 1)); }
@keyframes spark { 0%{ transform: translateY(0) scale(var(--s)); opacity: 1; }
  100%{ transform: translate(calc(var(--dx,0)*120px), -70vh) scale(0); opacity: 0; } }

/* ---------- install coach ---------- */
.install-coach { position: fixed; left: 50%; transform: translateX(-50%); bottom: 14px; width: calc(100% - 28px);
  max-width: 452px; background: var(--panel2); border: 1px solid rgba(255,126,46,0.4); border-radius: 18px;
  padding: 16px 18px; z-index: 70; box-shadow: 0 14px 40px rgba(0,0,0,0.5); }
.coach-x { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--muted); font-size: 1.3897rem; cursor: pointer; }
.coach-title { font-family: var(--display); font-size: 1.3897rem; margin: 0 0 8px; }
.coach-steps { margin: 0; padding-left: 18px; font-size: 1.1581rem; color: var(--text); }
.coach-steps li { margin: 4px 0; }
.coach-arrow { text-align: center; font-size: 1.6985rem; color: var(--ember); animation: bob 1.2s ease-in-out infinite; }
@keyframes bob { 0%,100%{transform:translateY(0);} 50%{transform:translateY(5px);} }

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

/* desktop niceties */
@media (min-width: 520px) {
  .app-frame { box-shadow: 0 0 80px rgba(0,0,0,0.5); min-height: 100dvh; }
}

/* ---------- item 43: respect prefers-reduced-motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .ember-burst, .ember-spark, .big-burst, .fade-in, .coach-arrow,
  .celebrate-overlay, .celebrate-glyph, .fp-like.pop, .sk-line, .sk-block { animation: none !important; transition: none !important; }
  .celebrate-overlay.open { opacity: 1; }
}

/* ---------- item 45: high-contrast / larger-text mode ---------- */
html.high-contrast body { font-size: 1.4669rem; color: #ffffff; }
html.high-contrast .muted, html.high-contrast .muted.small, html.high-contrast .small { color: #ddd0ee; }
html.high-contrast .panel, html.high-contrast .feed-post, html.high-contrast .field input,
html.high-contrast .field textarea, html.high-contrast textarea, html.high-contrast input[type=number] {
  border-width: 2px; border-color: rgba(255,206,110,0.4); }
html.high-contrast .btn-ghost { border-width: 2px; color: var(--text); }
html.high-contrast .display { letter-spacing: 0; }

/* ---------- XP focal-point styling (design mandate: XP/journal/like are THE
   focal points, not equal-weight features) ---------- */
.xp-ticker { display: flex; align-items: center; gap: 8px; background: linear-gradient(135deg, rgba(255,206,110,0.22), rgba(255,77,31,0.12));
  border: 1px solid rgba(255,206,110,0.4); border-radius: 999px; padding: 8px 16px; margin-bottom: 14px;
  font-family: var(--display); font-size: 1.1581rem; width: fit-content; }
.xp-ticker-flame { font-size: 1.2353rem; }
.saved-badge { color: #7be29a; font-family: var(--label); font-size: 1.1581rem; letter-spacing: .06em;
  text-transform: uppercase; margin-top: 8px; }
.next-level-note { text-align: center; color: var(--amber); font-family: var(--label); font-size: 1.1581rem;
  margin: 10px 0 0; }

/* ---------- Hearth additions ---------- */
.notif-dot { position: absolute; top: 6px; right: 6px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--red); box-shadow: 0 0 0 2px var(--ink); }
.notif-dot.hidden { display: none; }
.hearth-pills { position: relative; }
/* 44px is the tap-target floor (Apple HIG / WCAG 2.5.5) — this was 40px */
.hearth-pills .icon-btn { width: 44px; height: 44px; font-size: 1.3897rem; position: relative; }
.risk-banner { display: flex; gap: 12px; align-items: center; background: rgba(255,77,31,0.14);
  border: 1px solid rgba(255,77,31,0.4); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 16px; }
.risk-banner b { font-family: var(--display); font-size: 1.1581rem; }
.risk-banner small { color: var(--muted); display: block; }
.risk-emoji { font-size: 2.0074rem; }
.edit-checkin { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 12px; margin-left: auto; flex: 0 0 auto; }
.done-banner { flex-wrap: wrap; }
.quick-log { margin: -8px 0 18px; font-size: 1.1581rem; }
.flame-visual { margin-bottom: 16px; }
.flame-visual-bar { position: relative; height: 22px; border-radius: 11px; background: rgba(255,255,255,0.08);
  overflow: hidden; margin-bottom: 6px; }
/* transform: scaleX() off a fixed-width base, not `width` — same fix as
   .bar span above. JS sets --pct as a 0..1 fraction. */
.flame-visual-bar::before { content: ''; position: absolute; inset: 0; width: 100%;
  background: var(--grad); border-radius: 11px; transform: scaleX(var(--pct, 0.1)); transform-origin: left center;
  transition: transform .5s ease; }
.flame-visual-bar .fv-emoji { position: relative; z-index: 1; display: block; text-align: center; line-height: 22px; font-size: 1.1581rem; }
.belt-gate-mini { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 12px 14px; margin-bottom: 14px; }
.bgm-head { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 1.1581rem; }
.feed-controls { display: flex; gap: 6px; align-items: center; }
/* 44px tap-target floor — was 32px for both. Kept the glyph/label sizes the
   same, grew the hit area only. */
.icon-btn.sm { width: 44px; height: 44px; font-size: 1.2353rem; }
.chip-toggle { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 14px; border-radius: 999px;
  background: rgba(255,255,255,0.06); border: 1px solid var(--line); color: var(--muted); cursor: pointer; }
.chip-toggle.on { background: rgba(255,126,46,0.2); border-color: var(--ember); color: var(--amber); }

/* skeleton loading (item 22) */
.feed-post.skeleton { display: flex; flex-direction: column; gap: 8px; }
.sk-line, .sk-block { background: linear-gradient(90deg, rgba(255,255,255,0.06), rgba(255,255,255,0.14), rgba(255,255,255,0.06));
  background-size: 200% 100%; animation: shimmer 1.3s ease-in-out infinite; border-radius: 8px; }
.sk-line { height: 12px; } .sk-w60 { width: 60%; } .sk-w40 { width: 40%; }
.sk-block { height: 60px; margin-top: 6px; }
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* empty state art (item 27) */
.feed-empty { text-align: center; padding: 28px 14px; }
.feed-empty-glow { display: flex; justify-content: center; margin-bottom: 10px;
  filter: drop-shadow(0 0 18px rgba(255,126,46,0.35)); }
.feed-empty-title { font-family: var(--display); font-size: 1.3897rem; margin: 0 0 4px; }
/* TABBAR-DEADZONE-001 (2026-09-11): every empty-state CTA sits at the
   bottom of a short, page-scrolled view, right above the fixed .tabbar
   (position:fixed, z-index 40). .app-frame.has-tabs .view's 96px
   padding-bottom only guarantees clearance once scroll reaches its true
   max — it does nothing for the ~130px band of intermediate scrollY
   values where the button has entered the viewport from the bottom but
   hasn't yet scrolled clear of the bar's footprint. A short swipe that
   stops as soon as the button looks visible lands in exactly that band:
   confirmed live with elementFromPoint returning the tab bar, not the
   button, at scrollY=400 on a fresh signup's empty History. (CSS
   scroll-snap was tried and reverted here — with only one snap target
   far down the page and the top as the only other candidate, real wheel
   scrolling got pulled straight back to scrollY 0 on every attempt,
   which is worse than the bug it was meant to fix. The actual fix is
   the scroll-clearance rescue in js/ui.js — see guardTabbarClearance.) */
.feed-empty .btn { width: auto; margin: 14px auto 0; padding: 0 24px; }

/* flame-tier badge on feed cards (item 24) */
.flame-badge { font-size: 1.1581rem; font-family: var(--label); padding: 3px 9px; border-radius: 999px;
  white-space: nowrap; background: rgba(255,255,255,0.08); color: var(--muted); }
.flame-badge.tier-ember    { background: rgba(255,206,110,0.16); color: #ffce6e; }
.flame-badge.tier-flame    { background: rgba(255,166,90,0.18); color: #ffa65a; }
.flame-badge.tier-blaze    { background: rgba(255,126,46,0.2);  color: #ff7e2e; }
.flame-badge.tier-wildfire { background: rgba(255,77,31,0.22);  color: #ff6a3d; }
.flame-badge.tier-inferno  { background: linear-gradient(90deg, rgba(255,77,31,0.3), rgba(255,206,110,0.3)); color: #ffd9b0; }
.flame-visual-bar.tier-ember::before    { background: linear-gradient(90deg,#ffce6e,#ffce6e); }
.flame-visual-bar.tier-flame::before    { background: linear-gradient(90deg,#ffce6e,#ffa65a); }
.flame-visual-bar.tier-blaze::before    { background: linear-gradient(90deg,#ffa65a,#ff7e2e); }
.flame-visual-bar.tier-wildfire::before { background: linear-gradient(90deg,#ff7e2e,#ff4d1f); }
.flame-visual-bar.tier-inferno::before  { background: var(--grad); }

/* feed actions row: like + quick replies + delete/report (items 23, 26, 37, 38) */
.fp-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }
.fp-reply { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 10px; border-radius: 999px;
  background: rgba(255,255,255,0.05); border: 1px solid var(--line); color: var(--muted); cursor: pointer; }
.fp-reply.sent { background: rgba(120,220,150,0.16); border-color: rgba(120,220,150,0.4); color: #7be29a; }
.fp-del, .fp-report { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 8px; border-radius: 999px;
  background: transparent; border: 1px solid var(--line); color: var(--muted2); cursor: pointer; margin-left: auto; }
.fp-pin { background: none; border: none; font-size: 1.1581rem; cursor: pointer; padding: 2px; width: auto;
  /* 44px floor per arba-mom-friendly-ux-sop: the glyph stays small, the hit
     area does not. This sat at min-height:0 with 2px padding. */
  min-height: 44px; min-width: 44px; display: inline-grid; place-items: center; }
.fp-pin.on { filter: drop-shadow(0 0 4px rgba(255,206,110,0.7)); }
/* item 20 / like satisfaction pop (design mandate: the like button should
   feel genuinely fun to tap) */
.fp-like.pop { animation: likepop .22s ease; }
@keyframes likepop { 0% { transform: scale(1); } 45% { transform: scale(1.18); } 100% { transform: scale(1); } }

/* password show/hide (item 3) */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 52px; }
.pw-toggle { position: absolute; right: 2px; top: 50%; transform: translateY(-50%); background: none;
  border: none; font-size: 1.3897rem; cursor: pointer; width: 44px; height: 44px; }
.prominent-login { margin-bottom: 6px; background: rgba(255,126,46,0.1); border: 1px solid rgba(255,126,46,0.3); color: var(--amber); }

/* recovery code (item 4) */
.recovery-code { font-family: var(--display); font-size: 1.8529rem; letter-spacing: .04em; text-align: center;
  background: var(--panel); border: 1.5px dashed var(--ember); border-radius: var(--radius-sm); padding: 18px;
  margin: 14px 0; color: var(--amber); }

/* visual archetype cards (item 6) */
.arch-grid.visual { display: grid; grid-template-columns: 1fr; gap: 14px; }
.arch-card.visual { grid-template-columns: 1fr; text-align: center; padding: 26px 18px; }
.arch-card.visual .arch-emoji.big { grid-row: auto; font-size: 4.3235rem; display: block; margin: 0 auto 8px; }
.arch-card.visual .arch-name { display: block; font-size: 2.0074rem; }
.arch-card.visual .arch-sub { display: block; margin: 4px 0 8px; }
.arch-card.visual .arch-blurb { grid-column: auto; }

/* journal share confirm preview (item 36) */
.je-preview { background: var(--panel); border-radius: var(--radius-sm); padding: 12px 14px; font-style: italic;
  color: #e6d8f7; margin: 10px 0; }

/* watch-photo retake row on confirm step (item 18) */
.proof-thumb-row { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.proof-thumb-row .venue-thumb.sm { width: 64px; height: 64px; max-height: 64px; border-radius: 12px; flex: 0 0 auto; margin: 0; }
.proof-thumb-row .btn { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 14px; }
#plausWarn.warn { color: var(--red); font-weight: 600; }

/* season countdown (item 33) */
.season-countdown { color: var(--amber); font-size: 1.1581rem; font-family: var(--label); margin-top: 4px; }

/* notifications sheet (item 41) */
.notif-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; max-height: 50vh; overflow-y: auto; }
.notif-row { display: flex; gap: 10px; align-items: center; background: var(--panel); border-radius: var(--radius-sm);
  padding: 10px 12px; font-size: 1.1581rem; }
.notif-row.unread { border: 1px solid rgba(255,126,46,0.35); }
.perm-note { display: block; color: var(--muted2); font-size: 1.1581rem; margin-top: 2px; }

/* Coaches screen — real trainer bios, reachable from Settings + You */
.coaches-view h1 { margin-top: 4px; }
.class-info { margin-top: 16px; }
.coach-cards { display: flex; flex-direction: column; gap: 16px; margin-top: 16px; }
.coach-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; }
.coach-photo { width: 100%; aspect-ratio: 512/840; object-fit: cover; display: block; background: var(--panel2); }
.coach-card-body { padding: 16px 18px; }
.coach-card-body .display.sm { margin: 2px 0 4px; }
.coach-bio { color: var(--text); font-size: 1.1581rem; line-height: 1.5; margin: 10px 0; }
.coach-quote { color: var(--amber); font-style: italic; font-size: 1.1581rem; line-height: 1.4; margin: 0 0 10px;
  border-left: 2px solid var(--ember); padding-left: 10px; }

.coaches-ig-panel { text-align: center; margin-top: 20px; }
.coaches-ig-link { text-decoration: none; margin-top: 10px; }

/* Our Why — condensed founder-letter moment, opened from Settings */
.our-why h3 { font-size: 1.6985rem; }
.our-why .muted { line-height: 1.5; }

/* Iron Factory session-gift progress panel on You */
.iron-factory-panel .lede { margin-bottom: 8px; }
.coach-link-panel .btn { margin-top: 4px; }

/* ---------- Light Up: optional Streakboard box emoji/note (official class) ---------- */
.box-picker { margin-top: 14px; }
.box-emoji-row { display: flex; gap: 8px; margin: 8px 0; flex-wrap: wrap; }
.box-emoji-btn { width: 44px; height: 44px; border-radius: 12px; background: var(--panel);
  border: 1.5px solid var(--line); font-size: 1.5441rem; cursor: pointer; }
.box-emoji-btn.sel { border-color: var(--ember); background: linear-gradient(180deg, rgba(255,126,46,0.18), var(--panel)); }
.box-note { width: 100%; }

/* Item 3 (2026-09-06): no-watch intensity/outcome picker, replacing a typed
   calorie/HR guess. Same tap-target floor as .box-emoji-btn, text buttons
   instead of emoji since these need to read at a glance, half-asleep. */
.intensity-picker { margin-top: 14px; }
.pill-row { display: flex; gap: 8px; margin: 8px 0 14px; flex-wrap: wrap; }
.pill-btn { min-height: 44px; padding: 0 16px; border-radius: 999px; background: var(--panel);
  border: 1.5px solid var(--line); color: var(--text); font-size: 1rem; font-weight: 600; cursor: pointer; }
.pill-btn.sel { border-color: var(--ember); background: linear-gradient(180deg, rgba(255,126,46,0.18), var(--panel)); color: var(--amber); }

/* ---------- Coach: backfill form (set a client's starting whiteboard count) ---------- */
.coach-backfill-form { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.coach-backfill-form .field { margin: 8px 0; }
.coach-roster { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }

/* ---------- Streakboard — the app version of the physical whiteboard ---------- */
.streakboard-view .kicker { text-align: left; }
.sb-rows { display: flex; flex-direction: column; gap: 14px; margin-top: 16px; }
.sb-row { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 12px 14px; }
.sb-name { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-family: var(--display); }
.sb-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; box-shadow: 0 0 6px rgba(255,126,46,0.5); }
.sb-arch-emoji { font-size: 1.1581rem; opacity: .8; }
.sb-prestige-badge { margin-left: auto; font-family: var(--label); font-size: 1.1581rem; color: var(--amber);
  background: rgba(255,206,110,0.14); border: 1px solid var(--line); border-radius: 999px; padding: 2px 8px; }
/* 2026-09-03: the per-session .sb-strip/.sb-cell scroll strip is gone —
   replaced by a big count + real progress bar (below). Simplification per
   arba-mom-friendly-ux-sop.md's standing mantra; see streakboard.js header. */
.sb-count-row { display: flex; align-items: baseline; gap: 6px; margin: 2px 0 8px; }
.sb-count { font-family: var(--display); font-size: 2.625rem; line-height: 1; color: var(--amber); }
.sb-count-label { font-size: 1.1581rem; color: var(--muted); }
.sb-bar { margin-bottom: 6px; }
.sb-progress-caption { margin: 0; }
/* A single badge for her most recent session's emoji, if she set one — not
   a per-session strip. Sized well above the 44px floor when it's a real
   tap target (has a note attached); plain, non-interactive, and smaller
   when there's no note to reveal. */
.sb-emoji-badge { display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.5441rem; line-height: 1; }
button.sb-emoji-badge { font-family: inherit; cursor: pointer; background: var(--panel2);
  border: 1px solid var(--amber); border-radius: 999px; width: 44px; height: 44px; padding: 0; }
.sb-note-sheet .sb-note-text { background: var(--panel2); border-radius: 12px; padding: 12px 14px;
  margin: 10px 0 4px; line-height: 1.5; }

/* ---------- Streakboard v2 — full community feed (Part 3, 2026-09-04) ----- */
.sb2-log-btn { margin: 4px 0 16px; }

/* "Who's showing up" (2026-09-08) - passive, no-tap-required presence strip.
   Horizontal scroll, not a grid: a handful of names on launch day shouldn't
   wrap into an awkward half-filled row, and this scales cleanly as the
   community grows past what fits on one screen width. */
.whos-up-strip { display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 12px; margin-bottom: 4px; }
.whos-up-chip { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; min-height: 44px;
  padding: 0 14px; border-radius: 999px; background: var(--panel); border: 1px solid var(--line);
  font-size: 0.95rem; white-space: nowrap; }
.whos-up-emoji { font-size: 1.1rem; }
.whos-up-name { font-weight: 600; }
.whos-up-count { color: var(--amber); font-weight: 600; }
.sb2-filter-bar { margin-bottom: 16px; display: flex; flex-direction: column; gap: 8px; }
.sb2-search { width: 100%; min-height: var(--tap); border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--panel); color: var(--text);
  font-size: 1.2353rem; padding: 0 14px; font-family: var(--sheet); }
/* Stacked full-width, not side-by-side — at the 21px-equivalent base the
   three dropdowns truncated to "All ar… / All ac… / Any ti…" in a row
   (arba-mom-friendly-ux-sop 2026-09-04 render pass). Scroll is free; the
   filters stay, they just each get a full line now. */
.sb2-select-row { display: flex; flex-direction: column; gap: 8px; }
.sb2-select { width: 100%; min-height: 44px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--panel); color: var(--text);
  font-size: 1.1581rem; font-family: var(--sheet); padding: 0 12px;
  text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
.sb2-card { padding: 0; overflow: hidden; }
.sb2-card-tap { display: block; width: 100%; text-align: left; background: none; border: none;
  color: inherit; cursor: pointer; padding: 14px; font-family: inherit; }
.sb2-chevron { font-size: 1.1581rem; transition: transform .18s ease; }
.sb2-expand-toggle[aria-expanded="true"] .sb2-chevron { transform: rotate(180deg); }
/* Always-visible reaction row (2026-09-08 friction fix) - lives outside
   .sb2-card-tap (a <button> can't nest a <button>) so 🔥 is a real,
   one-tap control instead of hidden behind opening the card first. */
.sb2-quickbar { display: flex; align-items: center; gap: 8px; padding: 0 14px 12px; }
.sb2-quickbar .sb2-react { margin: 0; }
.sb2-expand-toggle { margin-left: auto; width: auto; min-height: 44px; padding: 0 12px;
  border-radius: 999px; background: none; border: none; color: var(--muted); cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px; font-family: inherit; font-size: 1.1581rem; }
.sb2-expand { padding: 0 14px 14px; }
.sb2-note-full { white-space: pre-wrap; }
.sb2-reactions { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.sb2-react, .sb2-edit-btn, .sb2-del-btn, .sb2-report-btn {
  width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 14px; border-radius: 999px;
  background: rgba(255,255,255,0.06); border: 1px solid var(--line); color: var(--muted); cursor: pointer; }
.sb2-react.liked { background: rgba(255,126,46,0.18); border-color: rgba(255,126,46,0.4); color: var(--amber); }
.sb2-react b { font-variant-numeric: tabular-nums; }
.sb2-comments { display: flex; flex-direction: column; gap: 10px; margin: 10px 0; }
.sb2-comment { display: flex; gap: 8px; align-items: flex-start; background: var(--panel2);
  border-radius: var(--radius-sm); padding: 10px 12px; }
.sb2-comment-emoji { font-size: 1.3897rem; line-height: 1.4; }
.sb2-comment-body { flex: 1; min-width: 0; }
.sb2-comment-body p { margin: 0; font-size: 1.1581rem; }
.sb2-comment-text { margin-top: 2px !important; word-break: break-word; }
.sb2-comment-del { width: 44px; height: 44px; flex: 0 0 auto; background: none; border: none;
  color: var(--muted); font-size: 1.1581rem; cursor: pointer; }
.sb2-no-comments { margin: 4px 0; }
.sb2-comment-form { margin-top: 8px; }
.sb2-comment-input { min-height: 56px; }
.sb2-comment-submit { width: auto; padding: 0 18px; margin-top: 8px; }
.note-disclaimer { margin-top: 4px !important; opacity: .85; }

/* ---------- Coach/admin: Feed moderation + audit (Part 4) ----------------- */
.mod-row { align-items: center; }
.mod-row-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.mod-row-actions .btn { width: auto; padding: 0 14px; }

/* A card's collapsed preview and its full note must never both be visible —
   when expanded the truncated copy is redundant and reads as a render bug. */
.sb2-card-tap[aria-expanded="true"] .sb2-note-preview { display: none; }

/* Every checkbox/permission row is tapped via its wrapping label, so the ROW
   is the real target. Short single-line rows were collapsing to ~29px, under
   the SOP's 44px floor, while multi-line siblings cleared it by accident. */
label.check, label.perm { min-height: 44px; display: flex; align-items: center; gap: 10px; }

/* UNIVERSAL ESCAPE (2026-09-05). Two controls, one rule: no screen and no
   sheet is ever a dead end. Both clear the SOP's 44px floor and both stay
   visible while their container scrolls - an exit below the fold is not an
   exit. */
.screen-escape {
  position: fixed; top: calc(env(safe-area-inset-top, 0px) + 12px); right: 12px;
  z-index: 60; width: 44px; min-height: 44px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  font-size: 1.15rem; line-height: 1; color: var(--text);
  background: rgba(22,10,38,0.92); border: 1px solid var(--line);
  backdrop-filter: blur(8px);
}
.sheet-close {
  position: sticky; top: 0; float: right; z-index: 3;
  width: 44px; min-height: 44px; border-radius: 50%; margin: -4px -4px 0 0;
  display: grid; place-items: center; cursor: pointer;
  font-size: 1.15rem; line-height: 1; color: var(--text);
  background: rgba(22,10,38,0.92); border: 1px solid var(--line);
}
.screen-escape:active, .sheet-close:active { transform: scale(0.94); }

/* ===========================================================================
   THE MORNING AFTER (2026-09-05) — hype welcome, pending sends, undo,
   caught-up, Hearth comments + edit. Every control here clears the 44px floor
   and every size is relative (arba-mom-friendly-ux-sop: 21px base, 15px hard
   floor, always take the bigger option).
   =========================================================================== */

/* Greet her by streak, not by name. The number is the loudest thing in the
   header on purpose - it is the app saying "I have been counting with you." */
.hearth-day { margin: 0 0 2px; font-family: var(--display); font-size: 1.5441rem;
  color: var(--amber); line-height: 1.1; }
.hearth-day b { font-size: 2.1rem; font-weight: 800; color: var(--text);
  font-variant-numeric: tabular-nums; }
.hearth-top .kicker { margin: 4px 0 0; }

/* "You came back to 6 fire." Built only from real unread notifications. */
.hype-welcome { position: relative; overflow: hidden; margin: 0 0 14px;
  padding: 18px 18px 14px; border-radius: var(--radius);
  border: 1px solid rgba(255,206,110,0.42);
  background: linear-gradient(160deg, rgba(255,126,46,0.22), rgba(255,206,110,0.08) 55%, var(--panel)); }
.hw-kicker { margin: 0 0 4px; font-family: var(--label); text-transform: uppercase;
  letter-spacing: .16em; font-size: 1.0rem; color: var(--amber); font-weight: 600; }
.hw-headline { margin: 0 0 10px; font-family: var(--display); font-size: 1.8529rem;
  line-height: 1.15; color: var(--text); }
.hw-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.hw-list li { font-size: 1.1581rem; color: #efe2ff; background: rgba(0,0,0,0.22);
  border-radius: 12px; padding: 10px 12px; }
.hw-more { margin: 8px 0 0; color: var(--muted); font-size: 1.1581rem; }
.hw-dismiss { margin-top: 12px; min-height: 44px; }

/* Quiet, never alarming. It says what is waiting, not that something broke. */
.pending-sends { display: flex; align-items: center; gap: 10px; margin: 0 0 14px;
  padding: 12px 14px; border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.05); border: 1px solid var(--line); }
.pending-sends.hidden { display: none; }
.ps-dot { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%;
  background: var(--amber); box-shadow: 0 0 10px rgba(255,206,110,0.7);
  animation: psPulse 2.4s ease-in-out infinite; }
@keyframes psPulse { 0%,100% { opacity: .45; } 50% { opacity: 1; } }
.ps-text { font-size: 1.1581rem; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .ps-dot { animation: none; opacity: .9; } }

/* Undo on delete. Sits above the tab bar so it is never covered by it. */
.undo-bar { position: fixed; left: 50%; bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%) translateY(14px); z-index: 80;
  width: min(92vw, 460px); display: flex; align-items: center; gap: 12px;
  padding: 12px 12px 12px 18px; border-radius: 16px;
  /* Opaque, not 0.97: at 97% the mini-cards underneath bled through the bar
     and "Post deleted." sat on top of "Battle Pass". Only visible rendered. */
  background: #170a2c; border: 1px solid rgba(255,206,110,0.35);
  box-shadow: 0 12px 34px rgba(0,0,0,0.62);
  opacity: 0; transition: opacity .18s ease, transform .18s ease; }
.undo-bar.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.undo-text { flex: 1; font-size: 1.1581rem; color: var(--text); }
.undo-btn { flex: 0 0 auto; min-height: 44px; padding: 0 22px; border-radius: 999px;
  border: 1px solid rgba(255,206,110,0.5); background: rgba(255,126,46,0.2);
  color: var(--amber); font-family: var(--label); font-weight: 700;
  font-size: 1.2353rem; cursor: pointer; }
.undo-btn:active { transform: scale(0.96); }

/* "You're all caught up" - a closed loop, not an empty room. */
.feed-morning-line { margin: 0 0 12px; padding: 12px 14px; border-radius: var(--radius-sm);
  background: rgba(255,126,46,0.10); border: 1px solid rgba(255,126,46,0.24);
  font-size: 1.1581rem; color: var(--amber); }
.feed-caughtup { text-align: center; padding: 22px 12px 6px; }
.fc-mark { display: inline-grid; place-items: center; width: 40px; height: 40px;
  border-radius: 50%; background: rgba(120,220,150,0.16); color: #7be29a;
  font-size: 1.3125rem; margin-bottom: 8px; }
.fc-title { margin: 0; font-family: var(--display); font-size: 1.3897rem; }
.fc-sub { margin: 4px 0 0; color: var(--muted); font-size: 1.1581rem; }

/* Feed action rows: hype first, canned replies second, manage last. Three
   calm rows beat one crowded one - scroll is free, cramped targets are not. */
.fp-actions.primary { gap: 10px; }
.fp-actions.primary .fp-like, .fp-comment-toggle { flex: 1 1 0; justify-content: center;
  display: inline-flex; align-items: center; gap: 8px; min-height: 52px;
  font-size: 1.3125rem; border-radius: 14px; }
.fp-comment-toggle { margin-top: 8px; background: rgba(255,255,255,0.06);
  border: 1px solid var(--line); color: var(--text); cursor: pointer; width: auto; }
.fp-comment-toggle b { font-variant-numeric: tabular-nums; }
.fp-actions.manage { margin-top: 10px; gap: 10px; }
.fp-edit { width: auto; min-height: 44px; font-size: 1.1581rem; padding: 0 18px;
  border-radius: 999px; background: transparent; border: 1px solid var(--line);
  color: var(--muted); cursor: pointer; }
.fp-actions.manage .fp-del, .fp-actions.manage .fp-report { padding: 0 18px; margin-left: 0; }
.fp-edited { margin: 2px 0 0; color: var(--muted2); font-size: 1.0rem;
  font-family: var(--label); text-transform: uppercase; letter-spacing: .1em; }

/* Comments on the Hearth. */
.fp-comments { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.fp-comments:empty { margin-top: 0; }
.fp-comment { display: flex; align-items: flex-start; gap: 10px;
  background: rgba(0,0,0,0.18); border-radius: 14px; padding: 10px 12px; }
.fpc-avatar { flex: 0 0 auto; font-size: 1.2353rem; }
.fpc-body { flex: 1; min-width: 0; }
.fpc-text { margin: 0; font-size: 1.1581rem; color: #e6d8f7; overflow-wrap: anywhere; }
.fpc-text b { color: var(--text); font-family: var(--display); }
.fpc-time { display: block; margin-top: 3px; color: var(--muted2); font-size: 1.0rem; }
.fpc-del { flex: 0 0 auto; background: none; border: none; cursor: pointer;
  color: var(--muted2); font-size: 1.1581rem; width: 44px; min-height: 44px;
  display: grid; place-items: center; }
.fp-commentbox { display: flex; gap: 10px; margin-top: 12px; }
.fp-commentbox.hidden { display: none; }
/* RENDER-PASS FIX (2026-09-05). This rendered as a WHITE box on the dark
   feed. The app's input styling is scoped to `.field input`, `textarea` and
   `input[type=number]` - a bare text input outside a .field gets browser
   defaults and nothing in code review says so. Style it explicitly. */
.fpc-input { flex: 1; min-width: 0; min-height: 52px;
  background: rgba(0,0,0,0.28); border: 1px solid var(--line); color: var(--text);
  border-radius: var(--radius-sm); padding: 12px 14px; font-size: 1.1581rem;
  font-family: var(--sheet); }
.fpc-input::placeholder { color: var(--muted2); }
.fpc-send { width: auto; flex: 0 0 auto; min-height: 52px; padding: 0 22px; }

/* RENDER-PASS FIX (2026-09-05). Adding the Day-N line made the header four
   stacked lines on a 390px phone, with the time/window kicker capped at
   210px and wrapping mid-phrase beside the XP pill. Two rows instead: title
   block and pills on the first, the kicker spanning the full width below,
   where it has room to be one calm line. */
.hearth-top { flex-wrap: wrap; align-items: flex-start; gap: 4px 10px; }
/* Three render passes to land this. "Day 12" + "The Hearth" is ~217px of a
   390px phone and the pill cluster is ~170px, so ANY side-by-side layout
   (grid `1fr auto`, or flex with the pills pushed right) broke the heading
   onto two lines or stranded the pills in dead space. Stacked instead:
   heading full width, pills as one horizontal row beneath it, kicker last.
   Bigger and simpler beats denser - arba-mom-friendly-ux-sop rule 1. */
.hearth-title { flex: 1 0 100%; }
.hearth-pills { flex-direction: row; width: 100%; justify-content: flex-start;
  align-items: center; gap: 8px; margin: 6px 0 0; }
.hearth-top .kicker { flex: 1 0 100%; max-width: none; margin: 2px 0 0; font-size: 1.0rem; }

/* RENDER-PASS FIX (2026-09-05). "just now" was clipped to "ju" on every card:
   name + archetype + flame badge + pin + timestamp do not fit one 390px row.
   Let the head wrap and the timestamp drop to its own right-aligned line
   rather than shrinking anything below the type floor. */
.fp-head { flex-wrap: wrap; }
.fp-who { min-width: 0; flex: 1 1 60%; }
.fp-time { margin-left: auto; }

/* TRAINER MODE SWITCH (2026-09-05). Persistent, top-right, on every screen —
   Keeno's explicit correction over the first Settings-panel version: "work
   quickly switch it so you can seamlessly refresh and switch over." Higher
   z-index than .screen-escape since it sits above it in the same corner;
   .has-trainer-toggle shifts the escape X below the toggle. The top position
   is measured from the toggle's real rendered bottom by router.js, so larger
   browser text settings cannot make the two overlap. */
.trainer-toggle {
  position: fixed; top: calc(env(safe-area-inset-top, 0px) + 12px); right: 12px;
  z-index: 70; min-height: 44px; padding: 0 14px; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--label, sans-serif); font-size: 1.0rem; font-weight: 600;
  letter-spacing: 0.02em; white-space: nowrap; cursor: pointer;
  color: var(--amber, #ffce6e);
  background: rgba(22,10,38,0.94); border: 1px solid var(--ember, #ff7e2e);
  backdrop-filter: blur(8px); box-shadow: 0 0 10px rgba(255,126,46,0.25);
}
.trainer-toggle.pop { animation: trainerTogglePop 0.22s ease; }
@keyframes trainerTogglePop { 0% { transform: scale(1); } 45% { transform: scale(1.08); } 100% { transform: scale(1); } }
.has-trainer-toggle .screen-escape {
  top: max(calc(env(safe-area-inset-top, 0px) + 64px), var(--trainer-toggle-escape-top, 64px));
}

/* The universal escape (.screen-escape) isn't the only top-right control in
   the app - the ritual's own header (.flow-head, used by lightup/coach/
   coaches/streakboard) and the Hearth's own header (.hearth-top) render
   their own back/exit/bell buttons in-flow at the same vertical band the
   fixed trainer pill occupies. 2026-09-06: Keeno's own live-URL screenshots
   showed the pill sitting directly on top of the ritual's exit X - the
   .screen-escape fix above never touched these because they're a different,
   older header pattern that predates it. Push them down by the same live-
   measured clearance instead of hand-guessing a second pixel value.
*/
.has-trainer-toggle .flow-head,
.has-trainer-toggle .hearth-top {
  margin-top: calc(var(--trainer-toggle-escape-top, 64px) - max(22px, env(safe-area-inset-top, 0px)));
}

/* Coach dashboard (2026-09-06): a real spreadsheet-style table for Riley and
   Keeno, not the mom-friendly member-facing floors - this is a two-person
   internal tool, information density is the point. Horizontal scroll on its
   own container (never the page) keeps this from breaking small viewports,
   the one responsiveness rule that still applies everywhere. */
.coach-dash-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius-sm, 14px); margin: 8px 0; }
.coach-dash { border-collapse: collapse; width: 100%; min-width: 640px; font-size: 0.95rem; }
.coach-dash th, .coach-dash td { padding: 10px 12px; text-align: left; white-space: nowrap; border-bottom: 1px solid var(--line); }
.coach-dash th { cursor: pointer; user-select: none; color: var(--muted); font-weight: 600; position: sticky; top: 0; background: var(--panel); min-height: 44px; }
.coach-dash th.sorted { color: var(--amber); }
.coach-dash tbody tr:hover { background: rgba(255,255,255,0.03); }
.dash-tag { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 0.85em; }
.dash-tag.claimed { background: rgba(120,220,150,0.14); color: #7be29a; }
.dash-tag.pending { background: rgba(255,206,110,0.14); color: var(--amber); }
.dash-tag.today { background: rgba(255,126,46,0.16); color: var(--ember); }

/* ---------- First-run guided tour (2026-09-08) --------------------------- */
/* .tour-hole is the "spotlight": a transparent box whose shadow is so large
   it blacks out the entire rest of the viewport — the classic cutout trick,
   no clip-path math, works at any viewport size. */
.tour-overlay { position: fixed; inset: 0; z-index: 200; pointer-events: none; }
.tour-hole {
  position: fixed; border-radius: 16px;
  box-shadow: 0 0 0 9999px rgba(5,2,12,0.86);
  transition: left .25s ease, top .25s ease, width .25s ease, height .25s ease;
  outline: 2px solid var(--ember); outline-offset: 2px;
}
.tour-caption {
  position: fixed; left: 16px; right: 16px; max-width: calc(var(--maxw) - 32px);
  margin: 0 auto; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 18px; pointer-events: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,0.5); transition: top .25s ease, bottom .25s ease;
  max-height: calc(100vh - 16px); overflow-y: auto;
}
.tour-progress { margin: 0 0 4px; color: var(--muted); font-family: var(--label); font-size: 0.95rem; letter-spacing: 0.04em; }
.tour-title { margin: 0 0 4px; font-size: 1.35rem; }
.tour-body { margin: 0 0 10px; color: var(--text); }
.tour-next { width: 100%; }
/* Body content underneath stays visible (that's the point of a spotlight)
   but must not be reachable — she progresses only via Next. */
/* The trainer pill lives at body level, NOT inside .screen or .tabbar, so
   the original rule left it the one live control during the forced tour.
   Found 2026-09-09 by looking: tapped it mid-tour as an admin, landed in
   Trainer Tools, and the tour overlay was still floating on top of it —
   a screen that looks broken, from the one member who is also the founder. */
body.tour-active .screen,
body.tour-active .tabbar,
body.tour-active .trainer-toggle { pointer-events: none; }
.tour-free { display: flex; align-items: center; justify-content: center; padding: 24px;
  background: rgba(5,2,12,0.92); pointer-events: auto; }
.tour-free-card { max-width: 420px; text-align: center; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 32px 24px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.55); }
.tour-free-emoji { font-size: 2.8rem; margin-bottom: 8px; }
.tour-free-card .lede { margin: 10px 0 20px; }
.tour-free-card .tour-next, .tour-free-card #tourFreeDone { width: 100%; }

/* LOCKOUT-001 (2026-09-08): the six digits a coach reads aloud to a member
   who's locked out. Big, spaced, tabular — this gets read across a gym
   floor and typed one-handed on a phone, so legibility beats subtlety. */
.reset-code {
  font-size: 40px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-align: center;
  font-variant-numeric: tabular-nums;
  margin: 14px 0 10px;
  color: var(--ember, #ff8a3d);
}

/* TRAINER-DOOR-001 (2026-09-09): divider inside the Trainer sheet, between
   the way into the tools and the posting-identity switch. */
.tt-rule { border: none; border-top: 1px solid var(--line, rgba(255,255,255,.12));
  margin: 18px 0 14px; }

/* ---- Trainer Tools (2026-09-09): items 4,5,6,9,10 of Keeno's punchlist.
   The screen used to be one long scroll of h2 sections with inline styles,
   rendering the same roster twice (a "Dashboard" table and a "Roster"
   list). Now: Today first, then one searchable member list. ---- */
.ct-h2 { margin-top: 30px; }

/* ITEM 6 — who is in this morning, answered before you scroll. */
.ct-today { background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--radius, 18px); padding: 16px 18px; margin: 14px 0 6px; }
.ct-today-count { font-size: 21px; margin: 4px 0 10px; }
.ct-today-count b { font-size: 30px; }
.ct-list { font-size: 17px; margin: 6px 0; line-height: 1.45; }
.ct-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 8px; vertical-align: middle; }
.ct-dot.in { background: #4ade80; }
.ct-dot.out { background: rgba(255,255,255,.28); }
/* ITEM 7 — the only signal staff get that anyone joined; no email is sent. */
.ct-new { font-size: 17px; margin: 8px 0; padding: 10px 12px; border-radius: 12px;
  background: rgba(255,138,61,.12); border: 1px solid rgba(255,138,61,.3); }

/* ITEM 5 — search + sort. */
.ct-controls { display: flex; gap: 10px; margin: 12px 0 8px; flex-wrap: wrap; }
.ct-search { flex: 1 1 190px; min-height: 44px; font-size: 17px; padding: 10px 14px;
  border-radius: 12px; background: var(--panel2); color: inherit;
  border: 1px solid var(--line); font-family: inherit;
  /* Narrow flex widths (e.g. tablet, sharing the row with the sort select)
     can be too tight for the full "Search by name or email" placeholder at
     17px. Browsers clip overflowing input text with a raw cut glyph, not an
     ellipsis, unless told to — this is what showed the bug. Applies to both
     the placeholder and whatever the coach actually types. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-sort { flex: 0 1 auto; min-height: 44px; font-size: 16px; padding: 10px 12px;
  border-radius: 12px; background: var(--panel2); color: inherit;
  border: 1px solid var(--line); font-family: inherit; }

/* ITEM 4 — a member row is a real control now, not a line of text. */
.ct-member { display: flex; align-items: center; gap: 12px; width: 100%;
  text-align: left; background: none; color: inherit; font-family: inherit;
  border: none; border-bottom: 1px solid var(--line); cursor: pointer;
  min-height: 60px; padding: 12px 4px; }
.ct-member:active { background: rgba(255,255,255,.04); }
.ct-avatar-lg { font-size: 26px; line-height: 1; }
.ct-member-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.ct-member-main b { font-size: 17px; }
.ct-member-main small { font-size: 15px; opacity: .65; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.ct-member-meta { display: flex; flex-direction: column; align-items: flex-end;
  gap: 4px; text-align: right; flex-shrink: 0; }
.ct-tag { font-size: 15px; padding: 2px 8px; border-radius: 999px; vertical-align: middle; }
.ct-tag.new { background: rgba(255,138,61,.2); color: #ffb37a; }

/* The member sheet's fact table. */
.ct-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; margin: 16px 0 4px; }
.ct-facts div { display: flex; flex-direction: column; gap: 2px; }
.ct-facts dt { font-size: 14px; opacity: .6; text-transform: uppercase; letter-spacing: .04em; }
.ct-facts dd { font-size: 17px; margin: 0; }

/* ITEM 7 (2026-09-09): new-member count on the Trainer pill. Quiet on
   purpose — staff had no signal at all that anyone joined, because the app
   sends no email on signup. */
.tt-badge { display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px; margin-left: 7px;
  border-radius: 999px; background: #ff8a3d; color: #1a0b28;
  font-size: 13px; font-weight: 700; line-height: 1; vertical-align: middle; }

/* ITEM 8 (2026-09-09): the trainer digest, in-app instead of by email.
   Same information a mailed digest would carry, somewhere staff actually
   look, and with no third-party mail provider to sign up for. */
.ct-since { background: rgba(255,138,61,.09); border: 1px solid rgba(255,138,61,.28);
  border-radius: var(--radius, 18px); padding: 14px 16px; margin: 12px 0 4px; }
.ct-since .kicker { margin-bottom: 6px; }
