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

/*
 * The `hidden` attribute must beat any layout rule that follows. The UA
 * stylesheet's [hidden] { display: none } is an author-rule loss waiting to
 * happen: a later `.foo { display: flex }` silently resurrects the element.
 * That is exactly how #intro-sync — the sign-in prompt, hidden on the
 * IndexedDB-only build — stayed on screen with no click handler bound to it,
 * so the button rendered and did nothing (rtm-21).
 */
[hidden] { display: none !important; }

:root {
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --bg: #121225;
  --panel: #1e1e3a;
  --panel-2: #2a2a50;
  --border: #3a3a6a;
  --row-border: #26264a;
  --text: #eee;
  --muted: #888;
  --accent: #359aff;
  --p1: #ea5200;
  --p1-text: #fff;
  --p2: #0060bf;
  --p2-text: #fff;
  --p3: #359aff;
  --p3-text: #002;
  --danger: #ff5c5c;
  --ok: #6bbf6b;
  --warn-text: #f0b090;
  --scrim: rgba(18, 18, 37, 0.85);
  --selected: rgba(53, 154, 255, 0.10);
  --selected-hover: rgba(53, 154, 255, 0.16);
  --shadow: rgba(0, 0, 0, 0.5);
}

/* ------------------------------------------------------------- themes */
/* Selected via data-theme on <html>; :root above is Midnight (default). */

[data-theme="black"] {
  --bg: #000;
  --panel: #101010;
  --panel-2: #1c1c1c;
  --border: #2e2e2e;
  --row-border: #1a1a1a;
  --text: #e8e8e8;
  --muted: #7a7a7a;
  --accent: #4da3ff;
  --p1: #ff6a00;
  --p2: #0066cc;
  --p3: #4da3ff;
  --scrim: rgba(0, 0, 0, 0.85);
  --selected: rgba(77, 163, 255, 0.10);
  --selected-hover: rgba(77, 163, 255, 0.16);
}

[data-theme="graphite"] {
  --bg: #17191c;
  --panel: #22252a;
  --panel-2: #2d3138;
  --border: #3d424b;
  --row-border: #24272c;
  --text: #e6e6e6;
  --muted: #8b929e;
  --accent: #58a6ff;
  --p1: #e5732e;
  --p2: #316dca;
  --p3: #58a6ff;
  --p3-text: #0d1420;
  --danger: #f47067;
  --ok: #57ab5a;
  --warn-text: #e0b184;
  --scrim: rgba(23, 25, 28, 0.85);
  --selected: rgba(88, 166, 255, 0.10);
  --selected-hover: rgba(88, 166, 255, 0.16);
}

[data-theme="dracula"] {
  --bg: #282a36;
  --panel: #313442;
  --panel-2: #3c3f52;
  --border: #44475a;
  --row-border: #363948;
  --text: #f8f8f2;
  --muted: #6272a4;
  --accent: #bd93f9;
  --p1: #ff79c6;
  --p1-text: #21222c;
  --p2: #bd93f9;
  --p2-text: #21222c;
  --p3: #8be9fd;
  --p3-text: #21222c;
  --danger: #ff5555;
  --ok: #50fa7b;
  --warn-text: #ffb86c;
  --scrim: rgba(40, 42, 54, 0.85);
  --selected: rgba(189, 147, 249, 0.12);
  --selected-hover: rgba(189, 147, 249, 0.18);
}

[data-theme="terminal"] {
  --font: ui-monospace, 'Cascadia Mono', Consolas, Menlo, monospace;
  --bg: #000;
  --panel: #0a120a;
  --panel-2: #142314;
  --border: #1f3d1f;
  --row-border: #122712;
  --text: #30e630;
  --muted: #1e8f1e;
  --accent: #7dff7d;
  --p1: #ffb000;
  --p1-text: #1a0e00;
  --p2: #30e630;
  --p2-text: #001900;
  --p3: #1e8f1e;
  --p3-text: #001900;
  --danger: #ff5f56;
  --ok: #30e630;
  --warn-text: #ffb000;
  --scrim: rgba(0, 0, 0, 0.88);
  --selected: rgba(48, 230, 48, 0.10);
  --selected-hover: rgba(48, 230, 48, 0.16);
}

