/*
 * BetterTrip design system (spec §5.3, §11).
 *
 * Tokens are CSS custom properties written into :root at runtime from the
 * authenticated tenant's theme, so one deployment serves every organization's
 * branding with no rebuild.
 *
 * Non-negotiables enforced here rather than left to each screen:
 *   - 44x44px minimum touch targets everywhere, not only on small screens
 *   - visible keyboard focus rings on every interactive element
 *   - prefers-reduced-motion respected globally
 *   - logical properties only (margin-inline-start, not margin-left) so one
 *     stylesheet serves LTR and RTL with zero direction-specific overrides
 */

:root {
  /* "Horizon Sky" — the travel palette (sky blue + adventure orange), with every
     pair adjusted until it clears the same AA validator tenant themes must pass:
     white on primary 7.5:1, dark text on accent 6:1, body text on background 14:1+. */
  --bt-color-primary: #0B5A83;
  --bt-color-secondary: #0C7FB0;
  --bt-color-accent: #EA580C;
  --bt-color-background: #F4F8FB;
  --bt-color-surface: #FFFFFF;
  --bt-color-text: #0F1B26;
  --bt-radius: 12px;
  /* One stack serves both scripts: Latin glyphs resolve from Plus Jakarta Sans,
     Arabic glyphs fall through to IBM Plex Sans Arabic. Full system fallbacks
     keep the app correct with no network at all. */
  --bt-font: "Plus Jakarta Sans", "IBM Plex Sans Arabic", "Segoe UI", system-ui, -apple-system, sans-serif;
  --bt-touch-min: 44px;

  --muted: color-mix(in srgb, var(--bt-color-text) 58%, var(--bt-color-surface));
  --line: color-mix(in srgb, var(--bt-color-text) 12%, var(--bt-color-surface));
  --line-strong: color-mix(in srgb, var(--bt-color-text) 24%, var(--bt-color-surface));
  --tint: color-mix(in srgb, var(--bt-color-primary) 8%, var(--bt-color-surface));
  --tint-strong: color-mix(in srgb, var(--bt-color-primary) 16%, var(--bt-color-surface));

  --ok: #1d7a4c;
  --warn: #a86a00;
  --danger: #b3261e;
  --info: var(--bt-color-secondary);

  /* Elevation scale — one shadow language everywhere, tinted by ink not black,
     so cards read as lifted paper rather than smudged. */
  --shadow-sm: 0 1px 2px rgba(15, 27, 38, .05), 0 1px 3px rgba(15, 27, 38, .05);
  --shadow-md: 0 10px 24px -12px rgba(15, 27, 38, .18), 0 2px 6px rgba(15, 27, 38, .05);
  --shadow-lg: 0 24px 48px -20px rgba(15, 27, 38, .28), 0 4px 12px rgba(15, 27, 38, .06);
  --shadow-primary: 0 10px 24px -10px color-mix(in srgb, var(--bt-color-primary) 55%, transparent);

  /* Motion tokens — one rhythm for the whole product (150–300ms micro, ≤400ms enter). */
  --motion-fast: 150ms;
  --motion-base: 220ms;
  --motion-enter: 380ms;
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-in: cubic-bezier(.7, 0, .84, 0);

  --space: 16px;
  --sidebar: 260px;
  --rail: 76px;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--bt-font);
  font-size: 16px;               /* 16px floor — also prevents iOS input auto-zoom */
  line-height: 1.55;
  color: var(--bt-color-text);
  background: var(--bt-color-background);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { margin: 0 0 .4em; line-height: 1.15; letter-spacing: -.022em; font-weight: 700; }
h1 { font-size: 1.7rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.08rem; }
p { margin: 0 0 .8em; }
a { color: var(--bt-color-primary); }

/* Prices, scores and timers keep their width while animating. */
.mono, .stat .value, [data-countup] { font-variant-numeric: tabular-nums; }

:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--bt-color-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

.skip-link {
  position: absolute; inset-inline-start: -9999px; top: 8px; z-index: 100;
  background: var(--bt-color-surface); padding: 10px 16px; border-radius: var(--bt-radius);
}
.skip-link:focus { inset-inline-start: 8px; }

/* --- Boot ---------------------------------------------------------------- */
.boot { min-height: 100vh; display: grid; place-content: center; justify-items: center; gap: 12px; color: var(--muted); }
.boot-mark, .brand-mark {
  width: 48px; height: 48px; border-radius: 15px; display: grid; place-items: center;
  background: linear-gradient(135deg, var(--bt-color-primary), var(--bt-color-secondary));
  color: #fff; font-weight: 800; letter-spacing: -.03em;
}

/* --- Login --------------------------------------------------------------- */
.login-wrap {
  min-height: 100vh; display: grid; place-items: center; padding: 24px;
  background:
    radial-gradient(1000px 460px at 8% -10%, color-mix(in srgb, var(--bt-color-secondary) 22%, transparent), transparent 62%),
    radial-gradient(800px 380px at 105% 8%, color-mix(in srgb, var(--bt-color-accent) 18%, transparent), transparent 60%),
    var(--bt-color-background);
}
.login-card {
  width: min(420px, 100%); background: var(--bt-color-surface); padding: 28px;
  border: 1px solid var(--line); border-radius: calc(var(--bt-radius) + 6px); box-shadow: var(--shadow-md);
}
.login-card .brand { display: flex; gap: 12px; align-items: center; margin-bottom: 18px; }
.login-hint { font-size: 13px; color: var(--muted); }
.demo-accounts { margin-top: 18px; border-top: 1px solid var(--line); padding-top: 14px; font-size: 13px; color: var(--muted); }
.demo-accounts button {
  display: block; width: 100%; text-align: start; background: var(--tint); border: 1px solid var(--line);
  margin-bottom: 6px; border-radius: 8px; min-height: 38px; padding: 6px 10px; cursor: pointer; font: inherit;
}

/* --- App frame ----------------------------------------------------------- */
.app-frame { min-height: 100vh; display: grid; grid-template-columns: var(--sidebar) 1fr; }

