/* ==========================================================================
   BonWega / GameUp - application stylesheet

   Mobile-first: every rule below is written for a phone, and the media queries
   only ever add to that as the viewport grows. Touch targets are at least
   44x44px, the bottom tab bar clears the iOS home indicator, and no layout
   depends on hover.

   Layers:
     1. Tokens          design variables, light + dark
     2. Reset           minimal, predictable base
     3. App shell       topbar / sidebar / main / tabbar
     4. Components      buttons, cards, forms, badges, avatars, modal, feed
     5. Screens         a few page-specific compositions
     6. Breakpoints     tablet (>=640px) and desktop (>=1024px)
   ========================================================================== */

/* 1. Tokens ------------------------------------------------------------- */
:root {
  --navy-900: #071528;
  --navy-800: #0b1f3a;
  --navy-700: #12305a;
  --sea-600: #0f8b8d;
  --sea-500: #14a8ab;
  --sea-100: #dff4f4;
  --sun-500: #f2a541;
  --sun-100: #fdf0dc;
  --coral-600: #d1495b;
  --coral-100: #fbe3e6;
  --grass-600: #2f8f4e;
  --grass-100: #e2f4e8;

  --bg: #f5f7fa;
  --surface: #ffffff;
  --surface-2: #eef2f7;
  --border: #dde4ed;
  --text: #10202f;
  --text-muted: #5c6b7a;
  --text-inverse: #ffffff;

  --accent: var(--sea-600);
  --accent-contrast: #ffffff;

  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 20px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  --shadow-sm: 0 1px 2px rgba(7, 21, 40, 0.06);
  --shadow: 0 2px 10px rgba(7, 21, 40, 0.08);
  --shadow-lg: 0 12px 32px rgba(7, 21, 40, 0.18);

  --tabbar-height: 60px;
  --topbar-height: 56px;
  --content-max: 720px;
  --wide-max: 1180px;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0a1420;
    --surface: #12202f;
    --surface-2: #182b3d;
    --border: #24384c;
    --text: #e8eef4;
    --text-muted: #9fb1c2;
    --accent: var(--sea-500);
    --sea-100: #10333a;
    --sun-100: #3a2c14;
    --coral-100: #3a1a20;
    --grass-100: #14301f;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
    --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.6);
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* The grey flash on tap is a browser tell; native controls do not do it. */
  -webkit-tap-highlight-color: transparent;
}

/* Stop a scroll at the end of the page from dragging the whole document and
   revealing the browser's background behind it. */
html, body { overscroll-behavior-y: none; }

/* Long-press on a control should offer the control, not a text selection. */
.btn, .tabbar__link, .pill-group__btn, .post__action, .comment__action { user-select: none; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;               /* never below 16px: iOS zooms smaller inputs */
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

h1, h2, h3, h4 { margin: 0 0 var(--space-3); line-height: 1.25; font-weight: 650; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.05rem; }
p { margin: 0 0 var(--space-3); }
img { max-width: 100%; height: auto; display: block; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
hr { border: 0; border-top: 1px solid var(--border); margin: var(--space-4) 0; }
ul, ol { margin: 0 0 var(--space-3); padding-left: 1.2em; }

:focus-visible {
  outline: 3px solid var(--sun-500);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--space-2);
  z-index: 100;
  background: var(--surface);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
}
.skip-link:focus { left: var(--space-2); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* 3. App shell ---------------------------------------------------------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  height: var(--topbar-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 0 var(--space-4);
  background: var(--navy-800);
  color: var(--text-inverse);
  padding-top: env(safe-area-inset-top);
  height: calc(var(--topbar-height) + env(safe-area-inset-top));
}

.topbar__brand { display: flex; align-items: center; gap: var(--space-2); color: inherit; }
.topbar__brand:hover { text-decoration: none; }
.topbar__logo {
  display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: 9px;
  background: var(--sea-600); font-weight: 800; font-size: 0.8rem; letter-spacing: 0.02em;
}
.topbar__name { font-weight: 700; letter-spacing: 0.01em; }

.topbar__actions { display: flex; align-items: center; gap: var(--space-1); }
.topbar__action {
  position: relative;
  display: grid; place-items: center;
  min-width: 44px; min-height: 44px;
  color: inherit; font-size: 1.15rem;
  border-radius: var(--radius-sm);
}
.topbar__action:hover { background: rgba(255, 255, 255, 0.1); text-decoration: none; }
.topbar__action .icon { font-size: 1.15rem; }

.topbar__avatar {
  display: grid; place-items: center;
  width: 36px; height: 36px; margin-left: var(--space-1);
  border-radius: 50%; overflow: hidden;
  background: var(--sea-600); color: #fff;
  font-size: 0.78rem; font-weight: 700;
}
.topbar__avatar:hover { text-decoration: none; }
.topbar__avatar img { width: 100%; height: 100%; object-fit: cover; }

.app-shell { display: block; }

.app-main {
  padding: var(--space-4);
  padding-bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom) + var(--space-5));
  max-width: var(--content-max);
  margin: 0 auto;
}
.app-main:focus { outline: none; }
.app-shell--bare .app-main { padding-bottom: var(--space-6); }

.sidebar { display: none; }

.tabbar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 40;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: 0 -1px 8px rgba(7, 21, 40, 0.06);
}

.tabbar__item {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  min-height: var(--tabbar-height);
  padding: var(--space-2) var(--space-1);
  color: var(--text-muted);
  font-size: 0.68rem; font-weight: 600;
}
.tabbar__item:hover { text-decoration: none; }
.tabbar__item.is-active { color: var(--accent); }
.tabbar__item.is-active .tabbar__icon { transform: translateY(-1px); }
.tabbar__icon { font-size: 1.3rem; line-height: 1; display: block; transition: transform 120ms ease; }
.tabbar__label { white-space: nowrap; }

/* 4. Components --------------------------------------------------------- */

/* Icons.

   Inline SVG from the sprite in partials/icons.njk. Sized in em so an icon
   always matches the text beside it, and filled with currentColor so it picks
   up the surrounding colour - including the active navigation state and dark
   mode - without a single extra rule. */
.icon {
  display: inline-block;
  vertical-align: -0.125em;   /* optical alignment with the text baseline */
  flex: none;
}

/* Buttons: minimum 44px tall, full width on phones where they are primary. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font: inherit; font-weight: 650;
  cursor: pointer;
  text-align: center;
  transition: background 120ms ease, opacity 120ms ease, transform 60ms ease;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: scale(0.985); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; transform: none; }

.btn--primary { background: var(--accent); color: var(--accent-contrast); }
.btn--primary:hover { background: var(--sea-500); }
.btn--danger { background: var(--coral-600); color: #fff; }
.btn--ghost { background: transparent; border-color: var(--border); }
.btn--subtle { background: var(--surface-2); color: var(--text); }
.btn--block { width: 100%; }
.btn--sm { min-height: 38px; padding: var(--space-2) var(--space-3); font-size: 0.88rem; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.btn-row--stack { flex-direction: column; }

/* Cards */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.card--flush { padding: 0; overflow: hidden; }
.card__header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.card__title { margin: 0; font-size: 1rem; font-weight: 700; }
.card__footer { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border); }

