/* Quietkeep — the shell.
 *
 * Colour tokens are ACCESSIBILITY.md B-11, with their measured ratios. Nothing
 * here is decided at the keyboard, and no new pair enters without joining the
 * gate in the same commit (B-08).
 *
 * Type is sized in rem throughout, so the reader's TEXT-SIZE preference is
 * honoured and not only page zoom (B-04, Doctrine §4). No fixed size ignores
 * the space available.
 */

/* `hidden` MUST win. Any rule that sets `display` on an element defeats the UA's
 * [hidden] { display: none } — the coverage list set display:flex and rendered
 * fully expanded while every gate and every screen reader was told it was
 * hidden. Caught by the smoke walk; this is the structural fix, so no future
 * display rule can reintroduce it. */
[hidden] { display: none !important; }

:root {
  color-scheme: light dark;

  --bg: #F4F1E9;
  --surface: #FFFFFF;
  --ink: #1B2333;
  --ink-soft: #4C5670;
  --line: #8E8A7F;   /* 3.45:1 on surface, 3.05:1 on bg — a control boundary is a graphical object (WCAG 1.4.11) */
  --accent: #33425F;
  --warm: #7A4E00;

  --gap: 1rem;
  --radius: 0.75rem;
  /* 44px at the default root size, but expressed in rem so it grows with the
     reader's text setting rather than staying put (B-06). */
  --target: 2.75rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #141A26;
    --surface: #1E2637;
    --ink: #F2F0EA;
    --ink-soft: #B3BCCE;
    --line: #6A7896;   /* 3.42:1 on surface, 3.93:1 on bg */
    --accent: #AFC0DC;
    --warm: #F5C978;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 0 var(--gap) calc(var(--gap) * 2);
  padding-inline: max(var(--gap), env(safe-area-inset-left)) max(var(--gap), env(safe-area-inset-right));
  background: var(--bg);
  color: var(--ink);
  font: 1rem/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* A comfortable measure, but never a floor that exceeds the space available. */
  max-width: 46rem;
  margin-inline: auto;
}

/* Off-screen until focused, but it carries its full target size the whole time.
 * A skip link that only becomes 44px at the moment it is focused is a target
 * that was too small right up until it mattered. */
.skip {
  position: absolute;
  left: -9999px;
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  padding: 0.5rem 0.75rem;
  color: var(--ink);
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: var(--radius);
}
.skip:focus { position: static; }

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

/* --- focus ---------------------------------------------------------------
 * Never removed. The ring is drawn with outline so it follows the shape and
 * survives a forced-colours mode. */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  padding-block: 1.25rem 0.5rem;
  flex-wrap: wrap;
}

.wordmark {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* --- capture -------------------------------------------------------------- */

#capture-form {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-block: 0.5rem 0.25rem;
}

#capture {
  flex: 1 1 14rem;
  min-width: 0;
  min-height: var(--target);
  padding: 0.5rem 0.75rem;
  font-size: 1.0625rem; /* >=16px equivalent: iOS zooms the page below that */
  color: var(--ink);
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--radius);
}
#capture::placeholder { color: var(--ink-soft); opacity: 1; }
#capture:disabled { opacity: 1; background: var(--bg); }

button {
  min-height: var(--target);
  min-width: var(--target);
  padding: 0.5rem 1rem;
  font: inherit;
  font-weight: 600;
  color: var(--bg);
  background: var(--accent);
  border: 2px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
}

button.ghost {
  color: var(--accent);
  background: transparent;
  border-color: var(--line);
  font-weight: 400;
}

.linklike {
  /* An inline text button (Undo) that reads as a link but is a real <button>,
     so it is keyboard-reachable with a focus ring. Full target height. */
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  padding: 0 0.25rem;
  font: inherit;
  color: var(--accent);
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
}

.status {
  min-height: 1.5rem;   /* reserves the line so nothing jumps when it fills */
  margin: 0.25rem 0 0;
  color: var(--ink-soft);
  /* Error text can contain one unbroken token (a quoted id, a URL); at 320px
     and 200% text that produced 449px of sideways scroll (audit). */
  overflow-wrap: anywhere;
}

/* --- triage: heat pass and clarify --------------------------------------- */

