/* Base shell styles. All styling lives here — CSP forbids inline style/script. */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

html {
  color-scheme: dark;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15.5px;
  line-height: 1.45;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

[x-cloak] {
  display: none !important;
}

/* The wordmark font — the ONE sanctioned webfont (DESIGN): self-hosted
   IBM Plex Mono 600 subset, used nowhere else. */
@font-face {
  font-family: "Plex Wordmark";
  src: url("../fonts/plex-mono-600.e71a597e376e.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}

/* Benchtop mark (chosen 2026-08-01): the word stands on its amber bench. */
.wordmark {
  display: inline-block;
  font-family: "Plex Wordmark", var(--mono);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.wordmark .bench {
  display: inline-block;
  padding: 0 6px 9px;
  background:
    linear-gradient(var(--accent), var(--accent)) left 12% bottom 0 / 1.5px 5.5px no-repeat,
    linear-gradient(var(--accent), var(--accent)) right 12% bottom 0 / 1.5px 5.5px no-repeat,
    linear-gradient(var(--accent), var(--accent)) left 0 bottom 5px / 100% 2px no-repeat;
}

.label,
.field > label:not(.check-label),
.field-head > label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Bare <label>s in forms get the same treatment as .label — plus air, so
   they read as captions rather than text snugged to the control.
   .check-label is excluded from both rules: a checkbox's label IS its
   interactive text (sans, flex row), not a caption. */
.field > label:not(.check-label) {
  display: block;
  margin-bottom: 6px;
}

.display {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 32px;
  letter-spacing: -0.01em;
}

@media (min-width: 720px) {
  .display {
    font-size: 36px;
  }
}

.card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  padding: 24px;
}

.stack > * + * {
  margin-top: 14px;
}

.field .label {
  display: block;
  margin-bottom: 6px;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 12px;
  color: var(--ink);
  font: inherit;
}

/* One control height for inputs AND selects — a select's intrinsic macOS
   height ignores padding and renders visibly shorter otherwise. 44px is
   the DESIGN touch target. (Textareas keep their rows; checkboxes stay
   checkbox-sized.) */
.field input:not([type="checkbox"]):not([type="radio"]),
.field select {
  height: 44px;
}

.field textarea {
  resize: vertical;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 0;
  border-color: transparent;
}

.field-row {
  display: flex;
  /* Wrappable so a field can claim a whole line — see the chip rule below.
     Nothing else in a row ever wraps: `.field-row .field` sets min-width: 0,
     so an ordinary field's hypothetical size is 0 and the line always fits.
     Row gap matches .task-form's own 14px, since a wrapped line is a new row
     of fields and should sit the same distance from the one above it. */
  flex-wrap: wrap;
  gap: 14px 10px;
  /* flex-end so a bare checkbox lines up with its neighbour's INPUT rather
     than floating level with that neighbour's caption (settings, remote tz). */
  align-items: flex-end;
}

/* A field that grew preset chips takes a line of its own. Four chips do not
   fit a third of a form: on the project form and the routing card they wrapped
   2 + 2 under the control while the fields beside them stood above a column of
   dead space, and the typed input hung past the form's right edge on the way
   (Jaroslav, 2026-08-15). Given a full line, control and chips read as one
   left-aligned block, exactly like the task form's due and remind-me.

   min-width, NOT flex-basis: the basis is the MAIN axis, and this row turns
   into a column below 520px, where `flex-basis: 100%` would set the field's
   HEIGHT. min-width means the same thing on both axes, so the phone block
   needs no exception to it. */
.field-row .field:has(.quick-row) {
  min-width: 100%;
}

/* Tops align in a chip-carrying row: flex-end would sink the fields on the
   first line to the bottom of the tallest one (2026-08-14 — before the rule
   above, that was DUE growing presets and dragging REPEAT ~150px below its
   own label; now it is the project form's one-line "fixed at creation."
   sitting beside a 44px select). Scoped, because the settings checkbox above
   still wants the bottom. */
.field-row:has(.quick-row) {
  align-items: flex-start;
}

.field-row .field {
  flex: 1;
  min-width: 0; /* flex children default to min-width:auto — date inputs
                   would refuse to shrink and overlap their neighbours */
}

.field input[type="date"] {
  display: block;
  min-width: 0; /* NO appearance:none — it strips the native control on iOS
                   and leaves a blank textarea-looking box */
}

/* iOS lays the VALUE out in its own inner box, sized to content and centred,
   and that box — not the input — is what used to push the control past the
   form (ARCHITECTURE §3). Constrain the box and the field behaves, with the
   native picker intact. Ignored everywhere that isn't WebKit. */
.field input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  min-width: 0;
}

/* The face and the real control, stacked. The INPUT sits on top, invisible
   but tappable: iOS opens its picker for a genuine tap on a date input, which
   is the one behaviour that is guaranteed — showPicker() on a collapsed input
   silently did nothing (2026-08-14). The wrapper is the positioning context
   the first attempt lacked (the input had only a sibling, so it was absolute
   against nothing) and carries the 44px target, so the visible chip can stay
   chip-sized. */
.date-field {
  position: relative;
  display: inline-flex;
  align-items: center;
  /* The same box as a .wheel — this is a control sitting beside hh/mm, not a
     chip sitting under one (Jaroslav, 2026-08-14). Height and treatment match
     so the datetime row reads as one set of controls. */
  min-height: 44px;
  min-width: 68px;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 12px;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  /* Load-bearing: iOS gives a date input an intrinsic width it will not give
     up, and the invisible one hung out past this box to the right — enough to
     make the whole PAGE scroll sideways. Clipping it costs nothing, since the
     tap area is this box. */
  overflow: hidden;
}

/* The focusable element inside is invisible, so its own outline is invisible
   too — this control had no focus indicator at all, on the one path where it
   exists (iPad keyboard, Full Keyboard Access). The ring goes on the wrapper.
   Not clipped by its own overflow: hidden clips descendants, not self. */
.date-field:focus-within {
  outline: 2px solid var(--accent);
  border-color: transparent;
}

/* The compound selector is deliberate: `:not()` takes its argument's
   specificity, so `.field input:not([type="checkbox"]):not([type="radio"])`
   is (0,3,1) and a plainer selector here loses — the overlay silently became
   a hard 44px rather than covering its face. The mechanism rests on that
   cover, so this height has to win. */
.field .date-field input[type="date"],
.date-field input[type="date"] {
  position: absolute;
  inset: 0;
  width: 100%;
  max-width: 100%;
  height: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  opacity: 0;
  cursor: pointer;
}

/* The date when set, a calendar glyph when empty. */
.date-face {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}

.date-face.empty {
  color: var(--ink-3);
}

/* On a mouse the field stays a real typeable input (a keyboard beats any
   calendar), just sized to its content instead of eating the row. */
.field input.date-alt {
  width: auto;
  min-width: 150px;
  /* A text input carries an intrinsic width of ~20 characters and flatpickr's
     alt input is a text input, so `width: auto` is ~206px here — wider than a
     third of a form. It overflowed its column and hung past the form's right
     edge, and past the routing card's border (2026-08-15). The field is a full
     line now, but nothing should depend on that to stay inside its box. */
  max-width: 100%;
}

@media (max-width: 520px) {
  /* The :has() rule above is (0,2,0) — :has() takes the specificity of its
     argument — and @media adds none, so a bare `.field-row` here loses to it
     and every chip-carrying row would stack at shrink-to-fit width instead of
     stretching. Matched selector, matched specificity. Third time this file
     has been bitten by that; see the notes at .quick-row and .datetime-parts. */
  .field-row {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
  }

  /* A SEPARATE rule, never grouped with the one above: a selector list is not
     forgiving, so on a browser without :has() the whole thing would be
     dropped — and every two-column row would stay side by side on a phone,
     far worse than the alignment this exists to restore. */
  .field-row:has(.quick-row) {
    align-items: stretch;
  }
}

.btn {
  display: inline-block;
  width: 100%;
  background: var(--accent);
  color: var(--accent-ink);
  border: 0;
  border-radius: 12px;
  padding: 12px 16px;
  min-height: 44px; /* touch target per DESIGN */
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* An <a class="btn"> needs the centring a <button> gets for free: with
   inline-block the label sits at the top padding edge, so min-height's
   spare 6px lands underneath it and the text rides high. `align-items`
   does the work — the `display` value is moot inside `.detail-actions`,
   which blockifies its items anyway. */
a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

.btn:hover {
  filter: brightness(1.06);
}

.btn-compact {
  width: auto;
  min-height: 36px;
  padding: 8px 18px;
  border-radius: 10px;
  font-size: 14px;
}

/* The capture composer: a bordered writing surface, not a form — the
   textarea is naked inside it and the whole card takes the focus ring. */
.composer {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  padding: 6px 6px 8px;
}

.composer textarea {
  width: 100%;
  background: transparent;
  border: 0;
  outline: none;
  padding: 10px 12px;
  color: var(--ink);
  font: inherit;
  resize: vertical;
}

.composer:focus-within {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

/* The same notes line in the inbox composer, which pads tighter and sizes
   its text from the body. `font: inherit` matches the textarea beside it
   exactly — including the 15.5px it already zooms iOS with. */
.composer .notes-line {
  padding: 10px 12px;
  font: inherit;
  line-height: 1.45;
}

.composer-bar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  padding: 0 8px 4px;
}

.kbd-hint {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-right: auto;
}

/* Sidebar mini calendar — quiet month grid; edge-week days dimmed, today
   amber, every day a link into /tasks/?day=. */
.minical {
  margin-bottom: 18px;
}

.minical-head {
  margin-bottom: 8px;
}

.minical-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
}

.minical-wd {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  text-align: center;
  padding-bottom: 3px;
}

.minical-day {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-2);
  text-align: center;
  text-decoration: none;
  padding: 3px 0;
  border-radius: 6px;
}

.minical-day:hover {
  background: var(--raised);
  color: var(--ink);
}

.minical-day.out {
  color: var(--ink-3);
  opacity: 0.55;
}

.minical-day.is-today {
  color: var(--accent);
  font-weight: 700;
}

/* Settings: draggable area rows — the dot previews the color select. */
.area-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--hairline);
  cursor: grab;
}