.section-title {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
  margin: var(--space-5) 0 var(--space-3);
}
.section-title:first-child { margin-top: 0; }
.section-title h2 { margin: 0; }
.section-title__link { font-size: 0.85rem; font-weight: 600; }

.page-header { margin-bottom: var(--space-4); }
.page-header__meta { color: var(--text-muted); font-size: 0.9rem; margin: 0; }

.muted { color: var(--text-muted); }
.small { font-size: 0.85rem; }
.strong { font-weight: 700; }
.nowrap { white-space: nowrap; }
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.m-0 { margin: 0; }
.p-0 { padding: 0; }
.text-right { text-align: right; }
.clickable { cursor: pointer; }
.cursor-default { cursor: default; }
.min-w-0 { min-width: 0; }
.fill { flex: 1; min-width: 0; }
.inherit-color { color: inherit; }

/* The strict CSP (style-src 'self') drops every style="" attribute, so layout
   that would once have been written inline has to be a class to have any
   effect at all. Everything below replaces a dead inline style. */

/* Rows inside a .card already sit within the card's horizontal padding, so
   their own list padding is removed rather than doubled. */
.flush-x { padding-left: 0; padding-right: 0; }
.flush-l { padding-left: 0; }

/* A horizontal group of items sharing a centre line. */
.row { display: flex; align-items: center; gap: var(--space-3); }
.row--split { justify-content: space-between; }
.row--tight { gap: var(--space-2); }
.row--wrap { flex-wrap: wrap; }

.rule-top { border-top: 1px solid var(--border); padding-top: var(--space-3); margin-top: var(--space-3); }
.rule-top--strong { border-top-width: 2px; padding-top: var(--space-4); margin-top: var(--space-4); }

/* Selects that sit beside another control instead of filling the row. */
.select--auto { min-height: 40px; width: auto; }
.select--compact { min-height: 36px; width: auto; }
.select--w-sm { width: 90px; }
.select--w-md { min-width: 140px; }
.select--w-lg { min-width: 160px; }

/* A <summary> styled as a button keeps the pointer but loses the marker. */
.summary-plain { list-style: none; cursor: pointer; }
.summary-plain::-webkit-details-marker { display: none; }

.code-block {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* An unread notification is tinted rather than badged. */
.is-unread { background: var(--surface-2); }

/* partials/icons.njk only defines <symbol>s for <use>; it must never paint. */
.sprite { display: none; }

/* A reply sits indented under the comment it answers. Replies nest inside
   their parent, so the indent compounds naturally with depth - and past a few
   levels it would run out of phone before the thread runs out of replies.
   comment--flat keeps those in reading order without stepping further right. */
.comment--reply { margin-top: var(--space-2); }
.comment--flat { margin-left: 0; }
.comment__replying { font-size: 0.78rem; color: var(--text-muted); }

.comment__actions {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin: var(--space-1) 0 0 var(--space-2);
  flex-wrap: wrap;
}
.comment__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 0.8rem;
  color: var(--text-muted);
  cursor: pointer;
}
.comment__action.is-active { color: var(--coral-600); font-weight: 650; }

/* Edit and delete belong to the same row as Reply and the heart, so they read
   as part of the comment rather than as page furniture. Delete arrives as a
   POST form - element-qualified so the .btn rules further down do not turn it
   back into a full-size button. */
/* Reels.

   The strip is a row of tall posters pushed sideways, sitting under the
   composer. It is deliberately the only horizontally scrolling thing on the
   page: it works because the cards are obviously cut off at the edge, which
   is what tells you there are more.
   ------------------------------------------------------------------------ */
.reel-strip {
  display: flex;
  gap: var(--space-2);
  overflow-x: auto;
  padding: var(--space-3) var(--space-1);
  margin-bottom: var(--space-3);
  scroll-snap-type: x proximity;
  /* No scrollbar on a strip this short - it would take a tenth of the card. */
  scrollbar-width: none;
}
.reel-strip::-webkit-scrollbar { display: none; }

.reel-strip__item {
  position: relative;
  flex: none;
  width: 112px;
  height: 196px;                 /* 9:16, the shape a phone records */
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-2);
  scroll-snap-align: start;
  text-decoration: none;
  color: #fff;
}
.reel-strip__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* A reel from before posters were captured. A flat tint rather than an empty
   hole, so the card still reads as a card. */
.reel-strip__video--blank { background: var(--surface-2); }

/* The author's face, top left, the way every one of these strips does it. */
.reel-strip__avatar {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  border: 2px solid var(--accent);
  border-radius: 50%;
  line-height: 0;
}
.reel-strip__time {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  font-size: 0.7rem;
  font-weight: 650;
}

/* A scrim rather than a solid bar: the name has to stay readable over a
   frame that might be white sand or a night game. */
.reel-strip__label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-4) var(--space-2) var(--space-2);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.2;
  /* Two lines at most, then cut - a long name must not climb the poster. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.reel-strip__create {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  padding-bottom: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
}
.reel-strip__plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast);
}
.reel-strip__create-label { font-size: 0.78rem; font-weight: 700; text-align: center; }

/* The player takes the whole screen.

   Fitting it between the app bars was the wrong instinct: a clip is the only
   thing you are looking at, and the bars steal the height that makes it worth
   watching. It covers them instead - above both z-indexes - and carries its own
   way out, which is what every player of this kind does.

   Every control sits on top of the video rather than beside it, so the video
   keeps the full screen and the chrome floats over the parts nobody misses. */
.reels {
  position: fixed;
  inset: 0;
  height: 100dvh;
  z-index: 60;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  background: #000;
  /* A drag past the last clip should not bounce the page behind it. */
  overscroll-behavior: contain;
}
/* Nothing behind the player scrolls while it is open. */
body.reels-open { overflow: hidden; }
/* A hairline along the very top, over the scrim rather than under it. Thin
   enough to read as progress rather than as a control to be dragged, which it
   is not: this player has no scrubbing. */
.reel__progress {
  position: absolute;
  top: env(safe-area-inset-top);
  left: 0;
  right: 0;
  z-index: 3;
  height: 3px;
  background: rgba(255, 255, 255, 0.25);
}
.reel__progress-fill {
  display: block;
  height: 100%;
  width: 0;
  background: #fff;
  /* No transition: a clip that loops would animate the bar backwards across
     the whole screen every time it restarts. */
}