.sidebar {
  background: linear-gradient(170deg, var(--bt-color-primary), color-mix(in srgb, var(--bt-color-secondary) 70%, var(--bt-color-primary)));
  color: #fff; padding: 18px 12px; display: flex; flex-direction: column; gap: 4px;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar .brand { display: flex; gap: 10px; align-items: center; padding: 6px 8px 16px; }
.sidebar .brand strong { font-size: 15px; }
.sidebar .brand small { display: block; opacity: .75; font-size: 12px; }
.sidebar nav { display: flex; flex-direction: column; gap: 2px; }
.nav-item {
  display: flex; align-items: center; gap: 10px; min-height: var(--bt-touch-min);
  padding: 0 12px; border-radius: 10px; color: #fff; text-decoration: none;
  background: transparent; border: 0; font: inherit; cursor: pointer; text-align: start; width: 100%;
  transition: background .15s ease;
}
.nav-item:hover { background: rgba(255, 255, 255, .12); }
.nav-item[aria-current="page"] { background: rgba(255, 255, 255, .2); font-weight: 600; }
.nav-item .ico { width: 20px; text-align: center; flex: none; }
.nav-group { margin-top: 14px; padding: 0 12px; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; opacity: .7; }
.sidebar-footer { margin-top: auto; padding-top: 12px; border-top: 1px solid rgba(255,255,255,.18); font-size: 13px; }

.main { min-width: 0; display: flex; flex-direction: column; }

.topbar {
  display: flex; align-items: center; gap: 12px; padding: 10px 20px;
  background: var(--bt-color-surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.topbar h1 { margin: 0; font-size: 1.15rem; }
.topbar .spacer { flex: 1; }
.hamburger { display: none; }

.content { padding: 20px; flex: 1; }
.content-narrow { max-width: 1180px; }

/* --- Responsive: tablet rail, mobile drawer + bottom nav (spec §11) ------- */
@media (max-width: 1100px) {
  .app-frame { grid-template-columns: var(--rail) 1fr; }
  .sidebar { padding-inline: 8px; }
  .sidebar .brand small, .nav-item span.label, .nav-group, .sidebar-footer .label { display: none; }
  .nav-item { justify-content: center; padding: 0; }
}

@media (max-width: 760px) {
  .app-frame { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; inset-block: 0; inset-inline-start: 0; width: min(280px, 84vw); z-index: 60;
    transform: translateX(-110%); transition: transform .22s ease; padding-inline: 12px;
  }
  html[dir="rtl"] .sidebar { transform: translateX(110%); }
  .sidebar.open { transform: none; }
  .sidebar .brand small, .nav-item span.label, .nav-group, .sidebar-footer .label { display: revert; }
  .nav-item { justify-content: flex-start; padding: 0 12px; }
  .hamburger { display: inline-flex; }
  .content { padding: 14px 14px calc(76px + env(safe-area-inset-bottom)); }
  .scrim { position: fixed; inset: 0; background: rgba(16,20,24,.45); z-index: 50; }
}

.bottom-nav { display: none; }
@media (max-width: 760px) {
  .bottom-nav {
    display: grid; grid-auto-flow: column; position: fixed; inset-inline: 0; bottom: 0; z-index: 40;
    background: var(--bt-color-surface); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .bottom-nav a {
    min-height: var(--bt-touch-min); display: grid; place-items: center; gap: 2px; padding: 6px 4px;
    text-decoration: none; color: var(--muted); font-size: 11px; position: relative;
  }
  .bottom-nav a[aria-current="page"] { color: var(--bt-color-primary); font-weight: 600; }
  .bottom-nav a[aria-current="page"]::before {
    content: ""; position: absolute; top: 0; inset-inline: 18%; height: 3px;
    background: var(--bt-color-accent); border-radius: 0 0 3px 3px;
  }
  .bottom-nav .ico { font-size: 18px; }
}

/* --- Primitives ---------------------------------------------------------- */
.card {
  background: var(--bt-color-surface); border: 1px solid var(--line);
  border-radius: var(--bt-radius); padding: 16px; box-shadow: var(--shadow-sm);
}
.card + .card { margin-top: 14px; }
.card h2, .card h3 { margin-bottom: .5em; }
.card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.card-head h2, .card-head h3 { margin: 0; }
.card-head .spacer { flex: 1; }

.grid { display: grid; gap: 14px; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row.tight { gap: 6px; }
.spacer { flex: 1; }
.stack { display: grid; gap: 10px; }

.stat { background: var(--bt-color-surface); border: 1px solid var(--line); border-radius: var(--bt-radius); padding: 14px; }
.stat .value { font-size: 1.8rem; font-weight: 700; letter-spacing: -.02em; }
.stat .label { color: var(--muted); font-size: 13px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--bt-touch-min); min-width: var(--bt-touch-min); padding: 0 18px;
  border-radius: var(--bt-radius); border: 1px solid transparent; cursor: pointer;
  font: inherit; font-weight: 600; background: var(--bt-color-primary); color: #fff;
  text-decoration: none; touch-action: manipulation;
  transition: filter var(--motion-fast) var(--ease-out),
              transform var(--motion-fast) var(--ease-out),
              box-shadow var(--motion-fast) var(--ease-out);
}
.btn:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: var(--shadow-primary); }
.btn:active { transform: translateY(0) scale(.97); box-shadow: none; transition-duration: 80ms; }
.btn[disabled] { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
.btn.ghost:hover, .btn.secondary:hover { box-shadow: var(--shadow-sm); filter: none; background: var(--tint-strong); }
.btn.secondary { background: var(--tint); color: var(--bt-color-primary); border-color: var(--line-strong); }
.btn.ghost { background: transparent; color: var(--bt-color-text); border-color: var(--line-strong); }
.btn.danger { background: var(--danger); }
.btn.accent { background: var(--bt-color-accent); color: #1a0d08; }
.btn.small { min-height: 34px; padding: 0 10px; font-size: 13px; font-weight: 600; }
.btn.block { width: 100%; }

label.field { display: block; margin-bottom: 12px; font-size: 13px; color: var(--muted); font-weight: 600; }
label.field > span { display: block; margin-bottom: 4px; }
input, select, textarea {
  width: 100%; min-height: var(--bt-touch-min); padding: 8px 12px; font: inherit; color: var(--bt-color-text);
  background: var(--bt-color-surface); border: 1px solid var(--line-strong); border-radius: var(--bt-radius);
}
textarea { min-height: 90px; padding-top: 10px; resize: vertical; }
input[type="checkbox"], input[type="radio"] { width: auto; min-height: auto; accent-color: var(--bt-color-primary); }
.inline-check { display: flex; align-items: center; gap: 8px; min-height: var(--bt-touch-min); font-weight: 500; color: var(--bt-color-text); }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: start; padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
th { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
tbody tr:hover { background: var(--tint); }
.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--bt-radius); background: var(--bt-color-surface); }

.pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 2px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600; background: var(--tint); color: var(--bt-color-primary);
  border: 1px solid var(--line-strong);
}
.pill.ok { background: color-mix(in srgb, var(--ok) 14%, var(--bt-color-surface)); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.pill.warn { background: color-mix(in srgb, var(--warn) 15%, var(--bt-color-surface)); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.pill.danger { background: color-mix(in srgb, var(--danger) 12%, var(--bt-color-surface)); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 28%, transparent); }
.pill.muted { background: transparent; color: var(--muted); }

.muted { color: var(--muted); }
.small { font-size: 13px; }
.tiny { font-size: 12px; }
.right { margin-inline-start: auto; }
.mono { font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 13px; }

.empty, .loading, .error-state {
  padding: 28px; text-align: center; color: var(--muted);
  border: 1px dashed var(--line-strong); border-radius: var(--bt-radius); background: var(--bt-color-surface);
}
.error-state { border-color: color-mix(in srgb, var(--danger) 40%, transparent); color: var(--danger); }

.notice {
  border-inline-start: 4px solid var(--bt-color-secondary); background: var(--tint);
  padding: 10px 14px; border-radius: var(--bt-radius); font-size: 14px;
}
.notice.warn { border-color: var(--warn); background: color-mix(in srgb, var(--warn) 10%, var(--bt-color-surface)); }
.notice.danger { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, var(--bt-color-surface)); }

/* Direction-implying icons flip under RTL; non-directional ones never do. */
html[dir="rtl"] .flip { transform: scaleX(-1); }

/* --- Connectivity indicator (spec §5.9) ---------------------------------- */
.conn { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.conn .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ok); }
.conn.offline .dot { background: var(--muted); }
.conn.syncing .dot { background: var(--warn); }

/* --- Toasts -------------------------------------------------------------- */
.toasts { position: fixed; inset-block-end: 16px; inset-inline-end: 16px; z-index: 200; display: grid; gap: 8px; max-width: min(360px, 92vw); }
.toast {
  background: var(--bt-color-text); color: #fff; padding: 12px 14px; border-radius: var(--bt-radius);
  box-shadow: var(--shadow-md); font-size: 14px; animation: toast-in .2s ease;
}
.toast.error { background: var(--danger); }
.toast.ok { background: var(--ok); }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* --- Modal --------------------------------------------------------------- */
.modal-backdrop { position: fixed; inset: 0; background: rgba(16,20,24,.5); z-index: 150; display: grid; place-items: center; padding: 16px; }
.modal {
  background: var(--bt-color-surface); border-radius: calc(var(--bt-radius) + 4px); padding: 20px;
  width: min(560px, 100%); max-height: 88vh; overflow: auto; box-shadow: var(--shadow-md);
}
.modal.wide { width: min(880px, 100%); }

/* --- Seat map ------------------------------------------------------------ */
.seatmap-wrap { overflow: auto; padding: 14px; background: var(--tint); border-radius: var(--bt-radius); border: 1px solid var(--line); }
.seatmap { display: grid; gap: 6px; justify-content: center; }
.seat {
  min-width: var(--bt-touch-min); min-height: var(--bt-touch-min); border-radius: 10px; border: 1px solid var(--line-strong);
  background: var(--bt-color-surface); cursor: pointer; font: inherit; font-size: 11px; line-height: 1.15;
  display: grid; place-content: center; padding: 2px; text-align: center; position: relative;
}
.seat .who { font-weight: 600; font-size: 10px; max-width: 62px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seat .lbl { color: var(--muted); font-size: 10px; }
.seat.taken { background: color-mix(in srgb, var(--bt-color-secondary) 20%, var(--bt-color-surface)); border-color: var(--bt-color-secondary); }
.seat.locked { background: color-mix(in srgb, var(--bt-color-accent) 22%, var(--bt-color-surface)); border-color: var(--bt-color-accent); }
.seat.reserved { background: repeating-linear-gradient(45deg, var(--tint-strong), var(--tint-strong) 5px, var(--bt-color-surface) 5px, var(--bt-color-surface) 10px); }
.seat.disabled { opacity: .4; cursor: not-allowed; }
.seat.accessible::after { content: "♿"; position: absolute; inset-block-start: 1px; inset-inline-end: 3px; font-size: 9px; }
.seat.selected { outline: 3px solid var(--bt-color-accent); outline-offset: 1px; }
.seat.drop-target { border-style: dashed; border-color: var(--bt-color-accent); }
.aisle-gap { width: 18px; }
.legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--muted); margin-top: 10px; }
.legend i { display: inline-block; width: 12px; height: 12px; border-radius: 4px; border: 1px solid var(--line-strong); margin-inline-end: 5px; vertical-align: -2px; }

.roster-list { display: grid; gap: 6px; max-height: 460px; overflow: auto; }
.roster-item {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; min-height: var(--bt-touch-min);
  border: 1px solid var(--line); border-radius: 10px; background: var(--bt-color-surface); cursor: grab; font: inherit; width: 100%; text-align: start;
}
.roster-item.selected { border-color: var(--bt-color-accent); background: color-mix(in srgb, var(--bt-color-accent) 10%, var(--bt-color-surface)); }

/* --- Map ----------------------------------------------------------------- */
.map { position: relative; height: 420px; border-radius: var(--bt-radius); overflow: hidden; border: 1px solid var(--line); background: #dfe6e4; touch-action: none; }
.map-tiles { position: absolute; inset: 0; }
.map-tiles img { position: absolute; width: 256px; height: 256px; user-select: none; pointer-events: none; }
.map-pin { position: absolute; transform: translate(-50%, -100%); display: grid; justify-items: center; pointer-events: none; }
.map-pin .bubble { background: var(--bt-color-surface); border: 1px solid var(--line-strong); border-radius: 999px; padding: 2px 8px; font-size: 11px; font-weight: 600; box-shadow: var(--shadow-sm); white-space: nowrap; }
.map-pin .needle { width: 2px; height: 10px; background: var(--bt-color-primary); }
.map-pin.stale .bubble { opacity: .6; font-style: italic; }
.map-pin.guide .bubble { background: var(--bt-color-primary); color: #fff; border-color: var(--bt-color-primary); }
.map-attrib { position: absolute; inset-block-end: 0; inset-inline-end: 0; background: rgba(255,255,255,.82); font-size: 10px; padding: 1px 6px; }
.map-controls { position: absolute; inset-block-start: 8px; inset-inline-end: 8px; display: grid; gap: 4px; }
.map-controls button { width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--line-strong); background: var(--bt-color-surface); cursor: pointer; font-size: 16px; }

/* --- Chat ---------------------------------------------------------------- */
.chat-log { display: grid; gap: 8px; max-height: 52vh; overflow-y: auto; padding: 4px; }
.bubble { padding: 8px 12px; border-radius: 14px; background: var(--tint); max-width: 78%; }
.bubble.mine { background: var(--bt-color-primary); color: #fff; margin-inline-start: auto; }
.bubble .meta { font-size: 11px; opacity: .75; margin-bottom: 2px; }

/* --- Progress ------------------------------------------------------------ */
.progress { height: 8px; background: var(--tint-strong); border-radius: 999px; overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--bt-color-secondary); transition: width .3s ease; }

.diff-row { display: grid; grid-template-columns: 110px 1fr auto; gap: 10px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--line); }
.explain { font-size: 13px; color: var(--muted); }
.explain b { color: var(--bt-color-text); }

/* --- Trivia -------------------------------------------------------------- */
.trivia-wrap { min-height: 100vh; display: grid; place-items: center; padding: 20px; background: linear-gradient(160deg, var(--bt-color-primary), var(--bt-color-secondary)); }
.trivia-card { width: min(520px, 100%); background: var(--bt-color-surface); border-radius: 20px; padding: 24px; box-shadow: var(--shadow-md); }
.trivia-options { display: grid; gap: 10px; margin-top: 14px; }
.trivia-options button { min-height: 56px; font-size: 15px; }

/* --- Alert takeover (spec §5.3, §5.7) ------------------------------------ */
/* Sits above everything but deliberately does not blank the app: someone told
   to gather may still need the map underneath. */
.alert-takeover {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 300;
  display: grid; gap: 8px; padding: 10px; pointer-events: none;
  padding-top: calc(10px + env(safe-area-inset-top));
}
.alert-card {
  pointer-events: auto; margin-inline: auto; width: min(560px, 100%);
  background: var(--bt-color-surface); border-radius: var(--bt-radius);
  border: 2px solid var(--bt-color-secondary); padding: 14px;
  box-shadow: 0 18px 40px -12px rgba(16, 20, 24, .5);
  animation: alert-in .22s ease;
}
.alert-card.gather { border-color: var(--bt-color-accent); }
.alert-card.urgent {
  border-color: var(--danger);
  animation: alert-in .22s ease, alert-pulse 1.1s ease-in-out 3;
}
.alert-card.info { border-color: var(--bt-color-secondary); }

@keyframes alert-in { from { opacity: 0; transform: translateY(-14px); } to { opacity: 1; transform: none; } }
@keyframes alert-pulse {
  0%, 100% { box-shadow: 0 18px 40px -12px rgba(16, 20, 24, .5); }
  50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--danger) 22%, transparent), 0 18px 40px -12px rgba(16, 20, 24, .5); }
}
/* The buzzer still sounds and vibrates under reduced motion — only the
   animation is dropped, never the alert itself. */
