/* ═══════════════════════════════════════════════════════
   ORTBITE — shared mobile-first foundation
   ─────────────────────────────────────────────────────────
   Tokens + primitives for the public site: the attendee
   dashboard, the landing page, and the public itinerary.

   This file deliberately does NOT extend style.css. That
   stylesheet is desktop-first, 1,296 lines with its media
   queries clustered in one early band, so everything built
   after that pass is desktop-only. Inheriting from it means
   inheriting its mobile bugs. This is a greenfield layer,
   lifted from publish/split.css — the one surface Hakeem
   singled out as working well on both phone and computer.

   Rules this file is built to (MOBILE-SPEC §2):
     • mobile-first cascade — base = 375px, min-width adds desktop
     • zero <table>; card lists that reflow
     • min-width: 0 on every flex/grid child
     • overflow-wrap: break-word (never `anywhere`) on user text
     • 100dvh, never 100vh
     • safe-area insets on anything fixed to the bottom
     • every tap target >= 44x44
     • inputs never below 16px (iOS zooms on focus otherwise)

   Colour theme is preserved from the live site: --brand is
   #2F4DA3, the existing ORTBITE blue.
═══════════════════════════════════════════════════════ */

/* ── Tokens ──────────────────────────────────────────── */
:root {
  --brand:        #2F4DA3;
  --brand-dark:   #243C80;
  --brand-soft:   #EAEFFA;
  --brand-tint:   rgba(47, 77, 163, .08);

  --ink:          #16181D;
  --text:         #3A3F4B;
  --muted:        #6B7280;
  --faint:        #9AA1AE;

  --page:         #F5F6F9;
  --surface:      #FFFFFF;
  --surface-2:    #FAFBFD;
  --border:       #E4E7EE;
  --border-soft:  #EEF0F5;
  --border-strong:#CFD4DF;

  --good:         #12805C;
  --good-soft:    #E6F5EF;
  --good-line:    #C7EBD9;
  --bad:          #C0392B;
  --bad-soft:     #FCEDEB;
  --bad-line:     #F3CACA;
  --warn:         #9A6700;
  --warn-soft:    #FFF6E0;
  --warn-line:    #F0DFC0;

  --r-sm: 8px;
  --r:    12px;
  --r-lg: 16px;
  --r-xl: 22px;

  --shadow:    0 1px 2px rgba(16,24,40,.05), 0 1px 3px rgba(16,24,40,.06);
  --shadow-md: 0 4px 12px rgba(16,24,40,.08);
  --shadow-lg: 0 -6px 28px rgba(16,24,40,.14);

  /* Fixed chrome heights, so scroll padding reserves exactly the right room */
  --topbar-h: 56px;
  --nav-h:    62px;

  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
}

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

html, body {
  margin: 0;
  padding: 0;
  /* 100dvh, not 100vh: mobile browser chrome collapses on scroll and 100vh
     overshoots by the toolbar height, cutting off the bottom of the page. */
  min-height: 100dvh;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.45;
  color: var(--text);
  background: var(--page);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  /* Kills the 300ms double-tap-to-zoom delay without disabling pinch zoom. */
  touch-action: manipulation;
  overscroll-behavior-y: none;
}

h1, h2, h3, h4, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
button { font: inherit; color: inherit; }
a { color: var(--brand); }
img { max-width: 100%; height: auto; display: block; }
[hidden] { display: none !important; }

/* Anything a person typed. Never nowrap — "Thomas O'Connell" in a narrow chip
   is exactly the overflow this prevents.

   break-word, NOT anywhere. `anywhere` also shrinks the element's min-content
   width, which lets a flex or grid child collapse to a few characters and
   shatters ordinary words down the column — that exact layout was rejected
   once already. break-word only breaks a word that genuinely cannot fit. */
.u-text { overflow-wrap: break-word; }

