/* ============================================================
   Biomate — screen-specific styles
   ============================================================ */

.block { padding: 14px var(--pad); }
.sectionhead {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--t-lg);
  padding: 18px var(--pad) 8px;
}
.pagetitle { font-size: var(--t-2xl); font-weight: 700; letter-spacing: -.02em; padding: 6px var(--pad) 14px; }
.prose { white-space: pre-line; color: var(--ink-soft); font-size: var(--t-sm); line-height: 1.55; }
.inline { display: flex; gap: 10px; align-items: center; }
.inline .field { flex: 1; }

.linky {
  color: var(--brand-text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  min-height: var(--tap);
}

/* ---------------- home ---------------- */
.brandbar { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 16px 0 4px; }
.brandword {
  font-family: var(--font-display);
  font-style: italic; font-weight: 900;
  font-size: 2rem; letter-spacing: -.03em;
}
.hello { text-align: center; padding: 4px var(--pad) 10px; }
.hello em { display: block; font-family: var(--font-display); font-size: var(--t-lg); color: var(--ink-soft); }
.hello span { display: block; font-size: var(--t-md); margin-top: 2px; }

.ausmap { position: relative; margin: 6px var(--pad) 4px; }
.ausmap__svg { width: 100%; height: auto; display: block; }
/* a real DOM element so it is announced, positioned over the map.
   Deep maroon on amber measures 5.2:1 — the brand red was 3.8:1 and
   genuinely hard to read against the map fill. */
.ausmap__welcome {
  position: absolute;
  /* Aufan asked for it nearer the middle of the map. Not dead centre:
     that sits it on the SA pin. 44/46 is the largest clear space in the
     interior — Northern Territory above, Western Australia left, South
     Australia below-right — and it reads as centred because the
     mainland's own centre is about 50/43.

     ⚠️ Moving this changes the colour behind it. The fill is a diagonal
     gradient, so the ink has to be re-checked on every move: at 44/46
     it sits at t≈0.45 of the ramp, giving 4.79:1 in light and 5.55:1
     in dark against a 4.5 bar. */
  left: 44%; top: 46%;
  transform: translate(-50%, -50%) rotate(-4deg);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 900;
  font-size: clamp(15px, 5.2vw, 24px);
  color: #7A2318;
  letter-spacing: -.02em;
  pointer-events: none;
  white-space: nowrap;
}
.ausmap__count {
  display: inline-grid;
  place-items: center;
  min-width: 16px; height: 16px;
  margin-left: 5px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  background: rgba(255,255,255,.92);
  color: var(--brand-text);
  font-size: 10px;
  font-weight: 800;
}
.ausmap__pins { position: absolute; inset: 0; }
.ausmap__pin {
  display: inline-flex;
  align-items: center;
  position: absolute;
  transform: translate(-50%, -50%);
  background: var(--brand-ink);
  color: var(--on-brand);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  padding: 5px 10px;
  min-height: 28px;
  border-radius: var(--r-pill);
  box-shadow: var(--lift-1);
  transition: transform var(--fast) var(--ease-spring);
}
.ausmap__pin:active { transform: translate(-50%, -50%) scale(.9); }
.ausmap__pin:hover { transform: translate(-50%, -50%) scale(1.06); }

/* the stack that fades downward. The gradient is on the WRAPPER, so
   nothing is actually hidden from assistive tech — every row is a
   real focusable button at full strength. */
.fadestack { display: flex; flex-direction: column; gap: 8px; padding: 10px var(--pad) 0; }
.fadestack__row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  text-align: left;
  padding: 14px 16px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
  font-size: var(--t-sm);
  opacity: calc(1 - (var(--i) * 0.18));
  transition: opacity var(--fast), transform var(--fast) var(--ease-out);
}
.fadestack__row:hover, .fadestack__row:focus-visible { opacity: 1; }
.fadestack__row:active { transform: scale(.99); }
.fadestack__row em { font-style: italic; color: var(--ink-soft); }

/* ---------------- hike ---------------- */
.hero { position: relative; margin: 0 var(--pad); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--lift-2); }
.hero__img { width: 100%; height: 230px; object-fit: cover; }
.hero__save {
  position: absolute; top: 12px; left: 12px;
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  border-radius: var(--r-md);
  background: rgba(255,255,255,.9);
  color: var(--brand-ink);
}
.hero__save[aria-pressed="true"] { background: var(--brand-ink); color: var(--on-brand); }

.detail { display: flex; align-items: center; gap: 12px; padding: 9px 0; font-size: var(--t-sm); }
.detail__ic { color: var(--brand); display: grid; place-items: center; flex-shrink: 0; }

/* ---------------- chat ---------------- */
.chat { display: flex; flex-direction: column; min-height: calc(100dvh - var(--nav-h)); }
.chat__title { display: flex; align-items: center; gap: 10px; flex: 1; text-align: left; min-width: 0; }
.chat__tools { display: flex; gap: 10px; padding: 0 var(--pad) 10px; }
.chat__tools .btn { flex: 1; font-size: var(--t-sm); padding: 10px 12px; }

.thread { flex: 1; display: flex; flex-direction: column; gap: 12px; padding: 8px var(--pad) 16px; overflow-y: auto; }