.area-form {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}

.area-form input[name="name"] {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: 0;
  border-radius: 6px;
  padding: 6px 8px;
  color: var(--ink);
  font: inherit;
}

.area-form input[name="name"]:focus {
  outline: 2px solid var(--accent);
  background: var(--raised);
}

.area-form select,
.area-add select {
  font-family: var(--sans); /* interactive text is sans — mono is for labels */
  font-size: 12.5px;
  background: var(--raised);
  color: var(--ink-2);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  padding: 4px 6px;
}

.area-add {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0 0;
}

.area-add input {
  flex: 1;
  min-width: 0;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 7px 10px;
  color: var(--ink);
  font: inherit;
}

/* The picked day's tasks lead the list in their own raised section. */
.day-sec {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  padding: 14px 16px 10px;
}

/* Interactive text is SANS; mono is reserved for capitalized informative
   labels (TODAY · W31, section heads, chips) — Jaroslav, 2026-08-01.
   One carve-out: .minical-day links stay mono for tabular calendar digits. */
.btn-quiet {
  background: none;
  border: 0;
  padding: 0;
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.2; /* identical box for <a> and <button> — keeps rows level */
  color: var(--ink-2);
  cursor: pointer;
}

.btn-quiet:hover {
  color: var(--ink);
}

.muted {
  color: var(--ink-2);
}

/* System-voice help/hints — "notes from the shop": serif italic, sentence
   case, quiet ink. Visually distinct so guidance never reads as data
   (Jaroslav, 2026-08-01). */
.note {
  font-family: var(--serif);
  font-style: italic;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--ink-3);
  margin: 2px 0 14px; /* breathing room before the controls it introduces */
}

.note a {
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
}

/* Area identity: 7px dot always paired with a mono text label (DESIGN).
   One class per approved palette color — inline styles are CSP-banned. */
.dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 6px;
  vertical-align: 1px;
}