[data-theme="paper"] {
  --bg: #f6f6f3;
  --panel: #fff;
  --panel-2: #ecece7;
  --border: #d4d4cc;
  --row-border: #e4e4dd;
  --text: #24282d;
  --muted: #767b83;
  --accent: #0b6bcb;
  --p1: #d94f00;
  --p2: #0060bf;
  --p3: #3d8fe0;
  --p3-text: #fff;
  --danger: #d0342c;
  --ok: #2e8b57;
  --warn-text: #9a5b19;
  --scrim: rgba(246, 246, 243, 0.85);
  --selected: rgba(11, 107, 203, 0.08);
  --selected-hover: rgba(11, 107, 203, 0.14);
  --shadow: rgba(0, 0, 0, 0.18);
}

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
}

body.dragging::after {
  content: 'Drop export to load';
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  background: var(--scrim);
  border: 3px dashed var(--accent);
  z-index: 50;
  pointer-events: none;
}

/* ------------------------------------------------------------- top bar */

.topbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 16px;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand { display: flex; flex-direction: column; }
.brand-title { font-weight: 700; letter-spacing: 1px; }
.data-info { font-size: 0.75rem; color: var(--muted); }

/* Sync status. Deliberately the same size and weight as .data-info beside it:
   this is a status line, not a surface. Only the error state gets a colour,
   because a dot that is always lit is a dot nobody reads. */
.sync-status { font-size: 0.75rem; color: var(--muted); cursor: default; }
.sync-status::before {
  content: '';
  display: inline-block;
  width: 0.45em; height: 0.45em;
  margin-right: 0.4em;
  border-radius: 50%;
  background: currentColor;
  vertical-align: 0.1em;
}
.sync-status[data-level="ok"]::before { background: var(--ok); }
.sync-status[data-level="pending"] { opacity: 0.85; }
.sync-status[data-level="error"] { color: var(--danger); }
/* No spinner and no animation on the pending state: a write is normally
   durable locally in under a frame, so anything animated would flicker on
   every keystroke-driven save rather than communicate. */

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  flex: 1;
  justify-content: flex-end;
}

.controls input[type="search"] {
  flex: 1;
  min-width: 200px;
  max-width: 520px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
}

.controls select,
.file-btn,
.ghost-btn,
.primary-btn {
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  font-size: 0.85rem;
  cursor: pointer;
}

.file-btn:hover, .ghost-btn:hover, .controls select:hover { border-color: var(--accent); }
.primary-btn { background: var(--p2); border-color: var(--p2); color: var(--p2-text); }
#file-input { display: none; }

.load-status {
  padding: 4px 16px;
  font-size: 0.85rem;
  color: var(--accent);
  min-height: 1.4em;
}

/* -------------------------------------------------------------- layout */

.layout { display: flex; align-items: flex-start; }

.sidebar {
  width: 250px;
  flex-shrink: 0;
  padding: 12px 8px 40px;
  position: sticky;
  top: 62px;
  max-height: calc(100vh - 62px);
  overflow-y: auto;
  display: none;
}

body.loaded .sidebar { display: block; }

.nav-heading {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--muted);
  margin: 16px 8px 4px;
}

.nav-section { list-style: none; }

.nav-btn {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  border: none;
  border-radius: 8px;
  background: none;
  color: var(--text);
  font-size: 0.9rem;
  cursor: pointer;
  text-align: left;
}

.nav-btn:hover { background: var(--panel); }
.nav-item.active .nav-btn { background: var(--panel-2); }
.nav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-count { font-size: 0.75rem; color: var(--muted); }
.nav-more { padding: 4px 10px; font-size: 0.75rem; color: var(--muted); }

.task-repeat {
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}

.repeat-hint {
  font-size: 0.72rem;
  color: var(--muted);
  margin-top: 2px;
  min-height: 1em;
}

.nav-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.nav-heading-row .nav-heading { flex: 1; }

.nav-heading-btn {
  border: none;
  background: none;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1;
  padding: 2px 8px;
  margin: 12px 4px 0 0;
  border-radius: 6px;
  cursor: pointer;
}

.nav-heading-btn:hover { background: var(--panel); color: var(--text); }

/* rename/delete buttons on list rows — revealed on hover or keyboard focus;
   they overlay the count, which yields to keep the row width stable */
