/* LUNER design system — Brand Guidelines v4, as code.
 *
 * This file exists so that generated screens have something to be generated
 * *against*. Two of the brand rules are the kind a machine breaks by default,
 * so they are enforced here rather than written down and hoped for:
 *
 *   1. No traffic lights, ever. Readiness, risk and performance render as Ink
 *      on White or Pale, label plus a shape — never red, amber, green. There is
 *      deliberately no --danger, --warning or --success token in this file. If a
 *      screen needs one it is doing something the guidelines forbid.
 *
 *   2. No pink as a primary colour. Blush is a gradient anchor and nothing else.
 *      "Shrink it and pink it" is the specific failure these guidelines were
 *      written to prevent.
 *
 * The third rule — one accent per gradient zone, always terminating in Pale —
 * is expressed by the .zone-* classes. Two saturated colours never meet.
 */

/* ------------------------------------------------------------- tokens ---- */

:root {
  /* ink and greys carry every functional meaning */
  --ink:       #111111;
  --slate:     #5A5A5A;
  --mist:      #686868;
  --white:     #FFFFFF;
  --pale:      #F5F5F3;

  /* accents. Backgrounds and gradient anchors only — never state, never text */
  --sky:       #C8E6F5;
  --sage:      #D4EDE6;
  --blush:     #EDD4E4;
  --lime:      #E4EDD4;
  --sand:      #EDE4D4;

  --hairline:  rgba(0, 0, 0, .07);
  --line:      rgba(0, 0, 0, .12);
  --input-border: #767676;
  --shadow:    0 4px 24px rgba(0, 0, 0, .05);
  --shadow-lift: 0 8px 32px rgba(0, 0, 0, .08);

  --radius-sm: 10px;
  --radius:    16px;
  --radius-pill: 100px;

  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px;

  --font-display: "Bricolage Grotesque", system-ui, sans-serif;
  --font-text:    "DM Sans", system-ui, sans-serif;

  --measure: 64ch;
  --max:     1100px;
}

@font-face {
  font-family: "Bricolage Grotesque";
  src: url("/fonts/bricolage-grotesque-latin.woff2") format("woff2");
  font-weight: 200 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "DM Sans";
  src: url("/fonts/dm-sans-latin.woff2") format("woff2");
  font-weight: 100 1000; font-style: normal; font-display: swap;
}

/* --------------------------------------------------------------- base ---- */

*, *::before, *::after { box-sizing: border-box; }

/* `hidden` must win. The attribute sets display:none at the weakest possible
   specificity, so a single inline style defeats it — which is exactly what left
   an empty check-in form sitting under the "you're done" screen, and under the
   waiting-for-consent screen, for anyone who came back the same day. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--pale);
  color: var(--ink);
  font: 400 16px/1.6 var(--font-text);
  -webkit-font-smoothing: antialiased;
}

/* Focus is never removed, only restyled. A coach on a pitch may be on a
   keyboard, and a screen reader user always is. */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 4px;
}

/* --------------------------------------------------------------- type ---- */

.h1, h1 {
  font: 800 clamp(32px, 5.6vw, 56px)/1.05 var(--font-display);
  letter-spacing: -1.6px; margin: 0 0 var(--s-5); max-width: 15ch;
}
.h2, h2 {
  font: 700 clamp(26px, 3.8vw, 40px)/1.12 var(--font-display);
  letter-spacing: -.9px; margin: 0 0 var(--s-4); max-width: 20ch;
}
.h3, h3 {
  font: 700 19px/1.3 var(--font-display);
  letter-spacing: -.3px; margin: 0 0 var(--s-3);
}
.h4, h4 {
  font: 700 16px/1.35 var(--font-display);
  margin: 0 0 var(--s-2);
}
p { margin: 0 0 var(--s-4); max-width: var(--measure); color: var(--slate); font-weight: 300; font-size: 17px; }
.lead  { font-size: 19px; color: var(--ink); font-weight: 300; max-width: 52ch; }
.small { font-size: 14px; }
.meta  { font-size: 13px; color: var(--mist); }