.c-3987e5 { background: #3987e5; }
.c-d95926 { background: #d95926; }
.c-199e70 { background: #199e70; }
.c-c98500 { background: #c98500; }
.c-d55181 { background: #d55181; }
.c-9085e9 { background: #9085e9; }
.c-8a8f98 { background: #8a8f98; }

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink-2);
}

.chip-on {
  color: var(--ink);
  border-color: var(--accent);
}

.log-row {
  display: flex;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--hairline);
  font-size: 14px;
}

.log-link:hover {
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
}

.log-meta {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
  padding-top: 3px;
}

.errors {
  color: var(--ink-2);
  font-size: 14px;
}

.danger {
  color: var(--danger);
}

/* ── capture sheet (design/capture-mobile.html) ─────────────────────── */

.scrim {
  position: fixed;
  inset: 0;
  background: rgba(5, 6, 8, 0.55);
  z-index: 50;
}

.sheet {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 0;
  width: 100%;
  max-width: 640px;
  max-height: 85vh;
  overflow-y: auto;
  background: var(--raised);
  border-radius: 22px 22px 0 0;
  border-top: 1px solid var(--hairline);
  box-shadow: 0 -18px 60px rgba(0, 0, 0, 0.6);
  padding: 10px 20px calc(20px + env(safe-area-inset-bottom));
  z-index: 60;
  transition: transform 0.32s cubic-bezier(0.32, 0.72, 0.33, 1);
}

/* Capture sheet visibility is class-driven (never display:none) so its
   textarea can be focused synchronously in the opening gesture — the only
   timing where iOS raises the keyboard. `away` parks it below the viewport. */
.sheet.away {
  transform: translateX(-50%) translateY(112%);
  pointer-events: none;
  box-shadow: none;
}

.sheet.in {
  /* --kb = keyboard height (app.js visualViewport tracking) — the sheet
     rides ABOVE the iOS keyboard instead of opening behind it */
  transform: translateX(-50%) translateY(calc(0px - var(--kb, 0px)));
}

.sheet.dragging {
  transition: none; /* follow the finger exactly during a pull-down */
}

@media (prefers-reduced-motion: reduce) {
  .sheet {
    transition: none;
  }
}

.grabber {
  width: 38px;
  height: 5px;
  border-radius: 3px;
  background: #33363d;
  margin: 0 auto 14px;
}

.sheet-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14px;
}

.types {
  display: flex;
  gap: 6px;
  margin-bottom: 14px;
}

.type {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 6px 11px;
  background: transparent;
  cursor: pointer;
}

.type.on {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
  font-weight: 600;
}

/* The composer is ONE box with two lines: the thought, and a quiet line
   under it for a link or a scrap of context. The border moved here from the
   textarea so the pair reads as a single surface, and the focus ring is
   drawn once around both (:focus-within) rather than jumping between them. */
.input-wrap {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 16px;
}

.input-wrap:focus-within {
  outline: 2px solid var(--accent);
  border-color: transparent;
}

.input-wrap textarea,
.notes-line {
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  color: var(--ink);
  font: inherit;
  font-size: 16px; /* 16px stops iOS zoom-on-focus */
}

.input-wrap textarea:focus,
.notes-line:focus {
  outline: none; /* the wrap carries the ring */
}

.input-wrap textarea {
  padding: 14px 15px;
  min-height: 96px;
  line-height: 1.45;
  resize: vertical;
}

/* Subordinate by weight and room, never by type size — anything under 16px
   zooms iOS on focus, and this field exists to take a pasted link on a
   phone. It is not the anchor while formulating; the textarea is. */
.notes-line {
  border-top: 1px solid var(--hairline);
  padding: 11px 15px;
  line-height: 1.3;
  color: var(--ink-2);
}

.notes-line:focus {
  color: var(--ink);
}

.notes-line::placeholder {
  color: var(--ink-3);
}

.route {
  margin-top: 14px;
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 16px;
  padding: 13px 15px;
}

/* The confirmation is a notice, not a panel: one line, tighter box, green
   edge. It stays until the next capture replaces it — he has just acted,
   and a receipt that vanishes is a receipt you can miss. */
.route.confirmed {
  border-color: color-mix(in srgb, var(--a-rejobs) 40%, transparent);
  padding: 10px 14px;
}

/* While a routing decision is pending, the composer gets out of the way:
   the chips, the textarea and the Capture/mic row all belong to writing a
   NEW capture, and there is exactly one decision to make on screen. They
   come back by themselves — dismiss empties the slot, confirming swaps in
   `.route.confirmed`, and only the pending card carries `.capture-card`. */
.sheet:has(#sheet-route .capture-card) .types,
.sheet:has(#sheet-route .capture-card) .input-wrap,
.sheet:has(#sheet-route .capture-card) .micbar {
  display: none;
}

.route-head {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.route-parse {
  margin-top: 9px;
  font-size: 14.5px;
  line-height: 1.45;
}

/* The captured notes line, echoed back on the card. Quiet, and allowed to be
   a bare URL: without overflow-wrap a long address has no break opportunity
   and pushes the whole card off a phone screen. */
.route-note {
  margin-top: 7px;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}

/* The margin above separates the line from `.route-head`. The confirmation
   receipt has no head, so there it was separating the text from nothing —
   19px of space above the line against 10px below, and the one line in the
   box sat visibly low. A first child has nothing to be pushed away from. */
.route-parse:first-child {
  margin-top: 0;
}

/* The receipt's title is a link to the thing just created. Underline rather
   than colour: amber is reserved (DESIGN) and the box already carries the
   green edge, so the affordance has to come from the text itself. */
.confirmed .route-parse a {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 3px;
}

.confirmed .route-parse a:hover {
  text-decoration-color: currentColor;
}

.facts {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.fact {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  padding: 4px 8px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.fact .dot {
  margin-right: 0;
}

.actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

/* `class="actions"` exists in exactly two places, and BOTH override the
   stretch: a routing card sizes its buttons to their labels (the stretched
   twins wrapped their labels and buried Dismiss — see .route-actions below),
   and so does the resurfacing card, where the quote is the point and the
   buttons are an aside. So `flex: 1` here had no consumers; deleting it beats
   adding a third override, and takes a source-order trap with it. */
.actions .btn {
  width: auto;
}

/* Routing card: one calm row — Add task · Adjust · Dismiss (right). The
   old stretched twins wrapped their labels and buried dismiss below. */
/* Left-aligned on a wide screen like the rest of the card; the phone rule
   in the Thumb zone block moves it. Dismiss carries `danger-action`, so it
   pins to the far edge by the same mechanism every other row uses. */
.route-actions {
  align-items: center;
  /* Without wrap the overflow escapes LEFT — right-aligned, the first child
     (Dismiss) walks off the viewport instead of the row growing a line. At
     375 the children already need 254.8px in a 253px box. */
  flex-wrap: wrap;
  gap: 10px;
}

.route-actions .btn {
  flex: 0 0 auto;
  /* 11px keeps the most-tapped control in the app at the 44px DESIGN
     minimum; min-height:0 had it at 42.5. */
  padding: 11px 18px;
  white-space: nowrap;
  border-radius: 10px;
}


.btn-ghost {
  background: transparent;
  color: var(--ink-2);
  border: 1px solid var(--hairline);
}

.adjust {
  margin-top: 12px;
}

/* Capture actions sit AFTER the thought, quiet and right-aligned — the
   textarea is the only visual anchor while formulating (2026-08-01 round:
   "huge and bright" buttons distract during cognitively demanding capture). */
.micbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}

.mic {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--hairline);
  display: grid;
  place-items: center;
  color: var(--ink-2);
  cursor: pointer;
  flex: none;
}

.mic.rec {
  border-color: var(--accent);
  outline: 2px solid color-mix(in srgb, var(--accent) 35%, transparent);
  animation: pulse 1.4s ease-in-out infinite;
}

@keyframes pulse {
  50% {
    outline-color: color-mix(in srgb, var(--accent) 8%, transparent);
  }
}

.voice-original summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 7px;
}

.voice-badge {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--a-rejobs);
  border: 1px solid color-mix(in srgb, var(--a-rejobs) 40%, transparent);
  border-radius: 5px;
  padding: 2px 6px;
}

.voice-toggle {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  text-decoration: underline dotted;
}

.send {
  flex: none;
  height: 42px;
  border: 0;
  border-radius: 12px;
  padding: 0 22px;
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
}

.auth {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: calc(20px + env(safe-area-inset-top, 0px)) 20px
    calc(20px + env(safe-area-inset-bottom, 0px));
}

.auth .card {
  width: 100%;
  max-width: 360px;
}

.auth .wordmark {
  display: block;
  text-align: center;
  margin-bottom: 18px;
  font-size: 17px;
}

/* ── app shell: sidebar (desktop, design/today-desktop.html) + bottom bar
   (mobile, design/today-mobile.html) ─────────────────────────────────── */

.layout {
  /* the third track is the right rail — `auto` collapses to nothing when a
     page renders no rail (or it's empty) */
  display: grid;
  grid-template-columns: 216px minmax(0, 1fr) auto;
  min-height: 100vh;
}

.rail {
  /* The rail takes the slack the main column cannot use. `.content` caps at
     860px, so on anything wider than 1376px everything past sidebar+cap is
     dead gutter — at 1596px that was 224px of nothing sitting between the
     two columns while the quote wrapped at 30 characters.

     Fluid, not a breakpoint, and that is the point: the previous step at
     1600px left a 1596px window — a 13" MacBook at "More Space", which is
     the actual machine — stranded 4px on the narrow side of the cliff.
     1076px = 216px sidebar + 860px content cap. Floor 300px (at 1280 the
     content is the squeezed one, so the rail must not grow); ceiling 460px,
     past which a quote stops being a column and becomes a paragraph.

     100vw counts a classic scrollbar the grid does not, so where scrollbars
     take space this runs ~15px wide; the middle track is minmax(0, 1fr) and
     absorbs it. Nothing overflows either way. */
  width: clamp(300px, calc(100vw - 1076px), 460px);
  border-left: 1px solid var(--hairline);
  padding: 30px 22px;
  /* Three panels stack here (resurfaced, slipping, done today) and none of
     them carries a margin — per design/today-desktop.html. */
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.rail-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  padding: 5px 0;
}

.rail-row a {
  color: var(--ink-2);
  text-decoration: none;
  text-decoration-line: line-through;
  font-size: 13.5px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-row a:hover {
  color: var(--ink);
}

.rail-time {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--ink-3);
  flex: none;
}

/* One authoritative breakpoint for the rail/strip split. Two complementary
   queries (max-width:1279 + min-width:1280) leave a gap at fractional widths
   — browser zoom, non-integer DPI, iPad split view — where NEITHER matched
   and both copies rendered at once. */
.rail {
  display: none;
}

@media (min-width: 1280px) {
  /* [hidden] is a UA rule without !important, so ANY author `display` beats
     it — the aside carries `hidden` when all three panels are empty and would
     still paint a bordered 364px column here. Below this breakpoint it was
     hidden by accident (display: none), which is why it was never noticed. */
  .rail[hidden] {
    display: none;
  }

  .rail {
    display: flex;
  }

  .strip-only {
    display: none;
  }
}

.sidebar {
  border-right: 1px solid var(--hairline);
  padding: 26px 18px 20px;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}

.sidebar .wordmark {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.tagline {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 3px;
}

.sidenav {
  margin-top: 30px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sidenav a {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: var(--ink-2);
  font-size: 14px;
  letter-spacing: -0.01em;
  padding: 8px 10px;
  border-radius: 9px;
}

.sidenav a.on {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
}

.badge {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-3);
}

.badge.hot {
  color: var(--accent);
}

.spacer {
  flex: 1;
}

.kbd-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 9px 10px;
  border-top: 1px solid var(--hairline);
}

.kbd-row .k {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

kbd {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 2px 6px;
}

.content {
  padding: 30px 36px calc(40px + env(safe-area-inset-bottom, 0px));
  max-width: 860px;
  width: 100%;
}

.mobile-only {
  display: none;
}

@media (max-width: 1023px) {
  .sidebar,
  .desktop-only {
    display: none;
  }

  .layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .content {
    padding: calc(22px + env(safe-area-inset-top, 0px)) 20px
      calc(120px + env(safe-area-inset-bottom, 0px));
    max-width: 640px;
    margin: 0 auto;
  }

  /* The 560px measure is for a WIDE screen, where a form left to fill 860px
     would read as a stretched line. Here the column is already capped at 640
     and centred, so that second cap only ends the form 40px short of the
     section head above it and the actions below — a ragged right edge with
     nothing in it (Jaroslav's screenshot, 2026-08-14). Both, because DESIGN
     ties them: "a record and its edit form should not be different widths". */
  .task-form,
  .detail-panel {
    max-width: none;
  }

  .mobile-only {
    display: block;
  }
}

.bottombar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 10px 24px calc(14px + env(safe-area-inset-bottom, 0px));
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: linear-gradient(to top, rgba(11, 12, 14, 0.97) 65%, rgba(11, 12, 14, 0));
  z-index: 30;
}

/* Four equal actions (search · today · capture · menu) — the capture
   button keeps the accent identity at the same footprint as its siblings
   (2026-08-01 feedback: no oversized floating fab). */
.bottombar .side {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--raised);
  border: 1px solid var(--hairline);
  display: grid;
  place-items: center;
  color: var(--ink-2);
  font-size: 17px;
  cursor: pointer;
  text-decoration: none;
}

.bottombar .side svg {
  display: block; /* kills the inline-baseline gap — geometric centering */
}

.side-accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.menu-sheet {
  padding-bottom: calc(28px + env(safe-area-inset-bottom, 0px));
}

.menu-nav {
  display: flex;
  flex-direction: column;
}

.menu-nav a {
  font-size: 16px;
  color: var(--ink);
  padding: 13px 4px;
  border-bottom: 1px solid var(--hairline);
}

.menu-nav .btn-quiet {
  width: 100%;
  text-align: left;
  font-size: 16px;
  padding: 13px 4px;
  color: var(--ink); /* identical to the other menu items */
}

/* ── today + task rows (design/today-*.html) ─────────────────────── */

.eyebrow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}

.label .n {
  color: var(--ink-2);
}

.tz {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--ink-3);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  padding: 3px 7px;
}

.today-date {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 32px;
  letter-spacing: -0.01em;
  margin-top: 6px;
}

@media (min-width: 1024px) {
  .today-date {
    font-size: 36px;
    margin: 8px 0 4px;
  }
}

.sec {
  margin-top: 26px;
}

.sec-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 8px;
}