.nav-item { position: relative; }

.nav-item-actions {
  display: none;
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  gap: 2px;
}

.nav-item:hover .nav-item-actions,
.nav-item:focus-within .nav-item-actions { display: flex; }

.nav-item.has-actions:hover .nav-count,
.nav-item.has-actions:focus-within .nav-count { visibility: hidden; }

.nav-action-btn {
  border: none;
  background: none;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1;
  padding: 4px 5px;
  border-radius: 6px;
  cursor: pointer;
}

.nav-action-btn:hover { background: var(--panel-2); color: var(--text); }
.nav-action-btn.danger:hover { color: var(--danger); }

.content { flex: 1; padding: 12px 20px 80px; min-width: 0; }

.view-header {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 10px;
}

.view-header h1 { font-size: 1.2rem; }
#view-count { color: var(--muted); font-size: 0.85rem; }

/* header star — favorite/unfavorite the current view */
.fav-btn {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.05rem;
  cursor: pointer;
  padding: 0 2px;
  line-height: 1;
}
.fav-btn:hover { color: var(--accent); }
.fav-btn.favorited { color: var(--accent); }

/* The first-run welcome (rtm-23). Three rules rather than one, because it has
   to be visible in two different situations that used to be the same one:
   before boot resolves (no .loaded yet), and inside a fully loaded app whose
   dataset happens to be empty. It is hidden the moment there is anything at
   all — one task or one list — so it cannot talk over a user who has started. */
.intro { max-width: 560px; color: var(--muted); line-height: 1.5; }
.intro p { margin-bottom: 12px; }
.intro code { color: var(--accent); }
body.loaded .intro { display: none; }
body.loaded.empty-data .intro { display: block; padding-top: 4px; }

/* --------------------------------------------------------------- tasks */

.smart-filter {
  margin-bottom: 10px;
  padding: 8px 12px;
  background: var(--panel);
  border-radius: 8px;
  font-size: 0.8rem;
  overflow-x: auto;
}

.smart-filter code { color: var(--accent); white-space: pre-wrap; }

.smart-filter {
  display: flex;
  align-items: center;
  gap: 10px;
}

.smart-filter code { flex: 1; }
.smart-filter-edit { flex-shrink: 0; }

/* active-search bar: shows the live query + save-as-smart-list */
.search-save {
  margin-bottom: 10px;
  padding: 6px 12px;
  background: var(--panel);
  border-radius: 8px;
  font-size: 0.8rem;
  display: flex;
  align-items: center;
  gap: 10px;
}

.search-save code {
  flex: 1;
  color: var(--accent);
  white-space: pre-wrap;
  overflow-x: auto;
}

.warning {
  margin-bottom: 8px;
  padding: 6px 12px;
  border-left: 3px solid var(--p1);
  background: var(--panel);
  border-radius: 0 8px 8px 0;
  font-size: 0.8rem;
  color: var(--warn-text);
}

.task-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--row-border);
  cursor: pointer;
}

.task-row:hover { background: var(--panel); }

/* ------------------------------------------------- swipe actions (rtm-26) */

/*
 * `pan-y` is the load-bearing line: it tells the browser it still owns
 * vertical scrolling on these rows, so the gesture handler never has to call
 * preventDefault to keep scrolling smooth — it only has to decline horizontal
 * movement it did not claim.
 */
.task-swipe {
  position: relative;
  touch-action: pan-y;
}

/*
 * The row is transparent at rest, so during a swipe it needs an opaque
 * background or the reveal behind it shows straight through the text.
 */
.task-swipe.swiping .task-row {
  background: var(--bg);
  position: relative;
  z-index: 1;
}

/*
 * Sized in JS to exactly the strip the row has uncovered, and anchored to the
 * edge it grew from — so it reads as the action emerging from under the row.
 */
.swipe-reveal {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 14px;
  overflow: hidden;
  border-bottom: 1px solid var(--row-border);
  opacity: 0.6;
}

/* Swiping right exposes the row's LEFT edge, so the label belongs there. */
.swipe-reveal.from-left {
  right: auto;
  left: 0;
  justify-content: flex-start;
}

/* Past the commit threshold — the difference between "let go now" and "keep
   going", which is the whole reason the reveal is shown before it fires. */