.eyebrow {
  font: 500 11px/1 var(--font-text); letter-spacing: .12em; text-transform: uppercase;
  color: var(--slate); margin: 0 0 var(--s-5);
}

/* A statistic is an argument, so it is set like one. */
.stat-value {
  font: 800 clamp(34px, 4.6vw, 48px)/1 var(--font-display);
  letter-spacing: -1px; margin: 0 0 var(--s-3); color: var(--ink);
}

/* ------------------------------------------------------------- layout ---- */

.wrap    { max-width: var(--max); margin: 0 auto; padding: 0 var(--s-7); }
.section { padding: var(--s-9) 0; }
.stack > * + * { margin-top: var(--s-4); }
.row     { display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap; }
.grid-2  { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-4); }
.grid-3  { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }

/* One accent per zone, always terminating in Pale. Never two saturated
   colours meeting each other — the guidelines file that under "rainbow
   gradients". */
.zone-sky   { background: linear-gradient(165deg, var(--sky)   0%, var(--sky)   26%, var(--pale) 94%); }
.zone-sage  { background: linear-gradient(180deg, var(--pale)  0%, var(--sage)  45%, var(--pale) 100%); }
.zone-sand  { background: linear-gradient(180deg, var(--pale)  0%, var(--sand)  60%, var(--pale) 100%); }
.zone-lime  { background: linear-gradient(180deg, var(--pale)  0%, var(--lime)  45%, var(--pale) 100%); }
.zone-pale  { background: var(--pale); }

/* -------------------------------------------------------------- cards ---- */

.card {
  background: var(--white); border: .5px solid var(--hairline);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: var(--s-5);
}
.card--flat  { box-shadow: none; }
.card--quiet { background: var(--pale); border-color: var(--line); box-shadow: none; }
.panel { composes: card; }

.tag {
  font: 500 10px/1 var(--font-text); letter-spacing: .12em; text-transform: uppercase;
  color: var(--mist); display: inline-block; margin-bottom: var(--s-3);
}