/* A clip in the feed, with its own sound control resting on it. */
.post__video { position: relative; }

.post__sound {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  cursor: pointer;
}
.post__sound:hover { background: rgba(0, 0, 0, 0.7); }

/* Sound sits under the author row, clear of both the progress bar and the
   caption, and large enough to hit without aiming. */
.reel__sound {
  position: absolute;
  top: calc(64px + env(safe-area-inset-top));
  left: var(--space-4);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 1.05rem;
  cursor: pointer;
}
.reel__sound:hover { background: rgba(0, 0, 0, 0.65); }

/* Author on the left, a way out on the right, laid over the top of the clip.
   The scrim keeps both readable over a bright frame. */
.reel__top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: calc(var(--space-3) + env(safe-area-inset-top)) var(--space-4) var(--space-5);
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent);
  color: #fff;
}
.reel__who { display: flex; align-items: center; gap: var(--space-2); min-width: 0; flex: 1; }
.reel__when { font-size: 0.8rem; opacity: 0.75; }

.reel__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.4);
  color: #fff;
  font-size: 1.1rem;
}
.reel__close:hover { background: rgba(0, 0, 0, 0.6); text-decoration: none; }
.reel {
  position: relative;
  height: 100dvh;
  scroll-snap-align: start;
  display: flex;
  align-items: center;
  justify-content: center;
}
.reel__video { width: 100%; height: 100%; object-fit: cover; background: #000; }

.reel__overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: var(--space-6) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom));
  background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
  color: #fff;
}
.reel__author { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 700; }
.reel__caption { margin: var(--space-2) 0 0; }
.reel__actions { display: flex; gap: var(--space-4); margin-top: var(--space-3); }
.reel__actions .post__action { color: #fff; }
.reel__actions .post__action:hover { background: rgba(255, 255, 255, 0.15); }

/* A post being carried inside a share: bordered and a little quieter, so it
   reads as something passed along rather than two posts stacked up. */
.shared {
  display: block;
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  text-decoration: none;
}
.shared:hover { border-color: var(--accent); }
.shared__head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.shared .post__body { font-size: 0.95rem; }
.shared .post__photo { margin-top: var(--space-2); }
.shared--gone { text-align: center; }

.comment__actions .inline-form { display: inline-flex; }
button.comment__action--icon {
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-muted);
}
button.comment__action--icon .icon { font-size: 0.95rem; }
button.comment__action--icon:hover { color: var(--text); }

/* The editor stands where the text it replaces stood. */
.comment-bar--edit { margin: var(--space-2) 0 0; }
.comment__action[open] { display: block; width: 100%; }
/* A reply form spans the comment it answers rather than the word "Reply". */
.comment__actions details[open] { flex-basis: 100%; }
.comment__actions details[open] .comment-bar { width: 100%; }

/* An icon-only button: a full circle, centred, named by aria-label. */
.btn--icon {
  width: 34px;
  min-width: 34px;
  min-height: 34px;
  padding-left: 0;
  padding-right: 0;
  border-radius: 999px;
}
.btn--icon .icon { font-size: 0.95rem; }

/* An action pill: sibling actions in one fully-rounded container, divided by
   hairlines. One visual object saying "things you can do with this person",
   rather than a scatter of separate buttons. */
.pill-group {
  display: inline-flex;
  align-items: stretch;
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: 999px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(7, 21, 40, 0.12);
}
.pill-group > * + * { border-left: 1px solid rgba(255, 255, 255, 0.35); }
.pill-group__form { display: flex; }
.pill-group__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  min-height: 34px;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background 120ms ease;
}
.pill-group__btn:hover, .pill-group__btn:focus-visible { background: var(--sea-500); }
.pill-group__btn .icon { font-size: 0.95rem; }

/* A quieter variant for decisions on a card, where a solid brand pill would
   shout. Same shape, surface colours, the destructive half tinted. */
.pill-group--soft { background: var(--surface-2); color: var(--text); box-shadow: none; border: 1px solid var(--border); }
.pill-group--soft > * + * { border-left: 1px solid var(--border); }
.pill-group--soft .pill-group__btn:hover, .pill-group--soft .pill-group__btn:focus-visible { background: var(--border); }
.pill-group--soft .pill-group__btn--yes { color: var(--sea-600, #178a80); }
.pill-group--soft .pill-group__btn--no { color: var(--coral-600); }

/* Suggestion rows carry two actions; on narrow screens they wrap below the
   name instead of crushing it. */
@media (max-width: 480px) {
  .list__item:has(> .list__aside.row) { flex-wrap: wrap; }
  .list__item > .list__aside.row { flex-basis: 100%; justify-content: flex-end; margin-top: var(--space-2); }
}

/* The line on the feed that opens the composer. Looks like the input it
   replaces, so tapping it is the obvious thing to do. */
.composer-trigger {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  text-align: left;
  border: 0;
  font: inherit;
  cursor: pointer;
  color: var(--text-muted);
}
.composer-trigger__text {
  flex: 1;
  min-width: 0;
  background: var(--surface-2);
  border-radius: 22px;
  padding: 11px var(--space-4);
  font-size: 16px;
}
.composer-trigger .icon { font-size: 1.15rem; color: var(--accent); flex: none; }

/* Writing a post gets the whole screen. A half-open box on top of a scrolling
   feed makes composing feel like an afterthought, and on a phone the keyboard
   would cover most of it anyway. */
.sheet {
  position: fixed;
  inset: 0;
  /* Cover exactly the part of the screen that can be seen, rather than the
     layout viewport.

     Both edges matter and both were wrong. The bottom ran under the keyboard,
     hiding the footer with the photo button and the counter. The top went with
     iOS when it scrolled the page to reveal the focused field, carrying the
     header - and Cancel and Post with it - off the top edge. */
  top: var(--viewport-top, 0px);
  bottom: var(--keyboard-inset, 0px);
  z-index: 80;
  background: var(--surface);
  display: flex;
  flex-direction: column;
}
.sheet[hidden] { display: none; }

.sheet__form { display: flex; flex-direction: column; height: 100%; min-height: 0; }

.sheet__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  padding-top: calc(var(--space-3) + env(safe-area-inset-top));
  border-bottom: 1px solid var(--border);
}
.sheet__title { flex: 1; text-align: center; margin: 0; font-size: 1.05rem; font-weight: 700; }

.sheet__body { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-4); }
.sheet__author { display: flex; gap: var(--space-3); align-items: flex-start; margin-bottom: var(--space-4); }
.sheet__name { font-weight: 700; }

/* The audience and sport pickers are metadata, not form fields: an outline
   around each one boxes up the top of a screen that should read as a blank
   page. A soft fill still says "tappable" without drawing the box. */
