/* Theme colours. Light is the default set. The dark set hangs off the
   data-theme attribute that the early script and app.js keep on <html>.
   The chosen mode (system, light or dark) resolves to one of these. */

/* The Spark family tokens (MemberSpark design system, pinned
   at design/system/ in this repo; the served copy stands at
   public/tokens.css and the local checks hold the two equal).
   The standard look below reads its colours from these tokens;
   the named looks that follow carry their own palettes and do
   not read them. */
@import url("tokens.css?v=r2026-11-07-65");
:root {
  color-scheme: light;
  --bg: var(--ms-color-page);
  --panel: var(--ms-color-surface);
  --column-bg: var(--ms-color-surface-muted);
  --ink: var(--ms-color-text);
  --muted: var(--ms-color-text-muted);
  /* The hairline clears 3:1 against the page background
     (issue #142) — the local checks measure it, so a softer
     value fails the run. */
  --line: var(--ms-color-line-strong);
  --accent: var(--ms-color-accent);
  --accent-ink: var(--ms-color-on-accent);
  --field-bg: var(--ms-color-surface);
  --copy-bg: var(--ms-color-surface-muted);
  --tag-bg: var(--ms-color-surface);
  --invert-bg: var(--ms-color-text);
  --invert-ink: var(--ms-color-page);
  --card-hover: var(--ms-color-line-strong);
  --good: var(--ms-color-badge-paid-fg);
  --due-soon: var(--ms-color-badge-due-fg);
  --flag: var(--ms-color-badge-due-fg);
  --flag-bg: var(--ms-color-badge-due-bg);
  --flag-border: var(--ms-color-line-strong);
  --late-bg: var(--ms-color-badge-lapsed-bg);
  --late-ink: var(--ms-color-badge-lapsed-fg);
  --late-border: var(--ms-color-line-strong);
  --warn-bg: var(--ms-color-badge-due-bg);
  --warn-ink: var(--ms-color-badge-due-fg);
  --warn-border: var(--ms-color-line-strong);
  --danger-bg: var(--ms-color-badge-lapsed-bg);
  --danger-ink: var(--ms-color-danger);
  --danger-border: var(--ms-color-line-strong);
  --radius: 10px;
  /* The Standard look's fonts. Every named look below carries
     its own pair; the stacks name fonts already on people's
     devices, so no look downloads a font. */
  --font: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: var(--ms-color-page);
  --panel: var(--ms-color-surface);
  --column-bg: var(--ms-color-surface-muted);
  --ink: var(--ms-color-text);
  --muted: var(--ms-color-text-muted);
  /* The dark hairline clears the same 3:1 bar (issue #142). */
  --line: var(--ms-color-line-strong);
  --field-bg: var(--ms-color-surface);
  --copy-bg: var(--ms-color-surface-muted);
  --tag-bg: var(--ms-color-surface);
  --invert-bg: var(--ms-color-text);
  --invert-ink: var(--ms-color-page);
  --card-hover: var(--ms-color-line-strong);
  --good: var(--ms-color-badge-paid-fg);
  --due-soon: var(--ms-color-badge-due-fg);
  --flag: var(--ms-color-badge-due-fg);
  --flag-bg: var(--ms-color-badge-due-bg);
  --flag-border: var(--ms-color-line-strong);
  --late-bg: var(--ms-color-badge-lapsed-bg);
  --late-ink: var(--ms-color-badge-lapsed-fg);
  --late-border: var(--ms-color-line-strong);
  --warn-bg: var(--ms-color-badge-due-bg);
  --warn-ink: var(--ms-color-badge-due-fg);
  --warn-border: var(--ms-color-line-strong);
  --danger-bg: var(--ms-color-badge-lapsed-bg);
  --danger-ink: var(--ms-color-danger);
  --danger-border: var(--ms-color-line-strong);
}

/* Named looks. Each look carries two authored palettes, light
   and dark, and the System, Light and Dark mode composes with
   every one: the light block answers the look alone and the
   dark block answers look and dark together, so it wins.
   Every block sets the whole variable set, so no value leaks
   in from another look. Label colours are not here — a
   label's colour keeps its identity inside every look.
   Every text pair in these palettes meets WCAG AA at 4.5:1
   (the flag pair 3:1), proven from the rendered styles. */

:root[data-look="comic"] {
  color-scheme: light;
  --bg: #fff3c4;
  --panel: #ffffff;
  --column-bg: #ffe08a;
  --ink: #232323;
  --muted: #5a5136;
  --line: #8a7a3a;
  --accent: #2050c8;
  --accent-ink: #ffffff;
  --field-bg: #ffffff;
  --copy-bg: #fffbe8;
  --tag-bg: #ffffff;
  --invert-bg: #232323;
  --invert-ink: #ffffff;
  --card-hover: #fff8dc;
  --good: #1d5c1e;
  --due-soon: #a31607;
  --flag: #b34700;
  --flag-bg: #ffe3c2;
  --flag-border: #d89a55;
  --late-bg: #ffd9d2;
  --late-ink: #8f1306;
  --late-border: #e8a396;
  --warn-bg: #ffedb8;
  --warn-ink: #6b4b00;
  --warn-border: #e3c979;
  --danger-bg: #ffd9d2;
  --danger-ink: #8f1306;
  --danger-border: #e8a396;
  --radius: 14px;
  --font: "Comic Sans MS", "Comic Neue", "Chalkboard SE", "Segoe UI", sans-serif;
  --font-display: "Comic Sans MS", "Comic Neue", "Chalkboard SE", "Segoe UI", sans-serif;
}

:root[data-look="comic"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #1d1b2e;
  --panel: #2a2742;
  --column-bg: #332f52;
  --ink: #fdf6d8;
  --muted: #cfc7a8;
  --line: #857fb0;
  --accent: #ffd23e;
  --accent-ink: #231f00;
  --field-bg: #332f52;
  --copy-bg: #332f52;
  --tag-bg: #3a3660;
  --invert-bg: #fdf6d8;
  --invert-ink: #1d1b2e;
  --card-hover: #332f52;
  --good: #a9e6a4;
  --due-soon: #ffab9e;
  --flag: #ffb35e;
  --flag-bg: #4a2c0d;
  --flag-border: #7a4d1c;
  --late-bg: #47201c;
  --late-ink: #ffb3a7;
  --late-border: #7a4038;
  --warn-bg: #453512;
  --warn-ink: #ffd97a;
  --warn-border: #7d6530;
  --danger-bg: #47201c;
  --danger-ink: #ffb3a7;
  --danger-border: #7a4038;
}

:root[data-look="console"] {
  color-scheme: light;
  --bg: #f2f5ec;
  --panel: #fbfdf7;
  --column-bg: #e2ead3;
  --ink: #0d2812;
  --muted: #3d5a36;
  --line: #64804c;
  --accent: #0e7a2f;
  --accent-ink: #ffffff;
  --field-bg: #fbfdf7;
  --copy-bg: #eef3e2;
  --tag-bg: #f4f8ec;
  --invert-bg: #0d2812;
  --invert-ink: #eafbe9;
  --card-hover: #eef3e2;
  --good: #0e6b24;
  --due-soon: #a31607;
  --flag: #b34700;
  --flag-bg: #f7e8c8;
  --flag-border: #d89a55;
  --late-bg: #f9dcd6;
  --late-ink: #8f1306;
  --late-border: #dfa89e;
  --warn-bg: #f9ecc4;
  --warn-ink: #6b4b00;
  --warn-border: #e0cd93;
  --danger-bg: #f9dcd6;
  --danger-ink: #8f1306;
  --danger-border: #dfa89e;
  --radius: 4px;
  --font: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
  --font-display: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
}

:root[data-look="console"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #000000;
  --panel: #0a120a;
  --column-bg: #101a0e;
  --ink: #4ae36a;
  --muted: #5cb871;
  --line: #3a7a44;
  --accent: #33e05c;
  --accent-ink: #001703;
  --field-bg: #0d150c;
  --copy-bg: #0d150c;
  --tag-bg: #12240f;
  --invert-bg: #33e05c;
  --invert-ink: #001703;
  --card-hover: #101a0e;
  --good: #4ae36a;
  --due-soon: #ff9d94;
  --flag: #ffb35e;
  --flag-bg: #33200a;
  --flag-border: #7a4d1c;
  --late-bg: #2e0f0c;
  --late-ink: #ffab9e;
  --late-border: #5e2a24;
  --warn-bg: #2e2305;
  --warn-ink: #f2cd7e;
  --warn-border: #5e4c14;
  --danger-bg: #2e0f0c;
  --danger-ink: #ffab9e;
  --danger-border: #5e2a24;
}

:root[data-look="pretty"] {
  color-scheme: light;
  --bg: #fdf0f6;
  --panel: #ffffff;
  --column-bg: #fbdff0;
  --ink: #472236;
  --muted: #7a4a61;
  --line: #ad7593;
  --accent: #b02a63;
  --accent-ink: #ffffff;
  --field-bg: #ffffff;
  --copy-bg: #fdf6fa;
  --tag-bg: #ffffff;
  --invert-bg: #472236;
  --invert-ink: #ffffff;
  --card-hover: #fdf6fa;
  --good: #256b22;
  --due-soon: #a31607;
  --flag: #b34700;
  --flag-bg: #fdeadb;
  --flag-border: #d89a55;
  --late-bg: #fbdcd6;
  --late-ink: #8f1306;
  --late-border: #e2a396;
  --warn-bg: #fdf0d9;
  --warn-ink: #6b4b00;
  --warn-border: #ecd9a8;
  --danger-bg: #fbdcd6;
  --danger-ink: #8f1306;
  --danger-border: #e2a396;
  --radius: 16px;
  --font: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: Georgia, "Times New Roman", serif;
}