.triage {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.triage-gauge { margin: 0 0 0.5rem; color: var(--ink-soft); font-size: 0.9375rem; }
.triage-prompt { margin: 0 0 0.5rem; font-size: 1.0625rem; font-weight: 600; }
.triage-card {
  margin: 0 0 1rem;
  font-size: 1.0625rem;
  overflow-wrap: anywhere;   /* a long captured token must not overflow */
}
.triage-actions {
  display: grid;
  /* min(9rem, 100%): the column floor collapses to the container's width
     before it can force a horizontal scrollbar — at 320px/200% one 9rem
     column is wider than the viewport, and the bare minmax overflowed. */
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  gap: 0.5rem;
}
.route {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  min-height: var(--target);
  padding: 0.5rem 0.75rem;
  text-align: left;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
}
.route-label { font-weight: 600; }
.route-hint { font-size: 0.8125rem; color: var(--ink-soft); }

.donow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  flex-wrap: wrap;
  margin: 0.75rem 0 0;
  padding: 0.5rem 0.75rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.donow-label { color: var(--warm); font-variant-numeric: tabular-nums; }
/* The offer now lives outside the triage section, so it needs its own room —
   it used to inherit the section's margins. */
#triage-donow:empty { display: none; }
#triage-donow { margin: 0 0 1rem; }
/* Done is the primary action here and reads as one. It is available before the
   timer, during it, and at the question — a two-minute job finished in forty
   seconds should never need the timer stopped first. */
.donow-done { min-height: var(--target); }
.donow button { min-height: var(--target); }

/* --- the list ------------------------------------------------------------- */

.section {
  margin: 1.75rem 0 0.25rem;
  font-size: 1.125rem;
  font-weight: 600;
}

/* The gauge is a real button: its number is a claim, and tapping it opens the
   itemised list that backs the claim. Styled as text, but a full target. */
.gauge {
  display: block;
  width: 100%;
  margin: 0 0 0.75rem;
  padding: 0.5rem 0;
  min-height: var(--target);
  text-align: left;
  color: var(--ink-soft);
  background: none;
  border: none;
  font: inherit;
  cursor: pointer;
}

.coverage {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0.5rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.coverage-item {
  display: flex;
  justify-content: space-between;
  gap: var(--gap);
  flex-wrap: wrap;
}
.coverage-title { overflow-wrap: anywhere; }
.coverage-when { flex: 0 1 auto; min-width: 0; color: var(--ink-soft); font-size: 0.9375rem; }

/* --- dates that have gone by (law 3) ---------------------------------------
 *
 * Deliberately the SAME visual weight as Next up. No red, no border that reads
 * as an alarm, nothing that says "this is a problem" — a passed date is a
 * decision to make, not a wall to be shouted at from. Every colour token here is
 * one already registered against these backgrounds; nothing new is introduced,
 * so the contrast gate needs no new pair.
 */

.replan {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* `.section` carries a 1.75rem top margin for headings that separate stretches
 * of the page. Inside a bordered card that reads as dead space above the title. */
.replan > .section { margin-top: 0; }
.replan-count { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
.replan-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.replan-open {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  width: 100%;
  min-height: var(--target);
  padding: 0.5rem 0.75rem;
  text-align: left;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
}
.replan-card-title { overflow-wrap: anywhere; }
.replan-card-when { color: var(--ink-soft); font-size: 0.875rem; }

.replan-when { margin: 0 0 0.25rem; color: var(--ink-soft); }
.replan-context { margin: 0 0 0.25rem; color: var(--ink-soft); font-size: 0.9375rem; }
/* The question needs air above it. Butted against the "that date was…" line it
 * reads as part of the same sentence rather than as the ask. */
#replan-sheet-ask { margin: 1rem 0 0; }
/* A failure gets its own line rather than overwriting the card's context. Plain
 * `--ink`, not an alert colour: "pick a date first" is an instruction, not an
 * alarm, and nothing on this surface shouts. */
.replan-error { margin: 0.25rem 0 0; color: var(--ink); font-weight: 600; }

/* --- a newer version is ready ----------------------------------------------
 * A line above the app, not over it. `--surface` on `--bg` with the border doing
 * the separating, exactly like a card — an update notice is not a different class
 * of object and should not look like an alert.
 */
.update {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.update-words { margin: 0 0 0.5rem; color: var(--ink); }

/* --- clearing things out ---------------------------------------------------
 * The confirmation is a typed word, and the box has to be as reachable as any
 * other control here: the same 44px floor and the same borrowed styling as the
 * file picker, because this is also a surface somebody reaches for at a moment
 * they are not at their best.
 */
.purge-label { display: block; margin: 0.5rem 0 0.25rem; color: var(--ink); }
#purge-word {
  display: block;
  width: 100%;
  min-height: var(--target);
  margin: 0 0 0.5rem;
  padding: 0.5rem;
  color: var(--ink);
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
}

/* --- bringing a copy back -------------------------------------------------
 * A real <input type="file">, not a styled button driving a hidden one: this is
 * the surface someone reaches for after something has already gone wrong, and
 * the platform's own picker is the part most likely to still work. It needs
 * explicit sizing, because the default control is well under the 44px floor.
 */
#import-file,
#other-file {
  display: block;
  width: 100%;
  min-height: var(--target);
  margin: 0.25rem 0 0.5rem;
  padding: 0.5rem;
  color: var(--ink);
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
}
#import-file::file-selector-button,
#other-file::file-selector-button {
  min-height: 2.25rem;
  margin-right: 0.75rem;
  padding: 0.25rem 0.75rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) / 2);
  font: inherit;
  cursor: pointer;
}