.sheet select.select {
  min-height: 32px;
  padding: var(--space-1) var(--space-5) var(--space-1) var(--space-3);
  border: 0;
  border-radius: 999px;
  background-color: var(--surface-2);
  background-position: calc(100% - 16px) 15px, calc(100% - 11px) 15px;
  /* Stays at 16px like every other control here - a select under that size
     makes iOS zoom the page when it takes focus. Weight and colour carry the
     "this is only metadata" job instead. */
  font-size: 16px;
  font-weight: 600;
  color: var(--text-muted);
}
.sheet select.select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* No border and no box: on a full screen the page IS the field. */
.sheet__input {
  width: 100%;
  border: 0;
  background: none;
  resize: none;
  font: inherit;
  font-size: 1.15rem;
  line-height: 1.45;
  color: var(--text);
  min-height: 40vh;
  padding: 0;
}
.sheet__input:focus { outline: none; }
.sheet__input::placeholder { color: var(--text-muted); }

.sheet__preview {
  display: block;
  width: 100%;
  max-height: 40vh;
  object-fit: cover;
  border-radius: var(--radius);
  margin-top: var(--space-4);
}
.sheet__preview[hidden] { display: none; }

.sheet__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
}
.sheet__attach {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.9rem;
  font-weight: 650;
  cursor: pointer;
}
.sheet__attach .icon { font-size: 1.1rem; color: var(--accent); }

/* Nothing behind the sheet should scroll while it is open. */
body.sheet-open { overflow: hidden; }

/* A photo in a post. The cap stops one image owning the whole screen. */
.post__photo {
  display: block;
  width: 100%;
  max-height: 420px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  margin-top: var(--space-3);
}
.post__photo--thumb { max-height: 180px; max-width: 320px; }

/* The comment bar: avatar, one thin line, send appears when it is in use. */
.comment-bar { display: flex; align-items: center; gap: var(--space-2); }
/* display:flex above outranks the [hidden] attribute on its own, so a hidden
   reply or editor would sit open on the page. */
.comment-bar[hidden] { display: none; }
textarea.comment-bar__input {
  flex: 1;
  min-width: 0;
  min-height: 36px;
  resize: none;
  border-radius: 18px;
  padding: 6px var(--space-3);
  background: var(--surface-2);
  border-color: transparent;
  line-height: 1.35;
  /* 16px exactly. Anything smaller and iOS zooms the page when the field takes
     focus - which is what made tapping a comment box feel like leaving the app.
     The base rule in this stylesheet already said so; this input ignored it. */
  font-size: 16px;
}
textarea.comment-bar__input:focus { background: var(--surface); }
.comment-bar:not(.is-active) textarea.comment-bar__input { overflow: hidden; }
/* No colour of its own: the same quiet icon button as the ones beside it. */
.comment-bar__send { display: none; flex: none; min-height: 36px; color: var(--text-muted); }
.comment-bar__send:hover { color: var(--text); }
.comment-bar.is-active .comment-bar__send,
.comment-bar--edit .comment-bar__send { display: inline-flex; }
/* Grows a little when in use, not into a panel: two lines is enough to see
   what you are writing, and the box is docked at the bottom by then. */
.comment-bar.is-active textarea.comment-bar__input { min-height: 40px; border-radius: var(--radius); }

/* Adverts in the feed.
   Deliberately quieter than a post: a hairline border and a small label, so it
   reads as paid placement rather than as something a player wrote. */
.ad { border: 1px dashed var(--border); }
.ad__label {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}
.ad__body { display: block; text-decoration: none; }
.ad__image {
  width: 100%;
  max-height: 220px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-3);
}
.ad__title { font-weight: 650; }
.ad__desc { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: 0.9rem; }

/* Forms */
.field { margin-bottom: var(--space-4); }
.field__label { display: block; margin-bottom: var(--space-2); font-weight: 650; font-size: 0.92rem; }
.field__hint { margin: var(--space-1) 0 0; font-size: 0.82rem; color: var(--text-muted); }
.field__error { margin: var(--space-1) 0 0; font-size: 0.85rem; color: var(--coral-600); font-weight: 600; }

.input, .select, .textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--space-3);
  font: inherit;
  font-size: 16px;             /* prevents the iOS zoom-on-focus */
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  appearance: none;
}
.textarea { min-height: 110px; resize: vertical; }
.select {
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                    linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 21px, calc(100% - 12px) 21px;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: var(--space-6);
}
.input:focus, .select:focus, .textarea:focus { border-color: var(--accent); }
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--coral-600);
}
.input[disabled], .select[disabled], .textarea[disabled] { background: var(--surface-2); color: var(--text-muted); }

.checkbox-row, .radio-row {
  display: flex; align-items: flex-start; gap: var(--space-3);
  min-height: 44px; padding: var(--space-2) 0;
}
.checkbox-row input, .radio-row input { width: 22px; height: 22px; margin-top: 2px; flex: none; accent-color: var(--accent); }

/* Chip-style multi-select, used for picking sports on onboarding. */
.chip-grid { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 0.92rem; font-weight: 600;
  cursor: pointer;
}
.chip input { position: absolute; opacity: 0; pointer-events: none; }
.chip:has(input:checked) { background: var(--sea-100); border-color: var(--accent); color: var(--accent); }
.chip:has(input:focus-visible) { outline: 3px solid var(--sun-500); outline-offset: 2px; }
.chip__icon { font-size: 1.05rem; }

/* Segmented filter control.

   Tabs, marked by an underline rather than a filled shape. A row of pills
   competes for attention with itself - six lozenges, each looking pressable -
   where an underline says "you are here" and nothing else.

   The hairline runs the width of the row so the underline reads as a marker
   sitting on it, not as a stray rule under one word. Every item reserves the
   same three pixels whether it is selected or not, so nothing shifts as the
   selection moves.

   The row scrolls sideways and fades at the right edge: a label cut off
   mid-word reads as a layout fault rather than as more to come. */
.segmented {
  display: flex;
  gap: var(--space-1);
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  padding-right: var(--space-4);
  margin-bottom: var(--space-4);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
}
.segmented::-webkit-scrollbar { display: none; }

/* The feed's filter row stays put while the feed scrolls under it.

   It used to scroll away with the posts, so changing sport meant scrolling all
   the way back to the top to find the tabs again - which is what made it feel
   like the row was moving about.

   It rides with the top bar rather than moving on its own. When the bar hides
   on a scroll down, this goes up with it by exactly the bar's height and pins
   to the top edge, on the same 220ms curve; when the bar comes back, so does
   this. Both move the same distance on the same curve, so the row's top edge
   is always exactly the bar's bottom edge and no gap ever opens between them.

   The background lives here rather than on .segmented because that row fades
   out at its right edge with a mask, and a mask takes the background with it -
   posts would scroll through the fade. */