.msg { display: flex; gap: 10px; align-items: flex-end; }
.msg--mine { flex-direction: row-reverse; }
.msg__body { max-width: 78%; }
.msg__who { display: block; font-size: var(--t-xs); color: var(--ink-faint); margin: 0 0 3px 12px; }
.msg__text {
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  padding: 11px 15px;
  border-radius: 20px 20px 20px 6px;
  font-size: var(--t-sm);
  line-height: 1.45;
  box-shadow: var(--lift-1);
}
.msg--mine .msg__text {
  background: var(--brand-ink);
  color: var(--on-brand);
  border-radius: 20px 20px 6px 20px;
}

/* a system event is a hairline with the words sitting on it */
.sysline { display: flex; align-items: center; gap: 10px; color: var(--ink-faint); font-size: var(--t-xs); }
.sysline::before, .sysline::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }

.composer {
  position: sticky; bottom: calc(var(--nav-h) + var(--safe-b) + 14px);
  display: flex; align-items: center; gap: 8px;
  margin: 0 var(--pad);
  padding: 6px 6px 6px 16px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  box-shadow: var(--lift-2);
}
.composer__input { flex: 1; border: none; outline: none; background: none; min-height: var(--tap); font-size: var(--t-md); }
.composer__send {
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--brand-ink);
  color: var(--on-brand);
  flex-shrink: 0;
}

/* ---------------- forms ---------------- */
.field {
  width: 100%;
  min-height: var(--tap);
  padding: 12px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: var(--t-md);
}
.planbox {
  width: 100%;
  padding: 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--surface);
  font-size: var(--t-md);
  font-family: inherit;
  resize: vertical;
  min-height: 130px;
}

.chiplist { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.chiplist li {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 8px 7px 14px;
  border-radius: var(--r-pill);
  background: var(--cream);
  font-size: var(--t-sm);
}
.chiplist__x { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; color: var(--brand-text); }

.tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; padding: 14px var(--pad); }
.tile {
  display: flex; align-items: center; gap: 10px;
  min-height: 62px;
  padding: 14px;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--hairline);
  box-shadow: var(--lift-1);
  font-weight: 600;
  font-size: var(--t-sm);
  text-align: left;
}
.tile__ic { color: var(--brand); display: grid; place-items: center; }
.tile--primary { background: var(--brand-ink); color: var(--on-brand); border-color: var(--brand-ink); justify-content: center; font-size: var(--t-md); }