/* ------------------------------------------------------------ controls --- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: 15px var(--s-5); border: 0; border-radius: var(--radius-pill);
  background: var(--ink); color: var(--white);
  font: 700 14px var(--font-text); cursor: pointer; transition: opacity .15s;
  text-decoration: none;
}
.btn:hover    { opacity: .82; }
.btn:disabled { opacity: .45; cursor: default; }
.btn--block   { width: 100%; }
.btn--quiet {
  background: var(--white); color: var(--ink); border: 1px solid var(--line);
}
.btn--quiet:hover { border-color: var(--ink); opacity: 1; }
.btn--text {
  background: none; color: var(--ink); border: 0; padding: 10px 4px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  font: 500 14px/1.5 var(--font-text); cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px; font-weight: 500;
}

.label {
  display: block; font: 500 12px/1 var(--font-text); letter-spacing: .06em;
  color: var(--ink); margin: 0 0 var(--s-2);
}
.label .optional { color: var(--mist); font-weight: 400; letter-spacing: 0; }

.input, .select, .textarea {
  width: 100%; min-width: 0; max-width: 100%; padding: 13px 15px; border: 1px solid var(--input-border);
  border-radius: var(--radius-sm); background: var(--white); color: var(--ink);
  font: 400 16px var(--font-text); transition: border-color .15s;
}
.input:focus, .select:focus, .textarea:focus { outline: 2px solid var(--ink); outline-offset: 2px; border-color: var(--ink); }
.field { min-width: 0; margin-bottom: var(--s-5); }

.pill {
  padding: 9px 18px; border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--white); color: var(--ink);
  font: 500 14px var(--font-text); cursor: pointer; transition: all .15s;
}
.pill:hover { border-color: var(--ink); }
/* A pill that is a link (Messages) looks like its button neighbours. */
a.pill { display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
.pill[aria-pressed="true"] { background: var(--ink); color: var(--white); border-color: var(--ink); }

.consent { display: flex; gap: var(--s-3); align-items: flex-start; margin: var(--s-1) 0 var(--s-5); }
.consent input[type="checkbox"] {
  width: 18px; height: 18px; flex: 0 0 auto; margin: 2px 0 0;
  accent-color: var(--ink); cursor: pointer;
}
.consent span { font: 300 13.5px/1.5 var(--font-text); color: var(--slate); }

/* ------------------------------------------------------------- status ---- */
/*
 * Ready · Monitor · Check In · Out.
 *
 * Ink on white, a label and a shape. The shape carries the difference, not the
 * colour — which is also why this works for a colour-blind coach and in
 * sunlight on a pitch. Out is an availability state, not a fourth score tier.
 */

.status {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 7px 14px 7px 12px; border: 1px solid var(--line);
  border-radius: var(--radius-pill); background: var(--white); color: var(--ink);
  font: 500 13px/1 var(--font-text); white-space: nowrap;
}
.status::before {
  content: ""; width: 11px; height: 11px; border-radius: 50%;
  border: 1.5px solid var(--ink); flex: 0 0 auto;
}
.status--ready::before   { background: var(--ink); }
.status--monitor::before { background: linear-gradient(to right, var(--ink) 0 50%, transparent 50% 100%); }
/* a ring with a centre dot — one element, so it cannot drift into the label */
.status--checkin::before {
  background: radial-gradient(circle at center, var(--ink) 0 2.5px, transparent 2.5px);
}
.status--out::before {
  background: none;
  background-image: linear-gradient(45deg, transparent 44%, var(--ink) 44% 56%, transparent 56%);
}
.status--quiet { border-color: var(--hairline); color: var(--mist); }
.status--quiet::before { border-color: var(--mist); }

/* ------------------------------------------------------------- scale ----- */
/*
 * The 1–5 used by every wellness field in the check-in. Numbers, not faces and
 * not colours: the check-in is answered by a seventeen-year-old before training
 * and has to be fast, unambiguous and free of judgement.
 */

.scale { display: flex; gap: var(--s-2); }
.scale button {
  flex: 1; min-width: 44px; aspect-ratio: 1;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--white); color: var(--ink);
  font: 500 17px var(--font-text); cursor: pointer; transition: all .12s;
}
.scale button:hover { border-color: var(--ink); }
.scale button[aria-pressed="true"] { background: var(--ink); color: var(--white); border-color: var(--ink); }
.scale-ends {
  display: flex; justify-content: space-between;
  margin-top: var(--s-2); font: 400 12px var(--font-text); color: var(--mist);
}

/* ------------------------------------------------------------ feedback --- */
/* Errors are the one place a warm hue is allowed, and it is a background wash
   behind ink — never a red border, never a red icon on a status. */