.filter-bar {
  position: sticky;
  top: var(--topbar-height);
  z-index: 20;
  background: var(--bg);
  transition: transform 220ms ease;
}
.filter-bar .segmented { margin-bottom: 0; }
/* The spacing the row used to carry itself, moved out with the pin. */
.filter-bar { margin-bottom: var(--space-4); }
@media (max-width: 639px) {
  body.chrome-hidden .filter-bar {
    transform: translateY(calc(-1 * var(--topbar-height)));
  }
}
@media (prefers-reduced-motion: reduce) {
  .filter-bar { transition: none; }
}

.segmented__item {
  flex: none;
  min-height: 38px;
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-2);
  border: 0;
  border-bottom: 3px solid transparent;
  /* Sits on the row's hairline rather than above it. */
  margin-bottom: -1px;
  background: none;
  color: var(--text-muted);
  font-size: 0.88rem;
  font-weight: 650;
  white-space: nowrap;
  scroll-snap-align: start;
  transition: color 120ms ease, border-color 120ms ease;
}
.segmented__item:hover {
  text-decoration: none;
  color: var(--text);
  border-bottom-color: var(--border);
}
.segmented__item.is-active,
.segmented__item.is-active:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 700;
}

/* Badges */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px var(--space-2);
  border-radius: 999px;
  font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.badge--ok { background: var(--grass-100); color: var(--grass-600); }
.badge--done { background: var(--sea-100); color: var(--sea-600); }
.badge--warn { background: var(--sun-100); color: #a86a12; }
.badge--danger { background: var(--coral-100); color: var(--coral-600); }
.badge--info { background: var(--sea-100); color: var(--sea-600); }
.badge--muted { background: var(--surface-2); color: var(--text-muted); }

.badge-count {
  position: absolute; top: 6px; right: 6px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--coral-600); color: #fff;
  font-size: 0.66rem; font-weight: 800;
}
.badge-count--tab { top: 4px; right: calc(50% - 22px); }
.sidebar .badge-count { position: static; margin-left: auto; }

/* Avatars */
.avatar {
  display: grid; place-items: center; flex: none;
  width: 44px; height: 44px;
  border-radius: 50%; overflow: hidden;
  background: var(--navy-700); color: #fff;
  font-size: 0.85rem; font-weight: 700;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 34px; height: 34px; font-size: 0.72rem; }
.avatar--lg { width: 76px; height: 76px; font-size: 1.4rem; }
.avatar--xl { width: 96px; height: 96px; font-size: 1.7rem; }

/* Player rows and lists */
.list { list-style: none; margin: 0; padding: 0; }
.list__item {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.list__item:last-child { border-bottom: 0; }
.list__body { min-width: 0; flex: 1; }
.list__title { font-weight: 650; }
.list__meta { font-size: 0.84rem; color: var(--text-muted); }
.list__aside { flex: none; text-align: right; }
.list__link { color: inherit; display: contents; }

.player-row { display: flex; align-items: center; gap: var(--space-3); }
.player-row__name { display: block; font-weight: 650; }
.player-row__meta { display: block; font-size: 0.82rem; color: var(--text-muted); }

/* Rank position pill */
.rank-pos {
  flex: none;
  display: grid; place-items: center;
  min-width: 34px; height: 34px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-weight: 800; font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
}
.rank-pos--1 { background: #fdf0c9; color: #8a6d1a; }
.rank-pos--2 { background: #e9edf2; color: #566372; }
.rank-pos--3 { background: #f7e3d3; color: #8d5a2c; }
.rank-points { font-variant-numeric: tabular-nums; font-weight: 750; }
.rank-move { font-size: 0.78rem; font-weight: 700; }
.rank-move--up { color: var(--grass-600); }
.rank-move--down { color: var(--coral-600); }
.rank-move--flat { color: var(--text-muted); }

/* Stats */
.stat-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }
.stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-3);
}
.stat__value { font-size: 1.4rem; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.1; }
.stat__label { font-size: 0.78rem; color: var(--text-muted); font-weight: 600; }

/* Flash + toast */
.flash-stack { margin-bottom: var(--space-4); display: grid; gap: var(--space-2); }
.flash {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  font-weight: 600; font-size: 0.92rem;
  border: 1px solid transparent;
}
.flash--success { background: var(--grass-100); color: var(--grass-600); border-color: currentColor; }
.flash--error { background: var(--coral-100); color: var(--coral-600); border-color: currentColor; }
.flash--warning { background: var(--sun-100); color: #a86a12; border-color: currentColor; }
.flash--info { background: var(--sea-100); color: var(--sea-600); border-color: currentColor; }

.toast-region {
  position: fixed;
  left: var(--space-4); right: var(--space-4);
  bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom) + var(--space-3));
  z-index: 60;
  display: grid; gap: var(--space-2);
  pointer-events: none;
}
.toast {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  background: var(--navy-800); color: #fff;
  box-shadow: var(--shadow-lg);
  font-size: 0.9rem; font-weight: 600;
}
.toast--error { background: var(--coral-600); }

/* Empty states */
/* A section of a page that opens: the "add one of these" form under a list.
   Closed it is a single line, which is what it should cost on the visits where
   nobody is adding anything. */
.panel { padding: 0; }

.panel__summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  cursor: pointer;
  list-style: none;
  font-weight: 700;
}
.panel__summary::-webkit-details-marker { display: none; }
.panel__summary:hover { color: var(--accent); }

.panel__title { flex: 1; }
.panel__chevron { flex: none; color: var(--text-muted); transition: transform 120ms ease; }
.panel[open] .panel__chevron { transform: rotate(90deg); }

.panel__body {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px solid var(--border);
  padding-top: var(--space-4);
}

/* A list row that opens to reveal its controls.

   Collapsed it is one line of facts; open it holds whatever form belongs to
   that row. The chevron turns rather than swapping glyph, so the control does
   not change shape while it is being pressed. */
.row-open {
  border-bottom: 1px solid var(--border);
}
.row-open:last-child { border-bottom: 0; }

.row-open__summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-1);
  cursor: pointer;
  /* The default triangle would sit beside our own chevron. */
  list-style: none;
  min-height: 48px;
}
.row-open__summary::-webkit-details-marker { display: none; }
.row-open__summary:hover { background: var(--surface-2); }

/* A row with nothing to reveal. Same shape and padding as one that opens, so a
   mixed list keeps a single left edge, but it does not invite a press. */
.row-open__summary--static {
  cursor: default;
  border-bottom: 1px solid var(--border);
}
.row-open__summary--static:hover { background: none; }

.row-open__main { flex: 1; min-width: 0; }
.row-open__title { display: block; font-weight: 650; }
.row-open__meta { display: block; font-size: 0.85rem; color: var(--text-muted); }
.row-open__badge { flex: none; }

.row-open__chevron {
  flex: none;
  color: var(--text-muted);
  transition: transform 120ms ease;
}
.row-open[open] .row-open__chevron { transform: rotate(90deg); }