@media (prefers-reduced-motion: reduce) {
  .alert-card, .alert-card.urgent { animation: none; }
}

/* --- Landing page (spec §11 decorative layer) ---------------------------- */
.landing { min-height: 100vh; background: var(--bt-color-background); }

.landing-bar {
  display: flex; align-items: center; gap: 10px; padding: 12px 24px;
  background: color-mix(in srgb, var(--bt-color-surface) 88%, transparent);
  backdrop-filter: blur(8px); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 30;
}
.landing-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.landing-brand .brand-mark { width: 36px; height: 36px; border-radius: 11px; font-size: 14px; }
.landing-brand strong { font-size: 17px; letter-spacing: -.02em; }

.landing-hero {
  position: relative; overflow: hidden; padding: 64px 24px 96px;
  display: grid; place-items: center; text-align: center;
}
.landing-hero-copy { position: relative; max-width: 720px; }
.landing-hero-copy h1 { font-size: clamp(2rem, 5vw, 3.1rem); margin: 8px 0 12px; }
.landing-eyebrow {
  margin: 0; font-size: 12px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--bt-color-secondary);
}
.landing-lede { font-size: clamp(1rem, 2.2vw, 1.2rem); color: var(--muted); margin: 0 auto; max-width: 56ch; }
.landing-hero .row { justify-content: center; }

.landing-section { max-width: 1080px; margin: 0 auto; padding: 56px 24px; }
.landing-section > h2 { font-size: clamp(1.4rem, 3vw, 2rem); }
.landing-sub { color: var(--muted); max-width: 62ch; }

.landing-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin-top: 24px; }
.landing-card {
  background: var(--bt-color-surface); border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 4px); padding: 20px; box-shadow: var(--shadow-sm);
}
.landing-card h3 { margin-bottom: .35em; }
.landing-ico {
  width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center;
  background: var(--tint-strong); color: var(--bt-color-primary); font-size: 18px; margin-bottom: 12px;
}

.landing-feature { display: grid; gap: 32px; grid-template-columns: 1fr; align-items: center; }
@media (min-width: 900px) { .landing-feature { grid-template-columns: 1.05fr .95fr; } }
.landing-list { color: var(--muted); padding-inline-start: 20px; margin: 14px 0 0; }
.landing-list li { margin: 6px 0; }