.u-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── App shell ───────────────────────────────────────── */
.app {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--topbar-h);
  padding: 0 8px 0 14px;
  padding-top: var(--safe-t);
  box-sizing: content-box;
  background: rgba(255,255,255,.88);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.topbar-title {
  flex: 1;
  min-width: 0;                 /* flex children default to min-width:auto and refuse to shrink */
  display: flex;
  align-items: center;
  gap: 9px;
  /* It is a link home, so it is a tap target and takes the 44px floor like
     every other one — the text is only 24px tall on its own. */
  min-height: 44px;
  font-size: 1.02rem;
  font-weight: 800;
  letter-spacing: .01em;
  color: var(--ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 4px var(--brand-tint);
  flex: none;
}

.topbar-actions { display: flex; align-items: center; gap: 2px; flex: none; }

.main {
  flex: 1;
  min-height: 0;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  padding: 14px 14px calc(var(--nav-h) + var(--safe-b) + 26px);
}

/* Pages with no bottom nav (landing, public itinerary, the invite views)
   don't need to reserve room for one. */
.main-plain { padding-bottom: calc(var(--safe-b) + 40px); }

/* ── Bottom navigation ───────────────────────────────── */
/* Thumb reach. A sidebar is the wrong model on a phone, and the one on the
   director dashboard was literally unreachable until 2026-08-20. */
.nav {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 45;
  display: flex;
  align-items: stretch;
  height: calc(var(--nav-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: rgba(255,255,255,.94);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--border);
}

.nav-btn {
  flex: 1;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 44px;
  padding: 6px 2px;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;          /* fixed UI labels; never user text */
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.nav-btn .ico { font-size: 1.15rem; line-height: 1; font-style: normal; }
.nav-btn[aria-selected="true"] { color: var(--brand); }
.nav-btn:active { background: var(--brand-tint); }

/* Unmissable "there is something for you here" mark. Positioned off the icon,
   not the button, so it tracks the glyph rather than the label width. */
.nav-dot {
  position: absolute;
  top: 7px;
  left: 50%;
  margin-left: 7px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--bad);
  border: 1.5px solid var(--surface);
}

/* Same anchor as .nav-dot, but carrying a count — used on Plan when there is
   an unanswered quiz or vote, where "how many" is the whole point. Sized off
   the text so 1 and 9+ both stay a clean pill. */
.nav-badge {
  position: absolute;
  top: 3px;
  left: 50%;
  margin-left: 4px;
  min-width: 17px;
  height: 17px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--bad);
  border: 1.5px solid var(--surface);
  color: #fff;
  font-size: .62rem;
  font-weight: 800;
  line-height: 14px;
  text-align: center;
  letter-spacing: 0;
}

/* ── Cards ───────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
}

.card-pad { padding: 16px; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 22px 2px 10px;
}
.section-head:first-child { margin-top: 4px; }

.section-title {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

.section-note { font-size: .8rem; color: var(--faint); }

/* Label/value rows. The base unit of nearly every panel on the site. */
.rows { display: flex; flex-direction: column; }

.row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: .92rem;
}
.row:first-child { padding-top: 0; }
.row:last-child { padding-bottom: 0; border-bottom: 0; }

.row-label { flex: 1; min-width: 0; color: var(--muted); overflow-wrap: break-word; }
.row-value {
  flex: none;
  max-width: 62%;
  font-weight: 700;
  color: var(--ink);
  text-align: right;
  overflow-wrap: break-word;
}
.row-value.good { color: var(--good); }
.row-value.warn { color: var(--warn); }
.row-value.bad  { color: var(--bad); }
.row-value.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .86rem; }

/* ── Hero ────────────────────────────────────────────── */
.hero {
  padding: 24px 20px 22px;
  text-align: center;
  border-radius: var(--r-xl);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
}