.row-open__body {
  padding: var(--space-2) var(--space-1) var(--space-4);
  border-top: 1px dashed var(--border);
}

/* The premium mark. Gold, small, and never on its own line: it belongs to
   the name it follows, so it must not push anything around. */
.premium-badge {
  display: inline-flex;
  align-items: center;
  vertical-align: baseline;
  margin-left: 4px;
  color: var(--sun-500);
  /* Relative to the name it sits beside, so it stays in proportion whether
     that is a profile heading or a line of small print. */
  font-size: 0.8em;
  flex: none;
}

.empty-state {
  text-align: center;
  padding: var(--space-7) var(--space-4);
  color: var(--text-muted);
  /* A card, like everything else on the page. Bare on the background it read
     as a section that had failed to render rather than one with nothing in
     it - which is exactly what an empty page must not look like. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.empty-state__code { font-size: 3rem; font-weight: 800; margin: 0; color: var(--border); }
/* A label, not a page heading. Every caller passes the name of the page it is
   already on, so at h1 size it just says the same thing twice, loudly. */
.empty-state__title {
  color: var(--text);
  font-size: 1.05rem;
  margin: 0 0 var(--space-2);
}
.empty-state__body { max-width: 42ch; margin: 0 auto var(--space-4); }
.empty-state__details { text-align: left; margin: var(--space-4) 0; }
.empty-state__details pre {
  overflow-x: auto; font-family: var(--font-mono); font-size: 0.78rem;
  background: var(--surface-2); padding: var(--space-3); border-radius: var(--radius-sm);
}

/* Modal / dialog */
.modal { border: 0; padding: 0; background: transparent; max-width: min(560px, 100vw); width: 100%; }
.modal::backdrop { background: rgba(7, 21, 40, 0.55); }
.modal__panel {
  background: var(--surface);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: var(--space-5) var(--space-4) calc(var(--space-5) + env(safe-area-inset-bottom));
  max-height: 92dvh; overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
.modal[open] { margin: auto auto 0; }
.modal__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-4); }
.modal__close {
  min-width: 44px; min-height: 44px;
  border: 0; background: transparent; font-size: 1.4rem; cursor: pointer; color: var(--text-muted);
}

/* Feed */
.composer__actions { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-3); flex-wrap: wrap; }
.char-count { font-size: 0.8rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.char-count.is-over { color: var(--coral-600); font-weight: 700; }

.post__head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.post__author { font-weight: 650; }
.post__meta { font-size: 0.8rem; color: var(--text-muted); }
.post__body { white-space: pre-wrap; overflow-wrap: anywhere; }
.post__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
/* Delete sits apart from the two counts, at the far end of the row. The
   macro wraps the button in a form, so the margin has to go on the form:
   putting it on the button moves nothing. */
.post__actions form { margin-left: auto; }

/* The comment thread is a block under the actions row, never a child of it:
   as a flex item it was laid out in one narrow column, which stacked the
   avatar above a tall skinny textarea with the send button beneath. */
.post__comments {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
.post__comments[hidden] { display: none; }

/* Tapping the body or the photo opens the thread. A block link so the whole
   area is the target, without turning the text into link-blue. */
.post__open { display: block; text-decoration: none; }
.post__open:hover .post__body { opacity: 0.9; }

.post__more {
  display: inline-block;
  font-size: 0.88rem;
  font-weight: 650;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}
.post__more:hover { color: var(--accent); }

/* A row on the profile that opens the post it summarises. */
.activity-row { display: flex; align-items: center; gap: var(--space-3); text-decoration: none; }
.activity-row:hover { background: var(--surface-2); }

/* A reply box is hidden until asked for: a long thread should not be a column
   of empty inputs. */
.comment-bar--reply { margin-top: var(--space-2); }
.post__action {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 40px; padding: var(--space-2) var(--space-3);
  border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted);
  font: inherit; font-size: 0.88rem; font-weight: 650; cursor: pointer;
}
.post__action:hover { background: var(--surface-2); }
.post__action.is-active { color: var(--coral-600); }
.comment-list { margin-top: var(--space-3); display: grid; gap: var(--space-4); }

/* Comments carry no bubble. A tinted capsule around every line turns a long
   thread into a column of blocks; plain text on the card, with the name in
   bold, reads the way a conversation reads. */
.comment { display: flex; gap: var(--space-2); align-items: flex-start; }
.comment__avatar { flex: none; }
.comment__main { min-width: 0; flex: 1; }

.comment__head { display: flex; align-items: baseline; gap: var(--space-1); flex-wrap: wrap; }
.comment__author { font-weight: 650; font-size: 0.88rem; color: inherit; text-decoration: none; }
.comment__author:hover { text-decoration: underline; }
/* "A > B": who answered whom, when a reply was lifted out of its own level. */
.comment__arrow { color: var(--text-muted); display: inline-flex; }
.comment__arrow .icon { font-size: 0.7rem; }
.comment__target { font-weight: 650; font-size: 0.88rem; color: var(--text-muted); }
.comment__time { font-size: 0.78rem; color: var(--text-muted); }

.comment__text { font-size: 0.92rem; overflow-wrap: anywhere; }
/* The person being answered, named inline at the start of the reply. */
.comment__mention { font-weight: 650; color: var(--accent); text-decoration: none; margin-right: 0.35em; }
.comment__mention:hover { text-decoration: underline; }

/* Replies hang off a line that runs from the parent down to the last of them. */
.comment__replies {
  margin-top: var(--space-3);
  padding-left: var(--space-4);
  border-left: 2px solid var(--border);
  display: grid;
  gap: var(--space-4);
}
.comment__replies[hidden] { display: none; }

/* Each level costs an avatar, a gap and the indent. Three levels of that on a
   375px screen leaves a column of two-word lines, so the deeper ones give some
   of it back: a smaller avatar and a tighter indent. */
.comment__replies .comment__replies { padding-left: var(--space-3); }
.comment__replies .comment__replies .avatar { width: 28px; height: 28px; font-size: 0.62rem; }

.comment__show-replies {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 650;
  color: var(--text-muted);
  cursor: pointer;
}
.comment__show-replies:hover { color: var(--accent); }
/* A short stub of the connector, so the collapsed state still reads as "there
   is more below this". */
.comment__show-line { width: 20px; height: 2px; background: var(--border); flex: none; }

/* Match cards */
.match-card__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
.match-card__players { display: flex; align-items: center; gap: var(--space-3); }
.match-card__vs { font-weight: 800; color: var(--text-muted); font-size: 0.82rem; }
.match-card__meta { display: grid; gap: var(--space-1); font-size: 0.86rem; color: var(--text-muted); }
.match-card__meta strong { color: var(--text); font-weight: 650; }

.fee-callout {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius);
  background: var(--sun-100);
  color: #8a5a10;
  font-size: 0.88rem;
  margin-bottom: var(--space-3);
}
.fee-callout--free { background: var(--grass-100); color: var(--grass-600); }
.fee-callout--info { background: var(--sea-100); color: var(--sea-600); }
.fee-callout--warn { background: var(--sun-100); color: #a86a12; }

/* Slot picker */
.slot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: var(--space-2); }
.slot {
  position: relative;
  min-height: 52px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 0.9rem; font-weight: 650;
  cursor: pointer;
}
.slot input { position: absolute; opacity: 0; pointer-events: none; }
.slot__fee { font-size: 0.7rem; font-weight: 600; color: var(--text-muted); }
.slot:has(input:checked) { background: var(--accent); border-color: var(--accent); color: #fff; }
.slot:has(input:checked) .slot__fee { color: rgba(255, 255, 255, 0.85); }
.slot:has(input:disabled) { opacity: 0.4; cursor: not-allowed; background: var(--surface-2); }
.slot:has(input:focus-visible) { outline: 3px solid var(--sun-500); outline-offset: 2px; }

/* Tables become stacked cards on phones */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.table th, .table td { padding: var(--space-3); text-align: left; border-bottom: 1px solid var(--border); }
.table th { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); white-space: nowrap; }
.table tbody tr:last-child td { border-bottom: 0; }