.swipe-reveal.armed { opacity: 1; }

.swipe-reveal-label {
  font-size: 0.8rem;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px var(--shadow);
  white-space: nowrap;
}
.task-row.done .task-name { color: var(--muted); text-decoration: line-through; }

.prio {
  flex-shrink: 0;
  width: 26px;
  text-align: center;
  border-radius: 6px;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 3px 0;
  margin-top: 2px;
}

.prio-P1 { background: var(--p1); color: var(--p1-text); }
.prio-P2 { background: var(--p2); color: var(--p2-text); }
.prio-P3 { background: var(--p3); color: var(--p3-text); }
.prio-PN { background: var(--panel-2); color: var(--muted); }

.task-main { flex: 1; min-width: 0; }
.task-name { line-height: 1.35; word-break: break-word; }
.task-icon { margin-left: 6px; font-size: 0.75rem; opacity: 0.8; }

.task-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 3px;
}

.task-list {
  min-width: 0;
}

.task-meta .task-list {
  font-size: 0.7rem;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1px 8px;
}

.task-tag {
  font-size: 0.7rem;
  background: var(--panel-2);
  color: var(--accent);
  border: none;
  border-radius: 10px;
  padding: 2px 8px;
  cursor: pointer;
}

.task-tag:hover { background: var(--border); }

.task-due {
  flex-shrink: 0;
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 4px;
  text-align: right;
}

.task-due.overdue { color: var(--danger); font-weight: 700; }
.task-due.completed-date { color: var(--ok); }

.task-detail {
  padding: 10px 12px 14px 46px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  font-size: 0.85rem;
}

.detail-table th {
  text-align: left;
  color: var(--muted);
  font-weight: 400;
  padding: 2px 16px 2px 0;
  vertical-align: top;
  white-space: nowrap;
}

.detail-table td { padding: 2px 0; word-break: break-word; }

.detail-url { margin-top: 8px; word-break: break-all; }
.detail-url a { color: var(--accent); }

.detail-note {
  margin-top: 10px;
  padding: 8px 12px;
  background: var(--panel-2);
  border-radius: 8px;
}

.note-title { font-weight: 700; margin-bottom: 4px; }
.note-content { white-space: pre-wrap; word-break: break-word; }

.empty { padding: 30px 10px; color: var(--muted); }

.show-more {
  display: block;
  margin: 16px auto;
  padding: 10px 24px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  cursor: pointer;
}

.show-more:hover { border-color: var(--accent); }

/* --------------------------------------------------------------- modal */

.modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 100;
  align-items: center;
  justify-content: center;
}

.modal.open { display: flex; }

.modal-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px;
  width: 340px;
  max-width: 90vw;
}

.modal-box p { margin-bottom: 12px; }

.modal-box input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  margin-bottom: 14px;
}

.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }

#import-title { font-weight: 600; }
.import-copy { font-size: 0.82rem; color: var(--muted); }
.import-copy strong { color: var(--text); }

/* Sign-in (rtm-21). Reuses .modal / .modal-box; only the label, the error line
   and the busy state are new. */
#login-title { font-weight: 600; }
.login-field {
  display: block;
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 4px;
}
.login-error {
  font-size: 0.8rem;
  color: var(--danger);
  margin-bottom: 12px;
}
/* The form is disabled while a request is in flight — PBKDF2 verification is
   deliberately slow, so there is a visible pause where a second Enter would
   otherwise queue a duplicate attempt straight into the server's throttle. */
#login-form[data-busy="true"] input,
#login-form[data-busy="true"] button { opacity: 0.6; pointer-events: none; }

/* The Clear confirmation (rtm-28). Reuses .modal / .modal-box; what is new is
   that the confirm button is styled as the dangerous thing it is, and that it
   starts disabled until the word is typed.

   `--danger` is defined per theme as a colour that reads against `--bg`, so
   inverting the pair keeps the button legible in every theme without a second
   token per theme. */
.danger-box { width: 400px; }
.danger-counts {
  font-size: 0.82rem;
  color: var(--danger);
  font-weight: 600;
  margin-bottom: 12px;
}
#danger-title { font-weight: 600; }
#danger-typed-label {
  display: block;
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 4px;
}
.primary-btn.danger { background: var(--danger); border-color: var(--danger); color: var(--bg); }
.primary-btn.danger[disabled] { opacity: 0.45; cursor: not-allowed; }
/* Hidden rather than absent when there is no second direction to offer, so the
   button row does not reflow between backends. */