.notice {
  padding: 12px 15px; border-radius: var(--radius-sm);
  font: 400 13.5px/1.5 var(--font-text); margin: 0 0 var(--s-5);
  background: var(--pale); border: .5px solid var(--hairline); color: var(--slate);
}
.notice--error { background: #FBEAEA; border-color: rgba(150, 0, 0, .16); color: #7A1B1B; }

/* --------------------------------------------------------- responsive --- */

@media (max-width: 900px) {
  .wrap { padding: 0 var(--s-5); }
  .section { padding: var(--s-8) 0; }
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
  .wrap { padding: 0 var(--s-4); }
  .section { padding: var(--s-7) 0; }
  .card { padding: var(--s-5) var(--s-4); }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ==========================================================================
   App language — adopted 17 September 2026 (direction B, "expressive").
   Chosen by Luca from three directions built on the same screen. The idea it
   rests on: a player should feel which part of the check-in she is in without
   reading a counter. Each block gets its own accent ground, white cards float
   on it, and the status geometry from above returns at 96px as the
   illustration — so the product never needs stock artwork that would not look
   like it belongs.
   Accents are still grounds only. Nothing here introduces a functional colour.
   ========================================================================== */

.zone-blush { background: linear-gradient(180deg, var(--pale) 0%, var(--blush) 42%, var(--pale) 100%); }

/* --------------------------------------------------------- app chrome ---- */

.app-chrome {
  display: flex; align-items: center; justify-content: space-between;
  /* Tight vertically on purpose: the account control is a 48px target, and the
     chrome must not spend the page's height on the one row that is not the
     work. Loosening this pushed the densest question 8px off the screen. */
  padding: var(--s-3) var(--s-5);
}
.app-chrome .who { font: 700 17px/1 var(--font-display); letter-spacing: -.4px; }

/* Progress as a count. Dots were tried first and moved correctly, but they
   cannot say how short the thing is, and "she can see it is nearly over" is the
   whole job — Orlaith, 21 September, from watching players do this. One
   indicator rather than two, so nothing can disagree with itself. */
.step-count {
  font: 500 12px/1 var(--font-text); letter-spacing: .1em;
  text-transform: uppercase; color: var(--mist); white-space: nowrap;
}

/* kept: still used wherever a few discrete steps need showing at a glance */
.app-dots { display: flex; gap: 5px; }
.app-dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(0,0,0,.18); }
.app-dots i[data-on] { background: var(--ink); }

/* ------------------------------------------------------- block openers --- */

.block-hero { padding: var(--s-6) var(--s-5); text-align: center; }
.block-hero h1 {
  font: 800 clamp(28px,8.5vw,36px)/1.07 var(--font-display);
  letter-spacing: -1.3px; margin: 0 0 var(--s-3); max-width: none;
}
.block-hero p { margin: 0 auto; max-width: 28ch; }

/* The status shapes at illustration scale. Same geometry, same meanings. */
.mark-xl {
  width: 96px; height: 96px; border-radius: 50%;
  border: 3px solid var(--ink); margin: 0 auto var(--s-5);
}
.mark-xl--full { background: var(--ink); }
.mark-xl--half { background: linear-gradient(to right, var(--ink) 0 50%, transparent 50% 100%); }
.mark-xl--ring { background: radial-gradient(circle at center, var(--ink) 0 18px, transparent 18px); }
.mark-xl--slash { background-image: linear-gradient(45deg, transparent 44%, var(--ink) 44% 56%, transparent 56%); }

.section-rule { display: flex; align-items: center; gap: var(--s-3); padding: 0 var(--s-5) var(--s-4); }
.section-rule b {
  font: 500 11px/1 var(--font-text); letter-spacing: .12em; text-transform: uppercase; color: var(--ink);
}
.section-rule hr { flex: 1; border: 0; border-top: 1px solid rgba(0,0,0,.14); margin: 0; }

/* ------------------------------------------------------ question cards --- */

.qcard {
  background: var(--white); border-radius: 24px; box-shadow: var(--shadow-lift);
  margin: 0 var(--s-4) var(--s-4); padding: var(--s-6) var(--s-5) var(--s-5);
}
.qcard h2 {
  font: 700 23px/1.22 var(--font-display); letter-spacing: -.6px;
  margin: 0 0 var(--s-5); max-width: 18ch;
}
.qcard .hint {
  font: 300 13px/1.5 var(--font-text); color: var(--mist);
  margin: calc(-1 * var(--s-3)) 0 var(--s-4); max-width: none;
}

/* Answer buttons: one size and one type everywhere; a question only decides
   how many columns (checkin.html answerLayout). Lorraine, 15 October. */
.choices { display: grid; grid-template-columns: 1fr; gap: var(--s-2); align-items: stretch; }
.choices--grid { grid-template-columns: 1fr 1fr; }
.choices--grid-3 { grid-template-columns: repeat(3, 1fr); }
.choices--grid-3 button { padding-left: 6px; padding-right: 6px; }
.choices--row-2 { grid-template-columns: repeat(2, 1fr); }
.choices--row-3 { grid-template-columns: repeat(3, 1fr); }
.choices--few { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }
.choices button {
  width: 100%; border: 1px solid var(--line); background: var(--white); color: var(--ink);
  border-radius: var(--radius-pill); padding: 11px 12px; cursor: pointer; text-align: center;
  white-space: normal; overflow-wrap: anywhere;
  font: 500 14.5px/1.25 var(--font-text);
  transition: transform .12s ease, background .12s, color .12s, border-color .12s;
}
.choices button:active { transform: scale(.96); }
.choices button[aria-pressed="true"] { background: var(--ink); color: var(--white); border-color: var(--ink); }

/* ---------------------------------------------------------- cta dock ----- */
/* Sits on the ground it floats over, so it works on any zone. */

.cta-dock { position: sticky; bottom: 0; padding: var(--s-4) var(--s-4) var(--s-5); }
.cta-dock::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, var(--pale) 55%, transparent);
}
.cta-dock > * { position: relative; }