.landing-seatpanel {
  background: var(--bt-color-surface); border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 4px); padding: 20px; box-shadow: var(--shadow-md);
}
.landing-seatmap { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.landing-seat {
  aspect-ratio: 1; border-radius: 9px; border: 1px solid var(--line-strong);
  background: var(--bt-color-surface);
}
.landing-seat.a { background: color-mix(in srgb, var(--bt-color-secondary) 42%, var(--bt-color-surface)); border-color: var(--bt-color-secondary); }
.landing-seat.b { background: color-mix(in srgb, var(--bt-color-accent) 42%, var(--bt-color-surface)); border-color: var(--bt-color-accent); }
.landing-seat.c { background: color-mix(in srgb, var(--bt-color-primary) 34%, var(--bt-color-surface)); border-color: var(--bt-color-primary); }
.landing-aisle { }

.landing-honesty { border-top: 1px solid var(--line); }

.landing-footer {
  max-width: 1080px; margin: 0 auto; padding: 28px 24px 48px;
  border-top: 1px solid var(--line);
}

/* ==========================================================================
   Motion system (assets/js/core/motion.js)
   Initial hidden states are gated behind html.js-motion AND no-preference,
   so content is never trapped invisible if JS fails or motion is reduced.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  html.js-motion [data-reveal],
  html.js-motion [data-reveal-group] > * {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity var(--motion-enter) var(--ease-out),
                transform var(--motion-enter) var(--ease-out);
    will-change: opacity, transform;
  }
  html.js-motion [data-reveal="scale"] { transform: scale(.94); }
  /* Directional reveals are logical: they come from the start side, so RTL
     mirrors automatically by flipping the sign, not the markup. */
  html.js-motion [data-reveal="left"]  { transform: translateX(-26px); }
  html.js-motion [data-reveal="right"] { transform: translateX(26px); }
  html[dir="rtl"].js-motion [data-reveal="left"]  { transform: translateX(26px); }
  html[dir="rtl"].js-motion [data-reveal="right"] { transform: translateX(-26px); }

  html.js-motion .is-revealed,
  html.js-motion [data-reveal-group] > .is-revealed {
    opacity: 1;
    transform: none;
  }

  /* Route-change entrance for app screens: one gentle rise, never a blocker. */
  .content > * { animation: screen-in var(--motion-enter) var(--ease-out) both; }
  @keyframes screen-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }

  /* Hero entrance choreography — headline, lede and actions arrive in sequence. */
  .landing-hero-copy > * { animation: screen-in 520ms var(--ease-out) both; }
  .landing-hero-copy > *:nth-child(2) { animation-delay: 70ms; }
  .landing-hero-copy > *:nth-child(3) { animation-delay: 140ms; }
  .landing-hero-copy > *:nth-child(4) { animation-delay: 210ms; }
  .landing-hero-copy > *:nth-child(5) { animation-delay: 280ms; }

}

/* Interactive cards lift; static cards stay still. Transform + shadow only. */
.landing-card, .stat, .roster-item {
  transition: transform var(--motion-base) var(--ease-out),
              box-shadow var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out);
}
.landing-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
a.roster-item:hover, button.roster-item:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.stat:hover { box-shadow: var(--shadow-sm); }

@media (prefers-reduced-motion: reduce) {
  .landing-card:hover, a.roster-item:hover, button.roster-item:hover { transform: none; }
}

/* ==========================================================================
   Landing v2 — stronger hierarchy, travel-sky surface, hero polish
   ========================================================================== */
.landing-bar { transition: box-shadow var(--motion-base) var(--ease-out); }
.landing-bar.scrolled { box-shadow: var(--shadow-md); }

.landing-hero {
  /* The hero owns the first viewport (minus the bar), content centred in it. */
  min-height: calc(100dvh - 61px);
  padding: 48px 24px 72px;
  background:
    radial-gradient(1100px 480px at 50% -18%, color-mix(in srgb, var(--bt-color-secondary) 15%, transparent), transparent 65%),
    var(--bt-color-background);
}
/* A fine dot grid behind the hero, fading away before the fold. It sits in its
   own layer so the mask never touches the actual content. No shapes, no
   mascots — the demo card is the visual. */
.landing-hero::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: radial-gradient(color-mix(in srgb, var(--bt-color-primary) 16%, transparent) 1px, transparent 1px);
  background-size: 26px 26px;
  -webkit-mask-image: linear-gradient(to bottom, black 55%, transparent 92%);
  mask-image: linear-gradient(to bottom, black 55%, transparent 92%);
}
.landing-hero-copy { max-width: 780px; }
.landing-hero-copy h1 {
  font-size: clamp(2.2rem, 5.6vw, 3.6rem);
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.08;
}
.landing-hero-copy h1 em {
  font-style: normal;
  background: linear-gradient(92deg, var(--bt-color-secondary), var(--bt-color-accent));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

.hero-chips { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 22px; }
.hero-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px; border-radius: 999px; font-size: 13px; font-weight: 600;
  background: var(--bt-color-surface); border: 1px solid var(--line);
  box-shadow: var(--shadow-sm); color: var(--bt-color-text);
}
.hero-chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--bt-color-secondary); }
.hero-chip:nth-child(2) .dot { background: var(--bt-color-accent); }
.hero-chip:nth-child(3) .dot { background: var(--ok); }

/* Section rhythm: eyebrow, headline, sub — consistently. */
.landing-section { padding: 72px 24px; }
.landing-section > h2 { font-weight: 800; letter-spacing: -.025em; }

/* How-it-works strip */
.landing-steps { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); margin-top: 28px; counter-reset: step; }
.landing-step {
  position: relative; background: var(--bt-color-surface); border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 4px); padding: 22px 20px 20px; box-shadow: var(--shadow-sm);
}
.landing-step::before {
  counter-increment: step; content: counter(step);
  display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;
  background: linear-gradient(135deg, var(--bt-color-primary), var(--bt-color-secondary));
  color: #fff; font-weight: 700; margin-bottom: 12px;
}
.landing-step h3 { margin-bottom: .3em; }

/* Feature icons pick up the gradient treatment for a consistent visual voice. */
.landing-ico {
  background: linear-gradient(135deg, var(--tint-strong), color-mix(in srgb, var(--bt-color-secondary) 22%, var(--bt-color-surface)));
  border: 1px solid var(--line);
}


/* CTA band before the footer. */
.landing-cta { max-width: 1080px; margin: 0 auto; padding: 0 24px 72px; }
.landing-cta-inner {
  background: linear-gradient(120deg, var(--bt-color-primary), var(--bt-color-secondary));
  border-radius: calc(var(--bt-radius) + 8px); padding: 44px 32px; text-align: center;
  color: #fff; box-shadow: var(--shadow-lg); position: relative; overflow: hidden;
}
.landing-cta-inner h2 { color: #fff; font-weight: 800; letter-spacing: -.02em; }
.landing-cta-inner p { color: color-mix(in srgb, #fff 82%, transparent); max-width: 52ch; margin-inline: auto; }
.landing-cta-inner .row { justify-content: center; margin-top: 18px; }
.landing-cta-inner .btn { background: #fff; color: var(--bt-color-primary); }
.landing-cta-inner .btn.ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.5); }
.landing-cta-inner .btn.ghost:hover { background: rgba(255,255,255,.12); }
.landing-cta-glow {
  position: absolute; width: 420px; height: 420px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,255,255,.16), transparent 65%);
  inset-block-start: -210px; inset-inline-end: -120px; pointer-events: none;
}

/* Sidebar/bottom-nav active states get a smooth press instead of a snap. */
.nav-item { transition: background var(--motion-fast) var(--ease-out), transform var(--motion-fast) var(--ease-out); }
.nav-item:active { transform: scale(.98); }
.bottom-nav a { transition: color var(--motion-fast) var(--ease-out); }

/* Tilt targets promote to their own layer while interacted with. */
[data-tilt] { will-change: transform; transform-style: preserve-3d; }

/* ==========================================================================
   Hero product demo — the seating screen, alive on a 10s loop.
   Everything is transform/opacity/background; the loop is pure CSS so it costs
   nothing and needs no JS. Reduced motion sees the finished state, static.
   ========================================================================== */
.hero-demo {
  position: relative; display: none;
  /* A product-shot pose, straightening softly under the pointer. */
  perspective: 1400px;
}
@media (min-width: 920px) {
  .hero-demo { display: block; justify-self: end; width: min(400px, 100%); }
}
.hero-demo-glow {
  position: absolute; inset: -12% -8%;
  background:
    radial-gradient(60% 55% at 30% 25%, color-mix(in srgb, var(--bt-color-secondary) 26%, transparent), transparent 70%),
    radial-gradient(50% 45% at 75% 80%, color-mix(in srgb, var(--bt-color-accent) 16%, transparent), transparent 72%);
  filter: blur(2px); pointer-events: none;
}

