/* House Manager — warm, calm, high-contrast, mobile-first. System fonts only. */
:root {
  --accent: #5b7553;
  --accent-dark: #435a3d;
  --accent-soft: rgba(91, 117, 83, 0.16);
  /* A calmer tint than accent-soft, for a control that is real but quiet. Kept
     as a token so it follows the theme, and kept above 0.05 alpha because a
     clickable surface must not be invisible — see the notes banner. */
  --accent-faint: rgba(91, 117, 83, 0.09);
  --bg: #f7f4ef;
  --card: #ffffff;
  --surface: #fbf9f6;      /* insets: quoted text, table heads, empty states */
  --text: #211d18;
  --muted: #6f665a;
  --border: #e6ded2;
  --border-strong: #d5c9b8;
  --done-bg: #eaf3e5;
  --done-border: #bcd8ac;
  --warn-bg: #fdf1e3;
  --warn-border: #ebcda6;
  --warn-text: #7a4a12;
  --danger: #a5473a;
  --danger-bg: #fbeae7;
  --danger-on: #ffffff;   /* text that sits ON a solid danger fill */
  --info-bg: #eef1f7;
  --info-text: #3a4a6b;
  --info-border: #ccd6e8;
  /* Layered shadows read as depth; a single flat one reads as a 2005 box. */
  --shadow: 0 1px 2px rgba(40, 30, 15, .05), 0 1px 3px rgba(40, 30, 15, .05);
  --shadow-lift: 0 2px 4px rgba(40, 30, 15, .06), 0 8px 20px rgba(40, 30, 15, .07);
  --radius: 14px;
  --radius-sm: 10px;
  font-size: 17px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --accent: #7fa876;
    --accent-dark: #9fc397;
    --accent-soft: rgba(127, 168, 118, 0.22);
    --accent-faint: rgba(127, 168, 118, 0.14);
    --bg: #191712;
    --card: #232019;
    --surface: #2b2720;
    --text: #ede6da;
    --muted: #a89d8c;
    --border: #3a3527;
    --border-strong: #4d4636;
    --done-bg: #223526;
    --done-border: #3c5a38;
    --warn-bg: #3a2c17;
    --warn-border: #6b4d21;
    --warn-text: #f0c98a;
    --danger: #e08a7c;
    --danger-bg: #3a2420;
    --danger-on: #2a1512;
    --info-bg: #21283a;
    --info-text: #b9c7e4;
    --info-border: #37425c;
    --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 1px 3px rgba(0, 0, 0, .3);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, .4), 0 8px 20px rgba(0, 0, 0, .45);
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

body {
  padding-bottom: 90px;
}

a { color: var(--accent-dark); }

.container {
  max-width: 560px;
  margin: 0 auto;
  padding: 12px 14px 24px;
}

.card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
  padding: 18px;
  margin-bottom: 14px;
}
.card > h2:first-child, .card > h3:first-child { margin-top: 0; }

h1, h2, h3 { line-height: 1.22; margin: 0 0 8px; letter-spacing: -0.011em; }
h1 { font-size: 1.5rem; font-weight: 700; }
h2 { font-size: 1.15rem; font-weight: 650; }
h3 { font-size: 1.02rem; font-weight: 650; }
p { line-height: 1.5; }
.muted { color: var(--muted); font-size: 0.9rem; }
/* Figures line up in columns instead of dancing about. */
.tabular, td, th, .hours-item b + *, .num { font-variant-numeric: tabular-nums; }

button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 48px;
  padding: 10px 18px;
  font-size: 1rem;
  font-weight: 600;
  border-radius: 12px;
  /* No white buttons. A plain button used to be card-white with a hairline
     border, which is one shade away from the page behind it and identical to
     a disabled control -- Joel, 14 Sep: "all buttons that are an option to be
     clicked need to be green or a color and not white or grayed out." */
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--accent-soft);
  color: var(--accent-dark);
  cursor: pointer;
  text-decoration: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
button, .btn { transition: background .15s ease, border-color .15s ease, transform .06s ease, box-shadow .15s ease; }
button:active, .btn:active { transform: scale(0.98); }
button:focus-visible, .btn:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
button.primary, .btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: var(--shadow);
}
button:disabled { opacity: 0.5; cursor: default; }
@media (hover: hover) {
  button:not(:disabled):hover, .btn:hover { border-color: var(--border-strong); }
  button.primary:not(:disabled):hover, .btn.primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
}

/* :not() on the type is load-bearing. Without it a checkbox inherited
   width:100% and min-height:48px, so on a wide screen the box stretched across
   the row and crushed its own label into a 60px column -- which is exactly what
   the admin "Today's plan" toggles looked like. */
input:not([type=checkbox]):not([type=radio]), select, textarea {
  width: 100%;
  min-height: 48px;
  padding: 10px 12px;
  font-size: 1rem;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}
input:not([type=checkbox]):not([type=radio]):focus-visible,
select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input[type=checkbox], input[type=radio] {
  width: 20px; height: 20px; min-height: 0;
  accent-color: var(--accent);
  flex: 0 0 auto;
  margin: 0;
}
textarea { min-height: 90px; resize: vertical; }
label { display: block; font-weight: 600; margin: 12px 0 6px; font-size: 0.92rem; }
.field { margin-bottom: 6px; }

.top-bar {
  position: sticky;
  top: 0;
  z-index: 10;
  /* Translucent so content scrolling under it reads as depth rather than a
     hard band sitting on top of the page. */
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  backdrop-filter: saturate(180%) blur(12px);
  padding: 12px 14px 6px;
  border-bottom: 1px solid var(--border);
}
@supports not (backdrop-filter: blur(4px)) { .top-bar { background: var(--bg); } }
.top-bar > .header-row, .top-bar > .tabs { max-width: var(--shell, 560px); margin-inline: auto; }

/* ---------- Collapsed bar (house manager's screen only) ----------
   today.php gives the bar a negative sticky `top` from script, so the bar
   scrolls up with the page and pins with only this strip left on screen. A
   negative sticky offset does the collapsing in CSS, which matters: the bar's
   height never changes, so the list underneath never shifts, and there is no
   scroll handler racing a reflow. Script only fades this strip in at the point
   the bar finishes pinning.

   Nothing here applies to the admin bar: the strip exists only in today.php,
   and without it the inline `top` is never set, so that bar stays as it was. */
.hdr-mini {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 48px; min-height: 0;
  display: flex; align-items: center;
  margin: 0; padding: 0 14px;
  border: 0; border-radius: 0;
  /* Opaque on purpose: it covers the tail of the tab strip behind it, so it
     cannot be the translucent --accent-soft. Mixed into the page colour
     instead: tinted enough to read as a control you can press rather than a
     band of paper — the whole strip is the tap target — and opaque enough to
     hide what it sits over. */
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
  color: var(--text);
  font: inherit; text-align: left;
  opacity: 0; pointer-events: none;
  transition: opacity .14s ease;
}
.hdr-mini:active { transform: none; }
body.hdr-collapsed .hdr-mini { opacity: 1; pointer-events: auto; }
.mini-inner {
  display: flex; align-items: center; gap: 10px;
  width: 100%; max-width: var(--shell, 560px); margin-inline: auto;
}
.ring.mini { width: 32px; height: 32px; }
.ring.mini::after { width: 25px; height: 25px; font-size: 0.6rem; font-weight: 700; }
.mini-copy { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.mini-count { font-size: 0.88rem; font-weight: 650; white-space: nowrap; }
.mini-where {
  font-size: 0.82rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mini-where::before { content: "·"; margin-right: 7px; }
/* An alarm that scrolled off the top still says so here. Hiding a warning
   because the screen is short is not the same as not having one. */
.mini-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--danger); flex: 0 0 auto;
}
.mini-up {
  margin-left: auto; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent-dark);
}
.mini-up svg { width: 17px; height: 17px; display: block; }
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
/* ---------- Brand lockup ----------
   Replaced a 🏡 emoji in the header. An emoji is drawn by the operating system,
   so its style is out of our hands and on Android it lands as a cartoon cottage
   that dates the whole screen. A tinted tile with a line mark reads as a product
   logo instead of a character in a sentence. */