.hero-label {
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

.hero-amount {
  margin-top: 8px;
  font-size: clamp(2.3rem, 12vw, 3rem);
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1.05;
  color: var(--ink);
}
.hero-amount.good { color: var(--good); }
.hero-amount.bad  { color: var(--bad); }
.hero-amount.warn { color: var(--warn); }

.hero-sub { margin-top: 6px; font-size: .88rem; color: var(--muted); overflow-wrap: break-word; }

/* Base is label/value rows, not columns across. Three currency columns do not
   fit a 320px card: "£2,193.77" measures 85px of ink inside an 83px third and,
   having no break opportunity, spills into its neighbour rather than wrapping.
   Rows hold for any amount at any width, so they are the floor and the strip
   is the min-width upgrade. */
.hero-stats {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.hero-stat {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.hero-stat-label {
  font-size: .68rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint);
}
.hero-stat-value { font-size: 1.02rem; font-weight: 700; color: var(--ink); }
.hero-stat-value.good { color: var(--good); }
.hero-stat-value.warn { color: var(--warn); }

@media (min-width: 380px) {
  .hero-stats {
    flex-direction: row;
    gap: 0;
    margin-top: 18px;
    padding-top: 16px;
  }
  .hero-stat { display: block; text-align: center; }
  /* Padding as well as the rule, or a longer total sits flush against it. */
  .hero-stat + .hero-stat {
    border-left: 1px solid var(--border);
    padding-left: 10px;
  }
  .hero-stat-value { margin-top: 3px; }
}

/* ── Buttons ─────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 44px;                 /* the tap-target floor, not a suggestion */
  padding: 11px 18px;
  border: 1px solid transparent;
  border-radius: var(--r);
  font-size: .95rem;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  text-decoration: none;
  text-align: center;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: default; transform: none; }

.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:active { background: var(--brand-dark); }

.btn-secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--border-strong);
}

.btn-ghost { background: none; color: var(--brand); font-weight: 600; }
.btn-quiet { background: none; color: var(--muted); font-weight: 600; }

.btn-danger  { background: var(--bad-soft);  color: var(--bad); }
.btn-warning { background: var(--warn);      color: #fff; }
.btn-success { background: var(--good);      color: #fff; }

/* "Small" is about text size and padding, never about the tap target — 44px
   is the floor for every button on the page, no exceptions. */
.btn-sm { min-height: 44px; padding: 9px 14px; font-size: .87rem; }

.btn-block { width: 100%; }

.btn-stack { display: flex; flex-direction: column; gap: 9px; }
.btn-stack > .btn { width: 100%; }

.btn-pair { display: flex; gap: 10px; }
.btn-pair > .btn { flex: 1; min-width: 0; }

/* Icon-only controls still have to clear 44px — the "buttons are missing"
   complaint on the legacy pages was 28px targets, not absent buttons. */
.icon-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--muted);
  font-size: 1.1rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.icon-btn:active { background: var(--brand-tint); }

/* ── Forms ───────────────────────────────────────────── */
.field { margin-bottom: 14px; }

.label {
  display: block;
  margin-bottom: 6px;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.input, .select, .textarea {
  width: 100%;
  min-height: 46px;
  padding: 11px 13px;
  /* 16px minimum. Below this iOS Safari zooms the viewport on focus and leaves
     the user stranded at 2x — the "page went weird" bug. */
  font-size: 16px;
  font-family: inherit;
  line-height: 1.35;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-tint);
}
.input:disabled, .input[readonly] { background: var(--surface-2); color: var(--muted); }
.textarea { min-height: 76px; resize: vertical; }

/* Free-text fields are auto-growing textareas, not <input type=text>.
   An input scrolls its content sideways the moment the text is longer than
   the box, which on a phone means the start of what you typed is simply gone.
   These wrap and grow downwards instead, so nothing ever scrolls horizontally
   inside a field. The page's JS sizes them via autoGrow(); the CSS keeps them
   looking like inputs. */
.input-auto {
  resize: none;
  overflow: hidden;             /* no scrollbar in either axis — JS sets the height */
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

.select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B7280' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  padding-right: 36px;
}

.field-row { display: flex; gap: 10px; }
.field-row > * { flex: 1; min-width: 0; }

.help { margin-top: 5px; font-size: .78rem; color: var(--muted); overflow-wrap: break-word; }
.help.bad { color: var(--bad); }
.err { min-height: 20px; margin-top: 8px; font-size: .84rem; color: var(--bad); overflow-wrap: break-word; }

/* ── Chips ───────────────────────────────────────────── */
.chips { display: flex; flex-wrap: wrap; gap: 8px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 13px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: .88rem;
  font-weight: 600;
  overflow-wrap: break-word;
  text-align: left;
}

/* Only an interactive chip needs the 44px floor. A static label chip — a role,
   a category — is not a tap target and padding it to 44px just wastes a phone
   screen. */
.chip-btn {
  min-height: 44px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.chip-btn[aria-pressed="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.chip-btn:active { transform: scale(.97); }

.chip-soft { background: var(--brand-soft); border-color: transparent; color: var(--brand); }
.chip-good { background: var(--good-soft); border-color: transparent; color: var(--good); }
.chip-warn { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.chip-bad  { background: var(--bad-soft);  border-color: transparent; color: var(--bad); }

/* Segmented control — two or three exclusive views */
.segmented {
  display: flex;
  gap: 4px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.segmented > button {
  flex: 1;
  min-width: 0;
  min-height: 40px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--muted);
  font-size: .87rem;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  overflow-wrap: break-word;
}
.segmented > button[aria-pressed="true"] {
  background: var(--surface);
  color: var(--brand);
  box-shadow: var(--shadow);
}

/* ── Bottom sheets ───────────────────────────────────── */
/* Modals become sheets on a phone: full-width, rising from the thumb end. */
.scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(16,24,40,.45);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  animation: fade-in .18s ease-out;
}

.sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 61;
  display: flex;
  flex-direction: column;
  max-height: 92dvh;
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--shadow-lg);
  animation: sheet-up .24s cubic-bezier(.32,.72,0,1);
}

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

@media (prefers-reduced-motion: reduce) {
  .scrim, .sheet { animation: none; }
}

.sheet-grip {
  flex: none;
  width: 40px; height: 4px;
  margin: 9px auto 3px;
  border-radius: 999px;
  background: var(--border-strong);
}

.sheet-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px 10px 16px;
  border-bottom: 1px solid var(--border);
}
.sheet-title {
  flex: 1;
  min-width: 0;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--ink);
  overflow-wrap: break-word;
}