.mock-window {
  position: relative;
  background: var(--bt-color-surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  transform: rotateY(-8deg) rotateX(3.5deg);
  transition: transform 600ms var(--ease-out);
}
html[dir="rtl"] .mock-window { transform: rotateY(8deg) rotateX(3.5deg); }
.hero-demo:hover .mock-window { transform: rotateY(0deg) rotateX(0deg); }

.mock-titlebar {
  display: flex; align-items: center; gap: 6px;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--bt-color-background) 60%, var(--bt-color-surface));
}
.mock-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--line-strong); }
.mock-titlebar .mock-dot:first-child { background: #f87171; }
.mock-titlebar .mock-dot:nth-child(2) { background: #fbbf24; }
.mock-titlebar .mock-dot:nth-child(3) { background: #34d399; }
.mock-title { margin-inline-start: 10px; font-size: 12px; font-weight: 600; color: var(--muted); }

.mock-body { padding: 16px; }
.mock-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }

.mock-seats { display: grid; grid-template-columns: 1fr 1fr 14px 1fr 1fr; gap: 7px; }
.mock-aisle { }
.mock-seat {
  aspect-ratio: 1; border-radius: 8px;
  border: 1px solid var(--line-strong);
  background: transparent;
}
/* Family colors, filled in sequence via --d (set per seat in landing.js). */
.mock-seat.fill-a { --c: color-mix(in srgb, var(--bt-color-secondary) 55%, var(--bt-color-surface)); --b: var(--bt-color-secondary); }
.mock-seat.fill-b { --c: color-mix(in srgb, var(--bt-color-accent) 50%, var(--bt-color-surface)); --b: var(--bt-color-accent); }
.mock-seat.fill-c { --c: color-mix(in srgb, var(--bt-color-primary) 45%, var(--bt-color-surface)); --b: var(--bt-color-primary); }
.mock-seat.fill-d { --c: color-mix(in srgb, var(--ok) 45%, var(--bt-color-surface)); --b: var(--ok); }

.mock-footer { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.mock-progress {
  flex: 1; height: 6px; border-radius: 999px; overflow: hidden;
  background: var(--tint-strong);
}
.mock-progress span { display: block; height: 100%; width: 100%; border-radius: inherit;
  background: linear-gradient(90deg, var(--bt-color-secondary), var(--bt-color-primary)); }

.mock-alert {
  position: absolute; inset-block-start: 10%; inset-inline-end: 4%;
  display: flex; align-items: center; gap: 10px;
  background: var(--bt-color-surface); border: 1px solid var(--line);
  border-inline-start: 4px solid var(--bt-color-accent);
  border-radius: 12px; padding: 10px 14px; box-shadow: var(--shadow-md);
  opacity: 1; transform: none;
}
.mock-alert-ico {
  width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--bt-color-accent) 18%, var(--bt-color-surface));
  color: var(--bt-color-accent); font-size: 14px;
}

/* The loop itself — motion users only. */
@media (prefers-reduced-motion: no-preference) {
  .mock-seat.fill-a, .mock-seat.fill-b, .mock-seat.fill-c, .mock-seat.fill-d {
    animation: mock-seat-fill 10s var(--ease-out) infinite;
    animation-delay: var(--d, 0s);
  }
  @keyframes mock-seat-fill {
    0%       { background: transparent; border-color: var(--line-strong); transform: scale(.82); }
    4%, 82%  { background: var(--c); border-color: var(--b); transform: scale(1); }
    90%,100% { background: transparent; border-color: var(--line-strong); transform: scale(.82); }
  }

  .mock-progress span { animation: mock-progress 10s linear infinite; transform-origin: 0 50%; }
  html[dir="rtl"] .mock-progress span { transform-origin: 100% 50%; }
  @keyframes mock-progress {
    0%       { transform: scaleX(0); }
    48%, 86% { transform: scaleX(1); }
    92%,100% { transform: scaleX(0); }
  }

  .mock-alert { animation: mock-alert 10s var(--ease-out) infinite; }
  @keyframes mock-alert {
    0%, 54%   { opacity: 0; transform: translateY(-10px) scale(.96); }
    58%, 84%  { opacity: 1; transform: none; }
    88%, 100% { opacity: 0; transform: translateY(-10px) scale(.96); }
  }

  .mock-live { animation: mock-live-pulse 2.4s ease-in-out infinite; }
  @keyframes mock-live-pulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
}

/* Fallback for the mock on narrow screens: the copy carries the hero alone. */
/* View Transitions: a short, directional handover between pages. */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) {
    animation: vt-out 200ms var(--ease-in) both;
  }
  ::view-transition-new(root) {
    animation: vt-in 320ms var(--ease-out) both;
  }
  @keyframes vt-out { to { opacity: 0; transform: translateY(-8px); } }
  @keyframes vt-in { from { opacity: 0; transform: translateY(10px); } }
}

/* Hero layout: copy + demo, stacked on mobile, two columns from 920px. */
.landing-hero-grid {
  position: relative;   /* above the hero's dot-grid layer */
  max-width: 1120px; margin-inline: auto;
  display: grid; grid-template-columns: 1fr; gap: 32px; align-items: center;
  text-align: center;
}
@media (min-width: 920px) {
  .landing-hero-grid { grid-template-columns: 1.06fr .94fr; text-align: start; }
  .landing-hero-grid .row.hero-actions { justify-content: flex-start; }
  .landing-hero-grid .hero-chips { justify-content: flex-start; }
}

/* ==========================================================================
   Stats band — real measurements, counted up on scroll (motion.js countUp)
   ========================================================================== */
.landing-stats {
  max-width: 1080px; margin-inline: auto; padding: 8px 24px 64px;
}
.landing-stats-inner {
  display: grid; gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 6px);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.landing-stat { background: var(--bt-color-surface); padding: 22px 18px; text-align: center; }
.landing-stat-value {
  font-size: clamp(1.8rem, 3.4vw, 2.4rem); font-weight: 800; letter-spacing: -.03em;
  color: var(--bt-color-primary); font-variant-numeric: tabular-nums; line-height: 1.05;
}
.landing-stats-note { max-width: 70ch; margin: 14px auto 0; text-align: center; }

/* ==========================================================================
   Proposal panel — the allocation run's real output shape
   ========================================================================== */
.proposal-panel {
  background: var(--bt-color-surface); border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 6px); box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.proposal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 18px; border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--bt-color-background) 55%, var(--bt-color-surface));
}
.proposal-score { text-align: end; line-height: 1.1; }
.proposal-score .value {
  display: block; font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em;
  color: var(--bt-color-primary); font-variant-numeric: tabular-nums;
}

.proposal-rows { padding: 6px 8px; }
.proposal-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center;
  padding: 11px 10px; border-radius: 10px;
}
.proposal-row + .proposal-row { border-top: 1px solid var(--line); }
.proposal-who { min-width: 0; }
.proposal-move { color: var(--muted); white-space: nowrap; }
/* Direction of travel reads left-to-right in LTR and right-to-left in RTL. */
html[dir="rtl"] .proposal-move { direction: rtl; }

.proposal-state { font-size: 11px; }
.proposal-state.is-moved {
  background: color-mix(in srgb, var(--warn) 14%, var(--bt-color-surface));
  color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent);
}
.proposal-state.is-new {
  background: color-mix(in srgb, var(--ok) 13%, var(--bt-color-surface));
  color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent);
}
.proposal-state.is-unchanged { background: transparent; color: var(--muted); }

.proposal-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 18px; border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--bt-color-background) 55%, var(--bt-color-surface));
}
/* Decorative: it looks like the real button but is not interactive. */
.proposal-cta { cursor: default; pointer-events: none; }

/* ==========================================================================
   Demo strip — attendance filling in, and the same alert in both languages
   ========================================================================== */
.demo-strip {
  display: grid; gap: 16px; margin-top: 26px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.demo-mini {
  background: var(--bt-color-surface); border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 4px); padding: 20px; box-shadow: var(--shadow-sm);
  transition: transform var(--motion-base) var(--ease-out), box-shadow var(--motion-base) var(--ease-out);
}
.demo-mini:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.demo-mini-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.demo-mini h3 { margin-bottom: .25em; }
.demo-count { text-align: end; flex: none; line-height: 1.1; }
.demo-count-value {
  display: block; font-size: 1.6rem; font-weight: 800; color: var(--ok);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}

.demo-roster { display: grid; gap: 6px; margin-top: 16px; }
.demo-roster-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 12px; border-radius: 10px; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--bt-color-background) 45%, var(--bt-color-surface));
}
.demo-status {
  background: color-mix(in srgb, var(--ok) 14%, var(--bt-color-surface));
  color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent);
  font-size: 11px;
}

/* The bilingual card: two faces of the same alert.
   BASE state shows both, stacked — so a reduced-motion visitor still sees the
   point being made (both scripts, both directions) instead of English alone.
   Only when motion is welcome do they stack up and crossfade. */