.checklist { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.checkline { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; min-height: var(--tap); padding: 8px 2px; font-size: var(--t-sm); }
.checkline__box {
  width: 24px; height: 24px; flex-shrink: 0;
  border: 1.5px solid var(--ink-faint);
  border-radius: 50%;
  display: grid; place-items: center;
  color: transparent;
}
.checkline[aria-pressed="true"] .checkline__box { background: var(--brand-ink); border-color: var(--brand-ink); color: var(--on-brand); }
.checkline[aria-pressed="true"] { color: var(--ink-faint); text-decoration: line-through; text-decoration-color: var(--brand); }

/* ---------------- availability grid ---------------- */
.verdict {
  margin: 0 var(--pad) 10px;
  padding: 10px 14px;
  border-radius: var(--r-md);
  background: var(--cream);
  font-size: var(--t-sm);
  font-weight: 600;
}

.grid {
  display: grid;
  grid-template-columns: 42px repeat(var(--cols), minmax(0, 1fr));
  gap: 3px;
  padding: 0 var(--pad);
}
.grid__corner { }
.grid__day {
  font-size: 10px;
  font-weight: 700;
  min-height: 34px;
  padding: 6px 0;
  border-radius: var(--r-sm);
  background: var(--cream);
  color: var(--ink-soft);
  line-height: 1.1;
}
.grid__hour { font-size: 10px; color: var(--ink-faint); display: flex; align-items: center; justify-content: flex-end; padding-right: 6px; }
/* A 7x15 grid cannot give every cell a 44px target without becoming
   660px tall and unusable. 30px is the honest compromise; the real
   accessible path is the keyboard (arrows + space) and the
   whole-column buttons, both of which exist. Documented in
   docs/ACCESSIBILITY.md rather than pretended away. */
.grid__cell {
  height: 30px;
  border-radius: 5px;
  background: var(--cream);
  border: 1px solid transparent;
  transition: background var(--fast), border-color var(--fast);
}
/* the heat bands. Colour is a bonus — every cell's accessible name
   states the count, so nothing here is colour-only. */
.grid__cell[data-heat="1"] { background: #FBE3D3; }
.grid__cell[data-heat="2"] { background: #F6C6A8; }
.grid__cell[data-heat="3"] { background: #EE9F71; }
.grid__cell[data-heat="4"] { background: #DE7440; }
.grid__cell[data-mine="1"] { border-color: var(--forest); box-shadow: inset 0 0 0 2px var(--forest); }

.legend { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 12px var(--pad) 0; }
.legend__sw { width: 16px; height: 16px; border-radius: 4px; background: var(--cream); }
.legend__sw--2 { background: #F6C6A8; }
.legend__sw--4 { background: #DE7440; }
.legend__sw--me { background: var(--cream); box-shadow: inset 0 0 0 2px var(--forest); }

/* ---------------- profile ---------------- */
.statgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding: 6px var(--pad); }
.stat {
  padding: 14px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
}
.stat__v { display: block; font-size: 1.6rem; font-weight: 700; letter-spacing: -.03em; }
.stat__l { display: block; font-size: var(--t-xs); color: var(--ink-soft); margin-top: 2px; }

/* the bookshelf — spine + top pages + fore-edge, per book */
.shelfwrap { padding: 6px var(--pad) 26px; perspective: 900px; }
.shelf {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 7px;
  transform-style: preserve-3d;
  min-height: 200px;
}
.book {
  position: relative;
  width: var(--bw);
  height: var(--bh);
  transform-style: preserve-3d;
  transform: rotateZ(var(--lean));
  transform-origin: bottom center;
  transition: transform var(--med) var(--ease-spring);
}
.book:hover, .book:focus-visible { transform: rotateZ(var(--lean)) translateY(-14px); }
.book__spine {
  position: absolute; inset: 0;
  background: var(--hue);
  border-radius: 3px 3px 2px 2px;
  box-shadow: inset -6px 0 12px rgba(0,0,0,.25), inset 3px 0 6px rgba(255,255,255,.18), var(--lift-2);
  display: grid; place-items: center;
  overflow: hidden;
}
/* White is correct HERE, but only because the spine colours were
   pinned to fixed hexes that carry it — see the note on BOOKS in
   js/screens/profile.js. When they were theme tokens, white on the
   amber spine measured 1.82:1 in dark mode. Change a spine colour and
   you must re-check this. */
.book__title {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  transform: rotate(180deg);
  color: #fff;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: .06em;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
  white-space: nowrap;
}
.book__pages {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: 5px;
  background: repeating-linear-gradient(180deg, #FBF6EF 0 2px, #E6DCCF 2px 3px);
  transform: rotateY(72deg);
  transform-origin: right center;
}
.book__edge {
  position: absolute; left: 3px; right: 3px; top: 0; height: 5px;
  background: linear-gradient(90deg, #F7F1E8, #E3D8C9);
  transform: rotateX(70deg);
  transform-origin: top center;
}
.shelf__plank {
  height: 12px;
  margin-top: -2px;
  border-radius: 3px;
  background: linear-gradient(180deg, #C99A6B, #8E6845);
  box-shadow: 0 10px 20px rgba(0,0,0,.18);
}

.switch { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%; min-height: var(--tap); text-align: left; }
.switch__label { display: flex; flex-direction: column; gap: 2px; font-size: var(--t-sm); font-weight: 600; }
.switch__track {
  width: 52px; height: 30px; flex-shrink: 0;
  border-radius: var(--r-pill);
  background: var(--cream);
  border: 1px solid var(--hairline);
  position: relative;
  transition: background var(--fast);
}
.switch__knob {
  position: absolute; top: 3px; left: 3px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--lift-1);
  transition: transform var(--fast) var(--ease-out);
}
.switch[aria-checked="true"] .switch__track { background: var(--brand-ink); border-color: var(--brand-ink); }
.switch[aria-checked="true"] .switch__knob { transform: translateX(22px); }

/* ---------------- photos + the lens ---------------- */
.lenswrap { position: relative; margin: 0 var(--pad); --lx: 50%; --ly: 44%; }
.pgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.pgrid__cell { aspect-ratio: 1; border-radius: var(--r-sm); overflow: hidden; }
.pgrid__cell img { width: 100%; height: 100%; object-fit: cover; }

/* one veil over the whole grid with a hole punched at the lens.
   Scales to any number of photos, unlike the two-copies trick. */
.lens__veil {
  position: absolute; inset: 0;
  pointer-events: none;
  border-radius: var(--r-sm);
  backdrop-filter: blur(7px) saturate(.4) brightness(.86);
  -webkit-backdrop-filter: blur(7px) saturate(.4) brightness(.86);
  -webkit-mask-image: radial-gradient(circle 34vw at var(--lx) var(--ly), transparent 0%, transparent 42%, black 100%);
  mask-image: radial-gradient(circle 34vw at var(--lx) var(--ly), transparent 0%, transparent 42%, black 100%);
}

/* the rim is what makes it grabbable. Without it the lens reads as a
   rendering artefact and nobody tries to move it. */
.lens__rim {
  position: absolute;
  width: 30vw; height: 30vw;
  max-width: 190px; max-height: 190px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,.9);
  box-shadow: 0 6px 20px rgba(0,0,0,.28), inset 0 0 0 1px rgba(0,0,0,.06);
  touch-action: none;            /* the lens drags; the page still scrolls */
  cursor: grab;
  transition: transform var(--fast) var(--ease-out), box-shadow var(--fast);
}
.lens__rim[data-held="1"] { cursor: grabbing; box-shadow: 0 10px 28px rgba(0,0,0,.34); }

/* ---------------- badges ---------------- */
.badgegrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding: 6px var(--pad); }
.badge {
  padding: 14px;
  border-radius: var(--r-lg);
  background: var(--cream);
  display: flex; flex-direction: column; gap: 4px;
  opacity: .55;
}
.badge--on { opacity: 1; background: linear-gradient(160deg, var(--peach-0), var(--peach-1)); box-shadow: var(--lift-1); }
.badge__ic { color: var(--brand); }
.badge__l { font-weight: 700; font-size: var(--t-sm); }

/* ---------------- trail ---------------- */
.trailstats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; padding: 6px var(--pad); }
.trailstat { text-align: center; padding: 12px 6px; border-radius: var(--r-lg); background: linear-gradient(160deg, var(--peach-0), var(--peach-1)); box-shadow: var(--lift-1); }
.big { display: block; font-size: 1.5rem; font-weight: 700; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.route { width: 100%; height: auto; border-radius: var(--r-lg); box-shadow: var(--lift-1); }

/* The live recorder stacks two canvases: the route is append-only and
   never cleared, the moving dot is cleared every fix. Same box, same
   size, so they share one coordinate system. */
/* ---------------- the planner: Calendar / Location / Gear ----------------
   The updated Figma gives all three the same header — back, the group's
   faces, the screen name — and the same four tiles underneath, which is
   what turns three dead ends into one place with three views. */
.planhead {
  display: grid;
  grid-template-columns: 44px 1fr 44px;
  align-items: center;
  justify-items: center;
  gap: 8px;
  padding: 6px var(--pad) 4px;
}
.planhead > .iconbtn { justify-self: start; }
.planhead__who { grid-column: 2; }
.planhead__t {
  grid-column: 1 / -1;
  font-size: 1.9rem;
  text-align: center;
  margin-top: 2px;
}

.tile.is-current { border-color: var(--brand); background: var(--cream); }

/* ---- gear: numbered cards, name + note, a ring to tick ---- */
.gearlist { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.gearitem {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 14px 16px; text-align: left;
  border-radius: var(--r-lg);
  background: var(--peach-0);
  border: 1px solid var(--hairline);
}
.gearitem__n {
  flex: 0 0 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--brand-ink); color: var(--on-brand);
  font-size: var(--t-xs); font-weight: 800;
}
.gearitem__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.gearitem__name { font-weight: 700; font-size: var(--t-sm); }
.gearitem__note { font-size: var(--t-xs); color: var(--ink-soft); }
.gearitem__ring {
  flex: 0 0 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--ink-faint);
}
/* packed reads as filled, and not by colour alone — the ring gains a
   solid core and the whole card lifts to the brand fill */
.gearitem[aria-pressed="true"] { background: var(--brand-ink); border-color: transparent; }
.gearitem[aria-pressed="true"] .gearitem__name,
.gearitem[aria-pressed="true"] .gearitem__note { color: var(--on-brand); }
.gearitem[aria-pressed="true"] .gearitem__n { background: var(--on-brand); color: var(--brand-ink); }
.gearitem[aria-pressed="true"] .gearitem__ring {
  border-color: var(--on-brand);
  box-shadow: inset 0 0 0 4px var(--on-brand);
}

.gearadd { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.gearadd .btn { align-self: flex-start; }

/* ---- location: the map area, saying what it is waiting for ---- */
.mapwell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; text-align: center;
  min-height: 190px; padding: 24px 22px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  border: 1px dashed rgba(210,85,42,.38);
}
.mapwell__ic { color: var(--brand-text); }
.mapwell__where { font-weight: 700; font-size: var(--t-md); }
.mapwell__note { font-size: var(--t-xs); color: var(--ink-soft); max-width: 34ch; line-height: 1.5; }

/* ---------------- choosing a group photo ----------------
   The preview is the size the card will use, so what you see when you
   pick is what people swipe on. */
.photopick { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.photopick__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-lg);
  background: var(--peach-0);
  box-shadow: var(--lift-1);
}
/* a <label for> triggering the input keeps the keyboard path a plain
   file input already has; styling it as a button is the only trick */
.photopick__btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap);
  padding: 12px 18px;
  border-radius: var(--r-pill);
  background: var(--brand-ink); color: var(--on-brand);
  font-weight: 700; font-size: var(--t-sm);
  cursor: pointer;
}
.photopick__btn:hover { filter: brightness(1.06); }
/* the input is .sr-only, so its focus ring has to be borrowed by the
   label or a keyboard user cannot see where they are */
.photopick input:focus-visible + .photopick__note,
.photopick__btn:focus-within { outline: 2px solid var(--brand); outline-offset: 2px; }
.photopick input:focus-visible ~ .photopick__btn,
.photopick input:focus-visible + * { outline: none; }
#group-photo:focus-visible ~ .photopick__btn,
.photopick:focus-within .photopick__btn { outline: 2px solid var(--brand); outline-offset: 2px; }
.photopick__note { color: var(--ink-soft); line-height: 1.5; }

/* ---------------- who runs a hike ----------------
   The leader is hikes.host_id; co-leader is a flag on the membership
   row. The chip is the only place either is visible, so it has to read
   at a glance without relying on colour alone — hence the words. */
/* ⚠️ A GRID, not a flex row. As flex, `.row` took whatever space the
   button left over — so the card's width depended on whether its label
   said "Step down" or "Make co-leader", and the leader's row, having no
   button at all, ran full width. Three cards, three different widths,
   for a reason that has nothing to do with the people on them.
   Reserving the action column makes every card line up, including the
   rows that have nothing to put in it. */
.personrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 8.75rem;
  align-items: center;
  gap: 8px;
}
.personrow .row { min-width: 0; }

.rolechip {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}
/* --brand-ink carries --on-brand at 4.79:1 light and 4.88:1 dark */
.rolechip--leader { background: var(--brand-ink); color: var(--on-brand); }
/* the co-leader chip is quieter: a real distinction, not a competing one */
.rolechip--coleader { background: var(--cream); color: var(--brand-text); border: 1px solid var(--hairline); }

.rolebtn {
  font-size: var(--t-xs);
  padding: 8px 12px;
  min-height: var(--tap);
  width: 100%;
}
@media (max-width: 420px) {
  /* Reserving 8.75rem here would leave the name about 195px and clip
     it, so the button drops to its own line.

     ⚠️ But a button on its own line ends up nearer the NEXT person's
     card than its own, and "Step down" floating above aaron.abbott
     reads as aaron's button. So at this width the CARD becomes the
     whole row: the person and their control share one surface, and
     ownership is unambiguous. `.row` inside gives up its background
     rather than nesting a card in a card. */
  .personrow {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0;
    padding: 12px 14px;
    border-radius: var(--r-lg);
    background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
    box-shadow: var(--lift-1);
  }
  .personrow .row {
    padding: 0;
    background: none;
    box-shadow: none;
    border-radius: 0;
  }
  .rolebtn { width: auto; justify-self: end; margin-top: 10px; }
}

/* ---------------- account ---------------- */

/* the guest prompt on the profile — a row, not a banner. It is an
   offer, not an interruption, and guest mode is the front door here */
.guestbar {
  display: flex; align-items: center; gap: 12px; width: calc(100% - var(--pad) * 2);
  margin: 4px var(--pad) 10px; padding: 12px 14px; text-align: left;
  border-radius: var(--r-lg); background: var(--cream);
  border: 1px solid rgba(210,85,42,.32); cursor: pointer;
}
.guestbar:hover { border-color: var(--brand); }
.guestbar__ic { display: grid; place-items: center; color: var(--brand-text); flex: 0 0 auto; }
.guestbar__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.guestbar__t { font-weight: 700; font-size: var(--t-sm); }
.guestbar__s { font-size: var(--t-xs); color: var(--ink-soft); }
.guestbar__go { color: var(--ink-soft); flex: 0 0 auto; }

.acct__badge {
  display: flex; align-items: center; gap: 12px; padding: 14px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
}
.acct__ic { display: grid; place-items: center; color: var(--forest); flex: 0 0 auto; }
.acct__badge--guest .acct__ic { color: var(--brand-text); }

/* An error has to be findable without colour — it sits directly under
   the field it belongs to, carries role="alert", and moves focus. */
.acct__err:empty { display: none; }
.acct__err {
  display: block; margin: 0 0 12px; padding: 10px 12px;
  border-radius: 12px; font-size: var(--t-sm);
  color: var(--brand-text); background: rgba(210,85,42,.10);
  border: 1px solid rgba(210,85,42,.30);
}

.acct__warn {
  display: flex; gap: 10px; margin: 0 0 14px; padding: 12px;
  border-radius: 12px; font-size: var(--t-xs); line-height: 1.5;
  background: rgba(240,163,42,.14); border: 1px solid rgba(240,163,42,.42);
}
.acct__warn .acct__ic { color: var(--amber); align-items: flex-start; padding-top: 1px; }

.routewrap { position: relative; }
.routewrap .route--dot { position: absolute; inset: 0; box-shadow: none; pointer-events: none; }

/* the little route shape on each logged-walk row */
.routethumb {
  width: 60px; height: 60px; flex: 0 0 60px;
  border-radius: 14px; box-shadow: var(--lift-1);
}

/* what the GPS is doing right now — visible so that discarded fixes
   read as the recorder working, not as the app being stuck */
.sig { display: flex; align-items: center; gap: 8px; padding: 2px var(--pad) 8px; }
.sig__text { font-size: var(--t-xs); color: var(--ink-soft); }
.sig__dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; background: var(--ink-soft); }
.sig__dot[data-state="on"]   { background: var(--forest); }
.sig__dot[data-state="weak"] { background: var(--amber); }
.sig__dot[data-state="bad"]  { background: var(--brand); }
.sig__dot[data-state="wait"] { background: var(--amber); animation: sigpulse 1.4s ease-in-out infinite; }