:root[data-look="pretty"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #221219;
  --panel: #301b26;
  --column-bg: #3a2230;
  --ink: #f9e4ef;
  --muted: #d3a4bc;
  --line: #96667e;
  --accent: #e58bb5;
  --accent-ink: #33101f;
  --field-bg: #3a2230;
  --copy-bg: #3a2230;
  --tag-bg: #422838;
  --invert-bg: #f9e4ef;
  --invert-ink: #2c1420;
  --card-hover: #3a2230;
  --good: #a9d6a4;
  --due-soon: #ffab9e;
  --flag: #ffb35e;
  --flag-bg: #40260e;
  --flag-border: #7a4d1c;
  --late-bg: #47201c;
  --late-ink: #ffb3a7;
  --late-border: #7a4038;
  --warn-bg: #453512;
  --warn-ink: #ffd97a;
  --warn-border: #7d6530;
  --danger-bg: #47201c;
  --danger-ink: #ffb3a7;
  --danger-border: #7a4038;
}

:root[data-look="artistic"] {
  color-scheme: light;
  --bg: #f5efe3;
  --panel: #fffdf6;
  --column-bg: #eadfc8;
  --ink: #2e2620;
  --muted: #66553f;
  --line: #97814f;
  --accent: #a52f14;
  --accent-ink: #ffffff;
  --field-bg: #fffdf6;
  --copy-bg: #f8f2e4;
  --tag-bg: #fffdf6;
  --invert-bg: #2e2620;
  --invert-ink: #f7f1e3;
  --card-hover: #f8f2e4;
  --good: #2d5c1e;
  --due-soon: #a31607;
  --flag: #b34700;
  --flag-bg: #f7e3c2;
  --flag-border: #d89a55;
  --late-bg: #f6d9d2;
  --late-ink: #8f1306;
  --late-border: #e0a08f;
  --warn-bg: #f7ecc4;
  --warn-ink: #6b4b00;
  --warn-border: #e6d194;
  --danger-bg: #f6d9d2;
  --danger-ink: #8f1306;
  --danger-border: #e0a08f;
  --radius: 10px;
  --font: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: Georgia, "Times New Roman", serif;
}

:root[data-look="artistic"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #171310;
  --panel: #231d17;
  --column-bg: #2d241b;
  --ink: #f0e7d6;
  --muted: #c4b198;
  --line: #867452;
  --accent: #e2703a;
  --accent-ink: #2a1200;
  --field-bg: #2d241b;
  --copy-bg: #2d241b;
  --tag-bg: #352a1f;
  --invert-bg: #f0e7d6;
  --invert-ink: #221a12;
  --card-hover: #2d241b;
  --good: #a9d6a4;
  --due-soon: #ffab9e;
  --flag: #ffb35e;
  --flag-bg: #40260e;
  --flag-border: #7a4d1c;
  --late-bg: #47201c;
  --late-ink: #ffb3a7;
  --late-border: #7a4038;
  --warn-bg: #453512;
  --warn-ink: #ffd97a;
  --warn-border: #7d6530;
  --danger-bg: #47201c;
  --danger-ink: #ffb3a7;
  --danger-border: #7a4038;
}

:root[data-look="sunset"] {
  color-scheme: light;
  --bg: #fdeedd;
  --panel: #fffaf2;
  --column-bg: #f9dcbc;
  --ink: #42202b;
  --muted: #7a4636;
  --line: #a97a52;
  --accent: #bc380f;
  --accent-ink: #ffffff;
  --field-bg: #fffaf2;
  --copy-bg: #fdf3e3;
  --tag-bg: #fffaf2;
  --invert-bg: #42202b;
  --invert-ink: #ffefe0;
  --card-hover: #fdf3e3;
  --good: #2d5c1e;
  --due-soon: #a31607;
  --flag: #a83e00;
  --flag-bg: #fbe3c2;
  --flag-border: #d89a55;
  --late-bg: #f8d5ce;
  --late-ink: #8f1306;
  --late-border: #e5a08e;
  --warn-bg: #f9ecc4;
  --warn-ink: #6b4b00;
  --warn-border: #e8d194;
  --danger-bg: #f8d5ce;
  --danger-ink: #8f1306;
  --danger-border: #e5a08e;
  --radius: 12px;
  --font: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
}

:root[data-look="sunset"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #1e1226;
  --panel: #2b1a33;
  --column-bg: #382142;
  --ink: #ffe9d4;
  --muted: #d9a98f;
  --line: #8f659a;
  --accent: #ff8c42;
  --accent-ink: #38160a;
  --field-bg: #382142;
  --copy-bg: #382142;
  --tag-bg: #42264d;
  --invert-bg: #ffe9d4;
  --invert-ink: #31182e;
  --card-hover: #382142;
  --good: #b5e3a8;
  --due-soon: #ffab9e;
  --flag: #ffb35e;
  --flag-bg: #40260e;
  --flag-border: #7a4d1c;
  --late-bg: #47201c;
  --late-ink: #ffb3a7;
  --late-border: #7a4038;
  --warn-bg: #453512;
  --warn-ink: #ffd97a;
  --warn-border: #7d6530;
  --danger-bg: #47201c;
  --danger-ink: #ffb3a7;
  --danger-border: #7a4038;
}

:root[data-look="forest"] {
  color-scheme: light;
  --bg: #edf4e2;
  --panel: #fbfdf5;
  --column-bg: #dae7c8;
  --ink: #1e2b16;
  --muted: #495c39;
  --line: #75895a;
  --accent: #2e6b30;
  --accent-ink: #ffffff;
  --field-bg: #fbfdf5;
  --copy-bg: #f0f5e6;
  --tag-bg: #fbfdf5;
  --invert-bg: #1e2b16;
  --invert-ink: #eef6e2;
  --card-hover: #f0f5e6;
  --good: #256b22;
  --due-soon: #a31607;
  --flag: #b34700;
  --flag-bg: #f7e8c8;
  --flag-border: #d89a55;
  --late-bg: #f6d9d2;
  --late-ink: #8f1306;
  --late-border: #e0a08f;
  --warn-bg: #f9ecc4;
  --warn-ink: #6b4b00;
  --warn-border: #e6d194;
  --danger-bg: #f6d9d2;
  --danger-ink: #8f1306;
  --danger-border: #e0a08f;
  --radius: 12px;
  --font: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: Georgia, "Times New Roman", serif;
}

:root[data-look="forest"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #0c1710;
  --panel: #14231a;
  --column-bg: #1b2f21;
  --ink: #e7f3de;
  --muted: #a9c69b;
  --line: #597d52;
  --accent: #69a85e;
  --accent-ink: #0d2108;
  --field-bg: #1b2f21;
  --copy-bg: #1b2f21;
  --tag-bg: #223a29;
  --invert-bg: #e7f3de;
  --invert-ink: #14240f;
  --card-hover: #1b2f21;
  --good: #a9d6a4;
  --due-soon: #ffab9e;
  --flag: #ffb35e;
  --flag-bg: #40260e;
  --flag-border: #7a4d1c;
  --late-bg: #47201c;
  --late-ink: #ffb3a7;
  --late-border: #7a4038;
  --warn-bg: #453512;
  --warn-ink: #ffd97a;
  --warn-border: #7d6530;
  --danger-bg: #47201c;
  --danger-ink: #ffb3a7;
  --danger-border: #7a4038;
}

:root[data-look="midnight"] {
  color-scheme: light;
  --bg: #e7edfc;
  --panel: #fbfcff;
  --column-bg: #d3defa;
  --ink: #16213d;
  --muted: #46557c;
  --line: #6f84b8;
  --accent: #2b4acb;
  --accent-ink: #ffffff;
  --field-bg: #fbfcff;
  --copy-bg: #eef2fd;
  --tag-bg: #fbfcff;
  --invert-bg: #16213d;
  --invert-ink: #e7edfc;
  --card-hover: #eef2fd;
  --good: #256b22;
  --due-soon: #a31607;
  --flag: #b34700;
  --flag-bg: #f7e8c8;
  --flag-border: #d89a55;
  --late-bg: #f6d9d2;
  --late-ink: #8f1306;
  --late-border: #e0a08f;
  --warn-bg: #f9ecc4;
  --warn-ink: #6b4b00;
  --warn-border: #e6d194;
  --danger-bg: #f6d9d2;
  --danger-ink: #8f1306;
  --danger-border: #e0a08f;
  --radius: 10px;
  --font: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Avenir Next", "Segoe UI", Roboto, sans-serif;
}

:root[data-look="midnight"][data-theme="dark"] {
  color-scheme: dark;
  --bg: #050914;
  --panel: #0b1428;
  --column-bg: #111c36;
  --ink: #e3ebff;
  --muted: #a4b5de;
  --line: #50679c;
  --accent: #7aa2ff;
  --accent-ink: #0a1430;
  --field-bg: #111c36;
  --copy-bg: #111c36;
  --tag-bg: #16233f;
  --invert-bg: #e3ebff;
  --invert-ink: #0d1730;
  --card-hover: #111c36;
  --good: #a9d6a4;
  --due-soon: #ffab9e;
  --flag: #ffb35e;
  --flag-bg: #33200a;
  --flag-border: #7a4d1c;
  --late-bg: #2e0f0c;
  --late-ink: #ffab9e;
  --late-border: #5e2a24;
  --warn-bg: #2e2305;
  --warn-ink: #f2cd7e;
  --warn-border: #5e4c14;
  --danger-bg: #2e0f0c;
  --danger-ink: #ffab9e;
  --danger-border: #5e2a24;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--ink);
  line-height: 1.375;
}

/* The text rhythm stands once, in the base rules (issue #50):
   22px lines on the 16px base, and a 1em gap between
   paragraphs. A component that needs tighter text says so in
   its own rule. */