/* A form page's head belongs to the form under it, not to the page. `.task-form`
   keeps its 560px measure on a wide screen while `.content` runs to 860, so
   "← back" floated some 300px right of every field it belongs to — the same
   "gap on the right" reported below 1024px (2026-08-14), where the fix was the
   other way round: drop the cap and let the form fill the already-narrow
   column. Above 1024 the measure is worth keeping and the head is what yields.
   Marked in the template rather than inferred with :has(), because .task-form
   also appears on settings and search, where the head belongs to the page. */
@media (min-width: 1024px) {
  .sec-head.narrow {
    max-width: 560px; /* the measure — see .task-form and .detail-panel */
  }
}

.sec-head a {
  font-size: 13px;
  color: var(--ink-3);
}

.sec-head a:hover {
  color: var(--ink-2);
}

.row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--hairline);
}

.row:last-child {
  border-bottom: none;
}

.cb {
  flex: none;
  width: 22px;
  height: 22px;
  border: 1.5px solid color-mix(in srgb, var(--ink) 18%, var(--bg));
  border-radius: 7px;
  margin-top: 1px;
  background: transparent;
  cursor: pointer;
  position: relative;
  padding: 0;
}

.cb.done {
  background: color-mix(in srgb, var(--ink) 10%, var(--bg));
  border-color: transparent;
}