/* the one looping animation in the app, and it earns it: it means
   "still waiting for a fix", which is a state that genuinely persists.
   It stops the moment a fix lands. */
@keyframes sigpulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .sig__dot[data-state="wait"] { animation: none; } }

/* offering back an interrupted recording */
.resume {
  margin: 0 var(--pad) 10px; padding: 14px 16px;
  border-radius: var(--r-lg); background: var(--cream);
  border: 1px solid rgba(210,85,42,.35);
}
.resume__t { font-size: var(--t-sm); margin-bottom: 10px; }
.resume__row { display: flex; gap: 10px; flex-wrap: wrap; }

/* a finished walk: four numbers, two up on a phone */
.walkstats {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px; padding: 6px var(--pad);
}
.walkstat {
  text-align: center; padding: 14px 6px; border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
}
.walkstat .big { font-size: 1.35rem; }
@media (min-width: 720px) {
  .walkstats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ---------------- onboarding ---------------- */
.onb { display: flex; flex-direction: column; min-height: calc(100dvh - var(--nav-h)); }
.onb__body { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: 20px var(--pad); gap: 14px; }
.onb__mark { display: grid; place-items: center; margin-bottom: 10px; }
.onb__q { font-size: var(--t-2xl); font-weight: 700; letter-spacing: -.02em; text-align: center; margin-bottom: 6px; }
.onb__foot { padding: 16px var(--pad) 28px; }
.onb .row[aria-pressed="true"] { outline: 2px solid var(--brand-ink); outline-offset: 2px; }

/* ============================================================
   DESKTOP
   The design is phone-first because the Figma is, but this ships as a
   web app — so on a laptop it becomes a real two-column layout rather
   than a phone-shaped column marooned in the middle of a 27" screen.

   The bottom nav becomes a left rail with visible labels. Nothing in
   any screen file changes; the nav is the same markup, and the
   sr-only labels simply stop being sr-only.
   ============================================================ */
@media (min-width: 900px) {
  body { background: var(--cream); }

  #app {
    max-width: 1120px;
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    /* ⚠️ Without this the two implicit rows are both `auto`, so the
       min-height surplus below is SHARED between the app bar and the
       content — and the emptier the screen, the taller the bar. The
       account screen measured a 398px app bar at 1280 and 498px at
       2000, against a natural 61px. Worst on the newest, sparsest
       screens and on a wide monitor. `auto 1fr` gives the slack to the
       content, where it belongs. */
    grid-template-rows: auto 1fr;
    background: var(--bg);
    box-shadow: var(--lift-3);
    min-height: 100dvh;
  }

  .appbar { grid-column: 2; top: 0; }

  #screen {
    grid-column: 2;
    padding: 0 12px 56px;
    max-width: 780px;
  }

  /* the nav: bottom pill -> left rail */
  .nav {
    position: sticky;
    top: 0;
    left: auto;
    bottom: auto;
    transform: none;
    grid-row: 1 / span 2;
    grid-column: 1;
    width: auto;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: 4px;
    padding: 24px 14px;
    border-radius: 0;
    border-right: 1px solid var(--hairline);
    background: linear-gradient(180deg, var(--peach-0), var(--peach-1));
    box-shadow: none;
  }
  /* in the rail it stops floating and becomes an ordinary item, first
     in the column — where a desktop app puts "new" */
  .fab {
    position: static;
    order: -1;
    width: 100%;
    justify-content: flex-start;
    padding: 12px 16px;
    margin-bottom: 10px;
    border-radius: var(--r-md);
    box-shadow: none;
  }
  .fab__t { position: static; width: auto; height: auto; clip-path: none; }

  .nav__item {
    height: auto;
    min-height: var(--tap);
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 14px;
    padding: 12px 16px;
    border-radius: var(--r-md);
    font-weight: 600;
    font-size: var(--t-sm);
  }
  .nav__item:hover { background: rgba(210,85,42,.10); }
  .nav__item[aria-current="page"] { background: var(--brand-ink); color: var(--on-brand); }
  .nav__item[aria-current="page"] svg { fill: none; }

  /* the labels were only ever hidden because a 5-icon phone bar has no
     room for them. There is room here. */
  .nav__item .sr-only {
    position: static;
    width: auto; height: auto;
    margin: 0; padding: 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: nowrap;
  }

  /* let the extra width actually do something */
  .statgrid  { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .badgegrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .tiles     { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .pgrid     { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .stack     { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .fadestack { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* a full-width swipe card on a wide screen looks absurd */
  .deck { max-width: 420px; margin-inline: auto; height: min(70vh, 540px); }
  .deck__actions { max-width: 420px; margin-inline: auto; }
  .ausmap { max-width: 560px; margin-inline: auto; }

  .chat { min-height: calc(100dvh - 40px); }
  .composer { bottom: 20px; }
  .grid__cell { height: 30px; }
}

/* Between phone and laptop: keep one column, just let it breathe. */
@media (min-width: 620px) and (max-width: 899px) {
  #app { max-width: 560px; box-shadow: var(--lift-2); }
  .statgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .deck { max-width: 400px; margin-inline: auto; }
  .deck__actions { max-width: 400px; margin-inline: auto; }
}

/* ---------------- levels and XP ----------------
   Gamification is explicitly SECONDARY to the social features in the
   brief, so it lives here on the profile — visible, not in the way of
   finding a hike. */
.levelcard {
  margin: 6px var(--pad) 0;
  padding: 16px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
}
.levelcard__top { display: flex; align-items: center; gap: 12px; }
.levelcard__num {
  width: 44px; height: 44px; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--brand-ink);
  color: var(--on-brand);
  font-weight: 800;
  font-size: 1.1rem;
}
.levelcard__top > span:nth-child(2) { flex: 1; min-width: 0; }
.levelcard__name  { display: block; font-weight: 700; font-size: var(--t-md); }
.levelcard__blurb { display: block; font-size: var(--t-xs); color: var(--ink-soft); }
.levelcard__xp {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--t-sm);
  color: var(--brand-text);
  font-variant-numeric: tabular-nums;
}

.xpbar {
  height: 10px;
  margin-top: 12px;
  border-radius: var(--r-pill);
  background: rgba(23,23,23,.10);
  overflow: hidden;
}
.xpbar__fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--amber), var(--brand-ink));
  /* one-shot: it grows on arrival and then holds */
  animation: xpgrow var(--med) var(--ease-out) both;
}
@keyframes xpgrow { from { transform: scaleX(0); transform-origin: left; } to { transform: none; } }