p { margin: 1em 0; }

/* Headings wear the look's display font. Form controls do not
   inherit the body font on their own, so in any look but
   Standard they take the look's font like everything else;
   Standard keeps the rendering it has always had. */
h1, h2, h3 { font-family: var(--font-display, var(--font)); }

:root:not([data-look="standard"]) button,
:root:not([data-look="standard"]) input,
:root:not([data-look="standard"]) select,
:root:not([data-look="standard"]) textarea {
  font-family: inherit;
}

/* Two looks carry a flavour past their colours: Comic draws its
   cards, columns and buttons with chunky borders, and Console
   sets its wordmark the way a terminal titles a session. */
:root[data-look="comic"] .card,
:root[data-look="comic"] .column,
:root[data-look="comic"] .btn {
  border-width: 2px;
}

:root[data-look="console"] .topbar h1 {
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.topbar {
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.topbar-inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: 14px 16px 12px;
}

h1 {
  margin: 0;
  font-size: 24px;
  letter-spacing: 0;
}

/* The header row: the wordmark, the board buttons, then the
   theme button, the display button and the gear. The switcher
   takes the room in the middle and scrolls sideways rather than
   wrapping. On a phone the row wraps on purpose instead: the
   board buttons take a second row of their own (see the phone
   block at the end of this file). */
.header-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.header-row h1 { flex: 0 0 auto; }

/* The app downloads two fonts, both self-hosted in /fonts.
   The wordmark's: the name in the header is set in Comfortaa
   Bold, the brand's face, from a subset that carries only the
   letters of "TaskSparkle" (about 1.4 KB; the font's licence
   stands beside it in /fonts/OFL.txt). */
@font-face {
  font-family: "Comfortaa";
  src: url("/fonts/comfortaa-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* The display face (design/system tokens.css:
   --ms-type-font-display): Bricolage Grotesque, the family's
   heading face, self-hosted the same way — the variable file
   carries weights 200 to 800, and its licence stands beside
   it in /fonts/OFL-bricolage.txt. The reading pages' headings
   and the console's name the face in their own rules; the
   token's stack follows it (Figtree, then the device faces)
   for any letter the file does not carry. The face never
   swaps (Part 9): it loads as optional, so a heading paints
   in the face or in the fallback and never changes face
   mid-page, and the reading pages preload the file so the
   face is the one that paints (deploy issue #219). */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("/fonts/bricolage-grotesque.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: optional;
}

.topbar h1 {
  font-family: "Comfortaa", var(--font-display);
  font-weight: 700;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

.settings-btn {
  appearance: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--panel);
  color: var(--ink);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}



/* The header's right-side set stands at the shared base above —
   44px targets with their glyphs at the design system's
   drawing size, 20px in the 44px target (issues #48, #61):
   the search and theme buttons' glyphs are svgs, the
   settings button's is Lucide's settings drawing. The display
   button stands with the board tools now, in the board row's
   action group. Every one of these targets stands at the 44px
   floor (Site and Email Standards, Part 10), its glyph drawn
   at 20px (issue #67). */
#search-toggle svg,
#theme-toggle svg,
#help-open svg,
#settings-open svg {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto;
}

a.settings-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

.board-row-actions #display-toggle svg {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto;
}

/* The Pilot tag stands beside the product name (Part 2),
   drawn at the design's own measure: small type in a pill,
   about 19px tall — a label, not a tap target — wearing the
   design's badge colours, the due pair from the tokens. */
.pilot-tag {
  flex: 0 0 auto;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .09em;
  line-height: 1;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: 99px;
  color: var(--ms-color-badge-due-fg);
  background: var(--ms-color-badge-due-bg);
}

/* The Account button stands at the shared .settings-btn shape
   — the 44px round target — with Lucide's person glyph inside
   (issue #238). It carries no name; the name stands only in
   its label. */

/* The search button wears the inverted colours while the search
   stands open, like the other toggles wear theirs. */
#search-toggle.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
}

/* The search area: the bar slides in under the top row when the
   search opens and slides back out when it closes. The results
   grow below the bar, one row per card. */
.search-area { margin: 0 0 14px; animation: search-in 0.18s ease-out; }

.search-area.closing { animation: search-out 0.15s ease-in forwards; }

@keyframes search-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes search-out {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(-6px); }
}

@media (prefers-reduced-motion: reduce) {
  .search-area, .search-area.closing { animation: none; }
}

.search-bar { display: flex; gap: 8px; }

#search-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--field-bg);
  color: var(--ink);
  font-size: 15px;
}

.search-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 10px;
  margin-top: 10px;
}

.search-filters[hidden] { display: none; }

/* The column-remove dialog's destination row is a
   search-filter-field, whose flex display below would
   defeat the hidden attribute the dialog toggles (issue
   #135): the guard stands so an empty column's dialog
   shows no destination to pick. */
#column-remove-dest-row[hidden] { display: none; }

.search-filter-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
}

.search-filter-field select {
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--field-bg);
  color: var(--ink);
  font-size: 14px;
  max-width: 220px;
}

.search-bubbles { display: flex; gap: 8px; padding-bottom: 2px; }

.search-results { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }

.search-results:empty { margin-top: 0; }

.search-result {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  color: var(--ink);
  font-size: 15px;
  cursor: pointer;
}

.search-result:hover { background: var(--card-hover); }

.search-result-title { font-weight: 600; flex: 1 1 auto; }

.search-result-meta { color: var(--muted); font-size: 13px; white-space: nowrap; }

.search-hint, .search-empty { color: var(--muted); margin: 2px 0; }

/* The copy-board dialog: the editor panel with a name field and
   four plain checkboxes. It opens over Settings, so its backdrop
   stands above the settings backdrop's layer. */
#copy-backdrop { z-index: 45; }

/* The column removal dialog opens from inside Settings, so it
   rides at the copy dialog's height, over the settings panel. */
#column-remove-backdrop { z-index: 45; }

/* The key dialogs open from inside Settings too (issue #98), so
   their backdrops ride at the same height, over the panel. */
#key-created-backdrop { z-index: 45; }
#key-delete-backdrop { z-index: 45; }

/* The delete dialogs of the dialog sweep (issue #99) open over
   Settings too — and the project one also over the press
   editor (z-index 40) — so their backdrops ride at the same
   height, over both. */
#project-delete-backdrop { z-index: 45; }
#account-delete-backdrop { z-index: 45; }
#board-delete-backdrop { z-index: 45; }
#label-delete-backdrop { z-index: 45; }

/* The People page's dialogs (issue #107) — the minted invite's
   link and the remove-person confirm — open over Settings the
   same way, so their backdrops ride at the same height. */
#invite-created-backdrop { z-index: 45; }
#person-delete-backdrop { z-index: 45; }

/* The password drop's confirm (issue #122) opens over the
   Account page inside Settings, so its backdrop rides at the
   same height. */
#password-drop-backdrop { z-index: 45; }

/* The own-passkey removal confirm (issue #136) opens over
   the Account page inside Settings the same way. */
#own-passkey-remove-backdrop { z-index: 45; }

.copy-dialog .search-filter-field { margin-top: 10px; }

#copy-name {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--field-bg);
  color: var(--ink);
  font-size: 15px;
}

.copy-checks {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 14px 0;
}

.copy-checks label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  cursor: pointer;
}

.copy-checks input { width: 18px; height: 18px; }

#settings-open {
  font-size: 16px;
}

.api-error {
  background: var(--danger-bg);
  color: var(--danger-ink);
  border: 1px solid var(--danger-border);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 12px;
  font-weight: 600;
}

/* A plain note from the live-update path: it speaks only when
   something the person was looking at changed under them, like
   the card in the open editor being gone. Routine updates repaint
   silently and never show it. */
.push-note {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 12px;
  font-weight: 600;
}

/* The Updated notice (Parts 8 and 9): raised when the build on
   the server has moved past the copy this page is running. It
   stands until the person presses Refresh now — the page never
   reloads itself. */
.update-note {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 12px;
  font-weight: 600;
}

.loading-note {
  color: var(--muted);
  font-size: 16px;
  padding: 24px 4px;
}

/* The board row: a wrapper around the board buttons and the
   board tools that stand beside them (See all columns, Schedule
   and the display button). On a wide screen the wrapper and its
   inner group step aside (display: contents), so the header row
   lays its pieces out as it always has and the tools join the
   header row beside the board buttons. On a phone the wrapper
   becomes the header's second row itself: the board buttons
   scroll inside the room it leaves, and the tools stand pinned
   at its right margin, outside the scrolling run, so no board
   button can slide underneath them (see the phone block at the
   end of this file). */
.board-row { display: contents; }

.board-row-actions { display: contents; }

.board-switcher {
  display: flex;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.board-switcher::-webkit-scrollbar { display: none; }

.board-tab {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  white-space: nowrap;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font-size: 16px;
  padding: 9px 16px;
  border-radius: 999px;
  cursor: pointer;
  min-height: 44px;
}

/* Every drawing in the topbar stands at the one size,
   20px (the heading's measure): the board tabs' icons
   included, drawn in a 20px box like the controls' glyphs. */
.board-tab-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  font-size: 20px;
  line-height: 1;
  flex: 0 0 auto;
}

.board-tab.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

/* The display mode, a device setting: board buttons and column
   names show their icon and word together, the word alone, or the
   icon alone. The class lives on <html> and comes from this
   device's own storage, never from the API. Card faces are not
   governed: their chips and tags keep their words in every mode. */
html.display-words .board-tab-icon,
html.display-words .column-icon,
html.display-words .step-icon { display: none; }
html.display-icons .board-tab-name,
html.display-icons .column-name,
html.display-icons .step-name { display: none; }

/* The display modes reach the card faces too, and the filter
   pills: in Words mode no icon shows anywhere; in Icons mode a
   chip, tag or pill that carries an icon lets its word step
   aside, and one with no icon keeps its word. */