.cb.done::after {
  content: "✓";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--ink-3);
  font-size: 13px;
}

.row .body {
  flex: 1;
  min-width: 0;
}

.row .t {
  font-size: 15.5px;
  line-height: 1.35;
  letter-spacing: -0.01em;
}

.row.done .t {
  color: var(--ink-3);
  text-decoration: line-through;
}

.meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.meta .dot {
  margin-right: 0;
}

.due-today {
  color: var(--accent);
}

/* "today" reads amber wherever a date means today — due chips, Up Next
   group label, the Today screen eyebrow, the mini calendar cell. */
.is-today {
  color: var(--accent);
}

.overdue {
  color: var(--danger);
}

.rec {
  font-size: 11px;
}

/* A reminder still ahead. Amber because it is the one bit of a row that is
   going to act on its own — everything else there is a fact, this is a
   promise. Never --danger: a pending reminder is not trouble. */
.rem {
  color: var(--accent);
}

.star {
  flex: none;
  background: none;
  border: 0;
  color: var(--accent);
  font-size: 15px;
  margin-top: 2px;
  cursor: pointer;
  padding: 0 2px;
}

.star.dim {
  color: var(--ink-3);
}

/* Top-3 drag-to-reorder */
[data-sortable] .row {
  cursor: grab;
}

.drag-ghost {
  opacity: 0.35;
  background: var(--raised);
  border-radius: 10px;
}

.nudge-form {
  flex: none;
  display: flex;
  align-items: center;
}

.nudge {
  background: none;
  border: 0;
  color: var(--ink-3);
  font-size: 13px;
  cursor: pointer;
  padding: 0 2px;
}

/* the arrows are the non-JS fallback only */
.js .nudge-form {
  display: none;
}

/* Task detail + form */
/* A detail page is a record, so it should read as one object: a bordered
   panel rather than loose text with an action row stranded across the full
   width of the viewport. The frame is also what gives the actions a footer
   to sit in — which is what makes "destructive far left, primary far right"
   read as a deliberate dialog footer instead of two buttons adrift. */
.detail-panel {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  padding: 20px 22px;
  max-width: 560px; /* same measure as .task-form — a record and its edit
                       form should not be different widths */
}

.detail-head {
  margin-bottom: 16px;
}

.detail-panel .detail-head {
  margin-bottom: 0;
}

.detail-body {
  margin-top: 16px;
}

/* A hairline separates the footer from the record, so the row reads as
   belonging to the panel rather than floating under it. */
.detail-panel .detail-actions {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}

@media (max-width: 640px) {
  .detail-panel {
    padding: 18px 16px;
  }
}

.detail-title {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 26px;
  letter-spacing: -0.01em;
  margin: 6px 0 8px;
}

.detail-title.struck {
  color: var(--ink-3);
  text-decoration: line-through;
  text-decoration-color: var(--ink-3);
}

.detail-head .meta {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-2);
}

.detail-head .meta a {
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
}

/* The confirming action sits on the trailing edge at EVERY width: that's
   the macOS/iOS convention this design already follows elsewhere, it's
   where the thumb lands on a phone, and one order across both viewports is
   one muscle memory instead of two. A mouse pays almost nothing for the
   travel, so there's no reason to disagree with the phone. */
.detail-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 18px;
  margin-top: 22px;
}

/* …and the irreversible action is pinned to the OPPOSITE end, so it is
   never adjacent to the button you actually came to press. Distance is the
   cheapest safeguard there is, and it costs a destructive action nothing to
   be far away — you arrive at it deliberately or not at all. */
.danger-action {
  margin-right: auto;
}

/* A bare link is 16px tall. These are real targets in a real row — Delete,
   Cancel, Dismiss — so they get the 44px DESIGN asks for, as padding only:
   the box stays invisible, the tap area doesn't. */
.detail-actions .btn-quiet,
.route-actions .btn-quiet,
.rs-actions .btn-quiet {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* Buttons live inside per-action <form>s — flatten them so every action
   (Done, Edit, Delete…) sits on the same baseline as the bare links.

   Without this the <form> is the flex item, not the button: it stretches to
   the tallest sibling and its button sits at the TOP of that box, so a bare
   Skip link floated 14px above the Keep button beside it. Every row whose
   actions are separate posts needs the rule, which is why it is a list. */
.detail-actions form,
.rs-actions form {
  display: contents;
}

.detail-actions .btn {
  width: auto;
  padding: 10px 22px;
}

/* An icon-only action is square-ish rather than pill-shaped — 44px each way
   keeps the touch target while the glyph stays centred (a.btn already
   flexes; buttons centre their content for free). */
.detail-actions .btn:has(> svg) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 52px;
  padding: 10px 16px;
}

.t a {
  color: inherit;
  text-decoration: none;
}

.t a:hover {
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
}

.task-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 560px;
}

/* A wrapper that groups fields (the repeat + custom-rule pair) is a single
   flex child of .task-form, so it has to repeat the form's own gap —
   otherwise the label inside it sits flush against the control above. */
.field-stack {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* A field that groups several controls is a <fieldset>, so the group has an
   accessible name — but it must not look like one: the UA draws a border and
   its own padding, and `min-width: min-content` on a fieldset would stop the
   row shrinking. Reset all three; the legend keeps the app's caption style. */
fieldset.field {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

fieldset.field > legend {
  padding: 0;
  margin-bottom: 6px;
}

/* The day picker on a weekly repeat. Django renders CheckboxSelectMultiple as
   a div of <label><input> pairs, and copies the widget's attrs onto every
   input as well as the wrapper (option_inherits_attrs), so every rule here is
   scoped to `div.day-toggle` or to the label — never the bare class. */
div.day-toggle {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.day-toggle div {
  display: contents;
}

.day-toggle label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 46px;
  min-height: 44px; /* the DESIGN touch target */
  padding: 0 10px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 8px; /* the .chip radius — this IS a chip, wearing a checkbox */
  color: var(--ink-2);
  font-size: 13px;
  cursor: pointer;
}

/* Baseline, and it has to stay usable on its own: a real checkbox inside a
   chip-shaped label. Everything below hides that checkbox and paints the
   state with :has() — which, unlike the usual :has() slip, would not merely
   look wrong without support but leave a control with NO visible selection
   and no focus ring. So the polish is gated on the feature itself. */
.day-toggle input {
  accent-color: var(--accent);
}

@supports selector(:has(*)) {
  .day-toggle input {
    /* Transparent and non-interactive — NOT display:none, which would take it
       out of the tab order and the a11y tree. The label activates it. */
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  .day-toggle label:has(input:checked) {
    color: var(--ink);
    border-color: var(--accent); /* .chip-on, same as every other "this one is on" */
  }

  .day-toggle label:has(input:focus-visible) {
    outline: 2px solid var(--accent);
  }
}

.day-toggle label:hover {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--hairline));
}

/* RRULE examples: rule on the left, plain English on the right, collapsing
   to stacked pairs when there isn't room. */
.rrule-help {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 14px;
  margin: 10px 0 0;
  align-items: baseline;
}

/* Same chip as `.md code` — mono here is a code literal, not a label, and
   inventing a third size for it would just be a third size. */
.rrule-help dt {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink-2);
  background: var(--raised);
  border-radius: 4px;
  padding: 1px 5px;
  justify-self: start;
  /* A longer example must wrap rather than give the page a sideways
     scrollbar; today's longest clears a 320px viewport, but only just. */
  overflow-wrap: anywhere;
}