#danger-alt[hidden], #danger-input[hidden], #danger-typed-label[hidden] { display: none; }

.intro-sync {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.account-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.account-row button { white-space: nowrap; flex-shrink: 0; }
.account-state { font-size: 0.82rem; color: var(--muted); }
.account-state strong { color: var(--text); font-weight: 600; }

/*
 * The jammed-outbox panel (rtm-37). Bordered and tinted rather than left as
 * more grey settings text: it appears only when a write the user made is not
 * going to reach the server, and the one action in it throws that write's
 * outbound copy away. Both of those want to be noticed before they are read.
 */
.stuck-op {
  border: 1px solid var(--danger);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 14px;
}
.stuck-op .account-state strong { color: var(--danger); }
.stuck-op-why {
  font-size: 0.78rem;
  color: var(--muted);
  margin: 6px 0 0;
}
.stuck-op button { margin-top: 10px; }

/* -------------------------------------------------------------- mobile */

/* The drawer toggle and its scrim exist only below the breakpoint (rtm-25).
   Declared hidden here so the desktop layout is the default and the mobile
   rules are the exception, not the other way round. */
.nav-toggle, .nav-scrim { display: none; }

@media (max-width: 720px) {
  /*
   * align-items: stretch, and it is the whole of rtm-29.
   *
   * `.layout` sets `align-items: flex-start` for the desktop two-column layout,
   * where it keeps the sidebar from stretching to the page height. Flipped to a
   * COLUMN here, that same property stops governing height and starts governing
   * width — so `main.content` was sized to its max-content, 441px inside a
   * 390px viewport, and the whole page panned sideways. `flex: 1` did nothing
   * about it: in a column container that is the main axis, i.e. height.
   *
   * Everything downstream (the smart-add bar, the view header, every task row)
   * was merely inheriting that width, which is why they all measured 401 — 441
   * less .content's 20px padding either side. One property, eleven overflowing
   * elements.
   */
  .layout { flex-direction: column; align-items: stretch; }

  /*
   * OFF-CANVAS, not an accordion. The sidebar used to be a full-width block
   * above the content, up to 40vh of it, with no way to dismiss it — so on a
   * phone you scrolled past four views, the lists and the tags before seeing a
   * single task. An accordion would have been less code and would still push
   * the content down whenever it is open; a drawer keeps the task list at the
   * top of the page always, which is the actual goal.
   *
   * position: fixed rather than absolute so it does not scroll away with the
   * page, and full height rather than the old max-height: 40vh — a scroll box
   * nested inside the page scroll is unpleasant to hit by accident on a touch
   * screen, and inside a drawer there is no reason for one.
   */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: min(280px, 82vw);
    height: 100vh;
    height: 100dvh;          /* dvh where supported: mobile URL bars move vh */
    max-height: none;
    padding: 12px 8px 40px;
    background: var(--panel);
    border-right: 1px solid var(--border);
    border-bottom: none;
    z-index: 60;             /* over the content, under the modals at 100 */
    transform: translateX(-100%);
    /* visibility, not just transform: an off-canvas element that is merely
       translated is still focusable and still read by a screen reader, so the
       whole nav would sit in the tab order in front of the task list while
       invisible. Delayed to the end of the slide so closing still animates. */
    visibility: hidden;
    transition: transform 0.18s ease-out, visibility 0s linear 0.18s;
    overscroll-behavior: contain;
  }
  body.loaded.nav-open .sidebar {
    transform: none;
    visibility: visible;
    transition: transform 0.18s ease-out, visibility 0s;
  }

  .nav-scrim {
    display: block;
    position: fixed;
    inset: 0;
    background: var(--scrim);
    z-index: 55;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.18s ease-out;
  }
  body.loaded.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }

  /* Shown only where there is something to toggle. Sized for a thumb. */
  .nav-toggle {
    display: block;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--panel-2);
    color: var(--text);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
  }
  body:not(.loaded) .nav-toggle { display: none; }

  /* The view header is one unwrapped row of title + ☆ + count + four select
     tools, which is 259px of buttons on the right of a 390px screen. Wrapping
     is the only thing that fits it; `margin-left: auto` still right-aligns the
     tools when they land on their own line. */
  .view-header { flex-wrap: wrap; }
  .view-header .select-tools { flex-wrap: wrap; row-gap: 6px; }

  /*
   * The topbar was 183px of an 844px screen — four wrapped rows, one per
   * control, because the search input is `flex: 1` with `min-width: 200px` and
   * the selects are sized by their longest option ("Sort: Due date", "Theme:
   * Pitch Black"). Search keeps its own row, since it is the one control that
   * wants the width; the three selects share the next one.
   */
  /* .controls is itself a flex item beside the brand and the toggle, so it was
     being squeezed into ~209px of the 390 and every control inside it wrapped
     to its own line regardless of how they were sized. It needs its own row
     first; only then does sizing the controls inside it mean anything. */
  .controls { flex-basis: 100%; justify-content: stretch; }
  .controls input[type="search"] { flex: 1 0 100%; min-width: 0; max-width: none; }
  .controls select { flex: 1 1 0; min-width: 0; }

  .task-due { font-size: 0.7rem; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar, .nav-scrim { transition: none; }
}