html.display-words .chip-icon,
html.display-words .project-icon { display: none; }
html.display-icons .chip.has-icon .chip-name,
html.display-icons .project-tag.has-icon .project-tag-name,
html.display-icons .filter-btn.has-icon .filter-name { display: none; }
html.display-icons .chip.has-icon .chip-icon,
html.display-icons .project-tag.has-icon .project-icon { margin-right: 0; }
html.display-icons .board-tab { padding-left: 13px; padding-right: 13px; }
html.display-icons .column-icon,
html.display-icons .step-icon { margin-right: 0; }

.page {
  max-width: 1400px;
  margin: 0 auto;
  padding: 12px 12px 32px;
}

.btn {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font-size: 15px;
  padding: 9px 16px;
  border-radius: 8px;
  cursor: pointer;
  min-height: 44px;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

/* Project filter row */
.project-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}

.filter-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font-size: 14px;
  padding: 8px 12px;
  border-radius: 999px;
  cursor: pointer;
  min-height: 44px;
  white-space: nowrap;
}

.filter-btn.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
  font-weight: 600;
}

.filter-add { border-style: dashed; color: var(--muted); }

/* A project with an icon shows the emoji beside its name. */
.project-icon {
  font-size: 15px;
  line-height: 1;
  margin-right: 6px;
  flex: 0 0 auto;
}

/* Project tag on a card: an outline pill naming the project.
   Deliberately plainer than the filled label chip. */
.project-tag {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--tag-bg);
  color: var(--ink);
}

.project-tag .project-icon { margin-right: 5px; font-size: 13px; }

/* The creator tag: the pill that names the agent key that made
   a card (on its face) or wrote a comment (in the editor). The
   same plain outline pill as the project tag; the name inside
   is a snapshot, spelled the way the key was named when the
   card or comment was written. It carries no icon, so — the
   way a label chip with no icon keeps its word — its word
   stands in every display mode. */
.creator-tag {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--tag-bg);
  color: var(--ink);
}

/* The editor's maker line (issue #126): "Made by" and the
   maker's name in the creator-tag pill, under the title
   field. The [hidden] guard stands because the flex display
   would otherwise defeat the attribute (the issue #125
   lesson). */
.made-by-line {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--muted);
}

.made-by-line[hidden] {
  display: none;
}

/* The next-step chip (issue #119): the pill that names the
   agent key holding a card's next step. It wears the accent
   border so it never reads as the creator tag beside it —
   created-by and whose-turn are different facts. While the
   card waits for its pickup the chip carries the wait's age
   after the name, and the chip fills with the accent tint so
   a waiting card stands out on the board. */
.step-chip {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--tag-bg);
  color: var(--ink);
}

.step-chip.waiting {
  background: var(--tag-bg);
  background: color-mix(in srgb, var(--accent) 20%, var(--tag-bg));
}

/* The editor's Next step row: the line names the holder as
   the step stands, above the chooser that hands it over. */
.step-line {
  margin: 0 0 6px;
  font-size: 14px;
}

.step-block select {
  width: 100%;
}

/* Inline add-project form */
.project-add-form {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  margin-bottom: 12px;
}

.project-add-label {
  display: block;
  font-weight: 600;
  margin-bottom: 6px;
}

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

.project-add-row input {
  flex: 1 1 180px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.btn-quiet { background: transparent; }
.btn-warn { background: var(--warn-bg); color: var(--warn-ink); border-color: var(--warn-border); }
.btn-danger { background: var(--danger-bg); color: var(--danger-ink); border-color: var(--danger-border); }

/* Column stepper: phone only. The chips scroll in a run of
   their own; the run holds chips and nothing else, since the
   See all columns and Schedule buttons live in the board row. */
.column-stepper {
  display: none;
  gap: 6px;
  align-items: center;
  padding-bottom: 8px;
  margin-bottom: 4px;
}

/* While the schedule stands open app.js hides the stepper, and
   the phone rule below would otherwise beat the hidden flag. */
.column-stepper[hidden] { display: none; }

/* The chips run: each chip sizes to its whole word, and the run
   scrolls sideways when the words need more room than the screen
   gives. The scrollbar stays a thin line, never a heavy bar.
   The line fades (issue #36): while the run is at rest the line
   stands at no strength, and while the run is being used it
   rises to full strength and fades back a second after the use
   stops. app.js names the state with the chips-in-use class;
   here the strength is a plain number, registered so the
   browser can ease between its values, and the thumb in both
   its spellings is the line colour mixed with transparency at
   that strength. The line colour is each look's own variable,
   so the fade is the same in every look, light and dark. The
   scrollbar's size is set once and never changes with the
   strength, so nothing shifts as the line comes and goes. */
@property --chips-bar-strength {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

.step-chips {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  flex: 1 1 auto;
  min-width: 0;
  scrollbar-width: thin;
  --chips-bar-strength: 0;
  transition: --chips-bar-strength 0.45s ease;
  scrollbar-color: color-mix(in srgb, var(--line) calc(var(--chips-bar-strength) * 100%), transparent) transparent;
}

.step-chips.chips-in-use { --chips-bar-strength: 1; }

.step-chips::-webkit-scrollbar { height: 6px; }

.step-chips::-webkit-scrollbar-thumb {
  --chips-bar-strength: 0;
  transition: --chips-bar-strength 0.45s ease;
  background: color-mix(in srgb, var(--line) calc(var(--chips-bar-strength) * 100%), transparent);
  border-radius: 3px;
}

.step-chips.chips-in-use::-webkit-scrollbar-thumb { --chips-bar-strength: 1; }

/* The See all columns button: a round button in the header
   buttons' family, 32 by 32 with a 16 pixel mark. While the
   overview stands it fills in, the way the active chip does,
   and its label reads as the way back. */
.overview-btn {
  appearance: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
}

.overview-btn svg {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto;
}

.overview-btn.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
}

/* The See all columns button is a phone's only: on a wide screen
   it never shows, and while the schedule stands open app.js hides
   it at any width, since there are no columns to zoom out to. */
@media (min-width: 900px) {
  #overview-toggle { display: none; }
}

#overview-toggle[hidden] { display: none; }

.step-btn {
  appearance: none;
  flex: 0 0 auto;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font-size: 14px;
  padding: 8px 12px;
  text-align: center;
  border-radius: 999px;
  cursor: pointer;
  min-height: 44px;
  white-space: nowrap;
}

.step-btn.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
  font-weight: 600;
}

/* A chip a card drag is hovering wears the drop ring. */
.step-btn.drop-target {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Columns */
.columns {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
  align-items: start;
}

.column {
  min-width: 0;
  background: var(--column-bg);
  border-radius: var(--radius);
  padding: 10px;
}

.column.drag-over { outline: 2px solid var(--accent); outline-offset: -2px; }

.column-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 0 2px 10px;
}

.column-head h2 {
  margin: 0;
  font-size: 16px;
}

.column-count { color: var(--muted); font-size: 13px; }

/* A column's icon sits before its name, in the board's column
   headers and in the phone's column switcher chips alike. */
.column-icon { margin-right: 6px; }
.step-icon { margin-right: 5px; }

.card-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 40px;
}

/* A column's foot carries its own plus: a round green button in
   the family of the header's small round buttons. A card added
   from here starts in this column. */
.column-foot {
  display: flex;
  justify-content: center;
  padding-top: 10px;
}

.column-add-btn {
  width: 44px;
  min-width: 44px;
  height: 44px;
  min-height: 44px;
  padding: 0;
  border-radius: 50%;
  font-size: 20px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* A column's touch handling: up-and-down pans scroll, the way
   they always have, and sideways pans are left delivering
   pointer events so the app can page between columns on a swipe.
   A card follows the same rule, except its grip, where a drag
   must own the touch outright so it can never scroll or page. */
.column, .card-list, .card { touch-action: pan-y; }

.card {
  position: relative;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px;
  cursor: pointer;
  text-align: left;
  width: 100%;
  font: inherit;
  color: inherit;
  user-select: none;
  -webkit-user-select: none;
}

.card:hover { border-color: var(--card-hover); }

/* The grip in a card's top corner: quiet grey dots until the card
   is hovered or the grip itself is touched. On a touch screen
   there is no hover, so the dots stand a little stronger there. */
.card-grip {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  color: var(--muted);
  opacity: 0.45;
  touch-action: none;
  cursor: grab;
}
.card-grip svg { width: 10px; height: 16px; display: block; }
.card:hover .card-grip, .card:focus-visible .card-grip { opacity: 0.95; }
@media (pointer: coarse) {
  .card-grip { opacity: 0.7; }
}

/* The drag's two stand-ins: a copy of the card that follows the
   pointer, and a dashed space in the list marking where the card
   will land. */
.drag-ghost {
  position: fixed;
  margin: 0;
  z-index: 80;
  pointer-events: none;
  opacity: 0.95;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
  cursor: grabbing;
}
.drop-indicator {
  flex: none;
  border: 2px dashed var(--accent);
  border-radius: 8px;
}
body.dragging-card { cursor: grabbing; }

.card-title {
  display: block;
  font-weight: 600;
  font-size: 15px;
  overflow-wrap: break-word;
  padding-right: 26px;
}

.card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: 8px;
}

.chip {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
}

/* A label's emoji sits inside its chip, before the name. */
.chip-icon { margin-right: 4px; }