/* Help copy, so it wears the help voice (DESIGN: serif italic, --ink-3) —
   it sits two lines under a `.note hint` that already does. */
.rrule-help dd {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-3);
}

@media (max-width: 520px) {
  .rrule-help {
    grid-template-columns: 1fr;
    gap: 2px;
  }

  .rrule-help dd {
    margin-bottom: 6px;
  }
}

.form-note {
  margin-bottom: 12px;
}

.field-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}

.tabs {
  display: flex;
  gap: 2px;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 2px;
}

.tab {
  background: none;
  border: 0;
  border-radius: 6px;
  padding: 3px 10px;
  font-size: 12px;
  color: var(--ink-3);
  cursor: pointer;
}

.tab.on {
  background: var(--surface);
  color: var(--ink);
}

.md-preview {
  min-height: 120px;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 12px;
}

.hint {
  font-size: 12px;
  margin-top: 6px;
}

.errorlist {
  list-style: none;
  color: var(--danger);
  font-size: 12.5px;
  margin: 4px 0 0;
  padding: 0;
}

/* Projects */
.project-row {
  display: block;
  padding: 10px 0;
  border-bottom: 1px solid var(--hairline);
  color: inherit;
  text-decoration: none;
}

.project-row:hover .t {
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
}

.project-row.quiet .t {
  color: var(--ink-2);
}

.kind-chip {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 1px 6px;
  margin-left: 6px;
  vertical-align: 2px;
}

.static-value {
  color: var(--ink-2);
  padding: 10px 0;
}

/* Checklist */
.check-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--hairline);
}

.check-item .ct {
  flex: 1;
}

.check-item.done .ct {
  color: var(--ink-3);
  text-decoration: line-through;
  text-decoration-color: var(--ink-3);
}

.check-del {
  opacity: 0;
  transition: opacity 0.12s;
}

.check-item:hover .check-del,
.check-del:focus-within {
  opacity: 1;
}

@media (hover: none) {
  .check-del {
    opacity: 1; /* no hover on touch — the × stays visible */
  }
}

.check-add input {
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px dashed var(--hairline);
  padding: 9px 2px;
  color: var(--ink);
  font: inherit;
}

.check-add input:focus {
  outline: none;
  border-bottom-color: var(--accent);
}

.check-add input::placeholder {
  color: var(--ink-3);
}

/* ⌘K search overlay */
.scrim-search {
  z-index: 69; /* directly under the panel — above sheets and bottombar */
}

.ksearch {
  position: fixed;
  top: 14vh;
  left: 50%;
  width: min(560px, calc(100vw - 32px));
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6);
  z-index: 70;
  overflow: hidden;
  transition:
    transform 0.16s ease-out,
    opacity 0.16s ease-out;
}

.ksearch.away {
  transform: translateX(-50%) translateY(-8px);
  opacity: 0;
  pointer-events: none;
}

.ksearch.in {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}

.ksearch input {
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  padding: 14px 16px;
  color: var(--ink);
  font: inherit;
  font-size: 15px;
}

.ksearch input:focus {
  outline: none;
}

.ksearch-results {
  max-height: 46vh;
  overflow-y: auto;
}

.ks-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 16px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
}

.ks-row:last-child {
  border-bottom: 0;
}

.ks-row.sel,
.ks-row:hover {
  background: color-mix(in srgb, var(--accent) 9%, transparent);
}

.ks-kind {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 1px 5px;
  flex: none;
}

.ks-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Done results: exactly what a finished row wears in the task list
   (.row.done .t), so "finished" reads the same everywhere. */
.ks-row.done .ks-title {
  color: var(--ink-3);
  text-decoration: line-through;
}

/* The word too — strike-through is thin at 13px, and a long ellipsised title
   can hide the struck tail entirely. Mono smallcaps, like every other
   informative caption. */
.ks-done {
  flex: none;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ks-empty {
  padding: 12px 16px;
}

/* Up Next (Today) */
.upnext-day {
  margin-bottom: 10px;
}

.day-label {
  margin: 6px 0 2px;
}

.ev {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 7px 0;
  border-bottom: 1px solid var(--hairline);
}

/* Calendar jump exists on Mac only (see app.js) — affordance follows */
.mac .ev[data-cal-link] {
  cursor: pointer;
}

.mac .ev[data-cal-link]:hover .ev-title {
  text-decoration: underline;
  text-decoration-color: var(--ink-3);
}

.ev-time {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-2);
  min-width: 46px;
  flex: none;
}

.ev-time.ev-allday {
  color: var(--ink-3);
}

.ev-title {
  flex: 1;
}

.chip-src,
.chip-kind {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 1px 5px;
}

.stale-note {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  margin-top: 6px;
}

/* Stale is quiet; disconnected is trouble — the one state here that earns
   the reserved red (DESIGN: auth failure), because nothing fixes it but him. */
.stale-note.is-trouble,
.stale-note.is-trouble a {
  color: var(--danger);
}

/* Resurfacing card (M1): a quiet daily quote — serif like display type,
   never louder than the work above it. It was: at 17px this was the largest
   text on Today after the date heading, against 13.5px task rows, so the one
   panel carrying nothing urgent read as the loudest thing on the page
   (Jaroslav, 2026-08-25). 15px keeps it a quote and not a headline. */
.resurface .rs-text {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.55;
  /* Prose measure. Inert in the rail, which is narrower than 62ch at every
     width it takes — but live in the .strip-only copy between 1024 and
     1279px, where the content column is 860px wide. */
  max-width: 62ch;
  overflow-wrap: anywhere;
  margin: 0;
}

.card.resurface {
  min-width: 0;
  overflow-wrap: anywhere;
  /* Scoped, so the base .card keeps its 24px everywhere else: a quote needs
     less room around it than a panel of work does. */
  padding: 16px;
}

.resurface .rs-text a {
  color: inherit;
}

.resurface .rs-src {
  display: flex;
  align-items: center;
  /* A flex item's min-width is `auto`, so a long book title refuses to
     shrink and pushes the card past the viewport — which is what ran the
     quote off the right edge. Wrap, and let the title break if it must. */
  flex-wrap: wrap;
  min-width: 0;
  overflow-wrap: anywhere;
  gap: 8px;
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--ink-2);
}

.rs-actions {
  margin-top: 12px;
  align-items: center;
}