/* ====================================================== rtm-app additions */

.app-only { display: none; }
body.loaded .app-only { display: flex; }

.smart-add-bar {
  gap: 8px;
  margin-bottom: 12px;
}

.smart-add-bar input {
  flex: 1;
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 0.95rem;
}

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

.ghost-btn.danger { color: var(--danger); }
.ghost-btn.danger:hover { border-color: var(--danger); }

.intro h1, .intro h2 { margin-bottom: 14px; font-size: 1.4rem; color: var(--text); font-weight: 600; }

.task-check {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ok);
  font-size: 0.8rem;
  cursor: pointer;
}

.task-check:hover { border-color: var(--ok); }

.task-meta .task-listchip {
  font-size: 0.7rem;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1px 8px;
}

/* ----------------------------------------------------------- edit form */

.edit-form {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px 16px;
  margin-bottom: 10px;
}

.form-row { display: flex; flex-direction: column; gap: 3px; }

.form-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--muted);
}

.edit-form input,
.edit-form select {
  padding: 7px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  width: 100%;
}

.due-inputs { display: flex; gap: 6px; }
.due-inputs input[type="date"] { flex: 1.4; }
.due-inputs input[type="time"] { flex: 1; }

.edit-facts {
  font-size: 0.72rem;
  color: var(--muted);
  margin-bottom: 8px;
  line-height: 1.5;
}

.note-edit {
  width: 100%;
  min-height: 60px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  font-size: 0.85rem;
  resize: vertical;
}

.note-actions { display: flex; gap: 8px; margin-top: 6px; }
.detail-note.new-note { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }

.edit-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* ---------------------------------------------------------------- toast */

.toast {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translate(-50%, 80px);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 20px;
  font-size: 0.85rem;
  opacity: 0;
  /* visibility keeps the hidden toast's Undo button out of tab order and
     clicks; it flips at transition end so the fade-out still shows */
  visibility: hidden;
  transition: transform 0.25s, opacity 0.25s, visibility 0.25s;
  z-index: 200;
  max-width: 90vw;
}

.toast.show { transform: translate(-50%, 0); opacity: 1; visibility: visible; }
.toast.error { border-color: var(--danger); color: var(--danger); }

.toast-action {
  margin-left: 14px;
  background: none;
  border: none;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  padding: 2px 4px;
}
.toast-action:hover { text-decoration: underline; }
.toast-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ---------------------------------------------- selection + keyboard UI */

body.nav-hidden .sidebar { display: none !important; }

.view-header .select-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

.select-count { font-size: 0.8rem; color: var(--accent); }

.select-dd { position: relative; }

.dd-menu {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 30;
  list-style: none;
  min-width: 180px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 6px;
  box-shadow: 0 8px 24px var(--shadow);
}

.dd-menu.open { display: block; }

.dd-menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 7px 12px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text);
  font-size: 0.85rem;
  cursor: pointer;
}

.dd-menu button:hover { background: var(--panel-2); }
.dd-divider { border-top: 1px solid var(--border); margin: 6px 4px; }