.sheet-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 16px;
}

/* Sticky action bar. Padding-bottom carries the safe-area inset or the primary
   button sits under the home indicator on every notched iPhone. */
.sheet-foot {
  flex: none;
  display: flex;
  gap: 10px;
  padding: 12px 16px calc(12px + var(--safe-b));
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.sheet-foot > .btn { flex: 1; min-width: 0; }

/* Freeze the page behind an open sheet, or scrolling the sheet drags the page
   underneath it — the classic iOS scroll-chaining bug. */
body.sheet-open { overflow: hidden; }

/* ── Disclosure (progressive detail) ─────────────────── */
.disclosure {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 10px 0;
  border: 0;
  background: none;
  color: var(--brand);
  font-size: .88rem;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.disclosure .caret { flex: none; font-style: normal; transition: transform .18s; }
.disclosure[aria-expanded="true"] .caret { transform: rotate(90deg); }

/* ── Empty / status states ───────────────────────────── */
.empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--muted);
}
.empty-icon { font-size: 2.2rem; font-style: normal; display: block; margin-bottom: 10px; opacity: .55; }
.empty-title { font-size: 1rem; font-weight: 700; color: var(--ink); margin-bottom: 5px; }
.empty p { font-size: .88rem; overflow-wrap: break-word; }