.demo-flip { position: relative; margin-top: 16px; display: grid; gap: 8px; }
.demo-flip-face {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; border-radius: 12px;
  border: 1px solid var(--line); border-inline-start: 4px solid var(--bt-color-accent);
  background: color-mix(in srgb, var(--bt-color-background) 45%, var(--bt-color-surface));
}
.demo-flip-ico {
  width: 32px; height: 32px; flex: none; border-radius: 9px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--bt-color-accent) 16%, var(--bt-color-surface));
  color: var(--bt-color-accent);
}

@media (prefers-reduced-motion: no-preference) {
  /* Attendance: rows land one after another, ~0.6s apart (--d per row). */
  .demo-roster-row {
    animation: demo-checkin 9s var(--ease-out) infinite;
    animation-delay: var(--d, 0s);
  }
  @keyframes demo-checkin {
    0%, 6%   { opacity: .35; transform: translateY(4px); }
    12%, 84% { opacity: 1; transform: none; }
    92%,100% { opacity: .35; transform: translateY(4px); }
  }
  .demo-status { animation: demo-status 9s steps(1, end) infinite; animation-delay: var(--d, 0s); }
  @keyframes demo-status {
    0%, 10%  { opacity: 0; }
    12%, 86% { opacity: 1; }
    90%,100% { opacity: 0; }
  }

  /* With motion allowed, the faces overlap and trade places every ~4 seconds. */
  .demo-flip { min-height: 74px; }
  .demo-flip-face { position: absolute; inset: 0; }
  .demo-flip-face.is-en { animation: demo-face-en 8s var(--ease-out) infinite; }
  .demo-flip-face.is-ar { animation: demo-face-ar 8s var(--ease-out) infinite; }
  @keyframes demo-face-en {
    0%, 42%  { opacity: 1; transform: none; }
    50%, 92% { opacity: 0; transform: translateY(-6px); }
    100%     { opacity: 1; transform: none; }
  }
  @keyframes demo-face-ar {
    0%, 42%  { opacity: 0; transform: translateY(6px); }
    50%, 92% { opacity: 1; transform: none; }
    100%     { opacity: 0; transform: translateY(6px); }
  }
}

/* ==========================================================================
   Kinetic layer (assets/js/ui/kinetic.js)
   Masked line reveals, magnetic buttons, scroll progress, marquee, pinned steps.
   Every rule here is transform/opacity only and inert under reduced motion.
   ========================================================================== */

/* --- Masked line reveal --------------------------------------------------- */
.kin-word { display: inline-block; }
.kin-word.is-em {
  background: linear-gradient(92deg, var(--bt-color-secondary), var(--bt-color-accent));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.kin-line { display: block; overflow: hidden; padding-block-end: .06em; }
.kin-line-inner { display: block; will-change: transform; }

@media (prefers-reduced-motion: no-preference) {
  .kin-line-inner {
    transform: translateY(112%);
    opacity: 0;
    transition: transform 900ms cubic-bezier(.16, 1, .3, 1),
                opacity 640ms cubic-bezier(.16, 1, .3, 1);
  }
  .is-kin-shown .kin-line-inner { transform: translateY(0); opacity: 1; }
}

/* --- Scroll progress ------------------------------------------------------ */
.kin-progress {
  position: fixed; inset-block-start: 0; inset-inline: 0; height: 3px;
  z-index: 90; pointer-events: none;
  background: color-mix(in srgb, var(--bt-color-primary) 10%, transparent);
}
.kin-progress span {
  display: block; height: 100%; transform-origin: 0 50%; transform: scaleX(0);
  background: linear-gradient(90deg, var(--bt-color-secondary), var(--bt-color-accent));
}
html[dir="rtl"] .kin-progress span { transform-origin: 100% 50%; }

/* Compositor-driven where the browser supports scroll-linked animation. */
@media (prefers-reduced-motion: no-preference) {
  .kin-progress.is-css-driven span {
    animation: kin-progress linear both;
    animation-timeline: scroll(root block);
  }
  @keyframes kin-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
}

/* --- Marquee -------------------------------------------------------------- */
.kin-marquee {
  position: relative; display: flex; overflow: hidden; gap: 0;
  padding-block: 18px; margin-block: 8px;
  border-block: 1px solid var(--line);
  background: color-mix(in srgb, var(--bt-color-surface) 70%, transparent);
  /* Fade the ends so items enter and leave instead of being chopped. */
  -webkit-mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
}
.kin-marquee-track {
  display: flex; align-items: center; gap: 40px; flex: none;
  padding-inline-end: 40px; white-space: nowrap;
}
.kin-marquee-item {
  display: inline-flex; align-items: center; gap: 12px;
  font-size: clamp(1.1rem, 2vw, 1.5rem); font-weight: 700; letter-spacing: -.02em;
  color: color-mix(in srgb, var(--bt-color-text) 72%, var(--bt-color-surface));
}
.kin-marquee-item::after {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--bt-color-accent); opacity: .75;
}

@media (prefers-reduced-motion: no-preference) {
  .kin-marquee-track { animation: kin-marquee 34s linear infinite; }
  .kin-marquee:hover .kin-marquee-track { animation-play-state: paused; }
  @keyframes kin-marquee { to { transform: translateX(-100%); } }
  /* In RTL the ticker travels the other way, so it still reads "forward". */
  html[dir="rtl"] .kin-marquee-track { animation-name: kin-marquee-rtl; }
  @keyframes kin-marquee-rtl { to { transform: translateX(100%); } }
}

/* --- Pinned step scroller ------------------------------------------------- */
.kin-steps { display: grid; gap: 32px; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 900px) {
  .kin-steps { grid-template-columns: .82fr 1.18fr; gap: 56px; }
  .kin-steps-aside { position: sticky; top: 120px; }
}

.kin-rail {
  position: relative; width: 3px; border-radius: 999px; margin-block-start: 22px;
  background: var(--line); height: 120px; display: none;
}
@media (min-width: 900px) { .kin-rail { display: block; } }
.kin-rail-fill {
  position: absolute; inset-block-start: 0; inset-inline: 0; height: 100%;
  border-radius: inherit; transform-origin: 50% 0; transform: scaleY(.33);
  background: linear-gradient(180deg, var(--bt-color-secondary), var(--bt-color-accent));
  transition: transform 500ms cubic-bezier(.16, 1, .3, 1);
}

.kin-step {
  position: relative;
  padding: 26px 26px 26px 30px;
  border: 1px solid var(--line);
  border-radius: calc(var(--bt-radius) + 6px);
  background: var(--bt-color-surface);
  box-shadow: var(--shadow-sm);
  transition: transform 420ms var(--ease-out), box-shadow 420ms var(--ease-out),
              border-color 420ms var(--ease-out), opacity 420ms var(--ease-out);
}
.kin-step + .kin-step { margin-block-start: 18px; }
.kin-step-n {
  display: inline-grid; place-items: center; width: 34px; height: 34px;
  border-radius: 10px; font-weight: 700; margin-block-end: 12px;
  background: var(--tint-strong); color: var(--bt-color-primary);
  transition: background 420ms var(--ease-out), color 420ms var(--ease-out);
}
/* The step level with the reader lifts; the others recede rather than vanish. */
.kin-step.is-active {
  border-color: color-mix(in srgb, var(--bt-color-secondary) 45%, var(--line));
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.kin-step.is-active .kin-step-n {
  background: linear-gradient(135deg, var(--bt-color-primary), var(--bt-color-secondary));
  color: #fff;
}
@media (min-width: 900px) {
  .kin-step:not(.is-active) { opacity: .62; }
}
@media (prefers-reduced-motion: reduce) {
  .kin-step, .kin-step:not(.is-active) { opacity: 1; transform: none; }
}

/* --- Card sheen ----------------------------------------------------------- */
/* A light sweeps across a card on hover. Masked to the card, pointer-driven,
   and purely decorative — it never moves layout. */
.landing-card, .demo-mini, .landing-step { position: relative; overflow: hidden; }
.landing-card::after, .demo-mini::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 35%,
    color-mix(in srgb, var(--bt-color-secondary) 12%, transparent) 50%, transparent 65%);
  transform: translateX(-120%);
  transition: transform 700ms cubic-bezier(.16, 1, .3, 1);
}
.landing-card:hover::after, .demo-mini:hover::after { transform: translateX(120%); }
@media (prefers-reduced-motion: reduce) {
  .landing-card::after, .demo-mini::after { display: none; }
}