.xpwhy { margin: 10px var(--pad) 0; font-size: var(--t-sm); }
.xpwhy summary {
  cursor: pointer;
  color: var(--brand-text);
  font-weight: 600;
  min-height: var(--tap);
  display: flex;
  align-items: center;
}
.xpwhy ul { list-style: none; margin-top: 4px; }
.xpwhy li {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--hairline);
}
.xpwhy li:last-child { border-bottom: none; }
.xpwhy b { color: var(--forest); font-variant-numeric: tabular-nums; }

/* ---------------- collection + missions ---------------- */
.ausmap__pin.is-visited {
  background: var(--forest);
  box-shadow: var(--lift-1), 0 0 0 3px color-mix(in srgb, var(--forest) 22%, transparent);
}
.ausmap__tick { display: inline-grid; place-items: center; margin-right: 4px; position: relative; z-index: 1; }
/* a state you haven't walked reads as an outline waiting to be claimed */
.ausmap__pin:not(.is-visited) {
  background: color-mix(in srgb, var(--brand-ink) 82%, transparent);
}

.collect {
  text-align: center;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  padding: 8px var(--pad) 0;
}
.collect b { color: var(--forest); font-weight: 800; }

.missions {
  margin: 16px var(--pad) 0;
  padding: 14px 16px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
}
.missions__head { display: flex; align-items: center; justify-content: space-between; }
.missions__count {
  font-size: var(--t-xs);
  font-weight: 800;
  color: var(--brand-text);
  background: var(--surface);
  border-radius: var(--r-pill);
  padding: 3px 10px;
}
.missions ul { list-style: none; margin-top: 10px; display: flex; flex-direction: column; gap: 10px; }