/* ------------------------------------------------------------ strip ------ */
/* Her own fortnight. Ink only, height carries the value — the same refusal to
   use colour that the status shapes make. */

.strip { display: flex; align-items: flex-end; gap: 3px; height: 40px; }
.strip i { flex: 1; background: var(--ink); border-radius: 2px; opacity: .22; }
.strip i[data-today] { opacity: 1; }
.strip i[data-none] { opacity: .08; }

/* ------------------------------------------------------- entrance -------- */

@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.rise { animation: rise .26s cubic-bezier(.2,.7,.3,1) both; }
.rise--2 { animation-delay: .05s; }
.rise--3 { animation-delay: .1s; }

/* ------------------------------------------------------- quiet actions --- */
/* Sign out, and anything else that must be reachable but never invites a tap.
   Underlined link styling made it read as the most interesting thing on the
   screen, which it is not. */

.btn--ghost {
  background: none; border: 0; padding: 6px 2px; cursor: pointer;
  font: 500 11px/1 var(--font-text); letter-spacing: .12em; text-transform: uppercase;
  color: var(--mist); transition: color .15s;
}
.btn--ghost:hover { color: var(--ink); }

/* ------------------------------------------------- paged wizard chrome --- */

.mark-lg { width: 56px; height: 56px; border-radius: 50%; border: 2.5px solid var(--ink);
           margin: 0 auto var(--s-3); }
.mark-lg--full  { background: var(--ink); }
.mark-lg--half  { background: linear-gradient(to right, var(--ink) 0 50%, transparent 50% 100%); }
.mark-lg--ring  { background: radial-gradient(circle at center, var(--ink) 0 11px, transparent 11px); }
.mark-lg--slash { background-image: linear-gradient(45deg, transparent 44%, var(--ink) 44% 56%, transparent 56%); }

.block-hero--compact { padding: var(--s-4) var(--s-5) var(--s-3); }
.block-hero--compact h1 { font-size: clamp(20px,5.5vw,23px); letter-spacing: -.6px; margin-bottom: var(--s-2); }
.block-hero--compact p { font-size: 14px; }
/* Smaller inside a block. The ring is worth its height when a part of the
   check-in opens; on the pages after it, that height is better spent on the
   question — which is what lets a follow-up sit beside the question that asked
   for it instead of landing on the next page. */
.block-hero--compact .progress-mark { width: 64px; height: 64px; margin-bottom: var(--s-2); }
.block-hero--compact .progress-mark::before {
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px));
}
.block-hero--compact .mark-inner { width: 28px; height: 28px; border-width: 2px; }
.block-hero--compact .mark-inner--ring { background: radial-gradient(circle at center, var(--ink) 0 5px, transparent 5px); }
.block-hero--compact .progress-count { margin-bottom: var(--s-3); }