/* Pull-to-refresh affordance (standalone PWA only, driven from app.js). */
/* A chip, not a bare glyph: against a page of text an unbacked 18px
   character is easy to miss, which is exactly what happened. */
.ptr-dot {
  position: fixed;
  top: calc(8px + env(safe-area-inset-top, 0px));
  left: 50%;
  translate: -50%;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: var(--ink-2);
  opacity: 0;
  pointer-events: none;
  z-index: 50;
}

/* Past the threshold: releasing now reloads. Amber is the app's "this is
   live" colour, and saying so beats making him guess. */
.ptr-dot.armed {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--hairline));
}

.pager {
  display: flex;
  padding-top: 12px;
}

.pager-older {
  margin-left: auto;
}

.rs-kept {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.synced-note {
  font-size: 11.5px;
  margin-left: 8px;
}

.sync-now-form {
  margin-top: 12px;
}

/* Settings (functional layer — designed pass comes in P5) */
.check-field {
  display: flex;
  align-items: flex-end;
  padding-bottom: 10px;
}

.check-label {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.check-label input[type="checkbox"] {
  accent-color: var(--accent);
  width: 15px;
  height: 15px;
}

.cal-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.cal-row {
  padding: 7px 0;
  border-bottom: 1px solid var(--hairline);
}

.disconnect-form {
  margin-top: 18px;
}

.form-error {
  color: var(--danger);
  font-size: 13px;
  margin: 6px 0;
}

/* flatpickr themed to tokens (vendor css loads before app.css, so these
   same-specificity overrides win by cascade order) */
.flatpickr-calendar {
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
  font-family: var(--sans);
}

.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after,
.flatpickr-calendar.arrowBottom::before,
.flatpickr-calendar.arrowBottom::after {
  display: none;
}

.flatpickr-months .flatpickr-month,
.flatpickr-current-month .flatpickr-monthDropdown-months,
.flatpickr-current-month input.cur-year {
  color: var(--ink);
  fill: var(--ink);
}

.flatpickr-current-month .flatpickr-monthDropdown-months {
  background: var(--raised);
}

.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
  color: var(--ink-2);
  fill: var(--ink-2);
}

.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
  fill: var(--accent);
}

span.flatpickr-weekday {
  color: var(--ink-3);
}

.flatpickr-day {
  color: var(--ink);
}

.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay,
.flatpickr-day.flatpickr-disabled {
  color: var(--ink-3);
}

.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover {
  background: var(--surface);
  border-color: var(--surface);
}

.flatpickr-day.today {
  border-color: var(--accent);
}

.flatpickr-day.today:hover {
  background: var(--surface);
  color: var(--ink);
}

.flatpickr-day.selected,
.flatpickr-day.selected:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* Rendered markdown (already sanitized by nh3 upstream) */
.md {
  line-height: 1.55;
  overflow-wrap: break-word;
}

.md h1,
.md h2,
.md h3 {
  font-size: 15px;
  font-weight: 600;
  margin: 14px 0 6px;
}

.md p,
.md ul,
.md ol {
  margin: 8px 0;
}

.md ul,
.md ol {
  padding-left: 22px;
}

.md a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
}

.md code {
  font-family: var(--mono);
  font-size: 12.5px;
  background: var(--raised);
  border-radius: 4px;
  padding: 1px 5px;
}

.md pre {
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 12px;
  overflow-x: auto;
}

.md pre code {
  background: none;
  padding: 0;
}

.md blockquote {
  border-left: 2px solid var(--hairline);
  margin: 8px 0;
  padding-left: 12px;
  color: var(--ink-2);
}

/* ── Routines (design/today-desktop.html) ──────────────────────────────── */

/* The streak, per design/today-desktop.html: mono, a step quieter than the
   row it annotates, with the RUN itself in amber (the token rule reserves
   amber for CTAs, stars and streaks). `flex: none` because this is a flex
   item in .r-item — without it the flame shrinks before the name does; and
   `font-style: normal` because the run sits in an <em> for colour, not for
   emphasis. This used to be a delta on a shared `.streak, .flame` base
   rule; that base went with the mobile strip, so it is spelled out in full
   here rather than silently inheriting body sans at 15.5px. */
.flame {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.05em;
  color: var(--ink-3);
  flex: none;
}

.flame em {
  color: var(--accent);
  font-style: normal;
}

/* The streak's mirror: a run of misses. A tool left untouched on a bench
   gathers cobwebs — the register is dust, not failure, which is the same
   register Slipping uses ("quiet 12d", never "overdue!"). Identical metrics
   to .flame because they share one slot in the row and swapping between
   them must not shift anything. `--danger` is the reserved trouble colour
   and DESIGN names "missed" in that reservation, so this is its one use on
   a routine — a single 10.5px digit, with the emoji carrying the meaning. */
.cobweb {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.05em;
  color: var(--ink-3);
  flex: none;
}

.cobweb em {
  color: var(--danger);
  font-style: normal;
}

/* Today's main column + /routines/: a checkable row per routine, by slot. */
.routines-card {
  padding: 6px 16px 10px;
}

.slot-label {
  display: block;
  margin: 8px 0 2px;
}

.slot-label:first-child {
  margin-top: 4px;
}

.r-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
}

.r-item .t {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.r-item.done .t {
  color: var(--ink-3);
  text-decoration: line-through;
}

.r-cb {
  flex: none;
  width: 18px;
  height: 18px;
  border: 1.5px solid color-mix(in srgb, var(--ink) 18%, var(--bg));
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  position: relative;
  padding: 0;
}

/* Neutral like .cb.done, per design/today-desktop.html: amber is for CTAs,
   stars, streaks and today — not for something already finished. */
.r-cb.done {
  background: color-mix(in srgb, var(--ink) 10%, var(--bg));
  border-color: transparent;
}

/* The visible box stays 18px (design), but the TAP target is 44px: an
   inset pseudo-element costs no layout and no pixel. */
.r-cb::before {
  content: "";
  position: absolute;
  inset: -13px;
}

.r-cb.done::after {
  content: "✓";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--ink-3);
  font-size: 11px;
}

.routine-row.paused .t a {
  color: var(--ink-3);
}

/* A rhythm that comes round TOMORROW wears the accent, so /routines/ answers
   "what's on me next" at a glance (Jaroslav, 2026-08-10). Amber's meaning is
   already "attention/energy, and today"; tomorrow is the next step along that
   same axis, and the label is a fact about time rather than an area or a
   status, so it doesn't tread on the reserved colours. Scoped to .routine-row
   so it can only ever paint the management list's own label. */
.routine-row .repeat.due-tomorrow {
  color: var(--accent);
}

/* The picked day's routines on /tasks/: the check-off row minus the checkbox,
   because that section is the record of a day, not a place to work it. */
.day-routines-head {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--hairline);
}

.r-item.r-static {
  padding: 5px 0;
}

/* Mono smallcaps, the same metrics as .label — these are informative
   captions (MORNING, DONE), which is exactly what mono is reserved for. */