.mission { display: flex; align-items: center; gap: 12px; }
.mission__ic {
  width: 32px; height: 32px; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--surface);
  color: var(--brand-ink);
  box-shadow: var(--lift-1);
}
.mission__body { flex: 1; min-width: 0; }
.mission__name { display: block; font-size: var(--t-sm); font-weight: 600; }
.mission__bar {
  display: block;
  height: 5px;
  margin-top: 5px;
  border-radius: var(--r-pill);
  background: rgba(23,23,23,.10);
  overflow: hidden;
}
.mission__bar > span {
  display: block; height: 100%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--amber), var(--brand-ink));
}
.mission__xp { font-size: var(--t-xs); font-weight: 800; color: var(--brand-text); flex-shrink: 0; }
.mission.is-done .mission__ic { background: var(--forest); color: #fff; }
.mission.is-done .mission__name { color: var(--ink-faint); text-decoration: line-through; text-decoration-color: var(--forest); }
.mission.is-done .mission__xp { color: var(--forest); }

/* ---------------- celebration effects ---------------- */
.fx-xp {
  position: fixed;
  z-index: 300;
  pointer-events: none;
  transform: translate(-50%, -50%);
  padding: 6px 12px;
  border-radius: var(--r-pill);
  background: var(--forest);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 800;
  box-shadow: var(--lift-2);
  animation: fxfly 1.2s var(--ease-out) forwards;
}
@keyframes fxfly {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.7); }
  18%  { opacity: 1; transform: translate(-50%, -50%) scale(1.06); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(.55); }
}

