/* ============================================================
   Grill Task - app.css
   Styles for the WEB APP (app.html). Ports the phone app's
   design system (src/theme) so both look like one product.

   Layout adapts to the device:
   • Phones (< 768px): app-style column with a bottom tab bar.
   • Laptops/desktops (≥ 768px): full-width layout with a left
     sidebar, larger type, and multi-column card grids.
   Light and dark themes via CSS variables + [data-theme].
   ============================================================ */

:root {
  color-scheme: light dark;

  /* Warm, grill-inspired brand palette (same as the app). */
  --brand: #e8552d;
  --brand-dark: #c33f1c;
  --brand-light: #ff7a4d;
  --ember: #ffb020;

  --pending: #8a8f98;
  --awaiting: #f5a623;
  --approved: #2f9e44;
  --danger: #e03131;

  --bg: #fff8f4;
  --surface: #ffffff;
  --surface-alt: #fdefe8;
  --border: #f0e2da;

  --text: #221812;
  --text-secondary: #5d5249;
  --text-muted: #8d7f74;
  --on-brand: #ffffff;

  --skeleton: #efe4dc;

  /* Amber "review / test" banners */
  --banner-bg: #fff8ec;
  --banner-border: #fbe3b8;
  --banner-text: #c07f0a;

  --badge-pending-bg: #f1f2f4;
  --badge-awaiting-bg: #fdf3df;
  --badge-approved-bg: #e5f4e8;

  /* SQUARER THAN IT WAS, ON PURPOSE.
     18px on a card and 26px on a dialog reads as a toy - rounded enough that
     nothing lines up with anything and every edge draws attention to itself.
     These are the radii of a tool: soft enough not to be harsh, small enough
     that a column of rows reads as a column. The palette is untouched. */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;

  /* How much of the screen the on-screen keyboard is covering. Kept at 0
     here so every calc() using it still works on desktop, where nothing sets
     it; js/app.js updates it live on phones. */
  --kb: 0px;

  /* Shadow is for things that genuinely float - a dialog, a sheet, the
     floating button. A list row is not floating, so it gets a border and a
     surface colour and nothing else. Everything soft was doing was making
     the page look padded. */
  --shadow-card: 0 1px 2px rgba(84, 33, 15, 0.10), 0 4px 12px rgba(84, 33, 15, 0.06);
  --shadow-soft: none;
  --shadow-floating: 0 6px 18px rgba(195, 63, 28, 0.28);
  /* One-pixel line used to separate rows inside a block. */
  --hairline: rgba(120, 80, 60, 0.14);

  --sidebar-w: 240px;
}

/* Larger text option (Profile → Appearance) - scales every rem-based size */
:root[data-textsize="large"] { font-size: 18px; }

/* ---------- Dark theme ---------- */
:root[data-theme="dark"] {
  --bg: #171009;
  --surface: #271c17;
  --surface-alt: #362621;
  --border: #47352b;

  --text: #faf3ee;
  --text-secondary: #dccfc4;
  --text-muted: #ab9a8e;

  --skeleton: #32231d;

  --banner-bg: #33270f;
  --banner-border: #57431c;
  --banner-text: #f0b74a;

  --badge-pending-bg: #33302c;
  --badge-awaiting-bg: #3a2d10;
  --badge-approved-bg: #17301c;

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.35);
  --shadow-soft: none;
  --shadow-floating: 0 6px 18px rgba(0, 0, 0, 0.5);
  --hairline: rgba(255, 240, 230, 0.10);
}

/* Smooth light↔dark switch - the .theme-anim class is added for ~450ms by
   applyTheme() while the theme flips, then removed so hovers stay snappy. */
:root.theme-anim, .theme-anim body, .theme-anim *, .theme-anim *::before, .theme-anim *::after {
  transition: background-color 0.4s ease, background 0.4s ease, color 0.4s ease,
    border-color 0.4s ease, box-shadow 0.4s ease !important;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { height: 100%; }

body.app-body {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: var(--text);
  line-height: 1.15;
}

button { font-family: inherit; color: inherit; }
a { color: inherit; }

/* ---------- App frame ---------- */
#app {
  max-width: 560px;
  margin: 0 auto;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  position: relative;
}

.view {
  flex: 1;
  width: 100%;
  padding: 16px 16px 104px; /* room for the bottom tab bar */
}

.view.no-tabs { padding-bottom: 32px; }

/* Narrow column for forms/detail screens so they don't stretch on desktop -
   centered so nothing hugs the left edge on wide screens. */
.narrow { max-width: 640px; margin-left: auto; margin-right: auto; }

/* ---------- Header ---------- */
.screen-header { margin: 8px 0 18px; }
.screen-header h1 { font-size: clamp(1.9rem, 3.5vw, 2.3rem); font-weight: 800; letter-spacing: -0.01em; }
.screen-header .subtitle { color: var(--text-secondary); margin-top: 4px; font-size: 0.95rem; }
/* The dot between subtitle segments. Each segment is its own span so it can
   be translated on its own - the separator must never be part of one. */
.screen-header .subtitle .sub-sep { color: var(--text-muted); }
.header-row { display: flex; align-items: center; gap: 8px; }
.back-btn {
  background: none; border: 0; cursor: pointer;
  font-size: 1.7rem; line-height: 1; color: var(--brand);
  padding: 4px 10px 4px 0;
}

/* ---------- Cards ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
}

.card + .card { margin-top: 8px; }

/* ---------- Card grids (desktop turns lists into columns) ---------- */
.grid { display: grid; gap: 12px; grid-template-columns: 1fr; }
.grid .card, .grid .task-item { margin: 0; }

/* ---------- Buttons ---------- */
.btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: 11px 18px;
  font-size: 0.95rem; font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
.btn:active { transform: scale(0.985); }
/* Unavailable, not invisible. Fading to 55% on a dark card left buttons you
   could barely read, so a disabled button keeps its shape and flattens to a
   neutral fill instead. */
.btn:disabled {
  cursor: not-allowed;
  opacity: 1;
  background: var(--surface-alt);
  color: var(--text-muted);
  border-color: var(--border);
  box-shadow: none;
}
.btn:disabled:hover { background: var(--surface-alt); }
.btn:disabled:active { transform: none; }

.btn-primary { background: var(--brand); color: var(--on-brand); box-shadow: var(--shadow-soft); }
.btn-primary:hover:not(:disabled) { background: var(--brand-dark); }

.btn-secondary { background: var(--surface); color: var(--brand); border-color: var(--border); }
.btn-secondary:hover:not(:disabled) { border-color: var(--brand); }

.btn-ghost { background: transparent; color: var(--text-secondary); }
.btn-ghost:hover:not(:disabled) { color: var(--brand-dark); }