.chip-household { background: #dcead9; color: #234d20; border-color: #b7d3b2; }
.chip-family { background: #fde8c8; color: #6b4b00; border-color: #f0d194; }
.chip-health { background: #dbeafe; color: #1d3f7d; border-color: #aecbf5; }
.chip-errands { background: #ece2f5; color: #472a66; border-color: #d0bce8; }
.chip-admin { background: #e5e5e0; color: #3d403d; border-color: #c9cdc4; }
.chip-writing { background: #e3e0f5; color: #3b2a6b; border-color: #c4bce8; }
.chip-building { background: #d7f0ec; color: #0f4c44; border-color: #a5d8d0; }
.chip-outreach { background: #fde3ef; color: #701a3e; border-color: #f2b8d0; }
.chip-money { background: #e6f2d8; color: #33590f; border-color: #c2dd9e; }
.chip-none { background: #eeeeea; color: #4c524c; border-color: #d5d8d0; }

.due {
  font-size: 13px;
  color: var(--muted);
}

.due.soon { color: var(--due-soon); font-weight: 600; }

.checklist-progress {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
}

.checklist-progress svg { width: 14px; height: 14px; flex: none; }

.checklist-progress.all-done { color: var(--good); font-weight: 600; }

.link-count {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
}

.link-count svg { width: 14px; height: 14px; flex: none; }

.comment-count {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
}

.comment-count svg { width: 14px; height: 14px; flex: none; }

/* The one marker a flagged card wears: a small flag in a warm
   colour, in the same row and at the same size as the checklist
   and link counts. Unflagged cards render nothing for it. */
/* The Important flag stands on its own tinted footing, clear of
   the due date beside it, so it never reads as decoration on the
   date. Its warm colour is the same in every theme's own shades. */
.important-flag {
  display: inline-flex;
  align-items: center;
  color: var(--flag);
  background: var(--flag-bg);
  border: 1px solid var(--flag-border);
  border-radius: 999px;
  padding: 2px 6px;
}

/* A card past its due date wears this tag beside the date. A card
   due today wears nothing: today is not late. */
.late-tag {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--late-bg);
  color: var(--late-ink);
  border: 1px solid var(--late-border);
}

.important-flag svg { width: 14px; height: 14px; flex: none; }

.empty-note {
  color: var(--muted);
  font-size: 13px;
  margin: 2px;
}


/* Editor */
.editor-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(20, 22, 20, 0.55);
  z-index: 20;
  overflow-y: auto;
  padding: 16px 12px;
}

.editor {
  background: var(--panel);
  border-radius: 12px;
  max-width: 560px;
  margin: 0 auto;
  padding: 16px;
}

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

.editor-head h2 { margin: 0; font-size: 20px; }

/* On a wide screen the card editor uses the room around it
   (issue #40): the board behind it runs the full width, and a
   560px editor left most of a desktop empty. The title, notes,
   checklist and links inside are fluid, so they gain the same
   room the dialog does. Phone widths keep the editor as it was,
   and the other dialogs keep their own widths. */
@media (min-width: 900px) {
  #editor-backdrop .editor {
    max-width: 980px;
  }
}

.editor label {
  display: block;
  font-weight: 600;
  margin: 12px 0 4px;
}

/* A field heading that is not a form label, styled to match one:
   the Labels tick list below it is a set of rows, not one control. */
.editor .field-heading {
  display: block;
  font-weight: 600;
  margin: 12px 0 4px;
}

/* The card's labels, inside the editor. One row per label on the
   board: a tick box and the label's chip, exactly as the chip reads
   on the card's face. Ticking a row puts that label on the card or
   lifts it off, at once. */
.label-tick-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 4px 0 8px;
}

.editor label.label-tick {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  margin: 0;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}

.label-tick input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex: none;
  margin: 0;
}

/* The Important toggle, under the due date: one tick box and the
   word. Ticking it flags the card at once, like every other field. */
.editor label.important-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 14px 0 4px;
  cursor: pointer;
}

.important-label input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
  flex: none;
  margin: 0;
}

.editor input[type="text"],
.editor input[type="date"],
.editor textarea,
.editor select {
  width: 100%;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}


/* The card's checklist, inside the editor. Each row is a move
   grip, a tick box, the step's text and a Remove button. The
   text stands as plain wrapped text — no field and no box — so
   a step of any length reads in full; tapping it swaps in the
   editing field for as long as the rename lasts. A ticked
   step's text is struck through, so where a card has got to
   reads at a glance. */
.checklist-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 4px 0 8px;
}

.checklist-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.checklist-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex: none;
  margin: 11px 0 0;
}

/* A step's move grip: the same six dots a card carries, standing
   at the row's left. It is the one place a step drag can start
   from, so a press on the text or the tick box never picks a
   step up. Quiet grey until the row is hovered or the grip
   itself is touched; on a touch screen there is no hover, so
   the dots stand a little stronger there. */
.step-grip {
  flex: none;
  width: 28px;
  height: 28px;
  margin-top: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  color: var(--muted);
  opacity: 0.45;
  touch-action: none;
  cursor: grab;
}
.step-grip svg { width: 10px; height: 16px; display: block; }
.checklist-row:hover .step-grip { opacity: 0.95; }
@media (pointer: coarse) {
  .step-grip { opacity: 0.7; }
}

/* The copy of a step row that follows the pointer while the
   step is dragged: it needs the editor's own ground under it,
   where a card's copy brings its own. */
.step-drag-ghost {
  background: var(--panel);
  border-radius: 8px;
  padding-right: 8px;
}

.checklist-row .step-text {
  flex: 1;
  min-width: 0;
  overflow-wrap: break-word;
  padding: 10px 2px;
  cursor: text;
}

.checklist-row .step-edit { flex: 1; min-width: 0; }

.checklist-row.done .step-text {
  text-decoration: line-through;
  color: var(--muted);
}

.checklist-row .btn { flex: none; }

.checklist-add-row { display: flex; gap: 8px; }
.checklist-add-row input[type="text"] { flex: 1; min-width: 0; }
.checklist-add-row .btn { flex: none; }

/* The card's links, inside the editor. Each row is the link itself
   and a Remove button. The link shows its name, or its address
   when it has no name, with the address under a named link, and
   following it opens the address in a new tab. */
.links-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 4px 0 8px;
}

.link-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.link-open {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--field-bg);
  color: var(--ink);
  text-decoration: none;
}

.link-open:hover { border-color: var(--card-hover); }

.link-name {
  font-weight: 600;
  font-size: 15px;
  overflow-wrap: break-word;
}

.link-url {
  font-size: 13px;
  color: var(--muted);
  overflow-wrap: break-word;
}

.link-row .btn { flex: none; }

.link-error {
  color: var(--due-soon);
  font-size: 14px;
  margin: 4px 0 8px;
}

.link-add-row { display: flex; flex-wrap: wrap; gap: 8px; }
.link-add-row #link-add-url { flex: 1 1 100%; }
.link-add-row #link-add-name { flex: 1; min-width: 0; }
.link-add-row .btn { flex: none; }

/* The card's comments, inside the editor. Each comment stands
   as its body — plain wrapped text, keeping the line breaks it
   was written with — over a quiet foot: the author tag when an
   agent key wrote it (the same pill a key-made card wears on
   its face), the time it was written, and its Remove button.
   A comment the person wrote wears no tag. */
.comments-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 4px 0 8px;
}

.comment-row {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--field-bg);
  padding: 8px 10px;
}

.comment-body {
  overflow-wrap: break-word;
  white-space: pre-wrap;
  font-size: 15px;
}

.comment-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}

.comment-time {
  font-size: 13px;
  color: var(--muted);
  flex: 1;
  min-width: 0;
}

.comment-foot .btn { flex: none; }

.comment-add-row { display: flex; gap: 8px; align-items: flex-start; }
.comment-add-row textarea { flex: 1; min-width: 0; }
.comment-add-row .btn { flex: none; }

.editor-conflict {
  border: 1px solid var(--accent);
  border-radius: 10px;
  background: var(--panel);
  padding: 10px 12px;
  margin-top: 16px;
}
.editor-conflict p { margin: 0 0 8px; }
.editor-conflict-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.move-block { margin-top: 20px; }
.move-block h3 { margin: 0 0 8px; font-size: 16px; }

.move-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.move-buttons .btn.current {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
}

.danger-actions {
  display: flex;
  gap: 8px;
  margin-top: 22px;
  border-top: 1px solid var(--line);
  padding-top: 14px;
}

/* Settings */
.settings-backdrop { z-index: 30; }

/* The Settings list: one row per page, a name and a quiet arrow.
   Tapping a row swaps the list for that page alone. */
.settings-list {
  display: flex;
  flex-direction: column;
  margin-top: 8px;
}

.settings-list[hidden] { display: none; }
.settings-list-btn[hidden] { display: none; }

.settings-list-btn {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  font: inherit;
  font-size: 17px;
  text-align: left;
  padding: 14px 2px;
  min-height: 52px;
  cursor: pointer;
}

.settings-list-btn:first-child { border-top: 1px solid var(--line); }

.settings-list-arrow {
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
}

.settings-back-row { margin-top: 4px; }
.settings-back-row[hidden] { display: none; }

.settings-panel {
  max-width: 720px;
}

/* The Account page's Log out button stands clear of the text
   above it (issue #43): the section's own paragraph spacing left
   it crowding the line over it. */
.settings-section .logout-row {
  margin-top: 30px;
}

/* The Account page's delete section (issue #57) stands apart
   at the foot of the page, clear of the sign-in facts and the
   Log out row above it: it is the one act here with no undo. */
.settings-section .account-delete {
  margin-top: 30px;
}

/* The Set Up Agent key list (issue #45): one row per key — its
   name, its ending and date, then its own Rename and Delete.
   Rename and the delete confirm open inside the row. */
.key-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 0;
  border-top: 1px solid var(--line);
}

.key-main { flex: 1 1 200px; min-width: 0; }
.key-name { margin: 0; font-weight: 600; overflow-wrap: break-word; }
.key-sub { margin: 2px 0 0; color: var(--muted); font-size: 14px; }
.key-actions { display: flex; gap: 8px; }