.chipstat--level.is-bumped { animation: fxbump .45s var(--ease-spring); }
@keyframes fxbump { 0%,100% { transform: none; } 40% { transform: scale(1.14); } }

/* ---------------- the moment ----------------
   One component for every celebration: joining, badges, levels,
   a saved walk, a streak. Before this there were three different
   grammars — a big centred card, a bottom pill and a grey toast —
   and the card and the pill could occupy the same 30 pixels at the
   same time.

   The stack is what fixes the overlap. Panels queue upward from
   above the nav instead of landing on each other, and because it is
   a real grid the browser does the spacing rather than a pile of
   setTimeouts guessing at it.

   ⚠️ pointer-events lives on the CHILDREN, not the container. A
   full-width fixed overlay with pointer-events:auto would eat every
   click in the bottom third of the screen for as long as a panel is
   up — including the nav directly underneath it. */
.fx-stack {
  position: fixed;
  left: 50%;
  bottom: calc(var(--nav-h) + var(--safe-b) + 30px);
  transform: translateX(-50%);
  z-index: 300;
  display: grid;
  gap: 10px;
  width: min(400px, calc(100vw - 32px));
  pointer-events: none;
}

.fx-moment {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 12px 12px 14px;
  border-radius: var(--r-lg);
  background: var(--ink);
  color: var(--bg);
  box-shadow: var(--lift-3);
  animation: fxmoment var(--life, 3200ms) var(--ease-out) forwards;
}

/* Reduced motion keeps the panel and drops the movement. Explicit
   `animation: none` rather than relying on the global 0.01ms rule in
   base.css — that rule makes an animation FINISH instantly, and this
   one finishes at opacity 0, so the panel would flash and vanish for
   exactly the people who need longest to read it. */
.fx-moment.is-still { animation: none; opacity: 1; transform: none; }

@keyframes fxmoment {
  0%   { opacity: 0; transform: translateY(14px) scale(.98); }
  6%   { opacity: 1; transform: none; }
  92%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-6px); }
}