/* Back and forward, always in the same place, never scrolled away. */
/* No padding of its own inside the dock — the dock already pads, and the two
   together made a 48px button occupy 120px of a 844px screen. */
.pager { display: flex; align-items: center; gap: var(--s-3); padding: 0; }
.pager .btn { flex: 1; }
.pager .back {
  background: none; border: 0; cursor: pointer; padding: 14px 10px;
  min-height: var(--tap); min-width: var(--tap);
  font: 500 14px var(--font-text); color: var(--mist); flex: 0 0 auto;
}
.pager .back:hover { color: var(--ink); }
.pager .back[hidden] { display: none; }

/* A question with a long option list — the eleven symptoms, the twenty body
   areas — gets a tighter setting so it still fits one screen. Chosen by option
   count rather than by hand, so a question added later gets it automatically. */
.qcard--dense { padding: var(--s-5) var(--s-4) var(--s-4); }
.qcard--dense h2 { font-size: 20px; line-height: 1.25; margin-bottom: var(--s-4); }

/* ==========================================================================
   Touch targets — mobile first, 18 September 2026.
   Added after a real defect: options were 40px tall, below every published
   minimum, on a screen a player uses with cold hands in a car park.

   The floor is 48px, not 44. Apple's guideline is 44pt and Material's is 48dp;
   WCAG 2.2 AA asks only 24px and AAA asks 44. We take the largest because the
   cost of a mis-tap here is not a moment's annoyance — it is a wrong answer in
   a dataset about someone's health, silently.

   Two rules follow from that and both are enforced here rather than remembered:
     * nothing tappable is under 48px in either direction;
     * when there are few options they grow to fill the row, because unused
       width next to a small target is a mis-tap waiting to happen.
   ========================================================================== */

:root {
  --tap: 48px;          /* the floor for anything a thumb touches */
  --tap-gap: 8px;       /* Material's minimum separation between targets */
}

.choices { gap: var(--tap-gap); }
.choices button,
.pill {
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 13px 12px;
  font-size: 15px;
  line-height: 1.25;
}

/* Two or three options fill the row: a "Yes" that occupies a fifth of the
   screen while four fifths sit empty is the easiest mis-tap in the product. */
.choices--row-2 button, .choices--row-3 button, .choices--few button { padding-left: 10px; padding-right: 10px; }

.scale { gap: var(--tap-gap); }
.scale button {
  min-width: var(--tap);
  min-height: var(--tap);
  aspect-ratio: auto;
  height: 56px;                 /* a number is a smaller visual target than a word */
  font-size: 18px;
}

/* 16px is the threshold below which iOS Safari zooms the page on focus, which
   on a form feels like the app lurching. Never go under it on an input. */
.input, .select, .textarea { font-size: 16px; min-height: var(--tap); }

/* Safari draws a native <select> at its own height and ignores min-height and
   padding, so the squad picker measured 26px on every iPhone and iPad while the
   rule above said 48. Dropping the native appearance is what lets the rule
   apply; the chevron goes back in as a background so it still reads as a
   menu. Found measuring the coach screen at tablet sizes, 4 October. */
.select {
  -webkit-appearance: none; appearance: none;
  padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23111' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
}

.btn { min-height: var(--tap); }
.btn--ghost { min-height: 40px; padding: 10px 6px; }   /* not a primary path */

/* A fixed footer must clear the home indicator, and the page must reserve room
   for it or the last control sits underneath. */
.cta-dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
  padding: var(--s-4) var(--s-4) calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  max-width: 460px; margin: 0 auto;
}
/* Set from the dock's measured height at runtime, not guessed. A guessed 104
   against a real 120 put the densest question 17px off the bottom of the
   screen — the kind of gap that only appears on the one page that needed the
   room. */
.has-dock { padding-bottom: var(--dock-height, 120px); }