.brand { display: flex; align-items: center; gap: 11px; min-width: 0; }
.brand-mark {
  width: 34px; height: 34px; flex: 0 0 auto;
  border-radius: 10px;
  display: grid; place-items: center;
  color: #fff;
  background: linear-gradient(150deg, var(--accent), var(--accent-dark));
  /* Hairline inner highlight; without it the tile reads as a flat green square. */
  box-shadow: 0 1px 2px rgba(40, 30, 15, .16), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.brand-mark svg { width: 19px; height: 19px; display: block; }
.brand-text { min-width: 0; }
.brand-title {
  margin: 0;
  font-size: 1.02rem; font-weight: 650; line-height: 1.2;
  letter-spacing: -0.016em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brand-sub {
  font-size: 0.7rem; font-weight: 650; color: var(--muted);
  letter-spacing: 0.07em; text-transform: uppercase;
  margin-top: 2px;
}
/* Login screen: same lockup, stacked and larger. */
.brand-lg { flex-direction: column; gap: 10px; text-align: center; margin-bottom: 4px; }
.brand-lg .brand-mark { width: 52px; height: 52px; border-radius: 15px; }
.brand-lg .brand-mark svg { width: 28px; height: 28px; }
.brand-lg .brand-title { font-size: 1.32rem; }

/* ---------- Header actions ----------
   "See her screen" used to be a plain link inside the Today tab's day-plan
   card, below the fold on a phone and absent from every other tab. It is now a
   header action so it is reachable from anywhere in the admin, and it outranks
   Log out: on a narrow screen the log-out label collapses to its icon and the
   preview action keeps its words. */
.hdr-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.hdr-action {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 38px; padding: 7px 12px;
  font-size: .84rem; font-weight: 640; letter-spacing: -0.01em;
  border-radius: 11px;
  /* Log out was pure white on every screen in the app. */
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  background: var(--accent-soft);
  color: var(--accent-dark);
  text-decoration: none;
  white-space: nowrap;
}
.hdr-action-primary {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 38%, transparent);
  color: var(--accent-dark);
  box-shadow: var(--shadow);
}
@media (hover: hover) {
  .hdr-action-primary:hover {
    background: color-mix(in srgb, var(--accent) 26%, transparent);
    border-color: var(--accent);
  }
}
.hdr-ico { display: grid; place-items: center; flex: 0 0 auto; }
.hdr-ico svg { width: 17px; height: 17px; display: block; }
/* On a phone all three fight for one row and the title loses -- "Grace Jo…".
   Wrapping the actions onto their own full-width row fixes the truncation and
   makes the preview action larger rather than smaller, which is the point of
   moving it here. Log out keeps its icon only; it is not the errand anyone
   opens the admin to run. */
@media (max-width: 430px) {
  .admin .header-row { flex-wrap: wrap; row-gap: 10px; }
  .admin .hdr-actions { width: 100%; }
  .admin .hdr-action-primary { flex: 1 1 auto; justify-content: center; min-height: 42px; font-size: .9rem; }
  /* Log out keeps its words on a phone.
     It used to collapse to a bare 41x38 icon here, on the reasoning that logging
     out is not the errand anyone opens the admin to run. Joel, on his phone:
     "it needs a easier way to log out." Measured, it was two faults at once --
     an unlabelled door glyph that has to be guessed at, and a target under the
     44x44 minimum for a thumb. The actions already wrap onto their own
     full-width row at this width, so there was room for the label all along. */
  .admin .hdr-label-soft { display: inline; }
  .admin #logout-btn { padding: 9px 14px; min-height: 44px; flex: 0 0 auto; }
}

.ring-wrap { display:flex; align-items:center; gap:12px; }
.ring {
  width: 56px; height: 56px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 0.95rem;
  background: conic-gradient(var(--accent) calc(var(--pct, 0) * 1%), var(--border) 0);
  flex-shrink: 0;
}
.ring::after {
  content: attr(data-label);
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.85rem;
}

.warning-banner {
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  color: var(--warn-text);
  border-radius: 12px;
  padding: 10px 12px;
  margin: 8px 0;
  font-size: 0.92rem;
}
.status-banner {
  background: var(--done-bg);
  border: 1px solid var(--done-border);
  border-radius: 12px;
  padding: 10px 12px;
  margin: 8px 0;
  font-size: 0.92rem;
}

.section { margin-top: 18px; }
.section-title {
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 0 0 8px 4px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.task-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 12px;
  margin-bottom: 8px;
  min-height: 44px;
}
.task-row.done { background: var(--done-bg); border-color: var(--done-border); }
.task-row.blocked { background: var(--danger-bg); border-color: var(--danger); }
.task-check {
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 2px solid var(--border);
  flex-shrink: 0;
  margin-top: 2px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
}
.task-row.done .task-check, .task-row.blocked .task-check {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}
.task-main { flex: 1; min-width: 0; }
.task-title { font-size: 1.03rem; font-weight: 600; }
.task-detail { font-size: 0.86rem; color: var(--muted); margin-top: 2px; }
.task-meta { font-size: 0.82rem; color: var(--muted); margin-top: 4px; display:flex; gap:8px; flex-wrap:wrap; align-items:center;}
.badge {
  display: inline-flex; align-items:center; gap:4px;
  font-size: 0.76rem; padding: 2px 7px; border-radius: 999px;
  background: var(--border); color: var(--text);
}
.task-time { font-weight: 600; color: var(--accent-dark); }
/* The row menu. This is where Not applicable, a miss reason and taking an added
   task back off all live, and it was a grey ellipsis on no background -- the
   least discoverable control in the app, on the row where the most decisions
   get made. A tinted disc says it is a button without shouting over the tick. */
.more-btn {
  width: 40px; height: 40px; min-height: 40px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  background: var(--accent-soft);
  color: var(--accent-dark); font-size: 1.3rem; font-weight: 700;
  flex-shrink: 0;
}

details.collapsible summary {
  cursor: pointer;
  list-style: none;
  padding: 6px 4px;
}
details.collapsible summary::-webkit-details-marker { display: none; }
details.collapsible summary .section-title { margin: 0; }

/* 22 Sep 2026 — a folded checklist section carried no disclosure marker at all.
   The native triangle is suppressed above (deliberately, it is ugly) and nothing
   replaced it, so OPENING & CHILDREN 6/6 and HOUSEHOLD BASELINE 11/11 read as
   headings with no content. Joel could not find a task that was present and
   ticked. Scoped to `.section` so the admin's `details.card.collapsible` cards
   are untouched. Pure CSS: no handler, no layout shift on toggle. */
details.collapsible.section > summary { position: relative; padding-right: 38px; }
details.collapsible.section > summary::after {
  content: '⌄';
  position: absolute; right: 4px; top: 6px;
  width: 28px; height: 28px;
  line-height: 24px; text-align: center;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  background: var(--accent-soft);
  color: var(--accent-dark);
  font-size: 1.15rem; font-weight: 700;
  transition: transform .15s ease;
}
details.collapsible.section[open] > summary::after { transform: rotate(180deg); }
.sec-peek {
  font-size: .75rem; margin-top: 3px; font-weight: 600;
  color: var(--accent-dark);
}
details.collapsible.section[open] > summary .sec-peek { display: none; }

.sheet-backdrop {
  position: fixed; inset: 0; background: rgba(20,15,5,0.4);
  display: none; align-items: flex-end; justify-content: center; z-index: 50;
}
.sheet-backdrop.open { display: flex; }
.sheet {
  background: var(--card);
  width: 100%; max-width: 560px;
  border-radius: 18px 18px 0 0;
  padding: 16px 16px 28px;
  max-height: 86vh;
  overflow-y: auto;
}
.sheet h3 { margin-bottom: 4px; }
.sheet .close-x {
  float: right; background: none; border: none; font-size: 1.4rem; min-height: 32px; color: var(--muted);
}
.sheet-action {
  display: block;
  width: 100%;
  text-align: left;
  margin-bottom: 8px;
}

.bottom-bar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  display: flex; gap: 8px;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: var(--bg);
  border-top: 1px solid var(--border);
  z-index: 20;
}
.bottom-bar button { flex: 1; }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th, td { text-align: left; padding: 10px 10px; border-bottom: 1px solid var(--border); }
th {
  color: var(--muted); font-weight: 600; font-size: 0.74rem;
  text-transform: uppercase; letter-spacing: .05em;
  background: var(--surface);
  position: sticky; top: 0;
}
tr:last-child td { border-bottom: none; }
@media (hover: hover) { tbody tr:hover td { background: var(--surface); } }
/* A table inside a card should reach the card's edges, not float in it. */
.card > table { margin: 4px -18px -18px; width: calc(100% + 36px); }
.card > table th:first-child, .card > table td:first-child { padding-left: 18px; }
.card > table th:last-child, .card > table td:last-child { padding-right: 18px; }

.tabs {
  display: flex; gap: 2px; overflow-x: auto; padding: 4px 0 10px;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  padding: 7px 14px; border-radius: 999px; background: transparent;
  border: 1px solid transparent; font-size: 0.88rem; font-weight: 600;
  white-space: nowrap; color: var(--muted); text-decoration: none;
  transition: background .15s ease, color .15s ease;
}
@media (hover: hover) { .tabs a:hover { background: var(--card); color: var(--text); } }
.tabs a.active {
  background: var(--accent); border-color: var(--accent); color: #fff;
  /* Lifts the selected tab off the bar instead of stamping a flat block on it. */
  box-shadow: 0 1px 2px rgba(40, 30, 15, .18);
}

.pill { padding: 2px 9px; border-radius: 999px; font-size: 0.76rem; font-weight: 700; }
.pill.ok { background: var(--done-bg); color: var(--accent-dark); }
/* An unrecorded fact, said out loud. Themed from the same variable as every
   other caution in the app so it stays legible in dark mode. */
.warn-text { color: var(--warn-text); font-weight: 600; }

.pill.warn { background: var(--warn-bg); color: var(--warn-text); }
.pill.bad { background: var(--danger-bg); color: var(--danger); }

.login-wrap {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.pin-display {
  display: flex; gap: 10px; justify-content: center; margin: 18px 0;
}
.pin-dot {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--border); background: var(--bg);
}
.pin-dot.filled { background: var(--accent); border-color: var(--accent); }
.pin-box {
  width: 46px; height: 56px; border-radius: 12px;
  border: 2px solid var(--border); background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.6rem; font-weight: 600; color: inherit;
}
.pin-box.filled { border-color: var(--accent); }

/* .warning-banner sets display:flex, which beats the plain hidden attribute.
   Without this rule a hidden element stays on screen. */
[hidden] { display: none !important; }
.keypad {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  margin-top: 10px;
}
.keypad button { font-size: 1.3rem; min-height: 58px; }

.thumb { width: 64px; height: 64px; border-radius: 10px; object-fit: cover; border: 1px solid var(--border); }

.error-text { color: var(--danger); font-size: 0.88rem; margin-top: 4px; }

.checkbox-row { display:flex; align-items:center; gap:8px; margin: 4px 0; }
.checkbox-row input[type=checkbox] { width: 22px; height:22px; min-height:auto; }

.empty-state { text-align: center; padding: 60px 20px; color: var(--muted); }

/* ---------- Hours, pace and alarms (added with the Gusto hours feature) ---------- */

/* Alarms sit above everything, are impossible to mistake for a normal banner,
   and the critical one pulses. This is the "big red alarm" for running past
   clock-out or running out of hours before the Friday school run. */
.alarm {
  border-radius: 10px;
  padding: 10px 12px;
  margin: 8px 0;
  border-left: 6px solid;
}
.alarm-title { font-weight: 700; font-size: 1rem; }
.alarm-body { font-size: 0.9rem; margin-top: 2px; line-height: 1.35; }