.fx-moment__body { flex: 1; min-width: 0; }
.fx-moment__k {
  display: block;
  font-size: var(--t-xs);
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: .72;
}
.fx-moment__h {
  display: block;
  font-weight: 800;
  font-size: var(--t-md);
  /* one line, because a five-word hike title must not push the
     dismiss button off a 375px panel */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fx-moment__s {
  display: block;
  font-size: var(--t-xs);
  opacity: .72;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fx-moment__art {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .14);
  flex-shrink: 0;
}
.fx-moment__glyph { display: grid; place-items: center; }
.fx-moment__lv { font-weight: 900; font-size: 1.05rem; }
.fx-moment__ring {
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 3px solid #B87333;
}
.fx-moment__ring[data-tier="silver"] { border-color: #9AA3AB; }
.fx-moment__ring[data-tier="gold"]   { border-color: #D4A017; }

/* The dismiss control. 34px rather than the 44px tap minimum is a
   considered exception: it sits inside a panel that also dismisses
   itself on any click and after a few seconds on its own, so it is
   a convenience rather than the only way out — and at 44 it
   dominated a 60px-tall panel. */
.fx-moment__x {
  width: 34px; height: 34px;
  flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  color: inherit;
  opacity: .7;
}
.fx-moment__x:hover { opacity: 1; background: rgba(255, 255, 255, .14); }
.fx-moment__x:focus-visible { opacity: 1; background: rgba(255, 255, 255, .2); }

/* Tones. Only the fill changes — the shape, spacing and behaviour
   are identical, which is the whole point of collapsing three
   components into one. */
.fx-moment--level {
  background: linear-gradient(150deg, var(--brand-ink), var(--brand-text));
  color: #fff;
}
.fx-moment--level .fx-moment__h {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 900;
  font-size: 1.25rem;
}
.fx-moment--join   { background: linear-gradient(150deg, #2E6B2E, #1F4C20); color: #fff; }
.fx-moment--walk   { background: linear-gradient(150deg, #2F5D7C, #21435A); color: #fff; }
.fx-moment--streak { background: linear-gradient(150deg, #B5651F, #8A4714); color: #fff; }

/* Above 900px the nav is a left rail, so there is nothing at the
   bottom of the screen to clear. */
@media (min-width: 900px) {
  .fx-stack { bottom: 30px; }
}

/* ---------------- the badge collection ----------------
   Locked badges stay visible and show their progress — a collection
   you cannot see the shape of is not a collection, it is a surprise. */
.badge {
  position: relative;
  text-align: left;
  border: 1px solid transparent;
  transition: transform var(--fast) var(--ease-out), border-color var(--fast);
}
.badge:not([disabled]):active { transform: scale(.97); }
.badge[disabled] { cursor: default; }
.badge--bronze .badge__ic { color: #B87333; }
.badge--silver .badge__ic { color: #8A929A; }
.badge--gold   .badge__ic { color: #C9971A; }
.badge.is-picked { border-color: var(--brand-ink); box-shadow: var(--lift-2); }

.badge__bar {
  display: block;
  height: 4px;
  margin-top: 8px;
  border-radius: var(--r-pill);
  background: rgba(23,23,23,.10);
  overflow: hidden;
}
.badge__bar > span { display: block; height: 100%; background: var(--ink-faint); border-radius: var(--r-pill); }

.badge__pin {
  position: absolute;
  top: 8px; right: 8px;
  width: 20px; height: 20px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--brand-ink);
  color: var(--on-brand);
}

/* ---------------- someone else's profile ---------------- */
.personhead {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 4px var(--pad) 0;
  padding: 16px;
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--peach-0), var(--peach-1));
  box-shadow: var(--lift-1);
}
.personhead__body { display: flex; flex-direction: column; min-width: 0; }
.personhead__name { font-size: var(--t-lg); font-weight: 800; letter-spacing: -.01em; }
.personhead__lv { display: flex; align-items: center; gap: 8px; margin-top: 6px; }

/* the showcase, on your own profile */
.showcase { display: flex; gap: 8px; flex-wrap: wrap; padding: 10px var(--pad) 0; }
.showcase__b {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  border-radius: var(--r-pill);
  background: var(--cream);
  border: 1px solid var(--hairline);
  font-size: var(--t-xs);
  font-weight: 700;
}
.showcase__b[data-tier="bronze"] { color: #8A5623; }
.showcase__b[data-tier="silver"] { color: #6E767E; }
.showcase__b[data-tier="gold"]   { color: #9A7210; }

/* The region pins have to stay small — seven 44px pills would cover
   the continent they sit on. So the VISUAL stays 28px and the TARGET
   is grown with a transparent pseudo-element, which is the standard
   way to satisfy the 44px guidance without wrecking the layout. */
.ausmap__pin { position: absolute; }
.ausmap__pin::after {
  content: "";
  position: absolute;
  inset: -9px -6px;          /* 28 + 18 = 46px tall target */
  border-radius: var(--r-pill);
}

/* On a phone these two wrappers are plain blocks and change nothing.
   On a laptop they become the two columns that stop the right half of
   the window being empty. */
@media (min-width: 900px) {
  #screen { max-width: 880px; }
  .home__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px 20px;
    align-items: start;
  }
  .home__left .ausmap { max-width: none; margin-inline: 0; }
  .home__right .missions { margin: 0 var(--pad) 0 0; }
  .home__right .fadestack { grid-template-columns: minmax(0, 1fr); padding-right: var(--pad); padding-left: 0; }
  .home__right .stack { grid-template-columns: minmax(0, 1fr); padding-right: var(--pad); padding-left: 0; }
}

/* ---------------- what you missed ----------------
   The derived inbox (js/notify.js). Rows reuse `.row` wholesale —
   the same peach card as a conversation, because a notification IS
   a pointer at a conversation, and giving it its own look would have
   invented a second visual language for the same thing.

   The only additions are a kind icon, a timestamp, and one
   colour-coded left edge. ⚠️ That edge is never the only signal: the
   kind is also in the icon, in the wording of the title, and in the
   row's accessible name. Colour alone would break the same rule the
   difficulty labels already follow. */
.notif__ic {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--bg);
  color: var(--brand-text);
  flex-shrink: 0;
}
.notif__when { flex-shrink: 0; align-self: flex-start; padding-top: 2px; }

.notif { border-left: 4px solid var(--hairline); }
.notif--message   { border-left-color: var(--brand); }
.notif--join      { border-left-color: var(--forest); }
.notif--confirmed { border-left-color: var(--amber); }
.notif--join .notif__ic { color: var(--forest); }

/* The subtitle carries a whole message body, so it needs room a
   conversation row does not. Two lines, then ellipsis — at one line
   every row read "someone: we just…" and told you nothing. */
.notif .row__sub {
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-clamp: 2;
  overflow: hidden;
}