/* ==========================================================================
   Account control, sheets, and the states a screen is in when it has nothing
   to show. Added 18 September while writing the system down properly.
   ========================================================================== */

/* Sign-out does not belong in the chrome of a task screen: a player checking in
   has no reason to leave, and a one-tap "sign out" beside "Next" is a mistake
   waiting to be made. It lives behind her own initial instead — two taps, which
   is right for something that ends a session. */
.avatar-btn {
  width: var(--tap); height: var(--tap); border-radius: 50%;
  border: 1px solid var(--line); background: var(--white); color: var(--ink);
  font: 600 15px var(--font-text); cursor: pointer;
  display: inline-grid; place-items: center; padding: 0;
  transition: border-color .15s;
}
.avatar-btn:hover { border-color: var(--ink); }

.sheet-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,.28); z-index: 40;
  animation: fade .18s ease both;
}
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 41;
  max-width: 460px; margin: 0 auto;
  background: var(--white); border-radius: 24px 24px 0 0;
  padding: var(--s-5) var(--s-5) calc(var(--s-6) + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -8px 40px rgba(0,0,0,.14);
  animation: sheet-up .24s cubic-bezier(.2,.7,.3,1) both;
}
.sheet .grabber {
  width: 36px; height: 4px; border-radius: 100px; background: var(--line);
  margin: 0 auto var(--s-5);
}
.sheet h3 { margin-bottom: var(--s-2); }
.sheet .meta { margin: 0 0 var(--s-5); }

@keyframes fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes sheet-up { from { transform: translateY(100%) } to { transform: none } }

/* ------------------------------------------------------- empty and busy --- */
/* A screen with nothing in it still has to say something. "Loading…" as the
   only answer to a slow network is how a page looks broken. */

.empty { text-align: center; padding: var(--s-8) var(--s-5); }
.empty .mark-lg { opacity: .18; }
.empty h3 { margin-bottom: var(--s-2); }
.empty p { margin: 0 auto; max-width: 30ch; }

.skeleton {
  background: linear-gradient(90deg, rgba(0,0,0,.05) 25%, rgba(0,0,0,.09) 37%, rgba(0,0,0,.05) 63%);
  background-size: 400% 100%; border-radius: 8px;
  animation: shimmer 1.4s ease infinite;
}
.skeleton--line { height: 13px; margin-bottom: 9px; }
.skeleton--title { height: 20px; width: 55%; margin-bottom: var(--s-4); }
@keyframes shimmer { from { background-position: 100% 0 } to { background-position: 0 0 } }

/* ------------------------------------------------------------ segmented --- */
/* Three or fewer mutually exclusive options that share one decision — "who can
   see this". A row of pills would read as three separate choices. */

.seg {
  display: flex; border: 1px solid var(--line); border-radius: var(--radius-pill);
  overflow: hidden; background: var(--white);
}
.seg button {
  flex: 1; border: 0; background: none; color: var(--ink); cursor: pointer;
  min-height: var(--tap); padding: 12px 8px;
  font: 500 14px var(--font-text);
}
.seg button[aria-pressed="true"] { background: var(--ink); color: var(--white); }

/* ---------------------------------------------- progress as the mark ------ */
/*
 * Lorraine, 21 September: make the circle above the question a completeness
 * graph, and take the indicator out of the corner — it was not rewarding.
 * Orlaith, the same day: a player needs to see the number, so she knows it is
 * short.
 *
 * Both, rather than either. The ring fills as she goes and the count sits under
 * it, so the reward and the fact are the same object. The block's own shape
 * stays inside the ring, so filling it in does not cost the sense of which part
 * of the check-in she is in.
 */
.progress-mark {
  position: relative; width: 104px; height: 104px; margin: 0 auto var(--s-4);
  display: grid; place-items: center;
}
/* The ring is masked to an arc rather than being a disc with another disc on
   top of it: the accent ground has to show through the middle, and a solid
   centre in --pale was visible against sage and blush. */