.btn-danger { background: var(--danger); color: #fff; }

.btn-row { display: flex; gap: 10px; }
.btn-row .btn { width: auto; flex: 0 0 auto; padding-left: 26px; padding-right: 26px; }

.btn .spinner { width: 16px; height: 16px; border-width: 2.5px; }

.icon-btn {
  background: none; border: 0; cursor: pointer;
  font-size: 1rem; color: var(--text-muted);
  padding: 8px; border-radius: 8px; line-height: 1;
}
.icon-btn:hover { color: var(--danger); background: var(--surface-alt); }

/* ---------- Inputs ---------- */
.field { margin-bottom: 14px; }
.field label {
  display: block;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em;
  color: var(--text-secondary);
  margin-bottom: 6px;
  text-transform: uppercase;
}
.field input, .field textarea, .search-input {
  width: 100%;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  padding: 13px 14px;
  font-size: 1rem;
  font-family: inherit;
  color: var(--text);
  outline: none;
  transition: border-color 0.15s ease;
}
.field input:focus, .field textarea:focus, .search-input:focus { border-color: var(--brand); }
.field textarea { min-height: 72px; resize: vertical; }
.search-input { display: block; margin: 0 auto 12px; padding: 11px 14px; }

/* ---------- Section labels & chips ---------- */
.section-label {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.07em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin: 16px 0 6px;
}
/* Anything that destroys something reads in the danger colour, everywhere,
   rather than each screen inventing its own inline style="color:...".
   `.danger-text` itself lives with the utilities further down. */
.section-label.danger-label { color: var(--danger); }
.icon-btn.danger:hover,
.icon-btn.danger:focus-visible { color: var(--danger); background: var(--surface-alt); }

/* A person's name in a row: bold, and allowed to wrap rather than push the
   row's buttons off the edge on a narrow phone. */
.person-name { font-weight: 600; overflow-wrap: anywhere; }

/* Pushes whatever follows to the far end of a flex row. */
.push-right { margin-left: auto; }

/* A small action living in a screen header next to the title. */
.header-action {
  width: auto; margin-left: auto;
  padding: 8px 14px; font-size: 0.85rem;
}

/* Dialogs whose content is a form read left-aligned; only the heading and
   its one-line explanation stay centred. */
.dialog-left { text-align: left; }
.dialog-left h2, .dialog-left > p.center { text-align: center; }
.dialog-wide { max-width: 420px; }

/* States it plainly, on the screen where it matters, that a group cannot be
   found by anyone who wasn't invited. */
.privacy-note {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  margin: 16px 0 4px;
  font-size: 0.85rem; line-height: 1.5;
  color: var(--text-secondary);
}

.chip-wrap { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 0.85rem; font-weight: 600;
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.chip.active { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }

/* ---------- Segmented control ---------- */
.segmented {
  display: flex;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 3px;
  margin: 6px auto 12px;
  max-width: 520px;
}
.segmented button {
  flex: 1;
  border: 0; background: transparent;
  border-radius: var(--radius-sm);
  padding: 7px 8px;
  font-size: 0.88rem; font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.15s ease;
}
.segmented button.active {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-soft);
}

/* ---------- Toggle switch ---------- */
.toggle-row { display: flex; align-items: center; gap: 12px; }
.toggle-row .toggle-text { flex: 1; }
.toggle-row .toggle-text .t-title { font-weight: 600; font-size: 0.98rem; }
.toggle-row .toggle-text .t-sub { font-size: 0.78rem; color: var(--text-muted); }

.switch { position: relative; display: inline-block; width: 50px; height: 30px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute; inset: 0;
  /* An OFF switch has to read as a switch, not as a smudge on the card. The
     track is darker than the surface behind it and carries a rim, so the
     control is visible before you know it is there - which matters most in
     dark mode, where the old track and the card were nearly the same tone. */
  background: #d8cabf;
  border: 1px solid rgba(0, 0, 0, 0.14);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
:root[data-theme="dark"] .switch .slider {
  background: #6b544a;
  border-color: rgba(255, 255, 255, 0.22);
}
.switch .slider::before {
  content: "";
  position: absolute;
  width: 24px; height: 24px;
  left: 2px; top: 2px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform 0.2s ease;
}
.switch input:checked + .slider {
  background: var(--brand);
  border-color: var(--brand-dark);
}
.switch input:checked + .slider::before { transform: translateX(20px); }
/* Keyboard users need to see where they are, on any control. */
.switch input:focus-visible + .slider { outline: 2px solid var(--brand); outline-offset: 2px; }

.divider { height: 1px; background: var(--border); margin: 14px 0; }

/* ---------- Status badges ---------- */
.badge {
  display: inline-block;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.03em;
  padding: 4px 10px;
  border-radius: 999px;
  text-transform: uppercase;
}
.badge.pending { background: var(--badge-pending-bg); color: var(--pending); }
.badge.awaitingApproval { background: var(--badge-awaiting-bg); color: var(--banner-text); }
.badge.approved { background: var(--badge-approved-bg); color: var(--approved); }

/* ---------- Task rows ----------
   A list, not a wall of cards. One line per task that you can run your eye
   down; the detail opens underneath on hover, and only where hovering is a
   thing the device does. */

.task-list { display: flex; flex-direction: column; gap: 0; }
.task-list > .task-item + .task-item { border-top: 0; }

.task-item {
  display: block; width: 100%; text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0;
  margin: 0 0 -1px;
  padding: 0;
  cursor: pointer;
  position: relative;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.task-list > .task-item:first-child { border-radius: var(--radius-md) var(--radius-md) 0 0; }
.task-list > .task-item:last-child { border-radius: 0 0 var(--radius-md) var(--radius-md); margin-bottom: 0; }
.task-list > .task-item:only-child { border-radius: var(--radius-md); }
.task-item:hover, .task-item:focus-visible {
  background: var(--surface-alt);
  border-color: var(--brand-light);
  z-index: 2;
}
.task-item.is-done { opacity: 0.78; }

.task-item .t-line {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  min-height: 44px;
}

/* The status marker: a small square, coloured by state. Square rather than
   round so it lines up with the type beside it. */
.task-item .t-dot {
  width: 8px; height: 8px; border-radius: 2px;
  flex: 0 0 auto;
  background: var(--pending);
}
.task-item .t-dot.awaitingApproval { background: var(--awaiting); }
.task-item .t-dot.approved { background: var(--approved); }
.task-item .t-dot.overdue { background: var(--danger); }

.task-item .t-title {
  font-weight: 600; font-size: 0.95rem;
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.task-item .t-who,
.task-item .t-when {
  font-size: 0.78rem; color: var(--text-muted);
  flex: 0 0 auto; white-space: nowrap;
}
.task-item .t-who { max-width: 30%; overflow: hidden; text-overflow: ellipsis; }
.task-item .t-when.overdue { color: var(--danger); font-weight: 700; }
.task-item .t-points {
  color: var(--brand); font-weight: 700; font-size: 0.9rem;
  flex: 0 0 auto; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  min-width: 3ch; text-align: right;
}

/* The strip that opens on hover. Collapsed with grid-template-rows so it
   animates from nothing to its own height without anybody measuring it. */
/* ONE child, always. `grid-template-rows: 0fr` sizes the first row and the
   first row only - every child after it lands in an implicit `auto` row and
   stays its full height, which is why the strip leaked its flags and its
   reference number out over the row below. The content lives in one wrapper
   so there is exactly one row to collapse. */
.task-item .t-reveal {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.16s ease;
}
.task-item .t-reveal-inner { min-height: 0; overflow: hidden; }
/* Only where there IS a pointer. On a phone a tap opens the whole task, so
   nothing here is information anybody loses. */
@media (hover: hover) and (pointer: fine) {
  .task-item:hover .t-reveal,
  .task-item:focus-visible .t-reveal,
  .task-item:focus-within .t-reveal { grid-template-rows: 1fr; }
}
/* Keyboard users get it on ANY device - the strip holds the quick actions,
   and reaching them must not depend on owning a mouse. */
.task-item:focus-visible .t-reveal,
.task-item:focus-within .t-reveal { grid-template-rows: 1fr; }

/* NO HOVER, NO HIDING THE ONE BUTTON THAT MATTERS.
   A phone never opens the strip, so on a touch device a row that is waiting
   for a single tap - Mark done, Approve - would have been rendering a button
   nobody could ever reach. Those rows stay open; the rest still collapse. */
@media (hover: none) {
  .task-item.has-quick .t-reveal { grid-template-rows: 1fr; }
}

/* NARROW SCREENS: the meta moves to its own line.
   Title, who, when and points all on one line at 390px left about eight
   characters for the title, so every task read "Take out the tr…".

   Below 560px the row is two lines: what it is and what it's worth, then who
   it's with and when it's due. The break is a zero-height pseudo-element
   with `flex-basis: 100%` sitting at order 4 - flex has no "line break" of
   its own, and letting the items wrap where they happened to fit is what
   produced a scatter of "For Sam" and "Overdue" on lines of their own. */
@media (max-width: 560px) {
  .task-item .t-line { flex-wrap: wrap; row-gap: 3px; }
  .task-item .t-line::after { content: ''; flex: 0 0 100%; height: 0; order: 4; }
  .task-item .t-dot, .task-item .sel-check { order: 1; }
  /* basis 0, not auto: flex decides where lines BREAK from the basis, and
     only then shrinks what is on each line. With `auto` a long title made
     the points wrap to a line of their own instead of the title truncating. */
  .task-item .t-title { order: 2; flex: 1 1 0; }
  .task-item .t-arrows { order: 3; }
  .task-item .t-points { order: 3; }
  .task-item .t-who {
    order: 5; flex: 1 1 auto; max-width: none;
    margin-left: 18px; /* line up under the title, past the status square */
  }
  .task-item .t-when { order: 6; margin-left: auto; }
  .task-item.selecting .t-who { margin-left: 30px; }
}

.task-item .t-desc {
  font-size: 0.85rem; color: var(--text-secondary);
  padding: 0 14px 6px 32px;
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.task-item .t-flags {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 0 14px 6px 32px;
}
.task-item .t-flag {
  font-size: 0.7rem; font-weight: 600;
  padding: 2px 7px;
  border-radius: 3px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  white-space: nowrap;
}
.task-item .t-flag.danger { color: var(--danger); border-color: currentColor; background: transparent; }
.task-item .t-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 0 14px 10px 32px;
}
.task-item .t-ref {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem; color: var(--text-muted);
}
.task-item .t-quick { margin-left: auto; display: flex; gap: 6px; }
.btn-quick {
  width: auto; padding: 5px 12px; font-size: 0.8rem;
  border-radius: var(--radius-sm);
}
.task-item .t-thumb {
  width: calc(100% - 46px); max-height: 180px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--skeleton);
  display: block;
  margin: 0 14px 8px 32px;
}

.task-item .t-arrows { display: flex; gap: 2px; flex: 0 0 auto; }

.group-header {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 18px 0 8px;
}

.review-banner, .amber-banner {
  background: var(--banner-bg);
  border: 1px solid var(--banner-border);
  border-radius: var(--radius-lg);
  padding: 12px;
  margin-bottom: 16px;
}
.review-banner .banner-label, .amber-banner .banner-label {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.05em;
  color: var(--banner-text);
  margin-bottom: 8px;
}
.review-banner .banner-body, .amber-banner .banner-body {
  color: var(--text-secondary);
  line-height: 1.5;
}

/* ---------- Empty state ---------- */
.empty {
  text-align: center;
  padding: 44px 24px;
  color: var(--text-secondary);
}
.empty .e-emoji { font-size: 3rem; }
.empty h3 { margin: 12px 0 6px; font-size: 1.15rem; }
.empty p { font-size: 0.92rem; max-width: 320px; margin: 0 auto; }
.empty .btn { max-width: 240px; margin: 18px auto 0; }

/* ---------- Floating action button ---------- */

/* The same action as the FAB, but spelled out and in the flow of the page -
   a floating circle is easy to miss, and impossible to find if anything ever
   covers it. */
.new-task-btn { max-width: 560px; margin: 0 auto 16px; }

/* ---------- Bottom tab bar (phone) / sidebar (desktop) ---------- */
.tabbar {
  position: fixed;
  bottom: 0; left: 50%;
  transform: translateX(-50%);
  width: 100%; max-width: 560px;
  display: flex;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
  padding: 8px 6px calc(8px + env(safe-area-inset-bottom));
  z-index: 50;
}
.tabbar.hidden { display: none; }
.tabbar a {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  text-decoration: none;
  color: var(--text-muted);
  font-size: 0.7rem; font-weight: 600;
  padding: 4px 0;
  border-radius: 10px;
}
.tabbar a .tab-icon { font-size: 1.35rem; line-height: 1.2; }
.tabbar a.active { color: var(--brand); }

/* Brand + footnote only exist visually on the desktop sidebar */
.tab-brand, .tab-foot { display: none; }

/* ---------- Balance / brand cards ---------- */
.balance-card {
  background: var(--brand);
  border: 1px solid var(--brand);
  border-radius: var(--radius-lg);
  padding: 20px;
  color: var(--on-brand);
  box-shadow: var(--shadow-card);
}
.balance-card .b-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; opacity: 0.85; }
.balance-card .b-number { font-size: 3rem; font-weight: 800; line-height: 1.15; }
.balance-card .b-sub { font-size: 0.88rem; opacity: 0.92; margin-top: 8px; }

/* ---------- Progress bar ---------- */
.progress {
  width: 100%; height: 12px;
  background: var(--surface-alt);
  border-radius: 999px;
  overflow: hidden;
}
.progress.on-brand { background: rgba(255,255,255,0.25); }
.progress .fill {
  height: 100%;
  background: var(--brand);
  border-radius: 999px;
  transition: width 0.4s ease;
}
.progress.on-brand .fill { background: #fff; }

/* ---------- People rows ---------- */
.person-row { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.person-row .person-info { flex: 1; min-width: 0; }
/* Rows with an action or two (accept / decline / connect) used to squeeze the
   name and caption into a couple of characters per line. The buttons keep
   their own width, and the text gets whatever is left. */
.person-row > .btn { flex-shrink: 0; white-space: nowrap; }
.avatar {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--brand-dark);
  display: grid; place-items: center;
  font-weight: 800; font-size: 1.05rem;
  flex-shrink: 0;
}
:root[data-theme="dark"] .avatar { color: var(--brand-light); }
.avatar.lg { width: 56px; height: 56px; font-size: 1.3rem; }
.avatar.sm { width: 26px; height: 26px; font-size: 0.78rem; border-radius: var(--radius-sm); }
.avatar.sm img { border-radius: var(--radius-sm); }
.avatar img {
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
}

/* Relationship label pill (e.g. "Son", "Roommate") */
.tag {
  display: inline-block;
  background: var(--surface-alt);
  color: var(--brand-dark);
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 0.78rem; font-weight: 700;
  margin-top: 4px;
}
:root[data-theme="dark"] .tag { color: var(--brand-light); }

/* ---------- Pairing code + QR ---------- */
.qr-box { display: flex; flex-direction: column; align-items: center; margin-top: 14px; }
.qr-box svg, .qr-box img, .qr-box canvas {
  width: 180px; height: 180px;
  background: #fff;
  border-radius: 12px;
  border: 1px solid var(--border);
  display: block;
}

.code-display {
  font-size: clamp(2.4rem, 8vw, 3.2rem); font-weight: 800; letter-spacing: 0.18em;
  color: var(--brand);
  text-align: center;
  margin: 8px 0;
}

/* ---------- Photo proof ---------- */
.proof-photo {
  width: 100%; max-height: 380px; object-fit: contain;
  border-radius: var(--radius-lg);
  background: var(--skeleton);
  display: block;
}

/* ---------- Notes ---------- */
.note {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
  margin-top: 20px;
  color: var(--text-secondary);
}
.note .n-emoji { font-size: 1.7rem; }

/* ---------- Chat ---------- */
.chat-list {
  min-height: 200px;
  max-height: calc(100dvh - 340px);
  overflow-y: auto;
  padding: 6px 2px;
}
.bubble-row { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
.bubble-row.mine { justify-content: flex-end; }
.bubble {
  max-width: 78%;
  padding: 10px 14px;
  border-radius: 16px 16px 16px 5px;
  background: var(--surface);
  border: 1px solid var(--border);
  overflow-wrap: anywhere;
  font-size: 0.95rem;
}
.bubble-row.mine .bubble {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  border-radius: 16px 16px 5px 16px;
}
.bubble .b-time { font-size: 0.66rem; opacity: 0.7; margin-top: 3px; }
.bubble-row .msg-del { opacity: 0; transition: opacity 0.15s ease; }
.bubble-row:hover .msg-del { opacity: 1; }
@media (hover: none) { .bubble-row .msg-del { opacity: 0.5; } }

.chat-inputrow {
  display: flex; gap: 8px;
  margin-top: 12px;
  position: sticky;
  bottom: calc(86px + env(safe-area-inset-bottom) + var(--kb));
}
.chat-inputrow input {
  flex: 1;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: 999px;
  padding: 12px 18px;
  font-size: 1rem; font-family: inherit;
  color: var(--text);
  outline: none;
}
.chat-inputrow input:focus { border-color: var(--brand); }
.chat-inputrow .btn { border-radius: 999px; }
@media (min-width: 768px) {
  .chat-inputrow { bottom: 16px; }
  .chat-list { max-height: calc(100dvh - 300px); }
}

/* ---------- History (points ledger) ---------- */
.history-row {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 13px 15px;
  margin-bottom: 8px;
}
.history-row .h-body { flex: 1; min-width: 0; }
.history-row .h-title { font-weight: 600; font-size: 0.96rem; overflow-wrap: anywhere; }
.history-row .h-sub { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.history-row .h-points { color: var(--approved); font-weight: 800; white-space: nowrap; }

/* minmax(0, …) for the same reason the calendar needs it: a stat's number
   or its label can be wider than its share, and `1fr` would let it push the
   row past the screen (four stats on a 390px phone did exactly that). The
   inline grid-template-columns some views set are normalised here too. */
.stat-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; margin-bottom: 16px;
}
.stat-row > * { min-width: 0; }
.stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px;
  text-align: center;
}
.stat .s-num { font-weight: 800; font-size: 1.3rem; color: var(--brand); }
.stat .s-label { font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 2px; }

/* ---------- Plan cards (paywall) ---------- */
.plan {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  width: 100%; text-align: left;
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
  margin-bottom: 10px;
  cursor: pointer;
  font-family: inherit;
}
.plan.selected { border-color: var(--brand); background: var(--surface-alt); box-shadow: var(--shadow-soft); }
.plan .p-badge {
  position: absolute; top: -10px; right: 16px;
  background: var(--approved); color: #fff;
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.04em;
  padding: 3px 10px; border-radius: 999px;
}
.plan .p-name { font-weight: 700; font-size: 1.05rem; }
.plan .p-sub { font-size: 0.75rem; color: var(--approved); font-weight: 700; }
.plan .p-price { margin-left: auto; text-align: right; }
.plan .p-price .amount { color: var(--brand); font-weight: 800; font-size: 1.1rem; }
.plan .p-price .per { font-size: 0.72rem; color: var(--text-muted); }

.radio {
  width: 24px; height: 24px; border-radius: 50%;
  border: 2px solid var(--text-muted);
  display: grid; place-items: center;
  flex-shrink: 0;
}
.plan.selected .radio, .option.selected .radio { border-color: var(--brand); }
.radio .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--brand); }

/* ---------- Role setup options ---------- */
.option {
  display: flex; align-items: center; gap: 12px;
  width: 100%; text-align: left;
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
  margin-bottom: 10px;
  cursor: pointer;
  box-shadow: var(--shadow-soft);
  font-family: inherit;
}
.option.selected { border-color: var(--brand); background: var(--surface-alt); }
.option .o-emoji { font-size: 2rem; }
.option .o-body { flex: 1; }
.option .o-title { font-weight: 700; font-size: 1.02rem; }
.option .o-sub { font-size: 0.85rem; color: var(--text-secondary); margin-top: 2px; }

/* ---------- Benefits list ---------- */
.benefits { display: grid; gap: 10px; margin: 18px 0; }

/* Paywall: Plus and Pro side by side on wide screens, stacked on phones */
.plan-cols { display: grid; grid-template-columns: 1fr; gap: 18px; align-items: start; }
@media (min-width: 860px) { .plan-cols { grid-template-columns: 1fr 1fr; } }
.plan-col { position: relative; padding: 20px; }
.plan-col.featured { border: 2px solid var(--brand); }
.pc-flag {
  position: absolute; top: -11px; left: 20px;
  background: var(--brand); color: #fff;
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.06em;
  border-radius: 999px; padding: 3px 12px;
}
.pc-name { font-size: 1.25rem; }
.pc-price { text-align: center; margin-top: 6px; }
.pc-price .amount { font-size: 1.8rem; font-weight: 800; color: var(--brand); }
.pc-price .per { color: var(--text-muted); font-weight: 600; }
.benefit { display: flex; align-items: center; gap: 10px; font-size: 0.95rem; }
.benefit .check {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--approved); color: #fff;
  display: grid; place-items: center;
  font-size: 0.7rem; font-weight: 800;
  flex-shrink: 0;
}