/* Pagination */
.pagination { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-4); }

/* Loading */
.spinner {
  display: inline-block; width: 18px; height: 18px;
  border: 2px solid var(--border); border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.is-loading { opacity: 0.6; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* 5. Screens ------------------------------------------------------------ */
.auth-screen {
  max-width: 420px; margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-7);
}
.auth-screen__brand { text-align: center; margin-bottom: var(--space-5); }
.auth-screen__logo {
  display: inline-grid; place-items: center;
  width: 56px; height: 56px; border-radius: 16px;
  background: var(--navy-800); color: #fff;
  font-weight: 800; margin-bottom: var(--space-3);
}
.auth-screen__alt { text-align: center; margin-top: var(--space-4); font-size: 0.92rem; }

.profile-hero { display: flex; gap: var(--space-4); align-items: center; margin-bottom: var(--space-4); }
.profile-hero__body { min-width: 0; }
.profile-hero__name { margin: 0; font-size: 1.3rem; }
.profile-hero__handle { color: var(--text-muted); font-size: 0.9rem; }

.sport-stat-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) 0; border-bottom: 1px solid var(--border);
}
.sport-stat-row:last-child { border-bottom: 0; }
.sport-stat-row__name { font-weight: 650; display: flex; align-items: center; gap: var(--space-2); }

.admin-grid { display: grid; gap: var(--space-3); grid-template-columns: repeat(2, 1fr); }
.admin-nav { display: flex; gap: var(--space-2); overflow-x: auto; padding-bottom: var(--space-2); margin-bottom: var(--space-4); scrollbar-width: none; }
.admin-nav::-webkit-scrollbar { display: none; }

/* On a phone the bars get out of the way when you scroll down, and come back
   on a deliberate flick upward - not on a slow scroll back, which is usually
   someone re-reading rather than asking for the navigation.

   Transform, not display: the bars keep their space in the layout, so nothing
   below them shifts as they move. */
@media (max-width: 639px) {
  .topbar, .tabbar { transition: transform 220ms ease; will-change: transform; }
  body.chrome-hidden .topbar { transform: translateY(-100%); }
  body.chrome-hidden .tabbar { transform: translateY(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .topbar, .tabbar { transition: none; }
}

/* A comment box in use docks to the bottom of the screen, so what you are
   typing sits above the keyboard rather than wherever the thread happened to
   put it. Only on phones: on a wide screen the box is already in view and
   pinning it would be strange.

   --keyboard-inset is set from visualViewport. On iOS a fixed element sits at
   the bottom of the LAYOUT viewport, which the keyboard covers - the inset is
   how much of the layout viewport the keyboard is hiding. */
@media (max-width: 639px) {
  .comment-bar.is-docked {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Lifted with a transform rather than by moving `bottom`: the offset is
       recalculated every frame while the keyboard is up, and a transform is
       the one the compositor can follow without a layout pass each time. */
    transform: translateY(calc(-1 * var(--keyboard-inset, 0px)));
    will-change: transform;
    z-index: 45;
    margin: 0;
    padding: var(--space-2) var(--space-3);
    /* The home-indicator inset only matters when the bar is sitting on the
       bottom edge. With the keyboard up the bar sits on the keyboard, which
       already covers that strip - adding it there just floats the box above
       the keyboard with a band of page showing underneath. */
    padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom));
  }
  body.keyboard-open .comment-bar.is-docked {
    padding-bottom: var(--space-2);
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 12px rgba(7, 21, 40, 0.10);
  }

  /* The docked box takes the tab bar's place rather than covering it. */
  body.has-docked-comment .tabbar { display: none; }

  /* A spacer holds the gap the box left behind, so the thread does not jump
     upward the moment it docks. */
  .comment-bar__spacer { display: block; }
}

/* 6. Breakpoints -------------------------------------------------------- */
@media (min-width: 640px) {
  .app-main { padding: var(--space-5); padding-bottom: var(--space-6); }
  .tabbar { display: none; }
  .toast-region { bottom: var(--space-5); left: auto; right: var(--space-5); width: min(360px, 90vw); }
  .stat-grid { grid-template-columns: repeat(4, 1fr); }
  .admin-grid { grid-template-columns: repeat(3, 1fr); }
  .btn--block { width: auto; }
  .modal__panel { border-radius: var(--radius-lg); }
  .modal[open] { margin: auto; }
  h1 { font-size: 1.75rem; }
}

@media (min-width: 1024px) {
  .app-shell {
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    gap: var(--space-5);
    max-width: var(--wide-max);
    margin: 0 auto;
    padding: 0 var(--space-5);
    align-items: start;
  }
  .app-shell--bare { display: block; }

  .sidebar {
    display: block;
    position: sticky;
    top: calc(var(--topbar-height) + var(--space-5));
    padding: var(--space-5) 0;
  }
  .sidebar__nav { display: grid; gap: 2px; }
  .sidebar__link {
    display: flex; align-items: center; gap: var(--space-3);
    min-height: 44px; padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--text); font-weight: 600; font-size: 0.94rem;
  }
  .sidebar__link:hover { background: var(--surface-2); text-decoration: none; }
  .sidebar__link .icon { font-size: 1.05rem; opacity: 0.85; }
  .sidebar__link.is-active { background: var(--sea-100); color: var(--accent); }
  .sidebar__link--button { width: 100%; border: 0; background: transparent; font: inherit; font-weight: 600; cursor: pointer; text-align: left; }
  .sidebar__form { margin: 0; }
  .sidebar__rule { margin: var(--space-3) 0; }

  .app-main { padding: var(--space-5) 0 var(--space-7); max-width: none; }
  .admin-grid { grid-template-columns: repeat(4, 1fr); }
}