/* --- Grain ---------------------------------------------------------------- */
/* A whisper of film grain over the whole page. It is what stops large flat
   gradients from looking like plastic. Fixed, non-interactive, 3.5% opacity. */
.kin-grain {
  position: fixed; inset: 0; z-index: 85; pointer-events: none; opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --- Bigger display type for the landing ---------------------------------- */
.landing-hero-copy h1 {
  font-size: clamp(2.5rem, 6.4vw, 4.4rem);
  letter-spacing: -.04em;
  line-height: 1.02;
}
.landing-section > h2, .kin-steps-aside h2, .landing-honesty h2 {
  font-size: clamp(1.7rem, 3.6vw, 2.7rem);
  letter-spacing: -.03em;
}

/* ==========================================================================
   Phone behaviour — making the web app behave like an installed application.
   Everything below was written against measured failures at 375x812, not
   assumptions: horizontal scroll, sub-44px targets, and a top bar carrying
   more controls than a phone has room for.
   ========================================================================== */

/* --- Never scroll sideways ------------------------------------------------
   A single stray transform used to drag the whole page 3px wide, which on a
   phone reads as a broken layout. Clipping at the root ends that whole class
   of bug; `clip` is used where supported because unlike `hidden` it does not
   create a scroll container that would break position: sticky. */
html { overflow-x: clip; }
body { overflow-x: clip; max-width: 100%; }
@supports not (overflow: clip) {
  html, body { overflow-x: hidden; }
}

/* --- Native-feeling surfaces ---------------------------------------------- */
html {
  -webkit-text-size-adjust: 100%;   /* rotating must not resize text */
  text-size-adjust: 100%;
}
body {
  /* Stops the rubber-band overscroll that reveals a white band behind the app
     and makes a PWA feel like a web page in a wrapper. */
  overscroll-behavior-y: none;
  -webkit-tap-highlight-color: transparent;   /* we draw our own press states */
}
/* Chrome is not selectable; content still is. Long-pressing a nav item to get a
   selection handle is the giveaway that something is a website, not an app. */
.topbar, .sidebar, .bottom-nav, .kin-marquee, .hero-demo, .mock-window {
  -webkit-user-select: none; user-select: none;
}

/* Dynamic viewport units everywhere a full-height box exists: on phones the
   URL bar collapses, and 100vh leaves a gap or forces a scroll. */
@supports (height: 100dvh) {
  .login-wrap, .boot { min-height: 100dvh; }
  .sidebar { height: 100dvh; }
  .app-frame { min-height: 100dvh; }
  .trivia-wrap { min-height: 100dvh; }
}

/* --- Touch targets --------------------------------------------------------
   The spec's 44px floor applies to every pointer, but `.btn.small` sat at 34px.
   On a coarse pointer the small variant grows to the full target size while
   keeping its compact typography. */
@media (pointer: coarse) {
  .btn.small, .btn { min-height: var(--bt-touch-min); }
  .demo-accounts button { min-height: var(--bt-touch-min); }
  select, input, textarea { min-height: var(--bt-touch-min); font-size: 16px; }
  .seat { min-width: 40px; min-height: 40px; }   /* grid density wins here */
}

/* --- Top bar on a phone ---------------------------------------------------
   Seven controls do not fit 375px. The connectivity label collapses to its
   dot (the colour still carries the state, and the title attribute keeps the
   text for assistive tech), and the page title truncates rather than wrapping
   the bar onto two lines. */
@media (max-width: 760px) {
  .topbar { padding: 8px 12px; gap: 8px; }
  .topbar h1 {
    font-size: 1rem; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap;
  }
  .conn .label { display: none; }
  .conn { gap: 0; }
  .conn .dot { width: 10px; height: 10px; }
  .topbar select { width: auto; min-width: 58px; padding-inline: 8px; }
}

/* --- Bottom navigation ----------------------------------------------------
   The single element that most decides whether this reads as an app. It sits
   above the home indicator, blurs what scrolls beneath it, and gives each item
   a real 44px target with its own press state. */
@media (max-width: 760px) {
  .bottom-nav {
    background: color-mix(in srgb, var(--bt-color-surface) 92%, transparent);
    backdrop-filter: saturate(180%) blur(14px);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    box-shadow: 0 -1px 0 var(--line), 0 -8px 24px -18px rgba(15, 27, 38, .45);
    padding-bottom: max(env(safe-area-inset-bottom), 4px);
  }
  .bottom-nav a {
    min-height: 52px; gap: 3px; font-size: 10.5px; font-weight: 600;
    -webkit-tap-highlight-color: transparent;
  }
  .bottom-nav a .ico { font-size: 19px; line-height: 1; }
  .bottom-nav a:active { background: var(--tint); }
  .bottom-nav a[aria-current="page"] .ico { transform: translateY(-1px); }

  /* Content must clear the bar plus the home indicator, or the last row of any
     list sits permanently under it. */
  .content {
    padding: 14px 14px calc(68px + max(env(safe-area-inset-bottom), 4px) + 12px);
  }
}

/* --- Drawer --------------------------------------------------------------- */
@media (max-width: 760px) {
  .sidebar {
    padding-top: max(env(safe-area-inset-top), 14px);
    box-shadow: 12px 0 32px -20px rgba(15, 27, 38, .6);
  }
  html[dir="rtl"] .sidebar { box-shadow: -12px 0 32px -20px rgba(15, 27, 38, .6); }
}

/* --- Installed (standalone) mode -----------------------------------------
   With no browser chrome the app owns the status bar area, so the top bar has
   to pay for it itself. */
@media (display-mode: standalone) {
  .topbar { padding-top: max(env(safe-area-inset-top), 10px); }
  .landing-bar { padding-top: max(env(safe-area-inset-top), 12px); }
  /* An installed app has its own back affordance; the browser's is gone. */
  .kin-progress { top: env(safe-area-inset-top); }
}

/* --- Reveals on narrow screens -------------------------------------------
   Horizontal reveal offsets are what pushed the page sideways at 375px, and a
   26px slide reads as a glitch on a narrow column anyway. Below the tablet
   breakpoint every reveal becomes vertical. */
@media (max-width: 760px) {
  html.js-motion [data-reveal="left"],
  html.js-motion [data-reveal="right"],
  html[dir="rtl"].js-motion [data-reveal="left"],
  html[dir="rtl"].js-motion [data-reveal="right"] {
    transform: translateY(18px);
  }
  /* Tilt needs a pointer; on touch it just fights the scroll. */
  [data-tilt] { transform: none !important; }
}

/* --- Landing on a phone ---------------------------------------------------
   The product demo was desktop-only, which left phones with a wall of text and
   no picture of the thing being sold. It now appears under the copy at full
   width, flat rather than posed, and slightly smaller. */
@media (max-width: 919px) {
  .hero-demo {
    display: block; width: 100%; max-width: 420px;
    margin-inline: auto; margin-block-start: 32px;
  }
  .mock-window { transform: none; }
  html[dir="rtl"] .mock-window { transform: none; }
  .mock-alert { inset-block-start: 8%; inset-inline-end: 2%; }
  .hero-demo-glow { inset: -6% -4%; }
}

@media (max-width: 760px) {
  .landing-hero { min-height: 0; padding: 32px 20px 56px; }
  .landing-hero-grid { gap: 0; }
  .landing-section { padding: 48px 20px; }
  .landing-stats { padding: 4px 20px 40px; }
  .landing-stats-inner { grid-template-columns: 1fr 1fr; }
  .landing-cta { padding: 0 20px 48px; }
  .landing-cta-inner { padding: 32px 22px; }
  .landing-footer { padding: 24px 20px 40px; }
  .landing-bar { padding: 10px 14px; }
  .landing-brand strong { font-size: 15px; }

  /* Actions go full width and stack — the thumb-friendly arrangement. */
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { width: 100%; }
  .hero-chips { justify-content: center; }
  .hero-chip { font-size: 12px; padding: 6px 12px; }

  /* Marquee gets smaller so it reads as a detail, not a headline. */
  .kin-marquee-item { font-size: 1rem; }
  .kin-marquee-track { gap: 26px; padding-inline-end: 26px; }

  /* The proposal panel's three columns collapse to two rows on a phone. */
  .proposal-row { grid-template-columns: auto 1fr; row-gap: 4px; }
  .proposal-move { grid-column: 2; justify-self: start; }
  .proposal-head, .proposal-foot { padding: 14px; }
}

/* --- Tables and wide content ---------------------------------------------
   Admin screens are dense by design. On a phone the wrappers scroll on their
   own, with momentum, instead of stretching the page. */
@media (max-width: 760px) {
  .table-wrap, .seatmap-wrap { -webkit-overflow-scrolling: touch; }
  .table-wrap table { min-width: 520px; }   /* readable, scrolls horizontally */
  .card { padding: 14px; }
  .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; }
  .card-head { flex-wrap: wrap; }
  .modal { padding: 16px; border-radius: 16px 16px 0 0; }
  /* Sheets rise from the bottom on a phone, the way a native sheet does. */
  .modal-backdrop { align-items: flex-end; padding: 0; }
  .modal { width: 100%; max-height: 90dvh; }
}