.key-rename {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex: 1 1 100%;
}

.key-rename input[type="text"] {
  flex: 1 1 160px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.key-add-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.key-add-row input[type="text"] {
  flex: 1 1 180px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

/* A labelled field standing on its own row in Settings, the
   account page's name field the first of them. */
.settings-field {
  display: block;
  margin-top: 12px;
  font-weight: 600;
}

/* The class's display would otherwise beat the hidden
   attribute (the pattern the other [hidden] guards in this
   file exist for): a field the page hides — the passkey name
   field where passkeys cannot run — must actually go. */
.settings-field[hidden] { display: none; }

.settings-field input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-top: 6px;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.settings-section {
  border-top: 1px solid var(--line);
  padding: 14px 0 6px;
  margin-top: 14px;
}

.settings-section h3 {
  margin: 0 0 8px;
  font-size: 18px;
}

/* The sub-headings inside a settings section (Passkeys,
   Change password, Sign out, Your keys): without a size of
   their own they fall back to the browser's h4, which is
   body size — on the phone they read as bold body text, not
   headings. 17px against the 16px body keeps them visibly
   headings at every width, under the section's 18px. */
.settings-section h4 {
  margin: 16px 0 6px;
  font-size: 17px;
}

.settings-section p { margin: 6px 0; }
.settings-subhead { font-weight: 700; margin-top: 14px !important; }
.settings-note { color: var(--muted); font-size: 14px; }

.preview-switcher {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

.preview-btn.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
  font-weight: 600;
}

/* The look picker: one button per look, each carrying two
   swatches — the look's light ground split with its accent, and
   its dark ground split with its accent. */
.look-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px;
  margin: 10px 0 16px;
}

.look-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--ink);
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.look-btn.active {
  border-color: var(--ink);
  box-shadow: 0 0 0 1px var(--ink);
}

.look-swatches { display: inline-flex; flex: 0 0 auto; }

.look-swatch {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.18);
}

.look-swatch + .look-swatch { margin-left: -6px; }