/* ---------- Hero (sign-in) ---------- */
.auth-hero { text-align: center; margin: 46px 0 30px; }
.auth-hero .logo { font-size: 4rem; }
.auth-hero h1 {
  font-size: 2.4rem; font-weight: 800; color: var(--brand);
  margin-top: 6px;
}
.auth-hero p { color: var(--text-secondary); margin-top: 6px; }

.auth-note {
  text-align: center;
  font-size: 0.82rem; color: var(--text-muted);
  margin: 14px 0;
  line-height: 1.6;
}

.link-btn {
  background: none; border: 0; cursor: pointer;
  color: var(--brand); font-weight: 600; font-size: 0.88rem;
  padding: 4px 0;
}
.link-btn:hover { color: var(--brand-dark); text-decoration: underline; }

/* ---------- Splash / loading ---------- */
.splash {
  min-height: 60dvh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 14px;
}
.splash .logo { font-size: 4rem; animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.12); opacity: 0.75; }
}

.spinner {
  width: 22px; height: 22px;
  border: 3px solid rgba(232, 85, 45, 0.25);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  display: inline-block;
}
.btn-primary .spinner, .btn-danger .spinner { border-color: rgba(255,255,255,0.35); border-top-color: #fff; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- Toast ---------- */
#toast {
  position: fixed;
  left: 50%; bottom: 90px;
  transform: translateX(-50%) translateY(20px);
  background: var(--text);
  color: var(--bg);
  padding: 12px 20px;
  border-radius: 999px;
  font-size: 0.92rem; font-weight: 500;
  box-shadow: 0 10px 30px rgba(0,0,0,0.25);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 200;
  max-width: min(90vw, 460px);
  text-align: center;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
#toast.error { background: var(--danger); color: #fff; }
#toast.success { background: var(--approved); color: #fff; }

/* ---------- Overlays: celebration, confirm, photo sheet ---------- */
.overlay-backdrop {
  position: fixed; inset: 0;
  background: rgba(20, 12, 8, 0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  z-index: 150;
  animation: fadeIn 0.2s ease;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.celebrate-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 26px 24px;
  text-align: center;
  max-width: 380px; width: 100%;
  /* A dialog taller than the window has to scroll, or its buttons are
     simply unreachable - which is what happens the moment a form-shaped
     dialog (the gift-shop decision, the reminder editor) meets a laptop in
     landscape. */
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* A dialog appearing should read as arriving, not as bouncing. The old
     overshoot curve was the single most toy-like thing on the screen. */
  animation: popIn 0.18s ease-out;
}
@keyframes popIn { from { transform: translateY(6px); opacity: 0; } to { transform: none; opacity: 1; } }
.celebrate-card .c-emoji { font-size: 3rem; }
.celebrate-card h2 { margin: 10px 0 6px; font-size: 1.25rem; overflow-wrap: anywhere; }
.celebrate-card p { color: var(--text-secondary); margin-bottom: 20px; }

/* The consequence line under a confirmation: quieter than the question, but
   boxed so it is read rather than skimmed past. */
.dlg-detail {
  background: var(--banner-bg);
  border: 1px solid var(--banner-border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  margin-bottom: 20px;
  text-align: left;
  line-height: 1.45;
}
/* The reminders line on a task: what will be sent, and a way to change it. */
.reminder-line {
  display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap;
}
.reminder-line .caption { line-height: 1.45; }
.reminder-line .link-btn { flex-shrink: 0; }
/* The reminder editor can get tall on a small screen, so it scrolls inside
   the card rather than pushing its own Save button off the bottom. */
.reminder-card { max-height: 88vh; overflow-y: auto; }

/* A field that shouldn't stretch the full width - a date, a short number. */
.narrow-field { max-width: 220px; }

/* Type-the-word confirmation - the heavier one. */
.confirm-typed { text-align: left; max-width: 400px; }
.confirm-typed h2, .confirm-typed > p { text-align: center; }
.confirm-typed label strong { color: var(--text); }
.confirm-typed .btn-danger[disabled] { opacity: 0.45; cursor: not-allowed; }

/* Team announcement: same card, plus a corner ✕ so it can always be waved
   away, and a message body that keeps the line breaks it was written with. */
.announcement-card { position: relative; max-width: 420px; }
.announcement-card .ann-close {
  position: absolute; top: 10px; right: 10px;
  width: 34px; height: 34px;
  font-size: 1rem; line-height: 1;
  color: var(--text-secondary);
}
.announcement-card .ann-message {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  text-align: left;
  max-height: 46vh;
  overflow-y: auto;
}
[dir='rtl'] .announcement-card .ann-close { right: auto; left: 10px; }

/* Bottom sheet on phones, centered dialog on desktop */
.sheet-backdrop {
  position: fixed; inset: 0;
  background: rgba(20, 12, 8, 0.55);
  display: flex; align-items: flex-end; justify-content: center;
  z-index: 150;
  animation: fadeIn 0.2s ease;
}
.sheet {
  background: var(--surface);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 20px 20px calc(20px + env(safe-area-inset-bottom));
  width: 100%; max-width: 560px;
  animation: slideUp 0.25s ease;
}
@keyframes slideUp { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
.sheet h3 { text-align: center; margin-bottom: 16px; font-size: 1.1rem; }

/* ---------- Utilities ----------
   These exist so the views stop carrying two hundred one-off style=""
   attributes. Same rule written inline in twenty places is twenty places to
   miss when the dark theme, the text-size setting or a colour changes - and
   it is why the screens had drifted out of step with each other. */
.muted { color: var(--text-muted); }
.small { font-size: 0.85rem; }
.caption { font-size: 0.78rem; color: var(--text-muted); }
.center { text-align: center; }
.mt-0 { margin-top: 0; }
.mt-sm { margin-top: 8px; }
.mt-md { margin-top: 14px; }
.mt-lg { margin-top: 20px; }
.mt-xl { margin-top: 28px; }
.mb-0 { margin-bottom: 0; }
.mb-md { margin-bottom: 14px; }

.text-secondary { color: var(--text-secondary); }
.fw-semi { font-weight: 600; }
.fw-bold { font-weight: 700; }
.fs-lg { font-size: 1.3rem; }
.fs-inherit { font-size: inherit; }
.lh-relaxed { line-height: 1.5; }
.no-underline { text-decoration: none; }
/* Long names, emails and group titles break inside a word rather than
   stretching a row until its buttons fall off a narrow screen. */
.wrap-anywhere { overflow-wrap: anywhere; }
.pad-lg { padding: 26px; }
.min-60 { min-height: 60px; }
.min-120 { min-height: 120px; }

/* Anything destructive is the same red everywhere - button, section label or
   caption - instead of each screen inlining the variable itself. */
.danger-text { color: var(--danger); }

/* A button sized to its own text, for rows where a full-width button would
   look absurd (Accept / Cancel next to a name). */
.btn-inline { width: auto; padding: 9px 14px; }
.btn-inline.btn-inline-lg { padding: 12px 18px; }

/* A <button> that is really a tappable card: kills the browser's default
   centring and font so the content inside lays out like a normal block. */
.card-button {
  text-align: left;
  cursor: pointer;
  font-family: inherit;
}

.hidden { display: none !important; }

.test-card { background: var(--banner-bg); border-color: var(--banner-border); }

.sim-banner {
  background: var(--banner-bg);
  border: 1px solid var(--banner-border);
  border-radius: var(--radius-md);
  padding: 12px;
  font-size: 0.8rem; color: var(--text-secondary);
  text-align: center;
  margin-top: 10px;
}

/* ============================================================
   DESKTOP / LAPTOP LAYOUT (≥ 768px)
   Sidebar navigation, full-width content, card grids.
   ============================================================ */
@media (min-width: 768px) {
  #app { max-width: none; }

  /* Signed in: make room for the sidebar */
  #app.with-nav { padding-left: var(--sidebar-w); }

  /* The tab bar becomes a left sidebar */
  .tabbar {
    top: 0; bottom: 0; left: 0; right: auto;
    transform: none;
    width: var(--sidebar-w); max-width: none;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 4px;
    border-top: 0;
    border-right: 1px solid var(--border);
    padding: 22px 14px;
  }
  .tabbar a {
    flex: 0 0 auto;
    flex-direction: row;
    justify-content: flex-start;
    gap: 12px;
    font-size: 0.98rem;
    padding: 11px 14px;
  }
  .tabbar a .tab-icon { font-size: 1.25rem; }
  .tabbar a.active { background: var(--surface-alt); }
  .tabbar a:hover { background: var(--surface-alt); }

  .tab-brand {
    display: flex; align-items: center; gap: 8px;
    font-family: "Poppins", sans-serif;
    font-weight: 800; font-size: 1.2rem;
    color: var(--text);
    padding: 4px 14px 20px;
  }
  .tab-foot {
    display: block;
    margin-top: auto;
    padding: 14px;
    font-size: 0.78rem;
    color: var(--text-muted);
    line-height: 1.5;
    border-top: 1px solid var(--border);
  }

  .view {
    /* Use the screen. 980px left a laptop showing a narrow ribbon of app
       with the monitor doing nothing on either side - and the calendar,
       which is the one screen that genuinely wants width, stuck inside it.
       A task row stays scannable at this measure because its columns are
       anchored: the title runs from the left, and who/when/points sit
       together against the right. */
    max-width: 1180px;
    margin: 0 auto;
    padding: 28px 36px 80px;
  }
  .view.no-tabs {
    max-width: 560px;
    padding-top: 4vh;
  }

  .screen-header { margin-bottom: 24px; }


  /* Toast above nothing in particular - bottom center of content */
  #toast { bottom: 40px; }

  /* Photo sheet becomes a centered dialog */
  .sheet-backdrop { align-items: center; padding: 24px; }
  .sheet { border-radius: var(--radius-lg); max-width: 420px; padding: 24px; }

  .stat-row { max-width: 640px; }
}

/* Two-plus column card grids once there's room */
@media (min-width: 900px) {
  .grid.cards { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
  .grid.people { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
}

/* Very large screens: more room and slightly larger type. */
@media (min-width: 1500px) {
  .view { max-width: 1400px; padding-left: 48px; padding-right: 48px; }
  body.app-body { font-size: 17px; }
}

/* ============================== The task bar ===============================
   New task, the list/calendar switch and the search box. Stacked on a phone,
   one row the moment there is width for it. */
.toolbar {
  display: flex; flex-direction: column; gap: 8px;
  max-width: 560px; margin: 0 auto 12px;
}
.toolbar .btn, .toolbar .segmented, .toolbar .search-input { margin: 0; max-width: none; width: 100%; }

@media (min-width: 900px) {
  .toolbar {
    flex-direction: row; align-items: center;
    max-width: none;
  }
  .toolbar .tb-new { width: auto; flex: 0 0 auto; white-space: nowrap; }
  .toolbar .tb-switch { flex: 0 0 auto; width: auto; min-width: 260px; }
  .toolbar .tb-search { flex: 1 1 220px; width: auto; }
}

/* ============================ Dense row lists ==============================
   Used for People and Groups. The card version gave one name four rows of
   chrome; this gives it one line, and a household fits on a screen. */

.row-list {
  display: flex; flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
}

.row-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  background: none; border: 0;
  border-bottom: 1px solid var(--hairline);
  padding: 9px 12px;
  min-height: 46px;
  cursor: pointer;
  font: inherit; color: inherit;
  transition: background-color 0.12s ease;
}
.row-list > .row-item:last-child { border-bottom: 0; }
.row-item:hover { background: var(--surface-alt); }

.row-item .ri-name {
  font-weight: 600; font-size: 0.93rem;
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row-item .ri-tag {
  font-size: 0.7rem; font-weight: 600;
  padding: 2px 7px; border-radius: 3px;
  background: var(--surface-alt); border: 1px solid var(--border);
  color: var(--text-secondary);
  white-space: nowrap; flex: 0 0 auto;
}
.row-item .ri-meta {
  font-size: 0.76rem; color: var(--text-muted);
  margin-left: auto; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.row-item .ri-pill {
  font-size: 0.7rem; font-weight: 700;
  padding: 2px 8px; border-radius: 999px;
  background: var(--surface-alt); color: var(--text-secondary);
  white-space: nowrap; flex: 0 0 auto;
}
.row-item .ri-pill.amber { background: var(--banner-bg); color: var(--banner-text); }
.row-item .ri-emoji { font-size: 1.1rem; width: 26px; text-align: center; flex: 0 0 auto; }
.row-item .ri-chev { color: var(--text-muted); flex: 0 0 auto; font-size: 1.05rem; }
.row-item .avatar.sm { flex: 0 0 auto; }

/* Buttons that sit side by side instead of stacking down the page. */
.action-row {
  display: flex; gap: 8px; flex-wrap: wrap;
  max-width: 520px; margin: 0 auto 4px;
}
.action-row .btn { width: auto; flex: 1 1 160px; }

/* ========================== Progressive disclosure =========================
   A block of setup that matters once and then never again - "add someone by
   email", the finder, the invite link - folded away rather than deleted.
   <details> so it works with no JavaScript at all and the browser handles
   the keyboard behaviour. */

.disclose {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  margin-top: 10px;
  max-width: 520px; margin-left: auto; margin-right: auto;
}
.disclose > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 14px;
  cursor: pointer;
  font-weight: 600; font-size: 0.92rem;
  list-style: none;
}
.disclose > summary::-webkit-details-marker { display: none; }
.disclose > summary::after {
  content: '⌄';
  color: var(--text-muted);
  transition: transform 0.15s ease;
}
.disclose[open] > summary::after { transform: rotate(180deg); }
.disclose > summary:hover { background: var(--surface-alt); }
.disclose > summary .caption { font-weight: 500; }
/* On a narrow phone the summary and its hint fight for the same line and
   both end up wrapping onto two. The label is the part that matters. */
@media (max-width: 480px) {
  .disclose > summary .caption { display: none; }
}
.disclose-body { padding: 0 14px 14px; border-top: 1px solid var(--hairline); padding-top: 12px; }

/* ============================== Gift shop ================================= */

.wish-row { display: flex; align-items: center; gap: 10px; }
.wish-row .person-info { flex: 1; min-width: 0; }
.wish-row.wish-no { opacity: 0.7; }
.wish-state { font-weight: 600; }
.wish-state.ok { color: var(--approved); }
.wish-state.no { color: var(--text-muted); }

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

/* ============================================================
   New components: plan tags, verify banner, one-page sections,
   reward lines, QR scanning, admin, group chat names.
   ============================================================ */

/* Plan badges */
.plan-tag {
  display: inline-block;
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.03em;
  padding: 3px 10px;
  border-radius: 999px;
  vertical-align: middle;
}
.plan-tag.premium { background: var(--surface-alt); color: var(--brand-dark); }
.plan-tag.business { background: #eef2ff; color: #4649c0; }
:root[data-theme="dark"] .plan-tag.premium { color: var(--brand-light); }
:root[data-theme="dark"] .plan-tag.business { background: #262a4a; color: #a5aaff; }

/* Verify-email banner */
.verify-banner {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 8px;
  background: var(--banner-bg);
  border: 1px solid var(--banner-border);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 0.85rem; color: var(--text-secondary);
}
.verify-banner .vb-actions { display: flex; gap: 14px; }

/* Same bar, but for something that costs money if ignored (a failed card). */
.verify-banner.danger {
  background: color-mix(in srgb, var(--danger) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
  color: var(--text);
  font-weight: 600;
}
.verify-banner.danger .link-btn { color: var(--danger); font-weight: 700; }

/* "Your plan, in numbers" - real usage against the caps, in Profile */
.usage-block { border-top: 1px solid var(--border); padding-top: 14px; }
.usage-row + .usage-row { margin-top: 12px; }
.usage-row .usage-top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; font-size: 0.85rem; color: var(--text-secondary);
  margin-bottom: 5px;
}
.usage-row .usage-top > span:last-child { font-weight: 700; color: var(--text); white-space: nowrap; }
.usage-row .usage-next {
  font-weight: 600; font-size: 0.74rem; color: var(--text-muted);
  margin-left: 6px;
}
.usage-row .usage-full { color: var(--danger); }
.usage-row .progress { height: 8px; }
.usage-row .progress .fill.full { background: var(--danger); }

/* Plan-changed overlay - slightly wider so the benefit list breathes */
.celebrate-card.plan-unlocked { max-width: 420px; }
.celebrate-card.plan-unlocked .benefit { font-size: 0.9rem; }

/* Banked weeks of Pro from invites, and the slider that spends them */
.credit-box {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
}
.credit-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.credit-picker { display: flex; align-items: center; gap: 14px; }
.credit-picker input[type="range"] {
  flex: 1;
  accent-color: var(--brand);
  height: 24px;
}
.credit-picker output {
  min-width: 4.6em;
  text-align: right;
  font-weight: 800;
  color: var(--brand);
  white-space: nowrap;
}

/* The four promises under the paywall's plan cards */
.assurance-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
  margin-top: 20px;
}
.assurance {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
}
.assurance .a-emoji { font-size: 1.25rem; line-height: 1.2; }
.assurance strong { display: block; font-size: 0.9rem; color: var(--text); }
.assurance span { font-size: 0.78rem; color: var(--text-muted); line-height: 1.4; }

/* One-page section blocks */
.summary-row { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.summary-chip {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 14px;
  font-size: 0.84rem; color: var(--text-secondary);
}
.summary-chip b { color: var(--text); }
.summary-chip.amber b { color: var(--banner-text); }
.summary-chip.green b { color: var(--approved); }

.section-block { margin: 18px 0; }
.section-title {
  display: flex; align-items: center; gap: 8px;
  font-family: "Poppins", sans-serif;
  font-weight: 700; font-size: 0.98rem;
  margin-bottom: 8px;
}

/* ---------- Lanes ----------
   Tasks and Rewards each show both halves on ONE page (given to you /
   given by you). The lane heading is the divider that keeps them read as
   two separate things rather than one long muddle. */
.task-lane { margin-top: 20px; }
.task-lane:first-child { margin-top: 6px; }
.lane-title {
  position: sticky; top: 0; z-index: 3;
  display: flex; align-items: center; gap: 8px;
  font-family: "Poppins", sans-serif;
  font-weight: 800; font-size: 0.82rem;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-secondary);
  background: var(--bg);
  padding: 9px 0 7px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
/* The first block inside a lane already brings its own top margin. */
.task-lane > .section-block:first-of-type { margin-top: 10px; }

/* ---------- Multi-select / bulk delete ---------- */
.section-actions { margin-left: auto; font-family: "Inter", sans-serif; font-weight: 600; }
.select-bar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  font-family: "Inter", sans-serif; font-size: 0.9rem;
}
.select-bar .btn-sm { padding: 7px 14px; font-size: 0.85rem; width: auto; }
.sel-check {
  flex-shrink: 0;
  width: 24px; height: 24px;
  border-radius: 999px;
  border: 2px solid var(--border);
  display: grid; place-items: center;
  font-size: 0.85rem; font-weight: 800; line-height: 1;
  color: #fff; background: var(--surface);
  transition: background 0.12s ease, border-color 0.12s ease;
}
.sel-check.on { background: var(--brand); border-color: var(--brand); }
.task-item.selecting .t-line { padding-left: 14px; }
.task-item.selecting .sel-check { flex: 0 0 auto; }
.task-item.selected, .reward-line.selected {
  border-color: var(--brand);
  box-shadow: inset 0 0 0 2px var(--brand);
}
.reward-line.selecting { cursor: pointer; }
.count-pill {
  background: var(--surface-alt);
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 0.75rem; font-weight: 700;
  font-family: "Inter", sans-serif;
}
.count-pill.amber { background: var(--banner-bg); color: var(--banner-text); }
.review-block {
  background: var(--banner-bg);
  border: 1px solid var(--banner-border);
  border-radius: var(--radius-md);
  padding: 12px;
}

.retention-note {
  margin-top: 12px;
  font-size: 0.78rem; color: var(--text-muted);
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  padding: 9px 12px;
}

/* Priority reordering */
.t-arrows { display: inline-flex; flex-direction: column; gap: 2px; }
.arrow-btn {
  background: var(--surface-alt);
  border: 0; border-radius: 6px;
  cursor: pointer;
  font-size: 0.62rem; line-height: 1;
  padding: 3px 7px;
  color: var(--text-secondary);
}
.arrow-btn:hover { background: var(--brand); color: #fff; }
.task-item.reorderable { cursor: grab; }
.task-item.dragging { opacity: 0.45; }

/* Proof media */
.proof-grid { display: grid; gap: 10px; grid-template-columns: 1fr; }
@media (min-width: 700px) { .proof-grid { grid-template-columns: 1fr 1fr; } }

/* Reward lines */
.reward-line {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  margin-bottom: 6px;
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.reward-line:hover { background: var(--surface-alt); border-color: var(--brand-light); }
.reward-line.claimed { opacity: 0.75; }

/* Drag-and-drop reordering. The grip is the honest signal that a row can be
   moved - "cursor: grab" alone is invisible until you are already on it. */
.reward-lane { display: flex; flex-direction: column; }
.reward-line.reorderable { cursor: grab; }
.reward-line.dragging { opacity: 0.4; cursor: grabbing; }
.reward-line.drop-target { border-color: var(--brand); box-shadow: inset 0 2px 0 var(--brand); }
.drag-grip {
  flex: 0 0 auto;
  color: var(--text-muted);
  font-size: 0.9rem; line-height: 1;
  padding: 2px 2px 2px 0;
  cursor: grab;
  opacity: 0.55;
}
.reward-line:hover .drag-grip { opacity: 1; color: var(--brand); }
.lane-hint {
  font-size: 0.72rem; color: var(--text-muted);
  margin: 0 0 6px 2px;
}

/* A phone has no pointer, so there is nothing to drag with: the grip goes,
   and the ▲▼ that work everywhere do the reordering. That also hands the
   title back the width it needs. */
@media (max-width: 700px) {
  .reward-line { gap: 10px; padding: 10px; flex-wrap: wrap; }
  .reward-line .drag-grip { display: none; }
  /* Four buttons and a picture left the title about 57px - enough for
     "Cine…". They get their own line, and the title gets the row. */
  .reward-line .rl-actions { flex: 0 0 100%; justify-content: flex-end; gap: 2px; }
  .reward-line > .btn, .reward-line > .btn-inline { flex: 0 0 auto; }
  .lane-hint { display: none; }
}
.reward-line .rl-body { flex: 1 1 auto; min-width: 0; }
.reward-line .rl-top { display: flex; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
/* `overflow-wrap: anywhere` is the right call for a long unbroken word - and
   a disaster when the box it is in has been squeezed to nothing, because
   "anywhere" then means "after every single letter". On a phone the row's
   grip, picture and four action buttons left the title about 12px, and every
   reward rendered as a vertical column of characters. The title truncates
   instead, and the row below gives it room. */
.reward-line .rl-title {
  font-weight: 700;
  min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.reward-line .rl-pts { color: var(--brand); font-weight: 800; white-space: nowrap; }
.reward-line .rl-actions { display: flex; gap: 2px; }

.reward-img {
  width: 52px; height: 52px;
  border-radius: 12px;
  object-fit: cover;
  flex-shrink: 0;
}
.reward-img.lg { width: 72px; height: 72px; border-radius: 16px; }
.reward-img-fallback {
  display: grid; place-items: center;
  background: var(--surface-alt);
  font-size: 1.6rem;
}
/* Time zone picker (Profile + onboarding) */
.tz-select {
  width: 100%;
  padding: 12px 14px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
}

/* Mystery rewards: a "?" box the receiver sees until they claim */
.mystery-img {
  background: linear-gradient(135deg, #7c3aed, #db2777);
  color: #fff;
  animation: mysteryPulse 2.2s ease-in-out infinite;
}
@keyframes mysteryPulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.22); }
}

.sender-balance {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--brand);
  border-radius: var(--radius-lg);
  padding: 12px 14px;
  margin-bottom: 12px;
}
.sender-balance .sb-points { color: var(--brand); font-weight: 800; font-size: 1.15rem; white-space: nowrap; }

.reward-pic-row { display: flex; align-items: center; gap: 14px; }

/* Revoked points in history */
.history-row.revoked { border-left: 3px solid var(--danger); }
.revoked-tag {
  display: inline-block;
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger);
  font-size: 0.68rem; font-weight: 800;
  border-radius: 999px;
  padding: 2px 8px;
  vertical-align: middle;
}

/* Emoji / admin input rows */
.emoji-row { display: flex; gap: 8px; }
.emoji-row input {
  flex: 1; min-width: 0;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  font-size: 1.05rem; font-family: inherit;
  color: var(--text);
  outline: none;
}
.emoji-row input:focus { border-color: var(--brand); }

/* QR scanning */
.scan-box {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: 14px;
  background: #000;
}
.scan-box video { width: 100%; max-height: 320px; object-fit: cover; display: block; }
.scan-frame {
  position: absolute; inset: 14%;
  border: 3px solid rgba(255, 255, 255, 0.85);
  border-radius: 18px;
  pointer-events: none;
}

/* Group chat: who said it */
.bubble .b-from { font-size: 0.72rem; font-weight: 700; color: var(--brand); margin-bottom: 2px; }

/* Breathing room so the floating + never covers content */
/* ---------- Room to type ----------
   --kb is how many pixels the on-screen keyboard is covering (set from
   js/app.js via visualViewport; 0 on desktop, and 0 whenever the keyboard is
   down). Everything pinned to the bottom of the screen adds it, so nothing
   you are typing into ends up underneath the keyboard. */
.view { padding-bottom: calc(150px + var(--kb)); }
@media (min-width: 768px) { .view { padding-bottom: calc(120px + var(--kb)); } }

/* Give a focused field somewhere to land when it is scrolled into view,
   rather than flush against the top of the screen or the keyboard. */
input, textarea, select { scroll-margin: 90px 0 120px; }

@media (max-width: 767px) {
  /* While the keyboard is up the tab bar is just stealing the little room
     that is left, so it steps aside until you're done. */
  .kb-open .tabbar { transform: translate(-50%, 130%); }
  .tabbar { transition: transform 0.22s ease; }
}

/* Google sign-in button - Google's brand guidelines: white, grey border */
.btn-google {
  background: #fff;
  color: #1f1f1f;
  border-color: #dadce0;
  font-weight: 600;
}
.btn-google:hover:not(:disabled) { background: #f7f8f8; border-color: #c9ccd1; }
:root[data-theme="dark"] .btn-google {
  background: #131314;
  color: #e3e3e3;
  border-color: #494b4d;
}
:root[data-theme="dark"] .btn-google:hover:not(:disabled) { background: #1e1f20; }

/* ---------- Getting started checklist (new accounts only) ---------- */
.getting-started {
  border-color: var(--brand);
  background: linear-gradient(180deg, var(--surface-alt), var(--surface) 60%);
  margin-bottom: 18px;
}
.gs-head { display: flex; align-items: flex-start; gap: 10px; }
.gs-head > div:first-child { flex: 1; }
.gs-title { font-weight: 800; font-size: 1.05rem; font-family: "Poppins", sans-serif; }
.gs-steps { display: grid; gap: 10px; margin-top: 14px; }
.gs-step { display: flex; align-items: center; gap: 11px; }
.gs-check {
  width: 24px; height: 24px; flex-shrink: 0;
  border-radius: 50%;
  border: 2px solid var(--border);
  display: grid; place-items: center;
  font-size: 0.75rem; font-weight: 800;
  color: transparent;
}
.gs-step.done .gs-check {
  background: var(--approved);
  border-color: var(--approved);
  color: #fff;
}
.gs-body { flex: 1; min-width: 0; }
.gs-step-title { font-weight: 600; font-size: 0.95rem; }
.gs-step.done .gs-step-title { color: var(--text-muted); text-decoration: line-through; }
.gs-step.done .gs-body .caption { display: none; }

/* Emoji in a plan subtitle needs its own breathing room - emoji glyphs
   carry no side bearing, so they sit flush against the next word. */
.p-sub-emoji { margin-right: 7px; }

/* A plan row that is deliberately unavailable right now (the Plus trial while
   free Pro is running) - visibly there, visibly not for today. */
.plan.plan-held { opacity: 0.5; cursor: not-allowed; }
.plan.plan-held:hover { border-color: var(--border); }

/* Tried to assign a task without picking anybody - point at the chips
   rather than only firing a toast that may scroll past unnoticed. */
.chip-wrap.needs-pick {
  outline: 2px solid var(--danger);
  outline-offset: 6px;
  border-radius: var(--radius-md);
  animation: nudgeShake 0.4s ease;
}
@keyframes nudgeShake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

/* ============================================================
   CALENDAR LAYOUT
   The same tasks as the list, arranged by when they are due.
   Free on every plan; the list stays the default.
   ============================================================ */

.layout-switch { max-width: 560px; margin-left: auto; margin-right: auto; }

.calendar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 14px;
  box-shadow: var(--shadow-soft);
  max-width: 560px;
  margin: 0 auto 18px;
}

.cal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 10px;
}
.cal-title {
  font-family: "Poppins", sans-serif;
  font-weight: 800; font-size: 1.1rem;
  text-align: center; flex: 1;
}
.cal-nav {
  background: none; border: 0; cursor: pointer;
  font-size: 1.5rem; line-height: 1; color: var(--brand);
  padding: 4px 12px; border-radius: 8px;
}
.cal-nav:hover { background: var(--surface-alt); }

/* minmax(0, 1fr), NOT 1fr.
   `1fr` is shorthand for `minmax(auto, 1fr)`, and `auto` there means "at
   least the content's min-content width". A day chip is `white-space:
   nowrap`, so its min-content width is the whole title - which pushed each
   of the seven tracks out to fit the longest task name in that column, blew
   the grid past the card it lives in, and left Sunday hanging in space off
   the right-hand edge. Pinning the minimum to 0 lets the tracks share the
   width they actually have and hands the overflow back to the chip, which
   already knows how to ellipsis. */
.cal-weekdays, .cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
}
.cal-weekdays > *, .cal-grid > * { min-width: 0; }
.cal-weekdays {
  margin-bottom: 4px;
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.05em;
  color: var(--text-muted); text-transform: uppercase; text-align: center;
}

.cal-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  min-height: 40px;
  min-width: 0; /* see the note on .cal-grid above */
  overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 3px;
  padding: 5px 2px 2px;
  background: transparent;
  border: 1.5px solid transparent;
  border-radius: 10px;
  cursor: default;
  font: inherit;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.cal-cell.has { cursor: pointer; background: var(--surface-alt); }
.cal-cell.has:hover { border-color: var(--brand); }
.cal-cell.outside { opacity: 0.35; }
.cal-cell.today { border-color: var(--brand); }
.cal-cell.today .cal-num { color: var(--brand); font-weight: 800; }

.cal-num { font-size: 0.86rem; font-weight: 600; line-height: 1; }

.cal-dots { display: flex; align-items: center; justify-content: center; gap: 2px; flex-wrap: wrap; }
.cal-dot {
  width: 6px; height: 6px; border-radius: 50%;
  display: inline-block; background: var(--pending);
}
.cal-dot.todo { background: var(--brand); }
.cal-dot.late { background: var(--danger); }
.cal-dot.wait { background: var(--awaiting); }
.cal-dot.done { background: var(--approved); }
.cal-more { font-size: 0.58rem; font-weight: 700; color: var(--text-muted); }
.cal-tick { position: absolute; top: 3px; right: 4px; font-size: 0.6rem; color: var(--approved); }

.cal-legend {
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 0.74rem; color: var(--text-muted);
}
.cal-legend span { display: inline-flex; align-items: center; gap: 5px; }
.cal-legend .cal-dot { width: 7px; height: 7px; }
.cal-legend .link-btn { margin-left: auto; }

/* The day being looked at - without this, tapping a square changed the panel
   below but nothing on the grid, which read as the tap not working. */
.cal-cell.sel { background: color-mix(in srgb, var(--brand) 12%, var(--surface)); border-color: var(--brand); }
.cal-cell.sel .cal-num { color: var(--brand); font-weight: 800; }
.cal-cell.overdue .cal-num { color: var(--danger); }

/* A sketched future repeat: hollow, so it reads as "will be" not "is". */
.cal-dot.ghost { background: transparent; border: 1.5px solid var(--brand); opacity: 0.7; }

/* Mini titles inside the squares - only once the squares are big enough. */
.cal-chips { display: none; min-width: 0; max-width: 100%; }
.cal-chip {
  display: block; width: 100%; min-width: 0; max-width: 100%;
  font-size: 0.62rem; font-weight: 600; line-height: 1.25;
  padding: 1px 4px; border-radius: 5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: left;
  background: color-mix(in srgb, var(--brand) 14%, var(--surface));
  color: var(--text);
}
.cal-chip.done { background: color-mix(in srgb, var(--approved) 18%, var(--surface)); color: var(--text-muted); text-decoration: line-through; }
.cal-chip.wait { background: color-mix(in srgb, var(--awaiting) 20%, var(--surface)); }
.cal-chip.late { background: color-mix(in srgb, var(--danger) 16%, var(--surface)); }
.cal-chip.ghost { background: transparent; border: 1px dashed color-mix(in srgb, var(--brand) 45%, var(--border)); color: var(--text-muted); }

/* A future occurrence of a repeating task, listed under the day. */
.cal-proj { display: flex; flex-direction: column; gap: 6px; }
.cal-proj-row {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  padding: 9px 12px;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  font: inherit; text-align: left; cursor: pointer;
  color: var(--text);
}
.cal-proj-row:hover { border-color: var(--brand); }
.cal-proj-title { font-weight: 600; overflow-wrap: anywhere; }

/* HOW MANY TITLES FIT IN A SQUARE.
   Four are always rendered; how many are shown - and which "+n more" line
   goes with that number - is decided here, by how tall the squares are at
   this width. Doing it in CSS rather than JS means resizing the window is
   enough: there is no re-render to miss. */
.cal-chip.r2, .cal-chip.r3 { display: none; }
.cal-more.only3, .cal-more.only4 { display: none; }

/* Wide enough for words in the squares: titles replace the dots. */
@media (min-width: 600px) {
  .cal-cell { aspect-ratio: auto; min-height: 76px; padding: 6px 4px 4px; gap: 2px; }
  .cal-cell .cal-dots { display: none; }
  .cal-cell .cal-chips { display: flex; flex-direction: column; gap: 2px; width: 100%; align-items: center; }
  .cal-cell .cal-num { align-self: flex-start; margin-left: 2px; }
}

/* Roomier squares once there is space for them. The calendar fills the
   column it is in rather than sitting at 720px in the middle of a 1400px
   screen with the rest of the width doing nothing. */
@media (min-width: 768px) {
  .calendar { max-width: 100%; padding: 20px; }
  /* A floor, not a height: a week with nothing in it should not be as tall
     as a week with three tasks a day. The chips grow the row that needs it. */
  .cal-cell { min-height: 64px; padding: 8px 6px 6px; }
  .cal-num { font-size: 1rem; }
  .cal-dot { width: 7px; height: 7px; }
  .cal-chip { font-size: 0.72rem; padding: 2px 6px; border-radius: 4px; }
  .cal-title { font-size: 1.25rem; }
  /* Room for a third title. */
  .cal-chip.r2 { display: block; }
  .cal-more.only2 { display: none; }
  .cal-more.only3 { display: block; }
}

/* A big screen gets a fourth, and squares you can actually read across. */
@media (min-width: 1200px) {
  .cal-cell { min-height: 72px; }
  .cal-chip { font-size: 0.76rem; }
  .cal-chip.r3 { display: block; }
  .cal-more.only3 { display: none; }
  .cal-more.only4 { display: block; }
}

/* Admin: Pro / Plus / Free lists, each ranked by when the plan started */
.ad-tabs button { position: relative; }
.ad-tabs .tab-count {
  display: inline-block;
  margin-left: 5px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--surface-alt);
  font-size: 0.72rem; font-weight: 800;
}
.ad-tabs button.active .tab-count { background: var(--surface); color: var(--brand); }
.ad-since { font-weight: 600; opacity: 0.85; margin-top: 2px; }

/* Invite scoreboard: how many people have used your code, at a glance.
   Read straight from the profile, so it is there before any button is
   pressed and ticks up the moment somebody joins. */
.ref-score {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
}
.ref-score-row { display: flex; gap: 10px; }
.ref-stat {
  flex: 1;
  display: flex; align-items: baseline; gap: 8px;
}
.ref-stat .ref-num {
  font-family: "Poppins", sans-serif;
  font-weight: 800;
  font-size: 2rem;
  line-height: 1;
  color: var(--brand);
}
.ref-stat .ref-lbl {
  font-size: 0.76rem;
  line-height: 1.25;
  color: var(--text-secondary);
}
.ref-score .progress { height: 8px; }

/* ============================================================
   DARK MODE CONTRAST
   Borders that read as edges on white almost vanish on a dark
   card. These lift the outlines that define a control without
   touching the palette itself.
   ============================================================ */
:root[data-theme="dark"] {
  /* An unselected chip and the card behind it were near-identical. */
  --chip-border-dark: #60493d;
}
:root[data-theme="dark"] .chip { border-color: var(--chip-border-dark); }
:root[data-theme="dark"] .chip:hover { border-color: var(--brand-light); }

/* The inactive half of a segmented control needs to look pressable. */
:root[data-theme="dark"] .segmented { border: 1px solid var(--chip-border-dark); }
:root[data-theme="dark"] .segmented button:not(.active) { color: var(--text); opacity: 0.72; }
:root[data-theme="dark"] .segmented button:not(.active):hover { opacity: 1; }

/* Text inputs: the outline is the only thing saying "type here". */
:root[data-theme="dark"] .field input,
:root[data-theme="dark"] .field textarea,
:root[data-theme="dark"] .search-input,
:root[data-theme="dark"] .tz-select,
:root[data-theme="dark"] .chat-inputrow input { border-color: var(--chip-border-dark); }

/* A secondary button is defined entirely by its border. */
:root[data-theme="dark"] .btn-secondary { border-color: var(--chip-border-dark); }

/* Keyboard focus, everywhere. Nothing should be reachable by tab but
   invisible once you get there. */
.btn:focus-visible,
.chip:focus-visible,
.segmented button:focus-visible,
.icon-btn:focus-visible,
.link-btn:focus-visible,
.task-item:focus-visible,
.cal-cell:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* ============================================================
   LANGUAGE
   ============================================================ */
.lang-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
  max-width: 560px;
  margin: 0 auto;
}
.lang-option {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  cursor: pointer;
  text-align: start;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.lang-option:hover { border-color: var(--brand); }
.lang-option.selected { border-color: var(--brand); background: var(--surface-alt); }
.lang-flag { font-size: 1.35rem; line-height: 1; }
.lang-names { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; flex: 1; }
.lang-native { font-weight: 700; font-size: 0.95rem; color: var(--text); }
.lang-en { font-size: 0.72rem; color: var(--text-muted); }
.lang-tick { color: var(--brand); font-weight: 800; }
:root[data-theme="dark"] .lang-option { border-color: var(--chip-border-dark); }

/* ---------- Right-to-left (Arabic) ----------
   Logical properties handle most of it. These are the few places that were
   written with a hard left or right and would otherwise point the wrong way. */
[dir="rtl"] .back-btn { transform: scaleX(-1); padding: 4px 0 4px 10px; }
[dir="rtl"] .task-item, [dir="rtl"] .person-row, [dir="rtl"] .lang-option { text-align: right; }
[dir="rtl"] .switch .slider::before { left: auto; right: 2px; }
[dir="rtl"] .switch input:checked + .slider::before { transform: translateX(-20px); }
[dir="rtl"] .cal-legend .link-btn { margin-left: 0; margin-right: auto; }
[dir="rtl"] .usage-row .usage-next { margin-left: 0; margin-right: 6px; }
[dir="rtl"] .ad-tabs .tab-count { margin-left: 0; margin-right: 5px; }
@media (min-width: 768px) {
  /* The sidebar swaps sides, and the content makes room on the other edge. */
  [dir="rtl"] .tabbar { left: auto; right: 0; border-right: 0; border-left: 1px solid var(--border); }
  [dir="rtl"] #app.with-nav { padding-left: 0; padding-right: var(--sidebar-w); }
}

/* ============================================================
   SPACING AND ALIGNMENT
   Two things kept going wrong: blocks with a max-width but no
   auto margins sat hard against the left while their centred
   neighbours floated in the middle, and small print was set
   flush against the control it describes.
   ============================================================ */

/* One centred column, the same width as every other centred block on the
   screen. Replaces a scattering of inline max-width rules, half of which
   forgot the auto margins and so left-aligned on desktop. */
.col {
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
}

/* Streak / due-today card */
.momentum { padding: 14px 16px; margin-bottom: 18px; }
.momentum .mo-streak { font-weight: 800; }
/* A rule between the two halves, rather than them just touching. */
.momentum .mo-split {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.momentum .mo-label { font-weight: 700; margin-bottom: 2px; }

/* Small print needs air. A caption pressed against a bar or a button reads
   as part of it rather than as a note about it. The :not() guard leaves any
   explicit mt-* spacing alone. */
.caption { line-height: 1.45; }
.progress:not([class*="mt-"]) { margin-top: 6px; }
.progress + .caption:not([class*="mt-"]) { margin-top: 6px; }
.card + .caption:not([class*="mt-"]),
.btn + .caption:not([class*="mt-"]) { margin-top: 8px; }

/* The calendar sits below the chips and above the day list; give it the
   same rhythm as the rest of the page instead of butting into both. */
.calendar { margin-top: 4px; }
.summary-row { margin-bottom: 16px; }

/* Section headings were tight against the block above them. */
.section-block:first-child { margin-top: 12px; }

/* The Tasks screen's chrome - the setup card, the buttons, the switches, the
   counts - now all share one column width. Previously the setup card spanned
   the full page while the controls under it were 560px, which made the page
   look like two different layouts stacked. The task LISTS stay full width,
   because on a wide screen they genuinely benefit from columns. */
.getting-started { max-width: 560px; margin-left: auto; margin-right: auto; }
.summary-row.col { justify-content: center; }

/* On a wide screen the page title used to sit hard against the left edge
   while everything under it was centred, so the heading looked like it
   belonged to a different page. Line it up with the column instead.
   Direct children only: screens wrapped in .narrow already have their own
   column and must not be indented twice. */
@media (min-width: 768px) {
  #view > .screen-header {
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* ---------- Username ---------- */
.handle {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--brand);
  overflow-wrap: anywhere;
}
.username-field {
  display: flex; align-items: center;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  padding-left: 12px;
  transition: border-color 0.15s ease;
}
.username-field:focus-within { border-color: var(--brand); }
.username-at { font-weight: 800; color: var(--text-muted); }
.username-field input {
  border: 0 !important;
  background: transparent !important;
  padding-left: 4px !important;
}
:root[data-theme="dark"] .username-field { border-color: var(--chip-border-dark); }

/* ---------- Danger zone ----------
   Outlined rather than filled: it has to read as serious without being the
   loudest thing on a settings screen. */
.danger-card {
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
  background: color-mix(in srgb, var(--danger) 5%, var(--surface));
}

/* ============================================================
   GROUP FEED (chat messages + pinned announcements)
   ============================================================ */
.g-post {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  margin-bottom: 8px;
}
/* An announcement has to stand out from chatter without shouting. */
.g-post.announce {
  border-color: var(--banner-border);
  background: var(--banner-bg);
}
.g-post-head {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
  font-size: 0.82rem;
  margin-bottom: 4px;
}
.g-post-head strong { color: var(--text); overflow-wrap: anywhere; }
.g-tag {
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.03em;
  color: var(--banner-text);
}
.g-post-body { font-size: 0.95rem; color: var(--text); overflow-wrap: anywhere; white-space: pre-wrap; }
:root[data-theme="dark"] .g-post { border-color: var(--chip-border-dark); }

/* The tab bar has to stay legible on a narrow phone. */
@media (max-width: 420px) {
  .tabbar a { font-size: 0.62rem; }
  .tabbar a .tab-icon { font-size: 1.05rem; }
}