.alarm-critical {
  background: #b3261e;
  border-left-color: #7f1710;
  color: #fff;
  animation: alarm-pulse 1.6s ease-in-out infinite;
}
.alarm-warning { background: #fff3cd; border-left-color: #d99400; color: #5c4300; }
.alarm-info    { background: #e7f1ff; border-left-color: #4a7fd4; color: #1d3c6e; }

@keyframes alarm-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(179, 38, 30, 0.55); }
  50%      { box-shadow: 0 0 0 10px rgba(179, 38, 30, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .alarm-critical { animation: none; }
}

/* Ahead / behind for the day, in task-time terms. */
.pace {
  border-radius: 8px;
  padding: 7px 10px;
  margin: 6px 0 2px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.3;
}
.pace-ahead   { background: #e3f4e4; color: #1e4620; }
.pace-behind  { background: #fde2e1; color: #7f1710; }
.pace-ontrack { background: #fdf3d6; color: #5c4300; }
.pace-idle    { background: #eceff1; color: #37474f; }

/* Worked-hours strip: today, clock in/out, lunch, week against the 40h cap. */
.hours-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: 0.82rem;
  margin: 6px 0 4px;
  color: #33413a;
}
.hours-item b { font-weight: 700; }
.hours-item .on-clock { color: #1e7a32; font-weight: 700; }
.lunch-ok { color: #1e4620; }
.lunch-missing { color: #b3261e; font-weight: 600; }

.hours-bar {
  height: 5px;
  background: #dfe5e1;
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 4px;
}
.hours-bar-fill { height: 100%; background: #5b7553; transition: width 0.4s ease; }
.hours-bar-fill.near { background: #d99400; }
.hours-bar-fill.over { background: #b3261e; }
.hours-stale { font-size: 0.75rem; }

/* Not-due-today sections: present, countable, but visually demoted. */
.flexible-section > summary { opacity: 0.92; }
.section-blurb { font-size: 0.75rem; margin-top: 2px; font-weight: 400; }
.badge.due-soon  { background: #b3261e; color: #fff; font-weight: 700; }
.badge.due-later { background: #e8ede9; color: #4a5a50; }
.badge.has-media { background: #e7f1ff; color: #1d3c6e; }

/* End-of-shift notes: a real writing surface, not a checkbox. */
.eos-panel { margin-top: 18px; }
.eos-panel textarea {
  width: 100%;
  min-height: 110px;
  font: inherit;
  padding: 10px;
  border: 1px solid #c7d0c9;
  border-radius: 8px;
  resize: vertical;
  box-sizing: border-box;
}
.eos-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}
.eos-actions .muted { font-size: 0.8rem; }

.req { color: #b3261e; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; }

.video-preview, .media-view {
  width: 100%;
  max-height: 46vh;
  border-radius: 8px;
  background: #000;
  display: block;
  margin-bottom: 8px;
}
.thumb-video { max-width: 220px; max-height: 160px; border-radius: 6px; display: block; margin-top: 6px; background: #000; }

/* ---- Admin task editor: reorder / edit / remove rows ---- */
.admin-task {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.admin-task:last-child { border-bottom: none; }
.admin-task.removed { opacity: 0.7; }
.admin-task-move { display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; }
.admin-task-move button {
  min-height: 32px;
  min-width: 40px;
  padding: 0;
  font-size: 0.78rem;
  border-radius: 8px;
}
.admin-task-main { flex: 1; min-width: 0; }
.admin-task-actions { display: flex; gap: 6px; flex-shrink: 0; }
.admin-task-actions button { min-height: 38px; padding: 8px 12px; font-size: 0.9rem; }
.day-picker { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.day-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 0.92rem;
  font-weight: 600;
}
.day-chip input { width: auto; min-height: 0; }

/* Below ~430px the two action buttons wrap under the title instead of
   squeezing it to a couple of characters per line. */
@media (max-width: 430px) {
  .admin-task { flex-wrap: wrap; }
  .admin-task-main { flex: 1 1 60%; }
  .admin-task-actions { margin-left: auto; }
}

/* --- September 2026 model ------------------------------------------------ */

/* Not applicable: visibly settled, but clearly not a completion. Muted rather
   than green so a glance down the list never mistakes it for work done. */
.task-row.na { opacity: .62; }
.task-row.na .task-check { background: var(--surface); color: var(--muted); border-color: var(--border-strong); }
.task-row.na .task-title { text-decoration: line-through; text-decoration-color: var(--border-strong); }

/* Token-based, so these survive dark mode. The literals here were invisible
   on a dark background -- indigo text on an indigo-white chip. */
.badge.owner { background: var(--info-bg); color: var(--info-text); border: 1px solid var(--info-border); }
.badge.deadline { background: var(--warn-bg); color: var(--warn-text); border: 1px solid var(--warn-border); }

.done-def {
  background: var(--surface);
  border-left: 3px solid var(--border-strong);
  padding: .55rem .75rem;
  border-radius: var(--radius-sm);
  margin: .4rem 0 .8rem;
  font-size: .92rem;
}

.section-blurb { margin-top: .25rem; font-size: .82rem; line-height: 1.35; }

details.card.collapsible > summary { cursor: pointer; padding: .2rem 0; }
details.card.collapsible[open] > summary { margin-bottom: .5rem; }

/* Day templates: a real switch, laid out as a card in a responsive grid.
   Previously a bare checkbox in a flex row -- which is what produced the giant
   empty squares with the text crushed beside them. */
.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 10px;
}
.template-row {
  display: flex; align-items: flex-start; gap: .75rem;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.template-row:has(input:checked) {
  border-color: var(--accent);
  background: var(--done-bg);
  box-shadow: var(--shadow);
}
@media (hover: hover) { .template-row:hover { border-color: var(--border-strong); } }
.template-row > label { flex: 1; margin: 0; font-weight: 400; cursor: pointer; min-width: 0; }
.template-row .tmpl-name { font-weight: 650; display: block; }
.template-row .tmpl-hint { font-size: .82rem; color: var(--muted); margin-top: 2px; line-height: 1.35; }

/* The switch itself. Native checkbox underneath, so keyboard and screen
   readers keep working. */
.switch { position: relative; flex: 0 0 auto; width: 42px; height: 25px; margin-top: 1px; }
.switch input[type=checkbox] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; margin: 0; cursor: pointer; z-index: 1;
}
.switch .switch-track {
  display: block; width: 42px; height: 25px; border-radius: 999px;
  background: var(--border-strong); transition: background .18s ease;
}
.switch .switch-track::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 19px; height: 19px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.28);
  transition: transform .18s cubic-bezier(.3,.8,.4,1);
}
.switch input:checked ~ .switch-track { background: var(--accent); }
.switch input:checked ~ .switch-track::after { transform: translateX(17px); }
.switch input:focus-visible ~ .switch-track { box-shadow: 0 0 0 3px var(--accent-soft); }
.switch input:disabled ~ .switch-track { opacity: .5; }

/* The old preview banner was hard-coded cream and had no dark-mode colours, so
   the one screen an admin lands on from the header was also the one screen that
   ignored the theme. Same warning intent, drawn from the shared tokens, with the
   way back as a control rather than an underlined word in a paragraph. */
.preview-bar {
  display: flex; align-items: center; gap: 10px;
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  color: var(--warn-text);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin-bottom: 10px;
  font-size: .82rem; line-height: 1.35;
}
.preview-tag {
  flex: 0 0 auto;
  font-size: .64rem; font-weight: 750; letter-spacing: .09em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--warn-text) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn-text) 26%, transparent);
}
.preview-copy { flex: 1 1 auto; min-width: 0; }
.preview-copy strong { font-weight: 680; }
.preview-back {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 3px;
  min-height: 34px; padding: 6px 11px 6px 8px;
  border-radius: 10px;
  font-size: .8rem; font-weight: 650;
  text-decoration: none;
  color: var(--warn-text);
  background: color-mix(in srgb, var(--warn-text) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn-text) 24%, transparent);
}
.preview-back .hdr-ico svg { width: 15px; height: 15px; }
@media (max-width: 430px) { .preview-bar { flex-wrap: wrap; } .preview-copy { order: 3; flex-basis: 100%; } }


/* ---------- Desktop shell (Sep 2026) --------------------------------------

   The whole app was locked to a 560px column because it was built for Grace's
   phone. That is right for her screen and wrong for the admin dashboard, which
   Joel and Jen read on a laptop: a phone-width strip down the middle of a
   1400px window is the single thing that made this look twenty years old.

   Grace's checklist stays a single readable column. Admin gets room, and its
   cards flow into two columns once there is space for them.
--------------------------------------------------------------------------- */

.container { max-width: var(--shell, 560px); }

body.admin { --shell: 1120px; }

@media (min-width: 900px) {
  body.admin .container { padding: 20px 24px 40px; }
  body.admin .top-bar { padding: 14px 24px 8px; }

  /* Masonry-ish two-column flow. Cards keep their order but stop stretching
     into 1000px-wide bands of whitespace. */
  body.admin .tab-pane.cols {
    column-count: 2;
    column-gap: 18px;
  }
  body.admin .tab-pane.cols > .card,
  body.admin .tab-pane.cols > details.card {
    break-inside: avoid;
    display: inline-block;
    width: 100%;
  }
  /* Anything genuinely wide -- history tables, the task editor -- opts out. */
  body.admin .tab-pane.cols > .card.full { column-span: all; }
}

@media (min-width: 700px) {
  /* Tap targets can relax on a pointer device; 48px everywhere looks clumsy. */
  body.admin button, body.admin .btn { min-height: 40px; }
  body.admin input:not([type=checkbox]):not([type=radio]),
  body.admin select, body.admin textarea { min-height: 40px; }
}

/* A long history table should scroll inside its card rather than force the
   whole page sideways on a phone. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll table { min-width: 520px; }

.empty-state {
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}
.empty-state h2 { color: var(--text); }

/* Reduced motion: honour it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}


/* Task clock windows and captured numbers (Sep 2026). */
.badge.start { background: var(--info-bg); color: var(--info-text); border: 1px solid var(--info-border); }
.badge.start.now {
  background: var(--warn-bg); color: var(--warn-text); border-color: var(--warn-border);
  font-weight: 700;
}
.badge.deadline.late {
  background: var(--danger-bg); color: var(--danger); border-color: var(--danger);
  font-weight: 700;
}
.badge.count { background: var(--done-bg); color: var(--accent-dark); border: 1px solid var(--done-border); font-weight: 650; }

/* ---------------- Groceries ---------------- */

.grocery-list { display: flex; flex-direction: column; gap: 2px; }

.grocery-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 4px; border-bottom: 1px solid var(--hairline, #e8e6e1);
}
.grocery-row:last-child { border-bottom: 0; }

.grocery-check {
  flex: 0 0 auto; width: 30px; height: 30px; margin-top: 1px;
  border-radius: 9px; border: 2px solid var(--border-strong);
  background: var(--card); cursor: pointer;
  font-size: 1rem; line-height: 1; color: #fff;
  display: flex; align-items: center; justify-content: center;
  transition: background .12s ease, border-color .12s ease, transform .08s ease;
}
.grocery-check:active { transform: scale(.94); }
.grocery-check:disabled { cursor: default; }
.grocery-row.bought .grocery-check {
  background: var(--accent, #5b7553); border-color: var(--accent, #5b7553);
}
.grocery-row.bought .grocery-name { text-decoration: line-through; opacity: .55; }

/* Locked rows read as "not yet", not as "broken": the hourglass sits where the
   tick would be, so the reason is visible without reading the banner again. */
.grocery-row.locked .grocery-check {
  border-style: dashed; background: transparent;
  color: var(--muted); font-size: .85rem;
}

.grocery-mid { flex: 1 1 auto; min-width: 0; }
.grocery-name { font-weight: 600; line-height: 1.35; word-break: break-word; }
.grocery-meta { font-size: .82rem; margin-top: 3px; line-height: 1.4; }

.store-group { margin-top: 12px; }
.store-head {
  font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); padding: 6px 4px; border-bottom: 2px solid var(--border);
}

.grocery-pending { border-left: 4px solid #d99a2b; }
.approval-warning {
  background: #fdf5e6; border: 1px solid #edd9b0; color: #6b4d12;
  border-radius: 10px; padding: 11px 13px; margin-bottom: 10px;
  font-size: .9rem; line-height: 1.5;
}
.grocery-declined { border-left: 4px solid #b4483c; }
.declined-row { padding: 10px 4px; border-bottom: 1px solid var(--hairline, #e8e6e1); }
.declined-row:last-child { border-bottom: 0; }
.declined-name { font-weight: 600; text-decoration: line-through; opacity: .7; }
.declined-reason {
  margin-top: 4px; font-size: .9rem; line-height: 1.45;
  border-left: 3px solid #d8b4ae; padding-left: 10px;
}
.declined-meta { font-size: .78rem; margin-top: 4px; }

.grocery-form { display: flex; flex-direction: column; gap: 9px; }
.grocery-form-row { display: flex; gap: 9px; }
.grocery-form-row > * { flex: 1 1 0; min-width: 0; }

.quick-add { margin-top: 16px; }
.quick-add-head {
  font-size: .78rem; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; margin-bottom: 8px;
}
.chip-row { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  border: 1px solid var(--border-strong); background: var(--card);
  border-radius: 999px; padding: 7px 13px; font-size: .87rem; cursor: pointer;
  font-family: inherit; color: inherit; line-height: 1.3;
}
.chip:active { background: var(--soft, #f1efe9); }
.chip-store { color: var(--muted); font-size: .78rem; }

.receipt-done { margin-bottom: 12px; }
.receipt-row { font-size: .88rem; padding: 5px 0; }
.receipt-tick { color: var(--accent, #5b7553); font-weight: 700; }

#toast {
  position: fixed; left: 50%; bottom: 84px; transform: translate(-50%, 14px);
  background: #2b2a27; color: #fff; padding: 12px 18px; border-radius: 11px;
  font-size: .92rem; line-height: 1.4; max-width: min(420px, calc(100vw - 32px));
  box-shadow: 0 8px 26px rgba(0,0,0,.28); z-index: 90;
  opacity: 0; pointer-events: none; transition: opacity .18s ease, transform .18s ease;
}
#toast.show { opacity: 1; transform: translate(-50%, 0); }

@media (prefers-color-scheme: dark) {
  .approval-warning { background: #3a2f19; border-color: #6b5423; color: #f0dcae; }
  .declined-reason { border-left-color: #7a4a43; }
}

/* Admin grocery panel */
.gro-admin-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--hairline, #e8e6e1);
}
.gro-admin-row:last-child { border-bottom: 0; }
.gro-admin-mid { flex: 1 1 auto; min-width: 0; }
.gro-admin-name { font-weight: 600; }
.gro-admin-row.bought .gro-admin-name { text-decoration: line-through; opacity: .6; }
.gro-add { margin-top: 18px; padding-top: 16px; border-top: 2px solid var(--border); }
.gro-add h3 { margin: 0 0 10px; font-size: .95rem; }

.receipt-admin { margin-bottom: 14px; }
.receipt-admin img {
  max-width: 260px; width: 100%; border-radius: 10px;
  border: 1px solid var(--border-strong); display: block; margin-bottom: 6px;
}
.card.needs-action { border-left: 4px solid #d99a2b; }
.request-row { padding: 12px 0; border-bottom: 1px solid var(--hairline, #e8e6e1); }
.request-row:last-child { border-bottom: 0; }
.row-actions { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }

/* ---------------- Returns ---------------- */

/* A count on the tab itself. The Returns tab is the one screen with a deadline
   behind it, so it is the one tab allowed to ask for attention. */
.tab-badge {
  display: inline-block; margin-left: 5px; min-width: 18px; padding: 0 5px;
  border-radius: 999px; background: var(--accent-soft); color: var(--accent-dark);
  font-size: .72rem; font-weight: 800; line-height: 18px; text-align: center;
}
.tab-badge.urgent { background: var(--danger-bg); color: var(--danger); }

/* The nudge on the checklist. A tab nobody opens is a refund window nobody
   meets, so a return that is close to its date says so on the screen she is
   already looking at. */
.returns-banner {
  margin: 0 0 12px; padding: 12px 14px;
  background: var(--warn-bg); border: 1px solid var(--warn-border);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.returns-banner.overdue { background: var(--danger-bg); border-color: var(--danger); }
/* Every open return is listed, so the ones that are not yet pressing must not
   borrow the amber. Plain card, same information, no false alarm. */
.returns-banner.calm { background: var(--card); border-color: var(--border); }
.returns-banner .rb-head { font-weight: 700; color: var(--warn-text); margin-bottom: 4px; }
.returns-banner.overdue .rb-head { color: var(--danger); }
.returns-banner.calm .rb-head { color: var(--text); }
.returns-banner .rb-list { margin: 0 0 8px; padding-left: 20px; font-size: .9rem; }
.returns-banner .rb-list li { margin: 2px 0; }
/* "Take a box" rides on amber, red and plain backgrounds, so it carries its own
   cool-toned palette rather than inheriting whichever tone the banner is in. */
.returns-banner .rb-box {
  display: inline-block; margin-left: 6px; padding: 1px 8px;
  border-radius: 999px; font-size: .78rem; font-weight: 700; white-space: nowrap;
  background: var(--info-bg); color: var(--info-text); border: 1px solid var(--info-border);
}

.returns-list { display: flex; flex-direction: column; gap: 10px; }

.return-row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card);
}
.return-row.urgency-overdue { border-color: var(--danger); background: var(--danger-bg); }
.return-row.urgency-today,
.return-row.urgency-soon { border-color: var(--warn-border); background: var(--warn-bg); }
.return-row.done { opacity: .72; }
.return-row.done .return-name { text-decoration: line-through; }

.return-mid { flex: 1 1 auto; min-width: 0; }
.return-name { font-weight: 600; line-height: 1.35; word-break: break-word; }
.return-meta { font-size: .84rem; margin-top: 2px; }
.return-note {
  margin-top: 6px; padding: 6px 9px; font-size: .86rem;
  background: var(--surface); border-radius: 8px;
}
.return-due { margin-top: 4px; font-size: .86rem; font-weight: 700; }
.return-due.overdue { color: var(--danger); }
.return-due.today, .return-due.soon { color: var(--warn-text); }
.return-due.ok, .return-due.none { color: var(--muted); font-weight: 600; }

.qr-btn { margin-top: 9px; }
.grocery-check.checked {
  background: var(--accent); border-color: var(--accent); color: #fff;
}

/* The QR code, full screen.

   Forced white and forced light: scanners read contrast, and dark mode turning
   the field behind a QR to charcoal is the difference between one beep and a
   queue watching someone rotate a phone. Nothing else is on screen. */
.qr-overlay {
  position: fixed; inset: 0; z-index: 9999;
  background: #ffffff; color: #111;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding: 18px;
}
.qr-overlay .qr-head {
  font-weight: 700; font-size: 1rem; text-align: center; color: #111;
}
.qr-overlay .qr-img-wrap {
  flex: 1 1 auto; width: 100%; display: flex; align-items: center; justify-content: center;
  background: #fff;
  /* Without this a flex child will not shrink below its content, so a 900px QR
     pushes the title and the Close button off the bottom of the screen. */
  min-height: 0;
}
.qr-overlay img {
  max-width: 100%; max-height: 100%; object-fit: contain; background: #fff;
  image-rendering: crisp-edges;
}
.qr-overlay .qr-hint { font-size: .85rem; color: #555; text-align: center; }
.qr-overlay .qr-close { min-width: 160px; }

/* "Take a box" is a thing to do before leaving the house, not a detail, so it
   does not get to look like the rest of the grey metadata. */
.return-meta.box-flag { color: var(--warn-text); font-weight: 700; }
.return-meta.box-flag.no { color: var(--muted); font-weight: 400; }

/* While the QR is up, nothing behind it moves. Scrolling the checklist under a
   full-screen code is only ever an accident. */
body.qr-open { overflow: hidden; }

/* The retailer box sits beside the drop-off picker, which grows a second field
   when "Other" is chosen. Without this it stretches to match and turns into a
   tall empty rectangle. */
#a-ret-retailer, #e-ret-retailer { align-self: flex-start; }

/* Paste / drop target for a return's QR code. */
.qr-drop {
  border: 2px dashed var(--border-strong); border-radius: var(--radius-sm);
  padding: 12px; background: var(--surface); transition: border-color .12s ease, background .12s ease;
}
.qr-drop.over { border-color: var(--accent); background: var(--accent-soft); }
.qr-drop-head { font-weight: 600; margin-bottom: 2px; }
.qr-drop-hint { font-size: .84rem; margin: 0 0 8px; }
.qr-drop kbd {
  font: inherit; font-size: .8em; padding: 1px 5px; border-radius: 5px;
  border: 1px solid var(--border-strong); background: var(--card);
}
.qr-drop input[type="file"] { width: 100%; }
.qr-preview-img {
  display: block; max-width: 160px; margin: 8px 0;
  border: 1px solid var(--border); border-radius: 6px; background: #fff;
}

/* The code on the row itself. Small, on white, and always clickable to full
   size: enough to confirm the right code is attached without opening anything. */
.ret-thumb {
  flex: 0 0 auto; display: block; width: 64px; height: 64px; padding: 3px;
  border: 1px solid var(--border); border-radius: 8px; background: #fff;
  cursor: pointer;
}
.ret-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ret-thumb.empty {
  display: flex; align-items: center; justify-content: center;
  border-style: dashed; background: var(--surface); cursor: default;
  font-size: .68rem; color: var(--muted); text-align: center; line-height: 1.1;
}
.ret-admin-mid { display: flex; gap: 12px; align-items: flex-start; }
.ret-admin-text { min-width: 0; }
.return-qr-row { display: flex; gap: 10px; align-items: center; margin-top: 9px; }
.return-qr-row .qr-btn { margin-top: 0; }

/* Zoomed QR: the wrapper becomes the scroll window and the image is allowed to
   overflow it, so she can drag to the code itself. */
.qr-overlay .qr-img-wrap.zoomed { overflow: auto; align-items: flex-start; justify-content: flex-start; }
/* Three times the screen width, and pan in both directions.
   Filling the width alone is not enough: on a phone the fitted image already
   fills the width, so "zoom" would do nothing at all -- and an Amazon return
   card at that size does not scan. Measured 9 Sep 2026 by decoding a screenshot
   of the real card: unreadable fitted, clean at this magnification. */
.qr-overlay .qr-img-wrap.zoomed img {
  max-width: none; max-height: none; width: 300%; height: auto; cursor: zoom-out;
  /* Without this the image is still a flex item and shrinks straight back to
     the container width, so "300%" quietly renders as 100% and the zoom does
     nothing on the phone where it is actually needed. */
  flex: 0 0 auto;
}
.qr-overlay .qr-img-wrap img { cursor: zoom-in; }

/* Live order count on the peptide task.
   Inset rather than a badge: the count carries a short list of which orders, and
   a list inside the badge strip would wrap into noise on a phone. */
.ship-queue {
  margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--radius-sm); background: var(--surface);
}
.ship-queue.work { border-left-color: var(--accent); background: var(--accent-soft); }
.ship-queue.clear { border-left-color: var(--done-border); background: var(--done-bg); }
.ship-queue.unknown { border-left-color: var(--warn-border); background: var(--warn-bg); }

.ship-queue-head { font-size: 0.88rem; font-weight: 700; color: var(--text); }
.ship-queue.unknown .ship-queue-head { color: var(--warn-text); }

.ship-queue-list { margin: 6px 0 0; padding: 0; list-style: none; }
.ship-queue-list li {
  display: flex; flex-wrap: wrap; gap: 2px 8px;
  font-size: 0.82rem; padding: 3px 0;
  border-top: 1px solid var(--border);
}
.ship-queue-list li:first-child { border-top: 0; }
.ship-who { font-weight: 600; color: var(--text); }
.ship-items { color: var(--muted); }

.ship-queue-note { margin-top: 6px; font-size: 0.76rem; color: var(--muted); }

/* How far along an order already is. Each stage is a different amount of work,
   and reading them as identical is how a box Joel already packed gets opened. */
.ship-stage {
  flex-basis: 100%;
  align-self: flex-start;
  display: inline-block; width: fit-content;
  margin-top: 2px; padding: 1px 7px;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.01em;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text);
}
.ship-stage-boxed { border-color: var(--done-border); background: var(--done-bg); }
.ship-stage-label_printed { border-color: var(--info-border); background: var(--info-bg); }
.ship-stage-help { flex-basis: 100%; font-size: 0.76rem; color: var(--muted); }

/* Unpaid orders. Deliberately not styled like a task: this is not work to get
   through, it is a thing that must not happen, so it reads as a stop sign. */
.ship-unpaid {
  margin-top: 10px; padding: 8px 10px;
  border: 1px solid var(--danger); border-left-width: 3px;
  border-radius: var(--radius-sm); background: var(--danger-bg, var(--warn-bg));
}
.ship-unpaid-head { font-size: 0.85rem; font-weight: 700; color: var(--danger); }
.ship-unpaid-note { margin-top: 6px; font-size: 0.79rem; font-weight: 600; color: var(--text); }
.ship-unpaid .ship-queue-list li { border-top-color: var(--border); }

/* ---- Ship queue on the admin dashboard ---- */
/* Grace sees a count; Joel sees the count plus every row deliberately kept out
   of it, so nothing can drift unnoticed the way order 10043 did since June. */
.ship-admin .overdue { color: var(--danger); font-weight: 600; }
.ship-admin table { width: 100%; }
.ship-admin td, .ship-admin th { vertical-align: top; }
.ship-admin a { color: var(--accent); text-decoration: none; }
.ship-admin a:hover { text-decoration: underline; }

/* ---------------- Feedback tab (admin only) ----------------
   Visually quieter than the rest of the admin panel on purpose. This list is
   read next to the person it is about as often as not, so nothing on it shouts. */

.fb-private { border-left: 4px solid var(--info-border); background: var(--info-bg); }
.fb-private h2 { margin-bottom: 4px; }
@media (prefers-color-scheme: dark) { .fb-private { background: var(--surface); } }

.fb-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--border);
}
.fb-row:last-child { border-bottom: 0; }
.fb-main { flex: 1 1 auto; min-width: 0; }
/* Notes are sentences, not labels: wrap them properly rather than truncating,
   because half a note is worse than none when you are about to read it aloud. */
.fb-body { font-weight: 500; line-height: 1.45; overflow-wrap: anywhere; white-space: pre-wrap; }
.fb-meta { font-size: .84rem; margin-top: 4px; }
.fb-outcome {
  margin-top: 8px; padding: 8px 10px; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--border);
  font-size: .9rem; white-space: pre-wrap; overflow-wrap: anywhere;
}
.fb-actions { display: flex; flex-direction: column; gap: 6px; flex: 0 0 auto; }
.fb-tick { white-space: nowrap; }
.fb-row.praise { border-left: 3px solid var(--done-border); padding-left: 10px; }
.fb-row.done .fb-body { opacity: .72; }

.fb-kinds { display: flex; gap: 8px; margin: 10px 0 4px; }
.fb-kind {
  flex: 1 1 0; min-height: 42px; font-size: .92rem;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  background: var(--accent-soft); color: var(--accent-dark);
}
.fb-kind.on { background: var(--accent-soft); border-color: var(--accent); font-weight: 600; }

/* Side-by-side is fine on a laptop; on a phone the tick button needs the width.
 *
 * Wrapping, not one long row. Four controls — Send to Grace, Mark discussed,
 * Edit, Remove — laid out in a single row reached 469px on a 390px screen, so
 * Remove sat 79px past the right edge with nothing to scroll it into view. It is
 * visible, clipped, in the screenshot Joel sent on 14 Sep: the last column reads
 * "Ren". `flex: 1 1 0` does not save it, because each button still has a
 * min-content width and four of those together are wider than the phone.
 *
 * Two per line instead. Same fix as the tab strip, for the same reason: a
 * control that is in the page but off the glass is not a control. */
@media (max-width: 560px) {
  .fb-row { flex-direction: column; gap: 8px; }
  .fb-actions { flex-direction: row; flex-wrap: wrap; width: 100%; }
  .fb-actions .btn { flex: 1 1 calc(50% - 6px); min-width: 0; }
}

/* ---- People (Settings tab), 10 Sep 2026 ------------------------------------
   Joel asked for add/remove/edit in Settings rather than asking me each time.
   A list of names is a list of who can see the hours, the receipts and the
   private feedback notes, so it is built to be read at a glance: role and
   access state are on the row, not behind the button. */

.people-list { margin: 10px 0 4px; }

.person-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--border);
}
.person-row:last-child { border-bottom: 0; }

.person-avatar {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--info-bg); color: var(--info-text);
  border: 1px solid var(--info-border);
  font-size: .84rem; font-weight: 700; letter-spacing: .02em;
}
.person-avatar.manager {
  background: var(--accent-soft); color: var(--accent-dark); border-color: var(--done-border);
}

.person-main { flex: 1 1 auto; min-width: 0; }
.person-name {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-weight: 600; line-height: 1.3; overflow-wrap: anywhere;
}
.person-meta { font-size: .84rem; margin-top: 3px; }
.person-more { flex: 0 0 auto; white-space: nowrap; }

.badge.role { background: var(--surface); color: var(--muted); border: 1px solid var(--border); font-weight: 600; }
.badge.you  { background: var(--accent-soft); color: var(--accent-dark); border: 1px solid var(--done-border); font-weight: 600; }
.badge.off  { background: var(--warn-bg); color: var(--warn-text); border: 1px solid var(--warn-border); font-weight: 600; }

/* Switched off, not gone. Faded enough to read as inactive, legible enough to
   still be checked. */
.person-row.off .person-avatar { opacity: .5; }
.person-row.off .person-name { opacity: .72; }

.status-banner.bad {
  background: var(--danger-bg); border-color: var(--danger); color: var(--danger);
}

/* Sheets: these two were used by the grocery decline sheet and never defined,
   so its buttons stacked full width. Defining them here fixes that sheet too. */
.sheet-actions { display: flex; gap: 10px; margin-top: 14px; }
.sheet-actions .btn { flex: 1 1 0; }
.sheet-stack { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.sheet-stack .btn { width: 100%; }

@media (max-width: 560px) {
  .person-row { flex-wrap: wrap; }
  .person-more { width: 100%; }
}

/* ---------------- Vehicle GPS ---------------- */
/* A rail with the stops on it. The drive between two stops is the line itself,
   labelled, so distance and duration read as the connection they are rather
   than as two more rows competing with the places. */
.head-icon {
  width: 20px; height: 20px; vertical-align: -4px;
  margin-right: 8px; color: var(--accent); display: inline-block;
}
.gps-timeline { position: relative; margin: 4px 0 0; }
.gps-stop { position: relative; padding: 0 0 0 26px; }
.gps-dot {
  position: absolute; left: 3px; top: 5px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--card); border: 2.5px solid var(--accent);
}
.gps-now .gps-dot {
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.gps-body { padding-bottom: 2px; }
.gps-title { font-weight: 600; line-height: 1.35; }
.gps-title a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--border-strong); }
.gps-title a:hover { border-bottom-color: var(--accent); }
.gps-when { color: var(--muted); font-size: .88rem; margin-top: 2px; }
.gps-dur {
  display: inline-block; margin-left: 6px; padding: 1px 7px;
  border-radius: 999px; background: var(--surface);
  border: 1px solid var(--border); color: var(--text);
  font-size: .8rem; font-variant-numeric: tabular-nums;
}
.gps-name-btn {
  margin: 6px 0 2px; padding: 3px 10px; font-size: .78rem;
  min-height: 0; border-radius: 999px;
  /* Dashed keeps the "nothing here yet" reading; the tint keeps it off the
     white-button list. Admin-only: this never renders on Grace's screen. */
  background: var(--accent-soft);
  color: var(--accent-dark); border: 1px dashed color-mix(in srgb, var(--accent) 55%, transparent);
}
.gps-leg {
  position: relative; margin: 2px 0; padding: 8px 0 8px 26px;
  color: var(--muted); font-size: .84rem;
}
/* The rail between two stops. Its left edge sits on the dot centre (3px + half
   of an 11px dot), so the line runs through the middle of the markers rather
   than beside them. */
.gps-leg::before {
  content: ""; position: absolute; left: 7px; top: 0; bottom: 0;
  width: 2px; border-radius: 2px; background: var(--border);
}
@media (max-width: 480px) {
  .gps-when { font-size: .84rem; }
}
.place-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.place-row:last-child { border-bottom: 0; }
.place-row { flex-wrap: wrap; }
.place-label { font-weight: 600; }
.place-meta { flex: 1 1 auto; font-size: .84rem; }
/* Name field and its button travel together, and stay one row on a phone. */
.place-rename { display: flex; align-items: center; gap: 6px; }
.place-rename input[type="text"] { width: 11em; font-weight: 600; margin: 0; }
.place-fixed { font-size: .84rem; }

/* ---------------- Notes sent for sign-off (13 Sep 2026) ----------------
   Colour comes from the theme tokens, not from literals, so the banner follows
   light and dark like everything else. The last hard-coded cream panel in this
   app was the Bouncie one, and it was the only screen that ignored the theme. */
/* Tinted, not near-white (17 Sep 2026).
   --info-bg is #eef1f7, which is lighter than this app's paper, so in light mode
   the one control on her screen asking her to do something rendered as a white
   slab. Joel's rule is that anything clickable is coloured and only a disabled
   thing looks inert, and the colour check caught this the first afternoon she
   actually had notes waiting — 5 of them, shared minutes earlier. */
.notes-banner {
  display: flex; align-items: center; gap: 10px; width: 100%;
  margin-top: 6px; padding: 10px 12px; text-align: left;
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--text);
  font: inherit; cursor: pointer; min-height: 48px;
}
.notes-banner:hover { border-color: var(--accent); }
.notes-banner .notes-copy { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.notes-banner .notes-sub { font-size: 0.84rem; opacity: 0.85; }
.notes-banner .notes-go {
  flex: none; font-size: 0.82rem; font-weight: 600;
  border: 1px solid var(--accent); border-radius: 999px; padding: 3px 10px;
  background: var(--accent); color: #fff;
}
/* A soft pulse, not an animation loop: it draws the eye once on paint and stops.
   Something that blinks forever gets tuned out within a week. */
.notes-banner .notes-dot {
  flex: none; width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft);
}
/* Signed off, and still a button.
   `background: transparent` made this one invisible rather than merely calm,
   which is the other half of the same rule: the scan reads an alpha of 0 as a
   button that is not there. Faintly tinted, dashed, quiet — but present. */
.notes-banner.quiet {
  background: var(--accent-faint); border-style: dashed; border-color: var(--accent);
  color: var(--muted); min-height: 0; padding: 6px 10px;
}
.notes-banner.quiet .notes-go { background: transparent; color: var(--accent); }
@media (prefers-reduced-motion: no-preference) {
  .notes-banner .notes-dot { animation: notes-pulse 2.4s ease-out 3; }
  @keyframes notes-pulse {
    0% { box-shadow: 0 0 0 0 var(--accent-soft); }
    70% { box-shadow: 0 0 0 9px transparent; }
    100% { box-shadow: 0 0 0 4px var(--accent-soft); }
  }
}

.note-card {
  border: 1px solid var(--border); border-left: 4px solid var(--accent);
  border-radius: var(--radius-sm); background: var(--surface);
  padding: 12px; margin: 10px 0;
}
.note-card.praise { border-left-color: var(--done-border); }
.note-card.signed { border-left-color: var(--border-strong); opacity: 0.88; }
.note-card .note-kind {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin-bottom: 4px;
}
.note-card .note-text { font-size: 1.02rem; line-height: 1.45; }
.note-card .note-meta { font-size: 0.8rem; margin-top: 6px; }
.note-card .note-signed { margin-top: 8px; color: var(--accent-dark); font-weight: 600; font-size: 0.88rem; }
.note-card .note-reply {
  margin-top: 6px; padding-left: 10px; border-left: 2px solid var(--border-strong);
  color: var(--muted); font-style: italic;
}
.note-card .note-reply-label { display: block; margin-top: 10px; font-size: 0.86rem; }
.note-card textarea { margin-top: 4px; }
.note-card .note-ack { margin-top: 8px; width: 100%; }

/* ---------------- "There is an issue with this one" ----------------
   Warm amber, which is the same vocabulary the app already uses for a warning,
   rather than the red it uses for something actually wrong or overdue. The point
   is take extra care here, not you are in trouble. */
.task-row.attention {
  border-color: var(--warn-border);
  box-shadow: inset 3px 0 0 var(--warn-border);
}
.task-row.attention.done { box-shadow: inset 3px 0 0 var(--warn-border); opacity: 0.9; }
.task-flag-note {
  margin-top: 4px; font-size: 0.86rem; line-height: 1.35;
  color: var(--warn-text); background: var(--warn-bg);
  border: 1px solid var(--warn-border); border-radius: var(--radius-sm);
  padding: 6px 9px;
}
.badge.attention {
  background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-text);
  font-weight: 600;
}
.admin-task.highlighted { box-shadow: inset 3px 0 0 var(--warn-border); }
.admin-task .admin-task-flag {
  margin-top: 4px; font-size: 0.84rem; color: var(--warn-text);
  background: var(--warn-bg); border: 1px solid var(--warn-border);
  border-radius: var(--radius-sm); padding: 5px 8px;
}

/* ---------------- Owner priority, at risk, owner excused ----------------
   Three states that must not look like each other.

   An owner priority is the accent colour, because it is the one thing on the
   screen somebody chose to put there today. At risk is amber, borrowing the
   vocabulary already used for "take extra care here". Excused is deliberately
   quiet and greyed: an owner has removed it from the day, and a settled row
   that still draws the eye is noise she has to learn to ignore. */
.task-row.priority {
  border-color: var(--accent);
  box-shadow: inset 3px 0 0 var(--accent);
}
.badge.priority {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent-dark);
  font-weight: 600;
}
.task-row.at-risk {
  border-color: var(--warn-border);
  box-shadow: inset 3px 0 0 var(--warn-border);
}
.task-row.priority.at-risk {
  /* Both at once: the accent stays on the edge so the row is still findable as
     an owner instruction, and the amber badge carries the warning. */
  box-shadow: inset 3px 0 0 var(--accent), inset 6px 0 0 var(--warn-border);
}
.badge.at-risk {
  background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-text);
  font-weight: 600;
}
.task-row.excused { opacity: 0.72; }
.task-row.excused .task-check { color: var(--muted); }
.task-row.excused .task-title { text-decoration: none; }

/* The standing explanation at the top of a section an owner has switched off
   for the day, with its one-tap resolution. */
.section-notice {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  flex-wrap: wrap;
  margin: 6px 0 10px;
  padding: 9px 11px;
  font-size: 0.88rem; line-height: 1.4;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.section-notice .btn { flex: 0 0 auto; }

/* A card that is allowed to ask for attention, because something in it is
   waiting on a decision rather than merely reporting a fact. */
.card.attention-card {
  border-color: var(--warn-border);
  box-shadow: inset 3px 0 0 var(--warn-border);
}

/* ---- School drop-off (vehicle card) -------------------------------------
   Colour carries meaning here, so it is never the only carrier: every state
   also prints its verdict in words. A red left edge on a phone in sunlight is
   not something to hang a payroll conversation on. Tokens throughout, so this
   follows the theme rather than pinning a cream card into dark mode -- the
   mistake the GPS banner shipped with on 10 Sep -- and which this very block
   shipped with for twenty minutes, using a --line token that does not exist and
   silently falling back to near-black. The token is --border. */
.gps-school {
  margin: 4px 0 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--card);
}
.gps-school-head {
  padding: 8px 12px;
  font-weight: 600;
  font-size: .95rem;
  border-bottom: 1px solid var(--border);
}
.gps-school-body { padding: 10px 12px; border-left: 4px solid transparent; }
.gps-school-body.is-ontime  { border-left-color: var(--accent); background: var(--accent-soft); }
.gps-school-body.is-unclear { border-left-color: var(--warn-border); background: var(--warn-bg); color: var(--warn-text); }
.gps-school-body.is-late    { border-left-color: var(--danger); background: var(--danger-bg); }
.gps-school-note { margin-top: 6px; font-size: .86rem; line-height: 1.45; }

/* A window is one idea, so its two ends belong on one line. Full-width stacked
   inputs with a stray "to" between them read as two unrelated settings. */
.time-range { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.time-range input[type="time"] { width: auto; flex: 0 1 11rem; }

/* ---------------- Additional work — informational only ----------------

   Deliberately the quietest block on the page. It sits last, uses the surface
   inset rather than a card, and borrows no colour from anything scored: the one
   thing this section must never do is look like it counts towards the day.

   Every colour below is a token, so it follows the theme. The GPS banner and
   five grocery rules shipped with hard-coded creams that vanished in dark mode,
   which is the mistake this comment exists to stop repeating. */
.additional-section {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.additional-section .section-title { color: var(--muted); }
.additional-entry {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 9px 11px; margin-bottom: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.additional-entry.flagged { border-color: var(--warn-border); }
.additional-body { flex: 1 1 100%; font-size: 0.94rem; line-height: 1.4; }
.additional-entry .task-meta { flex: 1 1 auto; margin: 0; }
.additional-gate { margin-top: 10px; }
.additional-input {
  width: 100%; box-sizing: border-box; margin-bottom: 8px;
  padding: 9px 11px;
  font: inherit; font-size: 0.94rem; line-height: 1.4;
  color: var(--text); background: var(--card);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  resize: vertical;
}
.additional-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Joel, 14 Sep: "all buttons that are an option to be clicked need to be green
   or a color and not white or grayed out."

   These were transparent with muted grey text, which is also exactly how this
   app draws a button you CANNOT press -- so a live action and a dead one were
   the same picture, and the only way to tell them apart was to try. Every
   secondary action now carries the accent as a tint: plainly quieter than a
   filled primary, plainly alive.

   The hierarchy that tint used to carry is still needed, so it moved to colour
   rather than disappearing: accent for a normal secondary action, .neutral for
   a way out of something (Cancel, Back), .danger for anything that destroys.
   Nothing is white; nothing that can be pressed looks disabled. */
button.subtle, .btn.subtle {
  background: var(--accent-soft);
  color: var(--accent-dark);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  font-weight: 600;
}
button.subtle:not(:disabled):hover, .btn.subtle:not(:disabled):hover {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
}
/* Cancel, Back, Not now. A way out of a dialog should not wear the same green
   as the thing the dialog is asking you to do. */
button.neutral, .btn.neutral {
  background: var(--info-bg);
  color: var(--info-text);
  border-color: var(--info-border);
  font-weight: 600;
}
button.neutral:not(:disabled):hover, .btn.neutral:not(:disabled):hover {
  border-color: color-mix(in srgb, var(--info-text) 45%, transparent);
}
/* Destructive. Coloured, like everything else, but never green: Remove sitting
   in the same colour as Save is how a record gets deleted by muscle memory. */
/* Solid, not a pale wash. --danger-bg measures at rgb(251,234,231), which is
   one step off white and read as white on Joel's screen -- the exact complaint.
   A destructive action is the one button on a row that must be unmistakable. */
button.danger, .btn.danger {
  background: var(--danger);
  color: var(--danger-on);
  border-color: var(--danger);
  font-weight: 600;
}
button.danger:not(:disabled):hover, .btn.danger:not(:disabled):hover {
  background: color-mix(in srgb, var(--danger) 82%, #000);
  border-color: color-mix(in srgb, var(--danger) 82%, #000);
}
/* Disabled is the ONE state allowed to look inert, and it now looks unlike
   anything pressable rather than like a quiet button. */
button:disabled, .btn:disabled {
  background: transparent; color: var(--muted);
  border-color: var(--border); font-weight: 400;
  opacity: .6; cursor: default;
}
/* A one-time assignment older than the day it is sitting on. */
.badge.carried {
  background: var(--info-bg); border-color: var(--info-border); color: var(--info-text);
}

/* ---------- Documents ---------- */
/* Every colour below is an existing token that is defined in both the light and
   the dark block above. The GPS banner shipped on 11 Sep with a colour that only
   existed in one of them and was invisible in the other; the fix is not to be
   more careful, it is to never name a colour that is not in that list. */

.doc-card .doc-links { display: flex; flex-direction: column; gap: 8px; }
.doc-link {
  display: flex; align-items: center; gap: 12px; text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; background: var(--surface); color: var(--text);
}
.doc-link:active { background: var(--accent-soft); }
.doc-link-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.doc-link-title { font-weight: 600; }
.doc-link-blurb { color: var(--muted); font-size: .88rem; }
.doc-link-meta { color: var(--muted); font-size: .78rem; }
.doc-link-go {
  flex: none; font-size: .85rem; font-weight: 600; color: var(--accent-dark);
  border: 1px solid var(--border-strong); border-radius: 999px; padding: 5px 12px;
}

.doc-list { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.doc-row {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; background: var(--surface);
  display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-start;
}
.doc-row.doc-hidden { opacity: .6; }
.doc-main { flex: 1 1 320px; min-width: 0; }
.doc-title { font-weight: 600; }
.doc-blurb { margin-top: 2px; }
.doc-meta { font-size: .82rem; margin-top: 4px; }
.doc-url a { word-break: break-all; }
.doc-check { margin-top: 6px; font-size: .82rem; }
.doc-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }

.chip-good { background: var(--done-bg); border-color: var(--done-border); }
.chip-bad { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }

/* ---------- Vehicle history ---------- */

.gps-switch { display: flex; gap: 6px; margin: 0 0 10px; }
.gps-switch .is-on { background: var(--accent-soft); border-color: var(--accent); font-weight: 600; }
.gps-back { margin-bottom: 10px; }
.gps-daylabel { margin: 0 0 6px; font-size: 1.05rem; }

.gps-days { display: flex; flex-direction: column; gap: 6px; }
.gps-day {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; background: var(--card);
}
.gps-day:hover:not([disabled]) { border-color: var(--border-strong); }
.gps-day[disabled] { cursor: default; opacity: .55; }
.gps-day-off { background: var(--surface); }
.gps-day-when { flex: 1 1 160px; }
.gps-day-figs { flex: 2 1 220px; font-size: .9rem; }
.gps-day-chip { flex: none; }

/* ---- Outside errand verification ---------------------------------------
   Tokens only. The GPS banner shipped on 11 Sep with a colour name that does
   not exist in this stylesheet, which looked fine in light mode and vanished
   in dark; every value here resolves in both. */
.errand-gate {
  flex-direction: column; align-items: stretch; gap: 8px;
  color: var(--text);
}
.errand-gate.unanswered {
  background: var(--warn-bg);
  border-color: var(--warn-border);
}
.errand-gate .errand-q { font-weight: 600; }
.errand-gate .errand-warn { color: var(--warn-text); font-size: 0.85rem; }
.errand-gate .errand-stamp { font-size: 0.82rem; }
.errand-answers { display: flex; gap: 8px; flex-wrap: wrap; }
.errand-answers .btn { flex: 1 1 auto; min-width: 44%; }

/* ---- End-of-day report (admin) ----------------------------------------- */
.report-head { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; justify-content: space-between; }
.report-forced {
  margin: 8px 0; padding: 9px 11px;
  background: var(--warn-bg); border: 1px solid var(--warn-border);
  color: var(--warn-text); border-radius: var(--radius-sm);
}
.report-metrics { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.report-metric {
  flex: 1 1 140px; padding: 9px 11px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.report-metric .metric-value { font-size: 1.15rem; font-weight: 650; color: var(--text); }
.report-metric .metric-label { font-size: 0.78rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.report-metric .metric-sub { font-size: 0.8rem; color: var(--muted); }
.report-section { margin-top: 14px; }
.report-section h4 { margin: 0 0 6px; font-size: 0.92rem; }
.report-item { padding: 7px 0; border-top: 1px solid var(--border); }
.report-item:first-of-type { border-top: none; }
.report-item .ri-title { font-weight: 550; }
.report-item .ri-note, .report-item .ri-risk { font-size: 0.85rem; color: var(--muted); }
.report-item .ri-risk.late { color: var(--warn-text); }
.amend-row { padding: 7px 0; border-top: 1px dashed var(--border); font-size: 0.87rem; }
.amend-row .amend-from { color: var(--muted); text-decoration: line-through; }
.amend-row .amend-to { color: var(--text); font-weight: 550; }
.comment-row { padding: 7px 0; border-top: 1px solid var(--border); }
.comment-row .comment-meta { font-size: 0.78rem; color: var(--muted); }

/* ---- Performance dashboard --------------------------------------------
   Every metric keeps its own tile. There is deliberately no "overall" tile:
   the build guide forbids collapsing these into one number, and the surest
   way to honour that is to give the layout nowhere to put one. */
.perf-switch { display: flex; gap: 6px; margin-bottom: 10px; }
.perf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.perf-tile {
  padding: 10px 12px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.perf-tile .pt-value { font-size: 1.3rem; font-weight: 650; color: var(--text); }
.perf-tile .pt-label { font-size: 0.78rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.perf-tile .pt-sub { font-size: 0.8rem; color: var(--muted); margin-top: 2px; }
.perf-tile.warn { border-color: var(--warn-border); box-shadow: inset 3px 0 0 var(--warn-border); }
.perf-caveat { font-size: 0.8rem; color: var(--muted); margin-top: 6px; }
.perf-repeats { margin-top: 12px; }
.perf-repeat-row { padding: 6px 0; border-top: 1px solid var(--border); display: flex; justify-content: space-between; gap: 10px; }
.perf-days { margin-top: 14px; overflow-x: auto; }
.perf-days table { border-collapse: collapse; width: 100%; font-size: 0.85rem; }
.perf-days th, .perf-days td { padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.perf-days th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
.perf-days td.prov { color: var(--muted); font-style: italic; }

/* ---------------------------------------------------------------------------
   School drop-off note — the one thing on the manager's side of the app that
   comes from the vehicle. Three tones, and only a proven late arrival is amber:
   "not timed yet" and "too close to call" both stay neutral, because an
   unmeasured morning must never look like a bad one.

   Tokens only. A literal colour here would be correct in light mode and wrong
   in dark, which is the mistake the GPS banner and five grocery rules already
   made once.
--------------------------------------------------------------------------- */
.dropoff-note {
  margin: 8px 0 2px;
  padding: 9px 11px;
  border: 1px solid var(--info-border);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--info-bg);
  color: var(--info-text);
  font-size: 13px;
  line-height: 1.45;
}
.dropoff-note.late {
  border-color: var(--warn-border);
  background: var(--warn-bg);
  color: var(--warn-text);
}
.dropoff-note.ok {
  border-color: var(--done-border);
  background: var(--done-bg);
  color: var(--text);
}
.dropoff-standard { font-weight: 600; }
.dropoff-line { margin-top: 4px; }
.dropoff-how {
  margin-top: 5px;
  font-size: 12px;
  opacity: .8;
}


/* ---------------------------------------------------------------- log out
   The house manager's log out sat at the end of the tab strip, which scrolls
   sideways. On a 390 px phone it rendered at x=430 -- forty pixels past the
   right edge, reachable only by someone who already knew to swipe the tabs.
   Present in the DOM, invisible in the hand.

   Her strip holds four tabs, so it can simply wrap instead of scrolling, which
   puts log out on a line of its own, right-aligned and always on screen. Scoped
   away from .admin deliberately: that strip has eleven tabs and wrapping it would
   build a wall of pills above every page. */
body:not(.admin) .tabs { flex-wrap: wrap; overflow-x: visible; row-gap: 4px; }
body:not(.admin) #logout-link {
  margin-left: auto;
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 7px 16px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted);
  font-size: 0.88rem; font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
@media (hover: hover) {
  body:not(.admin) #logout-link:hover { color: var(--text); border-color: var(--muted); }
}

/* ---------- Decisions ----------
   Questions waiting on an owner. The card has to carry enough evidence to be
   answerable on the spot, so it is denser than the rest of the admin panel --
   these were going unanswered because answering meant going and looking
   something up first. */

.tab-badge {
  display: inline-block;
  min-width: 18px; padding: 0 5px; margin-left: 6px;
  border-radius: 999px;
  background: var(--danger); color: #fff;
  font-size: 0.72rem; font-weight: 700; line-height: 18px; text-align: center;
  vertical-align: 1px;
}
/* On the selected tab the red badge would clash with the green pill, but white
   on a 28%-white tint measured 2.4:1 -- legible to me, not to everyone. Solid
   dark behind it reads in both colour schemes. */
.tabs a.active .tab-badge { background: rgba(0,0,0,.42); color: #fff; }

.dec-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 6px; font-size: .8rem; }
.decision-card h3 { margin: 0 0 6px; font-size: 1.1rem; line-height: 1.3; }
.dec-q { margin: 0 0 8px; font-weight: 600; }
.dec-context {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-bottom: 12px;
  font-size: .9rem; line-height: 1.45;
}
.dec-context p { margin: 0 0 7px; }
.dec-context p:last-child { margin-bottom: 0; }
.dec-bullets { margin: 0 0 7px; padding-left: 18px; }
.dec-bullets li { margin-bottom: 3px; }
.dec-bullets:last-child { margin-bottom: 0; }

/* One button per answer, stacked. A row of buttons on a phone truncates the
   labels, and a truncated label on an irreversible choice is a trap. */
.dec-options { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.dec-opt {
  display: block; width: 100%; text-align: left;
  padding: 11px 14px; min-height: 44px;
  border-radius: var(--radius-sm);
  line-height: 1.35;
}
.dec-opt-label { display: block; font-weight: 600; }
.dec-opt-detail { display: block; margin-top: 2px; font-size: .84rem; font-weight: 400; color: var(--muted); }
@media (hover: hover) { .dec-opt:hover { border-color: var(--accent); } }
.dec-opt:disabled { opacity: .55; }

.dec-note-wrap label { display: block; font-size: .82rem; margin-bottom: 4px; }
.dec-note-wrap textarea { min-height: 52px; }
.dec-effect { font-size: .82rem; margin: 8px 0 0; }

.dec-settled .task-side { flex-shrink: 0; }
.dec-quote { font-style: italic; margin-top: 2px; }
.dec-done { color: var(--accent-dark); font-size: .84rem; margin-top: 3px; font-weight: 600; }

/* ---------------- Daily Tasks | Other Tasks ----------------
 *
 * A segmented control rather than a second row of pills. The tab strip above it
 * already uses pills, and two pill rows stacked read as one wrapped strip of six
 * — which is exactly the confusion the split exists to remove. This is visibly a
 * different kind of control: one box, two halves, one of them filled.
 *
 * Both halves are 44px tall and share the row equally, so neither is a small
 * target on a phone and neither looks like the afterthought.
 */
.subtabs {
  display: flex;
  margin: 14px 0 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px;
  gap: 3px;
}
.subtab {
  flex: 1 1 0;
  min-height: 44px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 0.92rem;
  font-weight: 650;
  font-family: inherit;
  cursor: pointer;
  padding: 0 10px;
}
.subtab.active {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 1px 2px rgba(40, 30, 15, .18);
}

/* Scheduled against available. Plain by default: a number that is fine should
 * look like a fact, not a warning. Amber only once the day is genuinely over. */
.load-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin: 12px 0 2px;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 0.86rem;
}
.load-strip.over {
  background: var(--warn-bg);
  border-color: var(--warn-border);
  color: var(--warn-text);
}
.load-strip.over .muted { color: var(--warn-text); }
.load-strip .load-main { font-size: 0.95rem; }
.badge.added {
  background: var(--info-bg); color: var(--info-text);
  border: 1px solid var(--info-border); font-weight: 600;
}

/* A library card. Deliberately lighter than a task row: this is not work she
 * has been given, and it should not look like it until she adds it. */
.lib-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-top: 8px;
  box-shadow: var(--shadow);
}
.lib-card.on-today { background: var(--surface); }
.lib-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
}
.lib-title { font-weight: 640; flex: 1 1 60%; }
.lib-body { font-size: 0.82rem; margin-top: 5px; line-height: 1.35; }
.lib-add {
  margin-top: 10px;
  min-height: 44px;
  width: 100%;
}
.lib-on-today {
  margin-top: 8px; font-size: 0.82rem; font-weight: 600; color: var(--muted);
}

.own-task-form { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.own-task-form input[type="text"] {
  flex: 1 1 100%; min-height: 44px;
  padding: 8px 12px; font-size: 16px; /* 16px: anything smaller zooms iOS on focus */
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--card); color: var(--text); font-family: inherit;
}
.own-task-form input[type="number"] {
  flex: 0 0 110px; min-height: 44px;
  padding: 8px 12px; font-size: 16px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--card); color: var(--text); font-family: inherit;
}
.own-task-form .btn { flex: 1 1 auto; min-height: 44px; }
.own-task-form select {
  flex: 1 1 100%; min-height: 44px;
  padding: 8px 12px; font-size: 16px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--card); color: var(--text); font-family: inherit;
}

/*
 * The per-category entry point. Deliberately quiet: it sits under every section
 * of the day, so anything with the weight of a real task row would read as five
 * more things to do.
 */
.own-inline { margin-top: 10px; }
.own-inline-toggle {
  min-height: 44px;            /* thumb target, same floor as every other control */
  width: 100%;
  font-size: 14px;
  border-style: dashed;
}
.own-inline-form { margin-top: 0; }

/* The chip on a Daily Tasks row that did not arrive on its own. */
.badge.src {
  background: var(--accent-soft); color: var(--accent-dark);
  border: 1px solid var(--done-border); font-weight: 620;
}

@media (prefers-color-scheme: dark) {
  /*
   * White text on the accent is 2.7:1 in the dark palette — the selected tab was
   * the least readable thing on the screen, and it is the one that says where you
   * are. The accent lightens in dark mode, so what sits on it has to darken.
   *
   * Applied to the tab strip as well as the new segmented control: the strip has
   * carried the same defect since it was built, and fixing one and not the other
   * would leave two selected-state greens on the same screen disagreeing about
   * which one you can read.
   */
  .subtab.active,
  .tabs a.active { color: #12180f; }
}


/* ---- Paste choice: break it up, or keep it whole (Joel, 14 Sep 2026) ---- */
.fb-choice { display: flex; gap: 14px; flex-wrap: wrap; margin: 12px 0 4px; }
.fb-choice-opt { flex: 1 1 240px; min-width: 0; }
.fb-choice-opt p { margin: 8px 0 0; font-size: .88rem; }
.fb-choice-preview {
  margin-top: 12px; padding: 10px 12px; border-radius: 10px;
  background: var(--surface-2, rgba(127,127,127,.08));
}
.fb-choice-line {
  font-size: .9rem; line-height: 1.4; padding: 4px 0;
  overflow-wrap: anywhere; white-space: pre-wrap;
}
.fb-choice-line + .fb-choice-line { border-top: 1px solid var(--line, rgba(127,127,127,.22)); }

/* ---- Section progress counts on the daily list ---- */
.sec-count {
  font-variant-numeric: tabular-nums; font-weight: 600; font-size: .86rem;
  padding: 2px 8px; border-radius: 999px; margin-left: 6px;
  background: var(--surface-2, rgba(127,127,127,.12)); color: var(--text-muted, inherit);
}
.sec-count.all-done { background: var(--done-bg, rgba(46,125,50,.14)); color: var(--done-fg, inherit); }

/* A long note keeps its own line breaks on her screen too. */
.note-card .note-text { white-space: pre-wrap; overflow-wrap: anywhere; }


/* ---- Priority reminder dialog (Other Tasks -> Add to Today) ---- */
.modal-back {
  position: fixed; inset: 0; z-index: 60; display: flex;
  align-items: center; justify-content: center; padding: 18px;
  background: rgba(0,0,0,.45);
}
.modal-box {
  background: var(--card, #fff); color: inherit; border-radius: 14px;
  padding: 18px 18px 14px; max-width: 420px; width: 100%;
  box-shadow: 0 18px 44px rgba(0,0,0,.28); max-height: 86vh; overflow: auto;
}
.modal-box h3 { margin: 0 0 8px; font-size: 1.05rem; line-height: 1.3; }
.modal-box p { margin: 0 0 10px; line-height: 1.45; }
.modal-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.modal-actions .btn { flex: 1 1 auto; min-height: 44px; }

/* ---- Library card: what it costs today, which is nothing ---- */
.lib-cost { font-size: .84rem; margin: 6px 0 8px; }
.required-remaining { margin-top: 12px; }

.fb-quote {
  margin-top: 4px; padding-left: 10px; border-left: 3px solid var(--line, rgba(127,127,127,.3));
  font-style: italic; line-height: 1.4; overflow-wrap: anywhere;
}

/* The way out of a question, kept away from the answers to it. */
.dec-remove-wrap { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.dec-remove-wrap .btn { width: 100%; }
@media (min-width: 700px) { .dec-remove-wrap .btn { width: auto; } }

/* The lunch planner's answer line.
 *
 * Deliberately not `.muted`. A refusal ("Fridays schedule no lunch, so there is
 * nothing to waive") printed in the same grey as the hint text beside it is a
 * message that gets skipped, and a button that appears to do nothing is read as
 * a limit on the feature rather than an answer about the day. */
.lunch-plan-msg {
  margin-left: 8px;
  font-size: 0.9rem;
  color: var(--muted);
}
.lunch-plan-msg.bad {
  color: var(--danger);
  font-weight: 600;
}

/* ---------------- Approvals: what a request would actually do ------------
   Added 21 Sep 2026. The approval card used to show the request payload as
   raw JSON, in which "these values are identical to the task" is invisible --
   which is how an approved edit changed nothing and nobody could see it. */
.warn-box {
  background: var(--warn-bg); border: 1px solid var(--warn-border);
  color: var(--warn-text); border-radius: 8px; padding: 10px 12px;
  margin: 8px 0; font-size: .9rem; line-height: 1.45;
}
.diff-table { border-collapse: collapse; margin: 2px 0 10px; font-size: .9rem; }
.diff-table th, .diff-table td { padding: 3px 0; }
.diff-from { text-decoration: line-through; opacity: .75; }
.diff-to { font-weight: 700; }

/* ---------------- Store still open (shipping) ---------------------------
   One line by default. See the note in admin.js: the answer Joel gave is
   recorded and the row has left the work list; what is left is the store's
   disagreement, which is a fact to keep rather than a question to re-ask. */
.store-open-fold > summary {
  cursor: pointer; font-size: .9rem; padding: 7px 10px;
  background: var(--warn-bg); border: 1px solid var(--warn-border);
  color: var(--warn-text); border-radius: 8px; font-weight: 600;
}
.store-open-fold[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.store-open-dot { color: #d9a441; margin-right: 4px; }

/* The stage selector carries option text as long as "Label printed — needs
   filled & boxed", and a <select> will not shrink below its widest option. In
   a narrow column it therefore ate the row and left the order details rendering
   one word per line -- which is what Joel photographed on 21 Sep 2026. */
select.ship-stage-select { max-width: 100%; }
.task-side { min-width: 0; }
.task-row.store-open-row { flex-wrap: wrap; }
@media (max-width: 760px) {
  .task-row.store-open-row .task-side { width: 100%; }
  .task-row.store-open-row .task-side select.ship-stage-select { width: 100%; }
}

/* Day-by-day school cell. Matches the GPS card's grading: red only where the
   app is willing to say "late" out loud, amber where it is not. */
.perf-school-late    { color: #b3261e; font-weight: 600; }
.perf-school-unclear { color: var(--warn-text); }

/* ------------------------------------------------------------------ *
 * Coaching help on the feedback page (22 Sep 2026)
 * ------------------------------------------------------------------ */

/* A second door into the same list, so it reads as an alternative to the
 * primary action rather than a lesser version of it. Filled, not outlined:
 * Joel's standing rule is that anything clickable carries colour. */
button.accent, .btn.accent {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  color: var(--accent-dark);
  font-weight: 600;
}
button.accent:not(:disabled):hover, .btn.accent:not(:disabled):hover {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
}

.coach-day { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 10px 0 6px; }
.coach-day input[type="date"] { min-height: 40px; }
.coach-sub { margin: 10px 0 6px; font-size: .9rem; color: var(--muted); }

.coach-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
.coach-chip {
  min-height: 34px; padding: 4px 10px; font-size: .84rem; border-radius: 999px;
  border: 1px solid var(--accent); background: var(--accent-faint); color: var(--accent-dark);
  text-align: left;
}
.coach-chip:hover { background: var(--accent-soft); }
/* Tone colours the chip by what the fact is, not by how bad it is: a reader
 * scanning for the one green item should be able to find it. */
.coach-chip.tone-concern,
.coach-chip.tone-warning {
  border-color: var(--warn-border); background: var(--warn-bg); color: var(--warn-text);
}
.coach-chip.tone-good { border-color: var(--accent); background: var(--accent-soft); }

/* Suggested drafts. Wider than an evidence chip because the label is a sentence
 * fragment rather than a figure, and a draft is a bigger commitment than an
 * insert -- it replaces all four boxes, so it should not look like a chip. */
.coach-starters { margin: 10px 0 4px; }
.coach-starter {
  min-height: 36px; padding: 7px 12px; font-size: .86rem; border-radius: 10px;
  border: 1px solid var(--accent); background: var(--accent-faint); color: var(--accent-dark);
  text-align: left; font-weight: 600;
}
.coach-starter:hover { background: var(--accent-soft); }
.coach-starter.is-praise { border-color: var(--good-border, var(--accent)); }
.coach-starter.is-evidence { border-style: solid; border-width: 2px; }
.coach-tag {
  font-weight: 400; font-size: .76rem; opacity: .85; margin-left: 4px;
  border-left: 1px solid currentColor; padding-left: 6px;
}

.coach-blocked {
  border: 1px solid var(--warn-border); background: var(--warn-bg); color: var(--warn-text);
  border-radius: 10px; padding: 10px 12px; margin: 8px 0;
}

.coach-field { margin-top: 12px; }
.coach-hint { margin: 2px 0 4px; font-size: .84rem; color: var(--muted); }

.coach-notes {
  border: 1px solid var(--warn-border); background: var(--warn-bg); color: var(--warn-text);
  border-radius: 10px; padding: 10px 12px; margin: 12px 0;
}
.coach-note { margin-top: 6px; font-size: .9rem; line-height: 1.45; }
.coach-note-tag { font-weight: 700; }

.coach-preview { margin-top: 12px; }
.coach-preview-body {
  border: 1px dashed color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 10px; padding: 10px 12px; margin-top: 4px; line-height: 1.5;
  background: var(--accent-faint); white-space: pre-wrap;
}
.coach-balance { margin-top: 10px; font-size: .88rem; color: var(--muted); }
.coach-balance.lean { color: var(--warn-text); font-weight: 600; }

@media (max-width: 520px) {
  .coach-day { gap: 6px; }
  .coach-day input[type="date"] { flex: 1 1 100%; }
}

.coach-prov { color: var(--warn-text); font-weight: 600; }

/* ---------------- Gated rows and the deliberate undo (23 Sep 2026) ----------
 *
 * A gated row is one the server will refuse until something above it is
 * answered. It is dimmed and shows no pointer, so it reads as "not yet" rather
 * than "broken" -- but it is deliberately NOT hidden and NOT struck through:
 * it is still required work, it still counts in the day's denominator, and it
 * still has to be answered before the day can close.
 */
.task-row.gated { opacity: .72; cursor: default; }
.task-row.gated .task-check {
  background: var(--warn-bg);
  border-color: var(--warn-border);
  color: var(--warn-text);
}
.task-gate-note {
  margin-top: 4px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--warn-text);
}

/* The undo button in the row sheet. Coloured, per the rule that anything
 * clickable is coloured -- amber rather than green, because it removes a
 * recorded time rather than adding one. */
.sheet-action.btn.undo {
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  color: var(--warn-text);
  font-weight: 600;
}
.sheet-action.btn.undo:not(:disabled):hover { background: var(--warn-border); }

/* Rendered task detail (25 Sep 2026).
   Grace writes her task wording as bullets; these are the only rules that make
   a list look like one. Deliberately tight: a task row is a row on a phone, not
   a document, so a bulleted detail must not push the next task off the screen.
   `.rt-p` carries no top margin and no bottom margin on the last child, so a
   plain one-paragraph detail -- which is every other task on the list --
   occupies exactly the space it did before this existed. */
.rt-p { margin: 0 0 3px; }
.rt-p:last-child { margin-bottom: 0; }
.rt-list { margin: 2px 0 0; padding-left: 17px; }
.rt-list:first-child { margin-top: 0; }
.rt-list li { margin: 0 0 2px; }
.rt-list li:last-child { margin-bottom: 0; }
.task-detail .rt-list, .lib-body .rt-list { padding-left: 15px; }