.banner {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 12px 14px;
  border-radius: var(--r);
  font-size: .87rem;
  line-height: 1.5;
  overflow-wrap: break-word;
}
.banner + .banner { margin-top: 9px; }
.banner .ico { flex: none; font-style: normal; }
.banner-body { flex: 1; min-width: 0; }
.banner strong { font-weight: 700; }
.banner-warn { background: var(--warn-soft); border: 1px solid var(--warn-line); color: #7a5212; }
.banner-bad  { background: var(--bad-soft);  border: 1px solid var(--bad-line);  color: #93261a; }
.banner-good { background: var(--good-soft); border: 1px solid var(--good-line); color: #0c6b4b; }
.banner-info { background: var(--brand-soft); border: 1px solid transparent; color: #23386f; }

/* "Something new landed for you" — deliberately louder than banner-info,
   which is used for passive context. */
.banner-new {
  background: var(--brand-soft);
  border: 1px solid var(--brand);
  color: #23386f;
  box-shadow: 0 1px 0 var(--brand-tint);
}
.banner-kicker {
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--brand);
  margin-bottom: 3px;
}
.banner-cta {
  margin-top: 6px;
  font-weight: 700;
  color: var(--brand);
}
button.banner-new { width: 100%; text-align: left; cursor: pointer; font: inherit; }
button.banner-new:active { background: var(--brand-tint); }

/* A banner that is itself a link (e.g. the door into Split) rather than an
   onclick button — same tappable-strip look, no underline. */
a.banner { text-decoration: none; }
a.banner:active { background: var(--brand-tint); }

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--nav-h) + var(--safe-b) + 16px);
  transform: translateX(-50%);
  z-index: 70;
  max-width: calc(100vw - 32px);
  padding: 11px 18px;
  border-radius: 999px;
  background: var(--ink);
  color: #fff;
  font-size: .87rem;
  font-weight: 600;
  box-shadow: var(--shadow-md);
  animation: fade-in .16s ease-out;
  overflow-wrap: break-word;
  text-align: center;
  transition: opacity .3s;
}
.toast.bad  { background: var(--bad); }
.toast.good { background: var(--good); }

.spinner {
  display: inline-block;
  width: 16px; height: 16px;
  border: 2px solid rgba(255,255,255,.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin .7s linear infinite;
  vertical-align: -2px;
}
@keyframes spin { to { transform: rotate(360deg); } }

.loading { display: flex; justify-content: center; padding: 48px 0; }

/* ── Desktop ─────────────────────────────────────────── */
/* min-width only. The cascade adds desktop on top of a working phone layout,
   never the other way round. */
@media (min-width: 700px) {
  .main { padding: 22px 22px 40px; max-width: 660px; }
  .main-plain { padding-bottom: 48px; }

  /* Sticky under the topbar rather than fixed to the floor — a bottom bar on a
     desktop window is a long way from the pointer. It needs its own ground:
     with no background the page scrolled straight through the pill labels. It
     stays full-bleed and centres its buttons rather than being sized by
     max-width + margin:auto, because auto cross-axis margins on a flex item
     override align-self:stretch and collapse the bar to fit-content. */
  .nav {
    position: sticky;
    top: calc(var(--topbar-h) + var(--safe-t));
    bottom: auto;
    z-index: 39;
    justify-content: center;
    height: auto;
    padding: 8px 14px;
    gap: 6px;
    background: rgba(255,255,255,.85);
    -webkit-backdrop-filter: blur(16px) saturate(150%);
    backdrop-filter: blur(16px) saturate(150%);
    border-top: 0;
    border-bottom: 1px solid var(--border);
  }
  .nav-btn {
    flex: 0 0 auto;               /* pills sized to their label, not stretched */
    flex-direction: row;
    gap: 7px;
    border-radius: 999px;
    font-size: .88rem;
    padding: 8px 14px;
    white-space: nowrap;          /* a fixed UI label, not user text */
  }
  .nav-btn[aria-selected="true"] { background: var(--brand-soft); }
  .nav-dot { position: static; margin: 0 0 0 2px; }
  .nav-badge { position: static; margin: 0 0 0 4px; }

  .sheet {
    left: 50%;
    bottom: auto;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - 40px));
    max-height: min(82dvh, 780px);
    border-radius: var(--r-xl);
    animation: fade-in .18s ease-out;
  }
  .sheet-grip { display: none; }
  .sheet-foot { padding-bottom: 14px; }

  .toast { bottom: 26px; }
}