.task-row.selected { background: var(--selected); }
.task-row.selected:hover { background: var(--selected-hover); }

.task-row.cursor { box-shadow: inset 3px 0 0 var(--accent); }

.task-check.checked {
  background: var(--p2);
  border-color: var(--p2);
  color: var(--p2-text);
}

button:disabled { opacity: 0.4; cursor: default; }
button:disabled:hover { border-color: var(--border); }

/* -------------------------------------------------------------- palette */

.modal.palette { align-items: flex-start; padding-top: 12vh; }
.modal.palette .modal-box { width: 420px; }

.palette-list { list-style: none; max-height: 320px; overflow-y: auto; }

.palette-item {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 0.9rem;
}

.palette-item:hover { background: var(--panel-2); }
.palette-item.active { background: var(--panel-2); box-shadow: inset 3px 0 0 var(--accent); }
.palette-item.empty { color: var(--muted); cursor: default; }
.palette-hint { color: var(--muted); font-size: 0.75rem; }

/* ----------------------------------------------------------------- help */

.modal.help { overflow-y: auto; }

.help-box {
  width: 860px;
  max-width: 94vw;
  max-height: 86vh;
  overflow-y: auto;
}

.help-box h2 { margin-bottom: 16px; }
.help-box h3 {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--muted);
  margin: 14px 0 8px;
}

.help-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0 28px;
}

.help-box dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 5px 12px;
  align-items: center;
}

.help-box dt { white-space: nowrap; }
.help-box dd { font-size: 0.85rem; }

kbd {
  display: inline-block;
  min-width: 1.5em;
  padding: 2px 6px;
  border-radius: 5px;
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  background: var(--bg);
  font-family: ui-monospace, monospace;
  font-size: 0.72rem;
  text-align: center;
}

.help-note {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.8rem;
}

.help-note code { color: var(--accent); }

/* ------------------------------------------------------------ drag & drop */

.task-row.dragging { opacity: 0.5; }

/* insertion line while reordering under "Your order" */
.task-row.drop-before { box-shadow: inset 0 2px 0 var(--accent); }
.task-row.drop-after { box-shadow: inset 0 -2px 0 var(--accent); }

/* sidebar row reorder */
.nav-item.dragging { opacity: 0.5; }
.nav-item.drop-before { box-shadow: inset 0 2px 0 var(--accent); }
.nav-item.drop-after { box-shadow: inset 0 -2px 0 var(--accent); }

.nav-item.drag-over .nav-btn {
  background: var(--panel-2);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* Drag image for multi-task drags; parked offscreen, only the snapshot
   the browser takes at dragstart is ever visible. */
.drag-badge {
  position: fixed;
  top: -100px;
  left: -100px;
  padding: 4px 10px;
  border-radius: 12px;
  background: var(--accent);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 600;
}

/* ------------------------------------------------------------- settings */

.settings-box {
  width: 420px;
  max-width: 94vw;
  max-height: 86vh;
  overflow-y: auto;
}

.settings-box h2 { margin-bottom: 4px; }
.settings-box h3 {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--muted);
  margin: 16px 0 6px;
}

.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
  font-size: 0.9rem;
  cursor: pointer;
}

.settings-row select {
  max-width: 55%;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.85rem;
}

.settings-row select:hover { border-color: var(--accent); }

.settings-check { justify-content: flex-start; }
.settings-check input { accent-color: var(--accent); }

/* Settings → Data (rtm-27). Not a .settings-row: these are actions, not
   preferences, and each needs a line saying what it reaches on the current
   backend — a label-left/control-right row has nowhere to put that. */
.settings-action { padding: 8px 0; }
.settings-action .file-btn,
.settings-action button { display: inline-block; }
.settings-note {
  font-size: 0.78rem;
  color: var(--muted);
  line-height: 1.45;
  margin-top: 5px;
}

.sidebar-footer {
  margin-top: 18px;
  padding: 10px 6px 0;
  border-top: 1px solid var(--border);
}

.settings-btn {
  width: 100%;
  padding: 7px 10px;
  border: none;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  text-align: left;
  font-size: 0.9rem;
  cursor: pointer;
}

.settings-btn:hover { background: var(--panel); color: var(--text); }