/* --- Alert takeover on a phone -------------------------------------------- */
@media (max-width: 760px) {
  .alert-takeover { padding: 8px; padding-top: calc(8px + env(safe-area-inset-top)); }
  .alert-card { width: 100%; }
}

/* --- Toasts clear the bottom navigation ----------------------------------- */
@media (max-width: 760px) {
  .toasts {
    inset-inline: 12px; inset-block-end: calc(70px + max(env(safe-area-inset-bottom), 4px));
    max-width: none;
  }
}

/* The locale switcher: compact on a desktop, full touch target on a phone. */
.locale-select { min-height: 38px; }
@media (max-width: 760px), (pointer: coarse) {
  .locale-select { min-height: var(--bt-touch-min); }
  /* Width-based as well as pointer-based: a phone reports a coarse pointer, but
     a narrow window on a touchscreen laptop does not, and both need the target. */
  .btn, .btn.small, .demo-accounts button { min-height: var(--bt-touch-min); }
  select, input, textarea { min-height: var(--bt-touch-min); }
  /* 16px inputs stop iOS Safari zooming the page when a field gains focus. */
  input, select, textarea { font-size: 16px; }
}

/* --- Admin lists on a phone ----------------------------------------------
   A dense table is the right shape for this data and the wrong shape for a
   375px screen. Rather than rebuild every list as cards, the table keeps its
   real columns and scrolls sideways inside its own frame — with the identity
   column pinned, which is what stops sideways scrolling from becoming "which
   row am I looking at?". This is the pattern a native table view uses. */
@media (max-width: 760px) {
  /* Single-line rows: a wrapped three-line name in a squeezed column looks
     broken, whereas a wider table that scrolls looks deliberate. */
  .table-wrap th, .table-wrap td { white-space: nowrap; }

  .table-wrap th:first-child,
  .table-wrap td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 1;
    background: var(--bt-color-surface);
    /* The hairline plus a short shadow separates the pinned column from the
       content sliding underneath it. */
    box-shadow: 1px 0 0 var(--line), 6px 0 10px -8px rgba(15, 27, 38, .35);
    max-width: 46vw; overflow: hidden; text-overflow: ellipsis;
  }
  html[dir="rtl"] .table-wrap th:first-child,
  html[dir="rtl"] .table-wrap td:first-child {
    box-shadow: -1px 0 0 var(--line), -6px 0 10px -8px rgba(15, 27, 38, .35);
  }
  /* The hover tint would otherwise show through the pinned cell's own
     background and break the seam. */
  .table-wrap tbody tr:hover td:first-child { background: var(--tint); }

  /* Search takes the full width — a 148px field squeezed by a flex row is the
     single most-used control on these screens. The override is deliberate:
     the call sites set an inline max-width that suits a desktop toolbar. */
  .card-head input[type="search"], input[type="search"] {
    max-width: none !important; width: 100%;
  }
  .card-head { gap: 8px; }
  .card-head .row { width: 100%; flex-wrap: wrap; }
}

/* The page behind an open dialog must not move. */
html.modal-open, html.modal-open body { overflow: hidden; }

/* --- Dialogs as sheets ----------------------------------------------------
   Below the tablet breakpoint a dialog rises from the bottom edge with a grab
   handle above its title — the shape every native sheet uses, and the clearest
   single signal that this is an application rather than a page. */
@media (max-width: 760px) {
  .modal {
    overflow-y: auto;                       /* a long form scrolls in the sheet */
    padding-block-end: max(env(safe-area-inset-bottom), 16px);
    animation: bt-sheet-up var(--bt-motion-medium, 260ms) cubic-bezier(.22, 1, .36, 1);
  }
  .modal::before {
    content: ""; display: block; inline-size: 40px; block-size: 4px;
    margin: 2px auto 12px; border-radius: 999px;
    background: var(--line-strong);
  }
  /* The close button is redundant next to a handle on a phone, but it stays:
     it is the only keyboard-reachable dismissal on a touch device with a
     hardware keyboard attached. */
}
@keyframes bt-sheet-up {
  from { transform: translateY(14px); opacity: .6; }
  to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .modal { animation: none; }
}

/* The drawer dims and locks the page it covers, and fades its scrim in. */
html.drawer-open, html.drawer-open body { overflow: hidden; }
.scrim { animation: bt-fade-in 180ms ease both; }
@keyframes bt-fade-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .scrim { animation: none; } }

@media (max-width: 760px) {
  /* The organisation name and the signed-in person were running together on one
     line at this width. */
  .sidebar .brand strong { display: block; line-height: 1.25; }
}

/* --- Tab strips -----------------------------------------------------------
   Section tabs stay on one line and scroll sideways rather than wrapping onto
   a second row. A wrapped tab row moves the content down as you navigate,
   which is the sort of small instability that makes a screen feel like a
   document; a scrolling strip is what a native tab bar does. */
.tabstrip {
  flex-wrap: nowrap; overflow-x: auto; margin-block-end: 12px;
  scrollbar-width: none; scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
.tabstrip::-webkit-scrollbar { display: none; }
.tabstrip > * { flex: 0 0 auto; scroll-snap-align: start; }
/* Fades at the edges say "there is more this way" without a scrollbar. */
@media (max-width: 760px) {
  .tabstrip {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
    padding-inline: 2px;
  }
}

/* --- Chat on a phone ------------------------------------------------------
   A conversation in a short card with the composer floating in the middle of
   the screen is the clearest "this is a web page" tell in the whole app. Below
   the tablet breakpoint the thread takes the height that is actually left
   between the tab strip and the bottom navigation, and the composer sits at
   the bottom of it where a thumb expects to find it. */
@media (max-width: 760px) {
  .chat-shell { display: flex; flex-direction: column; }
  .chat-shell .card {
    display: flex; flex-direction: column;
    /* Viewport, less the top bar, tab strip, help button and bottom nav. */
    height: calc(100dvh - 250px); min-height: 300px;
  }
  .chat-log {
    flex: 1 1 auto; max-height: none;
    /* New messages arrive at the bottom, so that is where the view stays. */
    align-content: end;
  }
  .chat-composer { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line); }
  .chat-composer input { flex: 1 1 auto; min-width: 0; }
  .chat-composer .btn { flex: 0 0 auto; }
}

/* --- Seating on a phone ---------------------------------------------------
   The seat map and the not-seated-yet roster sit side by side on a desktop.
   At 375px that gives each about 160px, which is too little for either. They
   stack instead: the map first at full width, the roster under it. */
.seating-split { grid-template-columns: minmax(0, 2fr) minmax(240px, 1fr); }
@media (max-width: 919px) {
  .seating-split { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  /* The map scrolls in both directions inside its own frame rather than
     squeezing seats below a tappable size. */
  .seatmap-wrap { max-height: 60dvh; padding: 10px; }
  .seat { min-width: 44px; min-height: 44px; }
  .seat .who { max-width: 56px; }
  /* The roster below the map gets its own bounded, scrolling height so the
     page does not become one endless column. */
  .seating-split > div:last-child .card { max-height: 46dvh; overflow-y: auto; }
  .legend { flex-wrap: wrap; gap: 8px 12px; font-size: 12px; }
}

/* Map zoom controls are the one pair of buttons a thumb reaches for while the
   other hand is holding a flag; 34px is a mouse-sized target. */
@media (max-width: 760px), (pointer: coarse) {
  .map-controls { gap: 6px; inset-block-start: 10px; inset-inline-end: 10px; }
  .map-controls button {
    width: var(--bt-touch-min); height: var(--bt-touch-min);
    font-size: 20px; box-shadow: var(--bt-elevation-1, 0 1px 3px rgba(15,27,38,.18));
  }
}

/* A fixed 420px map plus a top bar, a card head, a button and the navigation
   overflows a smaller phone. The map takes a share of the viewport instead. */
@media (max-width: 760px) {
  .map { height: min(420px, 52dvh); }
}