.progress-mark::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--ink) calc(var(--done, 0) * 1turn), rgba(0,0,0,.14) 0);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #000 calc(100% - 7px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #000 calc(100% - 7px));
}
.progress-mark .mark-inner {
  width: 46px; height: 46px; border-radius: 50%; border: 2.5px solid var(--ink);
}
.mark-inner--full  { background: var(--ink); }
.mark-inner--half  { background: linear-gradient(to right, var(--ink) 0 50%, transparent 50% 100%); }
.mark-inner--ring  { background: radial-gradient(circle at center, var(--ink) 0 9px, transparent 9px); }
.mark-inner--slash { background-image: linear-gradient(45deg, transparent 44%, var(--ink) 44% 56%, transparent 56%); }

.progress-count {
  text-align: center; margin: 0 0 var(--s-5);
  font: 500 12px/1 var(--font-text); letter-spacing: .1em;
  text-transform: uppercase; color: var(--mist);
}

/* Shared auth and validation states. Colour is never the only error cue. */
a { color: var(--ink); text-underline-offset: 3px; }
.btn--text:disabled { opacity: .6; cursor: default; }
.password-field { position: relative; }
.password-field .input { padding-right: 84px; }
.password-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  min-height: 36px; padding: 6px 12px; border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--white); color: var(--ink); font: 500 13px var(--font-text); cursor: pointer;
}
.password-toggle:hover { border-color: var(--ink); }
.field-error { color: var(--ink); font-size: 14px; font-weight: 600; margin: 8px 0 0; }
[aria-invalid="true"] { border: 2px solid var(--ink); }
.notice ul { margin: 8px 0 0; padding-left: 20px; }
/* Focus the page moves itself, to announce a section or a result, gets no
   ring: the ring is for focus a person moved. Tab never lands on these
   (tabindex -1). Lorraine, 15 October: black boxes around whole sections. */
[tabindex="-1"]:is(section, div, main, aside, h1, h2, h3, p):focus { outline: none; }
.auth-hint { margin: 8px 0 0; }
.invite-summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; }
.invite-summary p { margin: 0; }
.auth-help { margin: 20px 0; font-size: 14px; color: var(--slate); }
.auth-help summary { cursor: pointer; min-height: 44px; display: list-item; padding: 10px 0; }
.auth-help p { font-size: 14px; margin-top: 12px; }
/* A labelled native modal keeps background controls inert. */
dialog.squad-dialog { position: fixed; inset: auto 0 0; margin: 0 auto; width: min(100%,460px); max-width: 100%; max-height: 85dvh; overflow-y: auto; padding: 0 24px 24px; border: 0; border-radius: 24px 24px 0 0; background: var(--white); color: var(--ink); }
dialog.squad-dialog::backdrop { background: rgba(0,0,0,.35); }
.squad-dialog-head { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 0; background: var(--white); }
.squad-dialog-head h2 { margin: 0; }

/* Native iOS date/time controls must shrink within their grid cell. */
.input:is([type="date"], [type="time"]) { -webkit-appearance: none; appearance: none; display: block; min-height: var(--tap); }
.input::-webkit-date-and-time-value { text-align: left; min-height: 1.2em; }
.input::-webkit-datetime-edit { padding: 0; min-width: 0; }
.form-help { font-size: 14px; line-height: 1.5; color: var(--slate); margin: 8px 0 0; }
.join-progress { font-size: 13px; color: var(--slate); margin: 0 0 16px; }
.btn--destructive { border: 2px solid var(--ink); border-radius: var(--radius-sm); background: var(--white); color: var(--ink); }

/* Contraception and periods (cycle-status.js): questions as plain groups. */
.cycle-question { border: 0; padding: 0; margin: 0 0 var(--s-6); min-width: 0; }
.cycle-question legend { padding: 0; margin: 0 0 var(--s-2); }
.cycle-question .meta { margin: 0 0 var(--s-3); }