/* Wide screens: let content breathe but never sprawl. */
@media (min-width: 1400px) {
  :root { --wide-max: 1280px; }
}

@media print {
  .topbar, .tabbar, .sidebar, .toast-region, .btn { display: none !important; }
  .app-main { padding: 0; }
}

/* Fair-play rating scale.

   Numbers with words rather than stars: five identical stars make people guess
   whether three is "average" or "bad", and the words settle it. Each option is
   a whole tappable box, because a radio dot on a phone is a target you miss. */
.review-question { font-size: 1.1rem; font-weight: 700; margin: 0 0 var(--space-2); }

.rating { border: 0; padding: 0; margin: 0 0 var(--space-4); }
.rating__scale { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
.rating__option { flex: 1; min-width: 0; cursor: pointer; }

/* The input stays focusable and reachable by keyboard - it is only moved out
   of sight, never hidden from assistive technology. */
.rating__option input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.rating__box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-2) 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: center;
}
.rating__score { font-size: 1.15rem; font-weight: 700; line-height: 1.1; }
.rating__word {
  font-size: 0.68rem;
  color: var(--text-muted);
  line-height: 1.15;
  overflow-wrap: anywhere;
}

.rating__option input:checked + .rating__box {
  border-color: var(--accent);
  background: var(--surface-2);
}
.rating__option input:checked + .rating__box .rating__score,
.rating__option input:checked + .rating__box .rating__word { color: var(--accent); }

/* Keyboard users get the same ring the rest of the interface uses. */
.rating__option input:focus-visible + .rating__box {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The standing itself, wherever it is shown. */
.fair-play { display: flex; align-items: baseline; gap: var(--space-2); }
.fair-play__score { font-size: 1.25rem; font-weight: 700; }
.fair-play__count { color: var(--text-muted); font-size: 0.85rem; }

/* The review modal.

   A card on a dimmed backdrop rather than a full-screen page. A review is one
   question about one evening; giving it the whole screen made it read as a
   form to fill in rather than a moment to answer. */
.review-modal {
  position: fixed;
  inset: 0;
  top: var(--viewport-top, 0px);
  bottom: var(--keyboard-inset, 0px);
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  /* Room for the avatar, which hangs above the card's top edge. */
  padding-top: calc(var(--space-6) + env(safe-area-inset-top));
  overflow-y: auto;
}
.review-modal[hidden] { display: none; }

.review-modal__backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
}

.review-modal__card {
  position: relative;
  width: 100%;
  max-width: 420px;
  margin: auto;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  /* Not hidden: the avatar hangs over the top edge on purpose, and clipping it
     would cut the face in half. The bar below rounds its own corners instead. */
  overflow: visible;
  /* Clear of the half of the avatar that sits above the card. */
  padding-top: 56px;
}

.review-modal__bar {
  position: absolute;
  inset: 0 0 auto;
  height: 10px;
  background: var(--accent);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* The face of whoever is being judged, first thing read. */
.review-modal__avatar {
  position: absolute;
  top: -48px;
  left: 50%;
  transform: translateX(-50%);
}
.review-modal__avatar .avatar {
  border: 4px solid var(--surface);
  box-shadow: var(--shadow-sm);
}

.review-modal__title {
  margin: var(--space-3) var(--space-4);
  text-align: center;
  font-size: 1.6rem;
  line-height: 1.15;
}

/* Three facts, divided the way a receipt divides them. */
.review-modal__facts {
  display: flex;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.review-modal__fact {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-2);
  text-align: center;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.review-modal__fact + .review-modal__fact { border-left: 1px solid var(--border); }
.review-modal__fact .icon { font-size: 1.35rem; color: var(--accent); }
.review-modal__fact span { overflow-wrap: anywhere; }

.review-modal__body { padding: var(--space-4); }
.review-modal__question {
  margin: 0 0 var(--space-2);
  text-align: center;
  font-size: 1.15rem;
}

/* Rendered 5 to 1 and reversed here, so that choosing a star can fill the ones
   below it using nothing but a sibling selector. */
.stars {
  display: flex;
  flex-direction: row-reverse;
  justify-content: center;
  gap: var(--space-2);
  border: 0;
  padding: 0;
  margin: 0;
}
.stars__star { cursor: pointer; line-height: 0; }
.stars__star svg {
  width: 46px;
  height: 46px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-linejoin: round;
  transition: fill 0.12s ease;
}

/* The chosen star and every star below it - which, reversed, is every later
   sibling in the markup. */
.stars input:checked ~ .stars__star svg { fill: var(--accent); }
.stars__star:hover svg,
.stars__star:hover ~ .stars__star svg { fill: var(--accent); }
.stars input:focus-visible + .stars__star svg { outline: 2px solid var(--accent); outline-offset: 3px; }

.stars__word {
  margin: var(--space-2) 0 var(--space-3);
  min-height: 1.25em;
  text-align: center;
  font-weight: 600;
  color: var(--accent);
}

.review-modal__actions { display: flex; border-top: 1px solid var(--border); }
.review-modal__cancel,
.review-modal__submit {
  flex: 1;
  padding: var(--space-4);
  border: 0;
  background: none;
  font: inherit;
  font-size: 1.15rem;
  cursor: pointer;
}
.review-modal__cancel { color: var(--text-muted); border-bottom-left-radius: var(--radius-lg); }
.review-modal__submit { border-bottom-right-radius: var(--radius-lg); }
.review-modal__submit { color: var(--accent); font-weight: 700; }
.review-modal__cancel + .review-modal__submit { border-left: 1px solid var(--border); }

/* Greyed until a star is chosen, exactly as it looks before you answer. */
.review-modal__submit:disabled { color: var(--text-muted); opacity: 0.55; cursor: default; }

@media (max-width: 380px) {
  .stars__star svg { width: 38px; height: 38px; }
  .review-modal__title { font-size: 1.35rem; }
}

/* What the stars are actually asking. Without it, five stars beside somebody's
   name reads as "how good are they" - which is the ranking's job, not this. */
.review-modal__about {
  margin: 0 0 var(--space-2);
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}
.review-modal__examples {
  margin: 0 auto var(--space-3);
  padding: 0;
  max-width: 19rem;
  list-style: none;
  color: var(--text-muted);
  font-size: 0.8rem;
  line-height: 1.45;
}
.review-modal__examples li { padding-left: 1.1rem; position: relative; }
.review-modal__examples li::before {
  content: '';
  position: absolute;
  left: 0.25rem;
  top: 0.62em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--accent);
}