/* A column, not a grid. At 320px and 200% zoom a two-up layout of options this
 * wordy overflows horizontally, and the page body must never scroll sideways —
 * the failure the triage actions already paid for once. */
.replan-options {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.5rem 0 1rem;
}
.replan-option { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.replan-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  width: 100%;
  min-height: var(--target);
  padding: 0.5rem 0.75rem;
  text-align: left;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
}
.replan-choice-hint { color: var(--ink-soft); font-size: 0.875rem; overflow-wrap: anywhere; }
.replan-option-label { flex: 1 1 100%; }
.replan-date { min-height: var(--target); flex: 1 1 8rem; min-width: 0; }
.replan-set { min-height: var(--target); }
.replan-option-hint { flex: 1 1 100%; color: var(--ink-soft); font-size: 0.875rem; }

/* --- next up and upkeep ---------------------------------------------------- */

.nextup {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.nextup-title { margin: 0 0 0.25rem; font-size: 1.0625rem; overflow-wrap: anywhere; }
.nextup-why { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
.nextup-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.nextup-actions button { min-height: var(--target); }
.nextup-count { margin: 0.75rem 0 0; color: var(--ink-soft); font-size: 0.9375rem; }
.nextup-behind {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.behind-item {
  display: flex;
  justify-content: space-between;
  gap: var(--gap);
  flex-wrap: wrap;
  color: var(--ink-soft);
  font-size: 0.9375rem;
}
.behind-title { overflow-wrap: anywhere; }
.behind-why { flex: 0 1 auto; min-width: 0; }

.chips {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.chip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  min-height: var(--target);
  padding: 0.4rem 0.75rem;
  text-align: left;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
}
.chip-why { color: var(--ink-soft); font-size: 0.8125rem; }

.empty {
  margin: 0;
  padding: 1rem;
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  color: var(--ink-soft);
}

.cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.group-head {
  /* A heading for a group, never a badge and never a count of things undone. */
  margin: 1rem 0 0.25rem;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  font-weight: 600;
}
.cards > .group-head:first-child { margin-top: 0; }
.cards-group {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* The card is a ROW with two controls — open it, or check it off. It used to be
   one big button, which is why it could not gain a second one: a button inside a
   button is invalid HTML. */
.card {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* WRAPS. The row held two controls and did not need to; "Work on this" made it
     three, and at 320px with 200% text the third pushed the PAGE sideways by
     42px (a11y gate, B-04's hardest case). A row of actions that cannot wrap is
     a row that only fits the widths it was written at. */
  flex-wrap: wrap;
  /* THE BORDER IS HERE, not on the title button, and that is a correctness fix
     rather than a style preference.
     It used to sit on `.card-open`, so the bordered box was only the TITLE and the
     action buttons floated outside it. Because the title is `flex: 1 1 auto`, the
     wrap point moved with the length of each title — so on a long row "Done"
     wrapped onto a line of its own, left-aligned, sitting directly above the NEXT
     item. A completion button that appears to belong to the thing below it is not
     an alignment complaint, it is a mis-tap waiting to happen, and Noah found it
     at 1,429 rows where almost every title is long.
     With the container on `.card`, anything that wraps stays visibly inside the
     item it acts on, at every width and every text size. */
  padding: 0.5rem 0.6rem;
  background: var(--surface);
  /* The border carries the separation, not the fill: surface against bg is only
     ~1.14:1, and one channel is never enough (B-01). */
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.card-open {
  flex: 1 1 12rem;
  min-width: 0;
  min-height: var(--target);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap);
  flex-wrap: wrap;
  padding: 0.25rem 0.4rem;
  /* Transparent, because the card around it now carries the box. A second border
     inside a border reads as two things. */
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
}
/* "N more under ready now — show them". A card-shaped row so it sits in the list
   rather than beside it, and its own text colour so it does not read as work. */
.card-more > .card-open { color: var(--ink-soft); justify-content: flex-start; }

/* The actions travel TOGETHER. One wrapper as a single flex item, so the group
   either sits beside the title or drops below it whole — never one button beside
   the title and the other stranded on the next line. */
.card-actions {
  /* SHRINKABLE, and `min-width: 0` with it. `flex: 0 0 auto` sized the group to
     its content and refused to narrow, so at 320px with 200% text the two buttons
     pushed the page 192px sideways — the a11y gate caught it on the first run,
     which is exactly the failure the note on `.card` was written about. Shrinking
     lets the buttons inside stack instead. */
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  /* Pushed right when there is room, and harmless when there is not. */
  margin-left: auto;
}

.card-done {
  flex: 0 0 auto;
  /* Its OWN floor, not one inherited from the sibling. `align-items: stretch` on
     .card made this min-height inert — the 44px came from .card-open next to it,
     so the stated guarantee was load-bearing on a neighbour (audit). align-self
     makes the button own its height, and the floor real. */
  align-self: center;
  height: auto;
  min-height: var(--target);
  padding: 0 0.9rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
}
.card-title { flex: 1 1 12rem; min-width: 0; overflow-wrap: anywhere; }

.card-when {
  /* Shrink-and-wrap, not fixed: at 320px/200% a longer status than "returns
     today" (e.g. a dated "returns Thu 30 Jul") must wrap within the card, not
     push the page sideways. The card already wraps this onto its own line. */
  flex: 0 1 auto;
  min-width: 0;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  /* The status is words. Nothing here depends on seeing a colour, and there is
     no alarm state to reach — a passed clock becomes a live card, not a red
     one (law 5, B-01). */
}

.foot {
  margin-top: 2.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  color: var(--ink-soft);
  font-size: 0.9375rem;
}
/* The build, on the main screen. `--ink-soft` on `--bg` is an already-registered
 * pair, so this adds no new colour to the contrast gate — a version stamp is not
 * a reason to introduce a shade nobody has measured. */
.build {
  margin: 0.25rem 0 0;
  color: var(--ink-soft);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
}

.foot a {
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  padding-inline: 2px;
}

/* --- the (i) panel -------------------------------------------------------- */

button.info {
  /* A circle, and a full-size target regardless of how little is inside it. */
  width: var(--target);
  padding: 0;
  border-radius: 50%;
  font-size: 1.125rem;
  font-style: italic;
  font-weight: 600;
  line-height: 1;
}

dialog {
  width: min(34rem, calc(100vw - 2rem));
  /* Never taller than the space available, and it scrolls inside itself rather
     than pushing content off the screen (B-04). */
  max-height: min(85vh, calc(100dvh - 2rem));
  overflow-y: auto;
  padding: 1.25rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
dialog::backdrop { background: rgb(0 0 0 / 0.5); }
dialog h2 { margin: 0 0 0.75rem; font-size: 1.25rem; }

/* THE ALWAYS-REACHABLE WAY OUT, and it no longer depends on `position: sticky`.
 *
 * It was sticky inside the dialog's own scroll container — which is correct, and
 * which every engine here honours, and which did NOT hold on Noah's iPad: the
 * header scrolled away with the content and both ways out ended up at the
 * extremes of a panel thousands of pixels tall. Reported twice.
 *
 * So the dependency is gone rather than debugged. `#about` is a flex column that
 * does not scroll; `.about-body` is the only thing that moves. The bar cannot
 * scroll away because it is not inside the box that scrolls. That needs no
 * support from any engine and cannot regress. */
/* `[open]` IS LOAD-BEARING. A bare `#about { display: flex }` beats the UA's
 * `dialog:not([open]) { display: none }` on specificity, so the panel closed
 * correctly and stayed on screen anyway — a worse version of the bug this is
 * fixing. Caught by measuring `checkVisibility()` after the close rather than
 * trusting that `close()` had done it. */
#about[open] {
  display: flex;
  flex-direction: column;
  overflow: hidden;          /* the BODY scrolls, never the dialog */
}
.about-body {
  flex: 1 1 auto;
  min-height: 0;             /* without this a flex child refuses to shrink */
  overflow-y: auto;
  /* The dialog's own padding moved here so the scrollbar sits at the edge and
     the bar spans the full width. */
  margin: 0 -1.25rem -1.25rem;
  padding: 0 1.25rem 1.25rem;
}
.about-bar {
  flex: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin: -1.25rem -1.25rem 0.75rem;
  padding: 1.25rem 1.25rem 0.5rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.about-bar h2 { margin: 0; }
/* A sticky header can hide the thing you just moved focus to — WCAG 2.2's
 * 2.4.11 Focus Not Obscured, which is AA and therefore this app's target. The
 * a11y gate found it immediately at 320px and 200%, where the bar takes a large
 * share of a short dialog. `scroll-margin-top` is what scroll-into-view honours,
 * so a focused control now stops BELOW the bar instead of under it. In rem, so
 * it grows with the text it has to clear. */
#about :is(button, a, input, label, h3, dt, dd) { scroll-margin-top: 5rem; }
/* At 200% text on a small screen the title wraps and the bar grew to 99% of the
 * dialog — the way out eating the thing it was meant to let you leave. Measured,
 * not guessed. Compact here, and the text still says everything it said. */
/* PX, not rem. Inside a media query `rem` resolves against the INITIAL root
 * font size, never the zoomed one — so a rem threshold silently failed to match
 * at exactly the zoom level it was written for, and the bar stayed at 99% of
 * the dialog. Measured after changing it, not assumed. */
@media (max-height: 640px) {
  .about-bar { padding: 0.4rem 1.25rem 0.35rem; margin-bottom: 0.4rem; }
  /* The title wraps at this size whatever we do — 320px at 200% is about ten
     characters a line. Tightening the leading is what is left, and it keeps
     every word rather than truncating any of them. */
  .about-bar h2 { font-size: 0.95rem; line-height: 1.15; }
  #about :is(button, a, input, label, h3, dt, dd) { scroll-margin-top: 3.5rem; }
}
.about-x {
  flex: none;
  min-width: var(--target);
  min-height: var(--target);
  padding: 0;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* The glyph, not the box. 1.5rem keeps the × legible at a 44px target. */
  font: inherit;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
/* At 320px/200% a long unbroken run (the wordmark inside a bold intro clause)
   pushed the dialog into sideways scroll where the page check could not see it
   (audit). Every text descendant may break. */
dialog, dialog * { overflow-wrap: anywhere; }

.version {
  font-size: 0.875rem;
  font-weight: 400;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.intro {
  margin: 0 0 1.25rem;
  padding: 0.875rem 1rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.intro p { margin: 0 0 0.6rem; }
.intro p:last-child { margin-bottom: 0; }
.intro-aside { color: var(--ink-soft); font-size: 0.9375rem; }

.about-section {
  margin: 1.5rem 0 0.5rem;
  font-size: 1rem;
  font-weight: 600;
}
.about-section:first-of-type { margin-top: 0; }
.about-p { margin: 0 0 0.6rem; color: var(--ink-soft); }
.about-p a {
  color: var(--accent);
  /* Real targets, like every other link here (B-06) — the dialog's links
     measured 186x19 while the audit was looking. */
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  padding-inline: 2px;
}

#storage-body {
  display: grid;
  /* minmax(0, 1fr): a bare 1fr refuses to shrink below content, and at 320px
     with 200% text the dd column pushed 51px of scroll INSIDE the dialog,
     where the page-level overflow check could not see it (audit). */
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.35rem 1rem;
  margin: 0;
}
#storage-body dt { color: var(--ink-soft); overflow-wrap: anywhere; }
#storage-body dd { margin: 0; overflow-wrap: anywhere; }
.storage-note {
  margin: 0.875rem 0 0;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line);
  color: var(--warm);
}

/* Patch notes. Written for the reader, so they are set as prose, not a table. */
.note-head {
  display: flex;
  flex-wrap: wrap;            /* the kind tag wraps under the triplet at 320/200 */
  align-items: baseline;
  gap: 0.25rem 0.5rem;
  margin: 1rem 0 0.35rem;
  font-size: 0.9375rem;
  font-weight: 600;
}
.note-head:first-child { margin-top: 0; }
.note-triplet { font-variant-numeric: tabular-nums; }
.note-kind {
  font-weight: 400;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.note-list { margin: 0; padding-left: 1.15rem; color: var(--ink-soft); }
.note-list li { margin-bottom: 0.35rem; }

.about-actions {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
  flex-wrap: wrap;
  margin-top: 1rem;
}

/* --- motion ---------------------------------------------------------------
 * Nothing here animates meaning, and the one transition there is goes away on
 * request (B-05). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* --- the detail sheet ------------------------------------------------------ */

.detail-title { margin: 0 0 0.25rem; font-size: 1.125rem; overflow-wrap: anywhere; }
.detail-state { margin: 0 0 1rem; color: var(--ink-soft); font-size: 0.9375rem; }
.detail-group { margin: 0 0 1.25rem; }
.detail-label {
  display: block;
  margin: 0 0 0.4rem;
  font-weight: 600;
}
.detail-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.detail-row button { min-height: var(--target); }
.detail-row input {
  min-height: var(--target);
  padding: 0.4rem 0.6rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
}
.detail-row input[type="number"] { width: 5rem; max-width: 100%; }
/* At 320px/200% the repeat row cannot fit "every [n] days, relaxed for [n]" on
   one line — the inline labels and fixed-width boxes overflowed by 121px. Let
   every child shrink, and let the row become a column when it must. */
.detail-row > * { min-width: 0; max-width: 100%; flex-shrink: 1; }
.detail-row input[type="date"] { flex: 1 1 10rem; }
.detail-row button { flex: 0 1 auto; }
@media (max-width: 26rem) {
  .detail-row { flex-direction: column; align-items: stretch; }
  .detail-row input, .detail-row button { width: 100%; }
  .detail-row input[type="number"] { width: 100%; }
}
.detail-inline { color: var(--ink-soft); font-size: 0.9375rem; }
.detail-hint { margin: 0.5rem 0 0; color: var(--ink-soft); font-size: 0.875rem; }

/* The snapshot caveat: quieter than the offer above it, but never hidden — a
   calendar that silently goes stale would be the app implying it is live. */
.about-caveat { color: var(--ink-soft); font-size: 0.9375rem; }

/* --- dependencies: what holds up what ------------------------------------- */
.detail-feeds { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.detail-feed {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.25rem 0;
}
.detail-feed button { min-height: var(--target); }
/* The arithmetic, in words. `--ink`, not an alert colour: "it needed starting
   two days ago" is a fact about the dates, and this app has no red walls. */
.detail-feed-words { margin-top: 0.35rem; color: var(--ink); font-weight: 600; }
#detail-feeds { min-height: var(--target); flex: 1 1 8rem; min-width: 0; font: inherit;
  color: var(--ink); background: var(--surface); border: 2px solid var(--line);
  border-radius: var(--radius); padding: 0.25rem 0.5rem; }
#detail-lead { min-height: var(--target); width: 5rem; }

/* --- containment: what holds what (law 4) --------------------------------- */
/* The picker shares #detail-feeds' rule so the two structural selects on this
   sheet cannot drift into looking like different kinds of control. */
#detail-parent { min-height: var(--target); flex: 1 1 8rem; min-width: 0; font: inherit;
  color: var(--ink); background: var(--surface); border: 2px solid var(--line);
  border-radius: var(--radius); padding: 0.25rem 0.5rem; }
/* Where it sits, stated before the control that changes it — you should know
   the answer before you are asked to alter it. */
#detail-place { margin: 0 0 0.5rem; color: var(--ink); font-weight: 600; }

/* --- review: exceptions only ---------------------------------------------- */
.review {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.review > .section { margin-top: 0; }
.review-count { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
.review-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.review-open {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem;
  width: 100%; min-height: var(--target); padding: 0.5rem 0.75rem; text-align: left;
  color: var(--ink); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); font: inherit; cursor: pointer;
}
.review-title { overflow-wrap: anywhere; }
.review-why { color: var(--ink-soft); font-size: 0.875rem; }

/* --- focus: one thing, and a way to be interrupted without losing it ------- */
.focus {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.focus > .section { margin-top: 0; }
.focus-title { margin: 0 0 0.25rem; font-size: 1.125rem; font-weight: 600; overflow-wrap: anywhere; }
/* Elapsed time is a FACT, not a pace to keep up with, so it is stated in the
   quiet token — the same one every other "when" line uses. Nothing here counts
   down and nothing goes red (law 5, B-01). */
.focus-elapsed { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
/* What you WROTE DOWN while you were at it. Ordinary ink: it is a thing you did. */
.focus-held { margin: 0.5rem 0 0; color: var(--ink); font-size: 0.9375rem; }
/* `min-width: 0` AND a small flex-basis. Without both, the input keeps its
   default intrinsic width (~20ch) inside the flex row, and at 320px with 200%
   text that pushed the PAGE sideways by 42px — B-04's hardest case, caught by
   the gate rather than by looking at it. */
#focus-interrupt { min-height: var(--target); flex: 1 1 6rem; min-width: 0; font: inherit; }
.focus-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
.focus-actions button { min-height: var(--target); }
#focus-cue { min-height: var(--target); width: 100%; box-sizing: border-box; font: inherit;
  color: var(--ink); background: var(--surface); border: 2px solid var(--line);
  border-radius: var(--radius); padding: 0.25rem 0.5rem; margin-top: 0.35rem; }
/* `--ink-soft` on `--surface` measured 3.28:1 in dark and the gate failed it.
   A placeholder is text someone has to read to know what the box wants, so it
   is held to 4.5:1 like any other text — `--ink` with the browser's own
   default opacity removed, which is what `#capture::placeholder` does. */
#focus-cue::placeholder { color: var(--ink); opacity: 1; }
#focus-interrupt::placeholder { color: var(--ink); opacity: 1; }
/* The way in, on the row itself. Starting work is the commonest act here and it
   should not cost two taps and a dialog. Its own height floor and its own
   flex-basis, so it never depends on a neighbour for either — the mistake
   `.card-done`'s comment records from an earlier audit. */
.card-focus {
  flex: 0 1 auto; align-self: center; min-height: var(--target);
  /* At 320px and 200% text "Pick it back up" is wider than the viewport, so it
     wrapped onto its own line and STILL hung 12px past the edge (a11y gate).
     `max-width` bounds it to the column and the label wraps inside the button
     rather than the button escaping the page. */
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* --- the person lens: what is with someone else ---------------------------- */
/* Shares the review surface's shape deliberately. Both answer "what is not
   ordinary work right now", and two different-looking boxes for the same kind
   of answer is a thing to learn rather than a thing to read. */
.people {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.people > .section { margin-top: 0; }
.people-count { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
.people-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.people-open {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem;
  width: 100%; min-height: var(--target); padding: 0.5rem 0.75rem; text-align: left;
  color: var(--ink); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); font: inherit; cursor: pointer;
}
.people-title { overflow-wrap: anywhere; }
/* How long, in the quiet token — it is a fact about a date, not a pace and not
   a rebuke aimed at whoever owes it. Nothing here rides on hue (B-01). */
.people-why { color: var(--ink-soft); font-size: 0.875rem; }
#detail-person { min-height: var(--target); flex: 1 1 6rem; min-width: 0; }
#detail-relation { min-height: var(--target); flex: 1 1 8rem; min-width: 0; font: inherit;
  color: var(--ink); background: var(--surface); border: 2px solid var(--line);
  border-radius: var(--radius); padding: 0.25rem 0.5rem; }
#detail-person::placeholder { color: var(--ink); opacity: 1; }
#detail-waiting-close { min-height: var(--target); }

/* --- the track portfolio: what you carry rather than do -------------------- */
/* Same shape as the review and person surfaces. Three boxes answering "what is
   not ordinary work right now" should look like one idea, not three. */
.portfolio {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.portfolio > .section { margin-top: 0; }
.portfolio-count { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
.portfolio-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.portfolio-open {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem;
  width: 100%; min-height: var(--target); padding: 0.5rem 0.75rem; text-align: left;
  color: var(--ink); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); font: inherit; cursor: pointer;
}
.portfolio-title { overflow-wrap: anywhere; }
/* Facts joined with middots. There is NO colour here that means "at risk", and
   there will not be one: a hue aimed at somebody else's work is this app
   grading them on evidence it does not have (B-01, law 5). */
.portfolio-why { color: var(--ink-soft); font-size: 0.875rem; overflow-wrap: anywhere; }
#detail-suspense { min-height: var(--target); }
#detail-suspense-set, #detail-track, #detail-untrack { min-height: var(--target); max-width: 100%;
  white-space: normal; overflow-wrap: anywhere; }

/* The report, when the clipboard was refused or the page is being printed. */
.report-preview {
  margin: 0.75rem 0 0; padding: 0.75rem; max-width: 100%;
  color: var(--ink); background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius);
  font: inherit; white-space: pre-wrap; overflow-wrap: anywhere;
}

/* --- the comms sweep, on the focus-exit ramp ------------------------------- */
.comms {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.comms > .section { margin-top: 0; }
/* An offer and a duration. Never a count of unread anything — an unread count is
   the most effective piece of shame-by-arithmetic in software, and this app
   cannot see your messages anyway. */
.comms-words { margin: 0 0 0.75rem; color: var(--ink); font-size: 0.9375rem; }
.comms-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.comms-actions button { min-height: var(--target); max-width: 100%;
  white-space: normal; overflow-wrap: anywhere; }
#comms-start, #comms-stop { min-height: var(--target); }

/* --- coming back (law 8: rest is legitimate) ------------------------------ */
/* Deliberately the SAME box as review, people and the portfolio. Coming back
   should not be met with a screen that looks unlike the app you left. */
.reentry {
  margin: 1.25rem 0 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.reentry > .section { margin-top: 0; }
/* "You were away a fortnight. Everything you put down is still here." — the
   reassurance is the content, so it gets full ink, not the hint token. */
.reentry-words { margin: 0 0 0.5rem; color: var(--ink); font-size: 1rem; }
/* What is waiting, as a count. Quieter, because it is the lesser fact. */
.reentry-waiting { margin: 0 0 0.75rem; color: var(--ink-soft); font-size: 0.9375rem; }
.reentry-amnesty-words { margin: 0 0 0.75rem; color: var(--ink); font-size: 0.9375rem; }
.reentry-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.reentry-actions button { min-height: var(--target); max-width: 100%;
  white-space: normal; overflow-wrap: anywhere; }

/* --- the Menu (law 6: demand-free) ---------------------------------------- */
.menu { margin: 0 0 1.25rem; padding: 0; }
.menu-cat { margin: 0.75rem 0 0.35rem; font-size: 0.9375rem; color: var(--ink-soft);
  font-weight: 600; }
.menu-list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.35rem; }
.menu-item {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem;
  width: 100%; min-height: var(--target); padding: 0.5rem 0.75rem; text-align: left;
  color: var(--ink); background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); font: inherit; cursor: pointer;
}
.menu-title { overflow-wrap: anywhere; }
/* Two numbers and the difference. There is deliberately NO bar and no
   percentage here, and no colour that changes as the numbers do — a bar is a
   machine for implying you are behind, on the one surface in this app that is
   structurally incapable of nagging. */
.menu-money { color: var(--ink-soft); font-size: 0.875rem; }
#detail-save-target, #detail-save-saved { min-height: var(--target); width: 6rem; }
#detail-save-set { min-height: var(--target); }

/* --- bothers: the thing that isn't a task --------------------------------- */
/* A <details>, closed by default. It sits under capture and asks nothing of you
   until you open it — the entry point for a worry must not itself be a prompt to
   find one. */
.bother-entry { margin: 0 0 1rem; }
#bother-summary {
  min-height: var(--target); display: flex; align-items: center;
  color: var(--ink-soft); font-size: 0.9375rem; cursor: pointer;
}
#bother-text { min-height: var(--target); flex: 1 1 6rem; min-width: 0; font: inherit; }
#bother-text::placeholder { color: var(--ink); opacity: 1; }
.bother {
  margin: 0 0 1.5rem; padding: 1rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
}
.bother-count { margin: 0 0 0.5rem; color: var(--ink-soft); font-size: 0.9375rem; }
.bother-prompt { margin: 0 0 0.5rem; font-size: 1.125rem; font-weight: 600; }
/* The worry itself, in the user's own words, at full weight. It is the thing
   being decided about and it should not be the quietest text on the card. */
.bother-card { margin: 0 0 0.85rem; color: var(--ink); overflow-wrap: anywhere; }
.bother-actions { display: flex; flex-direction: column; gap: 0.5rem; }
.bother-choice {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem;
  width: 100%; min-height: var(--target); padding: 0.5rem 0.75rem; text-align: left;
  color: var(--ink); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); font: inherit; cursor: pointer;
}
.bother-choice-label { font-weight: 600; }
/* The consequence of each choice. A forced choice with unlabelled outcomes is a
   guess, and this is the one question the whole flow turns on. All three are
   styled identically — "not mine to carry" is not a lesser option and must not
   look like one. */
.bother-choice-hint { color: var(--ink-soft); font-size: 0.875rem; }

/* --- printing (v1.5) ------------------------------------------------------ */
/* On screen the print area is not there at all. It is built at the moment of
   printing and emptied afterwards. */
.print-area { display: none; }

@media print {
  /* EVERYTHING except the print area is hidden. Before this rule the report's
     "Print it" produced the About dialog, the app behind it, and whatever the
     screen layout did under print media — the button worked and the output was
     unusable, which is the worst combination. */
  body > *:not(.print-area) { display: none !important; }
  .print-area { display: block !important; }

  /* Ink, not screen. Forcing white background and black text is the one place in
     this app where the theme is overridden on purpose: a dark theme printed is a
     page of toner and an unreadable result, and the printer is not a device
     anybody chose a theme for. */
  html, body { background: #fff !important; color: #000 !important; }
  .print-area { color: #000; font-size: 12pt; line-height: 1.4; }
  .print-h1 { font-size: 18pt; margin: 0 0 0.2rem; }
  .print-when { font-size: 10pt; margin: 0 0 1rem; }
  .print-head { font-size: 15pt; font-weight: 700; margin: 0 0 0.15rem; }
  .print-why { font-size: 11pt; margin: 0 0 1rem; }
  .print-h2 { font-size: 12pt; margin: 1rem 0 0.3rem; text-transform: uppercase;
    letter-spacing: 0.05em; }
  .print-list { margin: 0; padding-left: 1.2rem; }
  .print-list li { margin: 0 0 0.2rem; }
  /* A checkbox you can actually use with a pen. It is deliberately NOT a control
     that reports anything back — the honesty line above it says so. */
  .print-list li::marker { content: '☐  '; }
  .print-more { font-size: 10pt; margin: 0.2rem 0 0; }
  .print-note { font-size: 9pt; margin: 1.5rem 0 0; border-top: 1px solid #999;
    padding-top: 0.4rem; }
  .print-pre { white-space: pre-wrap; font-size: 11pt; }
  /* Never break a section across pages if it can be helped. */
  .print-block { break-inside: avoid; }
  @page { margin: 15mm; }
}

/* Older releases, folded. Rendering every release at once made the About panel
   17,000–25,000px tall — the reason a way out was ever far from a thumb. */
.note-older > summary,
.disclosure > summary {
  min-height: var(--target);
  display: flex;
  align-items: center;
  color: var(--ink);
  cursor: pointer;
}

/* The pairing key, shown rather than downloaded. The code is scanned across a
   table and the text beneath it is the same 44 characters — either road pairs,
   and neither writes the secret to a disk. */
.qr { margin: 0.75rem 0; }
.qr svg { width: min(15rem, 60vw); height: auto; display: block; }
.key-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9375rem;
  overflow-wrap: anywhere;
  user-select: all;              /* one tap selects the whole key, not a word of it */
  color: var(--ink);
}