.r-state {
  flex: none;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.r-state.trouble {
  color: var(--danger);
}

/* ── Slipping (design mockups: the one place red is allowed to speak) ───── */

.label.trouble {
  color: var(--danger);
}

.slip {
  padding: 4px 16px;
}

.slip .item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--hairline);
}

.slip .item:last-child {
  border-bottom: none;
}

.slip-body {
  min-width: 0;
  flex: 1;
}

.slip .t {
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.slip .t a {
  color: var(--ink);
  text-decoration: none;
}

.slip .t a:hover {
  color: var(--accent);
}

.slip .meta {
  margin-top: 3px;
}

.slip .age {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--danger);
  flex: none;
}

.rail .slip .age {
  font-size: 9.5px;
  letter-spacing: 0.06em;
}

/* Deliberately quiet: "later" is an escape hatch, not a call to action. */
/* Sans, not mono: mono is reserved for capitalized informative labels, and
   this is an interactive word. --ink-3 on --surface is only 3.7:1. */
.slip-later {
  font-size: 12px;
  color: var(--ink-2);
  flex: none;
  padding: 2px 0 2px 6px;
}

.slip-later:hover {
  color: var(--ink);
}

/* The per-area slipping fuse: narrow, quiet, placeholder shows the default. */
.quiet-days {
  width: 52px;
  flex: none;
  text-align: center;
  /* Matches .area-form select — it sits right beside one. */
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  padding: 4px 6px;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink-2);
  appearance: textfield;
}

.quiet-days::-webkit-inner-spin-button,
.quiet-days::-webkit-outer-spin-button {
  appearance: none;
  margin: 0;
}

/* Push status: the help voice is for help, not for outcomes. */
.note.ok {
  color: var(--ink-2);
  font-style: normal;
}

.note.bad {
  color: var(--danger);
  font-style: normal;
}

/* The morning brief: quiet, serif-free, gone by noon. */
.brief {
  padding: 16px 18px;
  font-size: 14px;
  color: var(--ink-2);
}

.brief strong {
  color: var(--ink);
  font-weight: 600;
}

.brief ul {
  margin: 6px 0 0;
  padding-left: 18px;
}

.brief > :first-child,
.brief p:first-child {
  margin-top: 0;
}

.brief p:last-child {
  margin-bottom: 0;
}

/* ── Thumb zone ────────────────────────────────────────────────────────
   Ponk is used one-handed, right-handed, on an iPhone 13 mini (DESIGN).
   The reachable arc runs bottom-right; the top-left corner is the stretch.

   So on a phone every row of controls hugs the RIGHT edge, and templates
   order each row destructive → secondary → primary. The primary then lands
   under the thumb and the destructive action sits furthest from it, which
   is exactly where a Delete button belongs. Wide screens keep the ordinary
   left edge, where reading order matters more than reach. */
@media (max-width: 640px) {
  .route-actions,
  .rs-actions,
  .pager {
    justify-content: flex-end;
  }

  /* NOT .quick-row, and NOT the date/time controls (Jaroslav, 2026-08-14):
     a form field reads down its left edge, and pushing the controls and their
     chips to the far right left every label stranded above a gap. Reach wins
     on an ACTION row — those are the last thing you touch on a screen — and
     loses inside a form, where alignment is what makes the fields scannable.
     The date and time controls, their captions and their chips all stay left
     now, which is why the two rules that moved them are gone rather than
     inverted. */

  .pager .pager-older {
    margin-left: 0;
  }
}

/* Date and time fields (see the block comment in app.js). One model: a date
   is day · month · year, a time is hour : minute, and a datetime is simply
   both — so every field is built from the same two pieces.

   Each wheel is sized to its CONTENT — the values are entirely predictable
   (two digits each), so a stretched box would
   only be empty space. `minmax(0, max-content)` is the important part: it
   fits the content when there's room and may still yield if there ever
   isn't, which is what keeps this from repeating the bug it replaced —
   under flex, Safari refused to shrink the time pair below its options'
   width, squeezing "day" to "d" and pushing the row off the screen. */
.time-wheels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, max-content));
  gap: 8px;
}

.time-wheels .wheel {
  /* auto, not the 100% `.field select` hands every other control: the track
     is already the width of the widest option. */
  width: auto;
  height: 44px;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 8px;
  font-family: var(--sans);
  font-size: 15px;
  color: var(--ink);
  min-width: 0;
}

/* No focus rule here on purpose: `.field select:focus` (0,2,1) already wins
   and gives every control in the app the same accent outline. A local
   override was inert dead code. */

/* A datetime is its date part and its time part, on ONE row at every width
   since the date became a chip (2026-08-14) — the five wheels that never fit
   a phone line are gone. Only the edge they pack against changes. */
.datetime-parts {
  display: grid;
  /* Both halves content-sized, at every width. A `1fr` first track stretched
     the date control across the whole column wherever the phone query did not
     match — an iPad, or an iPhone turned sideways (812px). The deleted
     .date-wheels rule guarded this with an UNCONDITIONAL justify-content;
     losing that guard is what let the box grow again (2026-08-14). */
  /* minmax(0, …), not bare max-content: the comment on .time-wheels promises
     these tracks can still yield, and a bare max-content cannot. The widest
     face ("Sat, 31 Dec 2033" — reachable exactly as dateChipLabel describes)
     plus the time pair fits 375px today with room to spare, but a track that
     cannot compress would overflow rather than shrink. */
  grid-template-columns: minmax(0, max-content) minmax(0, max-content);
  justify-content: start;
  gap: 8px;
  align-items: start;
}

/* No phone override any more (2026-08-14): the pair packs LEFT at every
   width, like every other control in a form. It used to flip to flex-end
   below 640px for thumb reach, which left the caption stranded above a gap
   and the row reading right-to-left against the fields around it. */

/* The native time field on desktop — themed to sit level with the calendar
   input beside it rather than wearing the UA's chrome.

   Scoped to .datetime-parts deliberately: the field-wide `.field input {
   width: 100% }` is (0,1,1) and would otherwise win, giving the time input a
   full-width line of its own and pushing it below the date. */
.datetime-parts > .dt-time {
  width: auto;
  min-width: 116px;
  height: 44px;
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 12px;
  font-family: var(--sans);
  font-size: 15px;
  color: var(--ink);
}

.datetime-parts > .dt-time::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.35;
  cursor: pointer;
}

/* Presets: the three shapes a reminder actually takes, plus an escape. */
.quick-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.quick-row .chip {
  cursor: pointer;
}

.quick-row .chip:hover {
  color: var(--ink);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--hairline));
}

/* Follows the presets inline, like any other chip in the row (Jaroslav,
   2026-08-14). It used to be pushed to the far right with `margin-left: auto`
   to read as an escape hatch rather than a fourth preset — but that took two
   exceptions to keep working (a phone override and a .field-row override),
   still wrapped `clear` onto a line of its own in a narrow column, and the
   distinction it bought was never worth a row that reads ragged. The quieter
   ink already says it isn't a preset. */
.chip-clear {
  color: var(--ink-3);
}