.look-swatch-standard-light { background: linear-gradient(135deg, #faf8f3 0 55%, #2b5fc7 55% 100%); }
.look-swatch-standard-dark { background: linear-gradient(135deg, #10121f 0 55%, #3a6fd8 55% 100%); }
.look-swatch-comic-light { background: linear-gradient(135deg, #fff3c4 0 55%, #2050c8 55% 100%); }
.look-swatch-comic-dark { background: linear-gradient(135deg, #1d1b2e 0 55%, #ffd23e 55% 100%); }
.look-swatch-console-light { background: linear-gradient(135deg, #f2f5ec 0 55%, #0e7a2f 55% 100%); }
.look-swatch-console-dark { background: linear-gradient(135deg, #000000 0 55%, #33e05c 55% 100%); }
.look-swatch-pretty-light { background: linear-gradient(135deg, #fdf0f6 0 55%, #b02a63 55% 100%); }
.look-swatch-pretty-dark { background: linear-gradient(135deg, #221219 0 55%, #e58bb5 55% 100%); }
.look-swatch-artistic-light { background: linear-gradient(135deg, #f5efe3 0 55%, #a52f14 55% 100%); }
.look-swatch-artistic-dark { background: linear-gradient(135deg, #171310 0 55%, #e2703a 55% 100%); }
.look-swatch-sunset-light { background: linear-gradient(135deg, #fdeedd 0 55%, #bc380f 55% 100%); }
.look-swatch-sunset-dark { background: linear-gradient(135deg, #1e1226 0 55%, #ff8c42 55% 100%); }
.look-swatch-forest-light { background: linear-gradient(135deg, #edf4e2 0 55%, #2e6b30 55% 100%); }
.look-swatch-forest-dark { background: linear-gradient(135deg, #0c1710 0 55%, #69a85e 55% 100%); }
.look-swatch-midnight-light { background: linear-gradient(135deg, #e7edfc 0 55%, #2b4acb 55% 100%); }
.look-swatch-midnight-dark { background: linear-gradient(135deg, #050914 0 55%, #7aa2ff 55% 100%); }

.theme-switch {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

.theme-mode-btn.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
  font-weight: 600;
}

.btn[disabled] { opacity: 0.55; cursor: not-allowed; }

.field-note {
  color: var(--muted);
  font-size: 13px;
  margin: 4px 0 0;
}

.board-manage-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.board-manage-row input[type="text"] {
  flex: 1 1 150px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.board-note {
  flex-basis: 100%;
  color: var(--muted);
  font-size: 13px;
  margin: 0;
}

/* The board's label colour mode picker (issue #95): a small
   labelled select standing on the board's own row, so the
   Boards page reads each board's mode at a glance. */
.board-mode {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 13px;
}

.board-mode select {
  font-size: 14px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.board-add-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.board-add-row input {
  flex: 1 1 180px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.label-row,
.label-add-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.label-row input[type="text"],
.label-add-row input[type="text"] {
  flex: 1 1 150px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

/* Settings → Projects rows wear the Labels rows' shapes: one
   board's projects under the page's board picker, the thing's
   mark first, its name in a field, and its delete asking on its
   own line when it asks. */
.project-row,
.project-add-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.project-row input[type="text"],
.project-add-row input[type="text"] {
  flex: 1 1 150px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.project-row input[type="color"],
.project-add-row input[type="color"] {
  flex: 0 0 auto;
  width: 48px;
  height: 44px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--field-bg);
  cursor: pointer;
}

.label-row select,
.label-add-row select {
  flex: 0 1 140px;
  min-width: 0;
  font-size: 16px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.label-preview {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid var(--line);
  flex: 0 0 auto;
}

.columns-board-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.columns-board-row label {
  display: inline;
  margin: 0;
}

.columns-board-row select {
  flex: 0 1 220px;
  min-width: 0;
  font-size: 16px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.column-manage-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.column-manage-row input[type="text"] {
  flex: 1 1 150px;
  min-width: 0;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 44px;
  background: var(--field-bg);
  color: var(--ink);
}

.column-note {
  flex-basis: 100%;
  color: var(--muted);
  font-size: 13px;
  margin: 0;
}

.column-add-row {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.settings-panel label {
  display: block;
  font-weight: 600;
  margin: 14px 0 4px;
}

.copy-field {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  line-height: 1.45;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--copy-bg);
  color: var(--ink);
}

::placeholder { color: var(--muted); opacity: 1; }

textarea.copy-field { resize: vertical; }

.copy-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

/* Phone: one column at a time */
@media (max-width: 899px) {
  .column-stepper { display: flex; }
  .columns { display: block; }
  .column { display: none; }
  .column.active { display: block; }
  .topbar-inner { padding: 8px 12px; }
  h1 { font-size: 19px; }
  /* On a phone the heading takes two rows: the wordmark and
     the three small buttons hold the first row, and the board
     row takes a second row of its own at full width. That row
     stays one row: the switcher scrolls sideways inside the
     room the pinned buttons leave it rather than wrapping,
     however many boards there are, and the See all columns and
     Schedule buttons stand at the row's right margin, pinned
     outside the scrolling run. Board buttons follow the display
     mode on a phone like everywhere else. */
  .header-row { flex-wrap: wrap; gap: 4px; row-gap: 8px; }
  .header-actions { margin-left: auto; gap: 4px; }
  /* The Pilot tag leaves the phone header (issue #238): it
     keeps its seat at desktop widths and on the reading
     pages, and the pilot fact stands in Help and the Terms. */
  .topbar .pilot-tag { display: none; }
  .board-row {
    order: 3;
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
  }
  .board-switcher { gap: 4px; }
  .board-row-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    margin-left: auto;
  }
  /* The board buttons stand small on a phone — 32 pixels — while
     the header's right-side set keeps the 44px floor on the
     first row (issue #48); the board row's own tool buttons
     stand at the board buttons' size beside them. */
  .board-tab { padding: 0 8px; height: 32px; min-height: 32px; }
}

/* The phone's overview never shows any wider than a phone. */
.board-overview { display: none; }

/* The phone's overview: every column side by side as narrow
   strips, standing where the columns stand while it is open.
   A strip is a place to look and to choose: its cards scroll
   inside it, and a tap lands inside the column. Names and
   titles cut short rather than pushing a strip wider, so
   however many columns a board has, the strips share the
   width and the page never scrolls sideways. */
@media (max-width: 899px) {
  /* The strips stand only as tall as their cards: the row takes
     the tallest strip's height and no more, and a long column's
     cards scroll inside its strip, capped at the room the
     screen gives the overview. */
  /* The overview keeps the columns' own spacing: the gap
     between strips is the columns' gap, a strip's top and
     bottom padding is a column's padding, its head's bottom
     margin is a column head's, and the gap between its cards
     is the card list's gap — the same values, taken from the
     column rules above, so the zoomed-out board breathes
     exactly as the full-size one does. A strip's side padding
     stays narrow: five strips share a phone's width, and the
     room belongs to the titles. */
  .board-overview {
    display: flex;
    gap: 10px;
    align-items: flex-start;
  }
  .board-overview[hidden] { display: none; }
  .show-overview .columns { display: none; }

  .ov-strip {
    flex: 1 1 0;
    min-width: 0;
    max-height: calc(100dvh - 216px);
    display: flex;
    flex-direction: column;
    background: var(--column-bg);
    border-radius: var(--radius);
    padding: 10px 4px;
    cursor: pointer;
  }
  .ov-strip.current {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }
  .ov-strip:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }

  /* A strip's head stands in two lines: the icon and the count
     on the first, the name on its own line under them, so the
     name gets the strip's whole width before it must cut short. */
  .ov-head {
    flex: 0 0 auto;
    margin: 0 1px 10px;
  }
  .ov-head-top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 2px;
  }
  .ov-icon { font-size: 12px; }
  .ov-name {
    display: block;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ov-count { flex: 0 0 auto; font-size: 11px; color: var(--muted); }

  .ov-cards {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
    touch-action: pan-y;
  }
  .ov-card {
    flex: 0 0 auto;
    display: block;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 10px 4px;
    font-size: 12px;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ov-empty { font-size: 11px; color: var(--muted); margin: 2px 1px 0; }
}

/* The Archived list stands under the columns: one row per
   archived card, naming the card and the column Restore takes
   it back to. A row asking its delete question gives the
   question its own line, the way the editor's does. */
/* The Schedule control and the schedule itself. The control is
   an icon button in the header buttons' family: round, 32 by
   32, with a 16 pixel calendar mark. It never carries its word;
   the name rides in its label and title. Pressed, it wears the
   inverted colours the active board tab wears. One button
   serves every screen: in the header's cluster on a wide
   screen, and pinned at the right of the board row on a phone. */
.schedule-btn {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
}

.schedule-btn svg {
  display: block;
  width: 20px;
  height: 20px;
}

.schedule-btn.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
}

/* The columns' own display rule would otherwise beat the hidden
   attribute app.js sets while the schedule stands open. */
#columns[hidden] { display: none; }

.schedule-tags { display: flex; gap: 8px; margin: 2px 0 16px; }

.schedule-tag {
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.schedule-tag.active {
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-color: var(--invert-bg);
}

.schedule-group { margin-bottom: 20px; }

.schedule-group-title { font-size: 15px; margin: 0 0 8px; }

.schedule-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin-bottom: 8px;
  color: var(--ink);
  font-size: 15px;
  cursor: pointer;
}

.schedule-row:hover { background: var(--card-hover); }

.schedule-row-title { font-weight: 600; flex: 1 1 auto; }

.schedule-row-meta { color: var(--muted); font-size: 13px; white-space: nowrap; }

.schedule-empty { color: var(--muted); margin: 8px 0; }

/* The schedule's back-to-top button: fixed at the lower right,
   shown by app.js once the schedule page has scrolled. */
.back-to-top {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 30;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--invert-bg);
  color: var(--invert-ink);
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

.back-to-top[hidden] { display: none; }

.archived-section {
  margin-top: 26px;
  border-top: 1px solid var(--line);
  padding-top: 12px;
}

.archived-heading {
  font-size: 16px;
  margin: 0 0 8px;
}

/* The heading line is the section's toggle (issue #26): a
   button wearing the heading's own type, so the closed
   section reads as one line of text and taps as a control. */
.archived-toggle {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.archived-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 8px 12px;
  margin-bottom: 8px;
}

.archived-title { font-weight: 600; }

.archived-from { color: var(--muted); font-size: 13px; }

.archived-actions {
  margin-left: auto;
  display: inline-flex;
  gap: 8px;
}

.confirm-note { font-size: 14px; margin: 2px 0 8px; }

/* The icon picker: one component everywhere an icon is chosen.
   A button showing the current icon opens a small panel with a
   grid of common emojis, a field for any other emoji, and a way
   back to no icon (or to the default, for boards and columns). */
.icon-picker {
  position: relative;
  display: inline-block;
  flex: 0 0 auto;
}

.icon-picker-btn {
  appearance: none;
  min-width: 48px;
  min-height: 44px;
  padding: 6px 10px;
  font-size: 20px;
  line-height: 1;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--field-bg);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.icon-picker-btn .icon-picker-plus {
  font-size: 18px;
  color: var(--muted);
}

.icon-picker-panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 6px);
  left: 0;
  width: 336px;
  max-width: 88vw;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
  padding: 10px;
  text-align: left;
}

/* The big picker's filter field and its scrolling grid area:
   the full set is a few hundred emoji, so the grids stand in a
   box of their own that scrolls inside the panel. */
.icon-search-input {
  width: 100%;
  padding: 8px 10px;
  margin-bottom: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--field-bg);
  color: var(--ink);
  font-size: 14px;
}

.icon-scroll {
  max-height: 320px;
  overflow-y: auto;
  padding-right: 2px;
}

.icon-grid-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  margin: 6px 0 4px;
}

.icon-no-match { color: var(--muted); font-size: 14px; margin: 8px 0; }

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

.icon-grid button {
  appearance: none;
  border: 1px solid transparent;
  background: transparent;
  font-size: 20px;
  line-height: 1.2;
  padding: 6px 0;
  border-radius: 8px;
  cursor: pointer;
}

.icon-grid button:hover { border-color: var(--line); background: var(--copy-bg); }

.icon-custom-row {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}

.icon-custom-row input {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 16px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 40px;
  background: var(--field-bg);
  color: var(--ink);
}

.icon-picker-msg {
  color: var(--danger-ink);
  font-size: 13px;
  margin: 6px 0 0;
}

.icon-picker-clear { margin-top: 8px; }

/* Press-and-hold rename. The buttons give up text selection and
   the long-press callout so a hold reads as a hold, and while a
   hold stands the button arms: a ring in its own colour fills
   in over the half second the press takes to fire, so a person
   can see it coming and let go in time. */
.board-tab,
.filter-btn {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}

.board-tab.press-arming,
.filter-btn.press-arming {
  animation: press-arm 0.5s linear forwards;
}

@keyframes press-arm {
  from { box-shadow: inset 0 0 0 0 transparent; }
  to { box-shadow: inset 0 0 0 3px currentColor; filter: brightness(0.94); }
}

/* The small editor a long press opens, anchored on the button it
   belongs to. It holds the name in a field and a Close, and —
   for a project — a Delete, which first names the cards the
   project carries and asks once. */
.press-editor {
  position: fixed;
  z-index: 40;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
  padding: 12px;
  text-align: left;
}

.press-editor-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
}

/* The icon row in the press-and-hold editor: the word stands
   over the picker, the way the name's word stands over its
   field. */
.press-editor-icon-row { margin-top: 10px; }

.press-editor input {
  width: 100%;
  font-size: 16px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 40px;
  background: var(--field-bg);
  color: var(--ink);
}

.press-editor-row {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.press-editor-delete { margin-left: auto; }




/* The public pages (issue #14): the signed-out front page at
   the root and the standalone Terms, Help and Feedback pages.
   They share one heading and one footer — the brand at the
   left, then Feedback and Help in the heading as icons alone,
   their names on their labels. No policy link stands in the
   heading: the Terms and the policies are reached through
   Help (Site and Email Standards, Part 2; deploy issue
   #208). The heading carries no sign-in button: the way in
   stands in the home page's body, below the heading
   (Derick, 2026-11-07). The smoke checks hold the heading's
   copies identical across the pages. The footer carries
   About, Feedback and Help as words, no icons (Part 3). */
/* The heading keeps one standing height on every page:
   its row is exactly 56px with no vertical padding, the
   tallest things in it the 44px glyph buttons (Part 2),
   whether the glyph links stand in it or the brand stands
   alone — a copy with no site address shows the brand
   alone, and the heading's box never changes with its
   contents (issue #39). At phone widths the heading may
   wrap to two rows, each row 56px. The heading sticks to
   the top as one unit (Part 2): it carries wayfinding —
   the brand home link, Help and the dark toggle — so it
   is a header, not a masthead. The vendored header
   component sets no position; TaskSparkle's reading
   stands in Part 13, entry 26 (deploy issue #220). */
.site-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 2px;
  min-height: 56px;
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bg);
}

.site-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ink);
  text-decoration: none;
  font-family: var(--font-display, var(--font));
  font-weight: 700;
  font-size: 21px;
}

.site-brand img { display: block; border-radius: 7px; width: 36px; height: 36px; }

.site-links {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}

.site-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--ink);
  text-decoration: none;
  font-size: 15px;
  min-width: 44px;
  height: 44px;
  padding: 0 12px;
  border-radius: 8px;
}

.site-head .site-link { min-width: 44px; height: 44px; padding: 4px; }

.site-head .site-link svg { width: 20px; height: 20px; flex: none; }
.site-head button.site-link { appearance: none; background: none; border: 0; cursor: pointer; font: inherit; }

.site-link:hover { background: var(--column-bg); }

/* One frame for every logged-out page (issue #176):
   the reading column below. The front page's landing
   stands in it like the standalone pages — the whole
   page, not only its heading and footer, which an
   earlier fix stepped into the column by a formula
   while the content kept the app shell's widths
   (deploy issue #173). While the landing stands, the
   app shell's padding steps aside: nothing of the app
   is on screen then, and the shell's own box returns
   untouched the moment any app content stands, so the
   signed-in app is never reflowed. */
main.page:has(> #landing:not([hidden])) { padding: 0; }

/* The Feedback entries stand hidden until the site script
   learns whether the deployment has a feedback target
   (issue #22); the guard keeps the link's own display rule
   from beating the hidden attribute in the meantime. */
.site-link[hidden] { display: none; }

.site-foot {
  border-top: 1px solid var(--line);
  margin-top: 40px;
  padding: 16px 2px 8px;
}

.site-foot .site-links { padding: 0; }

.site-foot-line {
  color: var(--muted);
  font-size: 14px;
  margin: 10px 0 0;
}

.site-foot-line a { color: var(--accent); }

/* The front page's own pieces. */
.landing-hero { text-align: center; padding: 12px 8px 6px; }

/* The hero opens with the line itself, at lead size —
   18px, regular weight (Derick, 2026-11-10: the line is
   lead text, not a headline; the same ruling he made on
   MemberSpark's home page). The brand stands in the
   heading bar only — the hero does not repeat it as a
   giant wordmark. */
.landing-line {
  font-family: var(--font-display, var(--font));
  font-weight: 400;
  font-size: 18px;
  line-height: 1.25;
  color: var(--ink);
  max-width: 34em;
  margin: 0 auto 18px;
}

.landing-cta { margin: 0 0 10px; }

.btn-big {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  font-size: 16px;
  font-weight: 800;
  padding: 14px 19px;
  min-height: 44px;
}

.landing-note { color: var(--muted); margin: 0; }

.landing-shot {
  margin: 26px 0 6px;
}

.landing-shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.landing-points {
  margin: 22px 0 0;
  font-size: 16.5px;
}

.landing-points p { margin: 0 0 10px; }

.landing-join {
  margin: 24px 0 0;
}

.landing-join h3 { margin: 0 0 6px; font-size: 20px; font-family: "Bricolage Grotesque", Figtree, "Segoe UI", system-ui, sans-serif; }
.landing-join p { margin: 0; }

/* The frame: one column of reading for every logged-out
   page — the standalone pages' wrap and the front page's
   landing alike — with the same heading and footer
   standing inside it. The account pages stand in this
   same frame through the copy of these rules their own
   shell carries (worker/auth.js). */
.static-wrap, #landing {
  max-width: 46em;
  margin: 0 auto;
  padding: 0 14px 40px;
}

/* The reading pages' headings stand at the design's type
   scale (design/system tokens.css: h1 2.5rem, h2 1.75rem,
   h3 1.25rem), set in the display face the shared sheet
   loads (issue #217). */
.static-wrap h1, .static-wrap h2, .static-wrap h3 {
  font-family: "Bricolage Grotesque", Figtree, "Segoe UI", system-ui, sans-serif;
}

.static-wrap h1 { font-size: 40px; margin: 10px 0 6px; }

.static-sub { color: var(--muted); font-size: 16.5px; margin: 0 0 18px; }

.draft-line {
  background: var(--warn-bg);
  color: var(--warn-ink);
  border: 1px solid var(--warn-border);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 14.5px;
}

.static-wrap h2 { font-size: 28px; margin: 26px 0 8px; }

.static-wrap h3 { font-size: 20px; }

.static-wrap p, .static-wrap li { font-size: 16px; }

.static-wrap ul { padding-left: 22px; }

.static-wrap li { margin-bottom: 7px; }

/* Prose links take the accent. The heading's and footer's
   own links are not prose: they carry the heading's ink,
   the way they do on the front page, whose heading stands
   outside this column — a heading that changed colour
   between the front page and these pages was the
   difference the headings walk caught (2026-11-08). */
.static-wrap a:not(.btn):not(.site-brand):not(.site-link):not(.help-box):not(.help-result) { color: var(--accent); }

.static-updated { color: var(--muted); font-size: 14px; margin-top: 34px; }
.static-updated.top { margin-top: 10px; }

/* Help: the topic list and the short answers. */
.help-topics { margin-top: 6px; }

.help-topic { margin: 0 0 16px; }

.help-topic h2 { margin: 0 0 2px; font-size: 19px; }

.help-topic p { margin: 0; color: var(--muted); }

/* The Feedback topic is one of the gated entries (issue #22):
   hidden until the site script settles it. */
.help-topic[hidden] { display: none; }

.faq-group {
  font-weight: 700;
  font-family: var(--font-display, var(--font));
  margin: 30px 0 4px;
  font-size: 17px;
}

.faq-item h3 { font-size: 20px; margin: 18px 0 4px; }

.faq-item p { margin: 0 0 6px; }

/* The feedback form: the shared service's shape — a kind, a
   note with its count, and two optional fields. The form
   itself stands hidden until the page's script learns the
   deployment takes feedback (issue #22). */
.feedback-form[hidden] { display: none; }

.feedback-form label {
  display: block;
  font-weight: 600;
  margin: 18px 0 6px;
}

.feedback-form textarea,
.feedback-form input[type="text"] {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 16px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--field-bg);
  color: var(--ink);
}

.kind-pick { display: flex; flex-wrap: wrap; gap: 8px; }

.kind-pick label {
  display: inline-flex;
  margin: 0;
  font-weight: 400;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 14px;
  cursor: pointer;
  background: var(--panel);
}

.kind-pick input { margin-right: 7px; }

.feedback-count { color: var(--muted); font-size: 13.5px; margin: 5px 0 0; }

.feedback-status { min-height: 1.4em; font-weight: 600; }

.honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

/* On a phone the heading's links stand as icons alone, so
   the heading stays light; their names ride on their
   aria-labels. The measures follow the reference heading
   at this width (issue #25): the brand's word shrinks
   (15px; the tile stands at 36px, issue #216)
   and the links keep their 44px glyphs, standing
   on the brand's own row — the one heading, laid out the
   same on the front page and the standalone pages. The
   brand's tile keeps the design's 36px at this width too
   (issue #216); the word alone shrinks. */
@media (max-width: 640px) {
  .site-link { padding: 0; }
  .site-brand { order: 1; font-size: 15px; gap: 6px; }
  .site-head .pilot-tag { order: 1; }
  .site-brand img { width: 36px; height: 36px; }
  .site-head { flex-wrap: wrap; row-gap: 0; padding-top: 0; padding-bottom: 0; min-height: 56px; }
  .site-brand, .site-links { min-height: 56px; }
  .site-links { order: 2; flex-basis: auto; margin-left: auto; }
}

/* The Canadian flag, standing at text height after the words
   it follows (the standards' flag: 1.05em tall, tucked to the
   text's baseline). */
.flag {
  height: 1.05em;
  width: auto;
  vertical-align: -0.14em;
  margin-left: 0.22em;
}

/* The footer's closing block (Part 3, ruled 2026-11-08):
   the seven-stripe Certifiable Media bar across the footer's
   width, then the three lines — the name and year, the maker
   with the flag, and the motto. It follows the footer's own
   alignment, which here is the left edge. */
.foot-closing { margin-top: 18px; }

.foot-bar {
  display: flex;
  height: 6px;
  border-radius: 99px;
  overflow: hidden;
  margin: 0 0 12px;
}

.foot-bar i { flex: 1; }
.foot-bar i:nth-child(1) { background: #a4bc45; }
.foot-bar i:nth-child(2) { background: #00eeb9; }
.foot-bar i:nth-child(3) { background: #07b9a7; }
.foot-bar i:nth-child(4) { background: #372f8f; }
.foot-bar i:nth-child(5) { background: #eb208c; }
.foot-bar i:nth-child(6) { background: #f9552c; }
.foot-bar i:nth-child(7) { background: #fa8c2e; }

.foot-line { margin: 2px 0; font-size: 15px; }

.foot-line a { color: var(--accent); }

/* Breadcrumbs (Part 7, ruled 2026-11-08): at the top of every
   page Help lists, each link standing at the 44px tap floor. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  margin: 2px 0 0;
  font-size: 15px;
}

.crumbs a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 6px 8px;
  font-weight: 600;
  text-decoration: none;
}

.crumbs .sep { color: var(--muted); padding: 0 1px; }

.crumbs .here {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 6px 8px;
  color: var(--muted);
}

/* Help in its ruled shape (Part 7, ruled 2026-11-08): a
   search bar, box buttons carrying only their word, and the
   questions below as headings that open and close. */
.help-search {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0 14px;
  min-height: 48px;
  margin: 14px 0 16px;
  color: var(--muted);
}

.help-search:focus-within { border-color: var(--accent); }

.help-search svg { flex: none; }

.help-search input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  font: inherit;
  color: var(--ink);
  min-height: 44px;
  outline: none;
}

.help-results { margin: 0 0 16px; }

.help-result {
  display: block;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 0 0 8px;
  text-decoration: none;
  color: var(--ink);
}

.help-result-t { display: block; font-weight: 700; color: var(--ink); }

.help-result-c { display: block; color: var(--muted); font-size: 14.5px; }

.help-none { color: var(--muted); margin: 0 0 8px; }

.help-boxes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 0 0 8px;
}

.static-wrap .help-box {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 76px;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: var(--column-bg);
  font-weight: 700;
  font-size: 17px;
  color: var(--ink);
  text-decoration: none;
  text-align: center;
  padding: 12px;
}

@media (max-width: 520px) {
  .help-boxes { gap: 8px; }
  .static-wrap .help-box { min-height: 52px; padding: 8px; }
}

.faq-title { margin-top: 30px; }

details.faq-item { border-top: 1px solid var(--line); }

details.faq-item:last-of-type { border-bottom: 1px solid var(--line); }

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
  padding: 10px 2px;
  cursor: pointer;
  font-weight: 700;
  font-size: 17px;
  list-style: none;
}

.faq-item summary::-webkit-details-marker { display: none; }

.faq-item summary svg {
  flex: none;
  color: var(--accent);
  transition: transform 0.18s ease;
}

.faq-item[open ] summary svg { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .faq-item summary svg { transition: none; }
}

.faq-a { padding: 0 2px 14px; }

.faq-a p { margin: 0 0 6px; }

/* Learn: the two kinds of guide, and the topics under them. */
.learn-kind-note { color: var(--muted); margin: -2px 0 12px; }

.help-topic h3 { margin: 0 0 2px; font-size: 20px; }

/* About: the name and tagline up front, then the maker block
   and the properties in the standard's three groups. */
.about-name {
  font-family: var(--font-display, var(--font));
  font-weight: 700;
  font-size: 24px;
  margin: 16px 0 2px;
}

.about-tagline { font-size: 18px; color: var(--muted); margin: 0 0 14px; }

.props-group { margin-top: 18px; }

.props-group h3 {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 4px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}

.prop-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: baseline;
  padding: 7px 0;
  margin: 0;
  border-bottom: 1px solid var(--line);
}

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

.prop-row a { font-weight: 700; white-space: nowrap; }

.prop-row span { color: var(--muted); }

.prop-row span::before { content: "— "; color: var(--muted); }
