/* Listam Desktop — Kinetic Minimalist v2 tokens
   (design-guide/proposals/2026-06-kinetic-minimalist-v2.md).
   Semantic surfaces/inks/signals with paired light & dark palettes; acid
   (--signal) is theme-constant and only ever sits on --ink-block or --on-signal
   pairs — never as text on light surfaces. No runtime font/CDN fetches: Geist
   and JetBrains Mono fall back to the closest system stacks. */

:root {
    color-scheme: light;

    --surface-0: #fbf9f9;
    --surface-1: #f5f3f3;
    --surface-2: #efeded;
    --surface-3: #e3e2e2;
    --card: #ffffff;

    --ink: #1b1c1c;
    --ink-mute: #5d5f5f;
    --ink-faint: #7e7576;
    --line: #cfc4c5;

    /* Theme-constant: the only fill acid text may sit on. Does NOT invert. */
    --ink-block: #1b1c1c;

    --inverse-surface: #303031;
    --inverse-ink: #f2f0f0;

    --signal: #c3f400;
    --on-signal: #161e00;
    --danger: #ba1a1a;
    --on-danger: #ffffff;
    --danger-container: #ffdad6;
    --on-danger-container: #93000a;
    /* Danger accent that reads on the inverse surface. The inverse surface is
       dark in the light theme (needs a bright red) and light in the dark theme
       (needs a deep red), so this token is the opposite polarity of --danger. */
    --danger-on-inverse: #ffb4ab;
    /* Unfinished work. The same amber the board already spends on "blocked" and
       on the delay rating, so "not settled yet" reads as one idea across the
       app. Theme-constant: it sits on --card in both themes and stays legible. */
    --draft: #ef9f27;
    --draft-tint: #fdf1de;

    /* Legacy aliases (inline styles in ui.mjs) */
    --secondary: var(--ink-mute);
    --outline: var(--ink-faint);

    --font-sans: 'Geist', 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

    --r-field: 0.25rem;
    --r-row: 0.5rem;
    --r-dialog: 0.75rem;
    --r-pill: 999px;

    --sidebar-width: 240px;
    --gutter-base: 2rem;
    --section-gap: 4rem;
    --element-gap: 1rem;
    /* Readable measure for the content column. The column is CENTRED in the
       canvas left of the sidebar (see .shell/.main) and this cap widens in two
       steps on large windows, so a stretched window gains room instead of
       growing a dead right margin. Panes that are structurally wide (board,
       planner, card grids) get the extra width for free. */
    --content-max: 64rem;
    /* Frameless Pear chrome: the drag strip the traffic lights sit in. Both the
       sidebar and the content column must clear it even when --section-gap
       tightens on small windows. */
    --titlebar-h: 44px;

    --t-fast: 120ms;
    --t-base: 200ms;
    --ease: cubic-bezier(0.2, 0, 0, 1);
}

[data-theme='dark'] {
    color-scheme: dark;

    --surface-0: #101212;
    --surface-1: #161919;
    --surface-2: #1d2121;
    --surface-3: #252a2a;
    --card: #181b1b;

    --ink: #f1efef;
    --ink-mute: #b0abab;
    --ink-faint: #8a8485;
    --line: #34393a;

    --ink-block: #000000;

    --inverse-surface: #e7e4e4;
    --inverse-ink: #1b1c1c;

    --danger: #ffb4ab;
    --on-danger: #690005;
    --danger-container: #93000a;
    --on-danger-container: #ffdad6;
    --danger-on-inverse: #ba1a1a;
    --draft-tint: #3a2c14;
}

/* Pre-JS fallback: follow the OS until main.mjs sets an explicit data-theme. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) {
        color-scheme: dark;
        --surface-0: #101212;
        --surface-1: #161919;
        --surface-2: #1d2121;
        --surface-3: #252a2a;
        --card: #181b1b;
        --ink: #f1efef;
        --ink-mute: #b0abab;
        --ink-faint: #8a8485;
        --line: #34393a;
        --ink-block: #000000;
        --inverse-surface: #e7e4e4;
        --inverse-ink: #1b1c1c;
        --danger: #ffb4ab;
        --on-danger: #690005;
        --danger-container: #93000a;
        --on-danger-container: #ffdad6;
        --danger-on-inverse: #ba1a1a;
        --draft-tint: #3a2c14;
    }
}

* { box-sizing: border-box; }

/* Floors: scrollbars stay visible (thin, tonal) instead of hidden. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--surface-3);
    border-radius: var(--r-pill);
    border: 3px solid transparent;
    background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--ink-faint); }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    background: var(--surface-0);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: 16px;
    line-height: 24px;
    -webkit-font-smoothing: antialiased;
    transition: background-color var(--t-base) var(--ease), color var(--t-base) var(--ease);
}

button {
    font: inherit;
    border: 0;
    cursor: pointer;
    background: none;
    color: inherit;
}

/* Typography roles */
.title-lg {
    font-family: var(--font-sans);
    font-size: 20px;
    font-weight: 600;
    line-height: 28px;
    letter-spacing: 0;
}
.headline-sm {
    font-family: var(--font-sans);
    font-size: 14px;
    font-weight: 600;
    line-height: 20px;
    letter-spacing: 0.05em;
}
.body-md { font-family: var(--font-sans); font-size: 16px; font-weight: 400; line-height: 24px; }
.label-md { font-family: var(--font-mono); font-size: 12px; font-weight: 500; line-height: 16px; }
.label-sm { font-family: var(--font-mono); font-size: 11px; font-weight: 500; line-height: 16px; }

/* Window chrome: Pear windows are frameless. The titlebar strip is the drag
   region; <pear-ctrl> inside it re-enables and anchors the macOS traffic
   lights (the runtime hides them until the element appears). In the browser
   preview pear-ctrl is an unknown element and the strip is inert. */
.titlebar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 44px;
    z-index: 40;
    -webkit-app-region: drag;
    user-select: none;
    display: flex;
    align-items: center;
    padding: 0 1rem;
}
.titlebar pear-ctrl {
    display: block;
    width: 72px;
    height: 20px;
    -webkit-app-region: no-drag;
}

/* Shell: fixed sidebar + content canvas.
   The sidebar is position:fixed, so .main is the shell's ONLY in-flow child —
   justify-content:center therefore centres the content column (its 240px left
   margin included in the margin box, which lands the column dead-centre of the
   canvas right of the rail). When the window is too narrow for the cap there is
   no free space to distribute and the column simply fills, so this never
   pushes content under the sidebar. */
.shell { display: flex; min-height: 100vh; justify-content: center; }

.sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--sidebar-width);
    background: var(--surface-1);
    display: flex;
    flex-direction: column;
    padding: max(var(--section-gap), calc(var(--titlebar-h) + 0.75rem)) 0 var(--section-gap);
    z-index: 20;
    /* Short windows: the rail scrolls rather than clipping its bottom nav. */
    overflow-y: auto;
    overscroll-behavior: contain;
    transition: background-color var(--t-base) var(--ease);
}

/* Brand row: wordmark only — the canonical icon is the OS app icon, not UI. */
.brand {
    display: flex;
    align-items: center;
    padding: 0 var(--gutter-base) 1.5rem;
    user-select: none;
}
.brand-wordmark {
    font-family: var(--font-sans);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--ink);
}

.ti-svg { display: block; flex-shrink: 0; }

.sidebar-bottom { margin-top: auto; display: flex; flex-direction: column; }

.nav-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.75rem var(--gutter-base);
    color: var(--ink-mute);
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    text-align: left;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.nav-item:hover { background: var(--surface-2); color: var(--ink); }
/* Contrast rule: acid text only on the theme-constant ink block. */
.nav-item.active {
    background: var(--ink-block);
    color: var(--signal);
    font-weight: 700;
}
.nav-item .badge {
    margin-left: auto;
    background: var(--signal);
    color: var(--on-signal);
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 14px;
    padding: 0.125rem 0.5rem;
    border-radius: var(--r-pill);
}
.nav-item .badge.zero { background: var(--surface-3); color: var(--ink-mute); }
.nav-item .nav-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.nav-item.soon { opacity: 0.5; cursor: default; }
.nav-item.soon:hover { background: transparent; color: var(--ink-mute); }
/* Marks a list that lives in its own SHARED base (co-edited with others). */
.nav-item .nav-shared {
    display: inline-flex;
    align-items: center;
    color: var(--ink-mute);
    flex: none;
}
.nav-item.active .nav-shared { color: inherit; }
.soon-chip { text-transform: lowercase; }
/* Dynamic list rail: group headers separate runs of list rows; the implicit
   Ungrouped group only prints a header when named groups sit above it. */
.rail-group-header {
    padding: 0.875rem 0 0.25rem 0.375rem;
    color: var(--ink-mute);
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.7;
}
.rail-empty { padding: 0.75rem var(--gutter-base); color: var(--ink-mute); }
/* List/group rows wrap their button so the hover "⋮" can overlay the right
   edge without nesting inside the (interactive) row button. */
.rail-row { position: relative; display: flex; align-items: stretch; }
.rail-row .nav-item { flex: 1; min-width: 0; }
.rail-group-row { align-items: center; }
.rail-group-row .rail-group-header { flex: 1; cursor: default; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Collapse/expand affordance: a chevron that holds the left gutter so the group
   name still lines up roughly with the list-row labels below it. */
.rail-group-toggle {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    padding: 0.5rem 0 0.5rem var(--gutter-base);
    color: var(--ink-faint);
    transition: color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.rail-group-toggle:hover { color: var(--ink); }
.rail-group-toggle .ti-svg { transition: transform var(--t-fast) var(--ease); }
.rail-group-row.collapsed .rail-group-toggle .ti-svg { transform: rotate(-90deg); }
/* Rolled-up badge shown on a collapsed group: sum of its surfaces' counts. The
   hover "⋮" overlays the right edge, so fade the badge then (as list rows do). */
.rail-group-badge { margin-left: auto; margin-right: 0.5rem; flex-shrink: 0; }
.rail-group-row:hover .rail-group-badge { opacity: 0; }
.rail-row-menu-btn {
    position: absolute;
    right: 0.5rem;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    padding: 0.25rem;
    border-radius: var(--r-sm, 4px);
    color: var(--ink-mute);
    opacity: 0;
    transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.rail-row:hover .rail-row-menu-btn,
.rail-row-menu-btn:focus-visible { opacity: 1; }
.rail-row-menu-btn:hover { color: var(--ink); background: var(--surface-3); }
/* The menu sits over the badge slot; fade the badge on hover so they don't collide. */
.rail-row:hover .nav-item .badge { opacity: 0; }
.rail-rename-input {
    flex: 1;
    min-width: 0;
    background: var(--surface-1);
    border: 1px solid var(--signal);
    border-radius: var(--r-sm, 4px);
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 12px;
    padding: 0.25rem 0.4rem;
}
.rail-new-btn {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.625rem var(--gutter-base);
    margin-top: 0.25rem;
    color: var(--ink-mute);
    font-family: var(--font-mono);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.rail-new-btn:hover { color: var(--ink); background: var(--surface-2); }
/* Rail drag-and-drop: drag a list onto another group to move it there. */
.rail-row[draggable="true"] { cursor: grab; }
.rail-row.dragging { opacity: 0.4; }
.rail-group { border-radius: var(--r-row, 6px); transition: box-shadow var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease); }
.rail-group.rail-drop { box-shadow: inset 0 0 0 2px var(--signal); background: var(--surface-2); }
.badge.pop { animation: badge-pop 360ms var(--ease); }
@keyframes badge-pop {
    0% { transform: scale(1); }
    45% { transform: scale(1.3); }
    100% { transform: scale(1); }
}

/* Settings dialog choice rows */
.choice-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1.5rem; }

.main {
    margin-left: var(--sidebar-width);
    flex: 1;
    /* Without this a flex item refuses to shrink below its content's intrinsic
       width — the reason narrow windows used to overflow instead of reflow. */
    min-width: 0;
    min-height: 100vh;
    padding: max(var(--section-gap), calc(var(--titlebar-h) + 0.75rem)) var(--gutter-base) var(--section-gap);
    max-width: calc(var(--content-max) + 2 * var(--gutter-base));
}
.shell.has-hints .main { padding-bottom: calc(var(--section-gap) + 2.5rem); }

.pane-enter { animation: pane-enter var(--t-base) var(--ease) backwards; }
@keyframes pane-enter {
    from { opacity: 0; transform: translateY(6px); }
}

/* Header action bar */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 3rem;
}
.page-header > *:first-child { min-width: 0; }
.page-title { color: var(--ink); margin: 0; overflow-wrap: anywhere; }
.header-actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }

.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    border-radius: var(--r-row);
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 500;
    transition: opacity var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.btn:active { transform: scale(0.97); }
.btn-icon { padding: 0.5rem 0.625rem; }
.btn-primary { background: var(--ink); color: var(--surface-0); padding: 0.5rem 1.5rem; }
.btn-primary:hover { opacity: 0.9; }
.btn-secondary { background: var(--surface-2); color: var(--ink); }
.btn-secondary:hover { background: var(--surface-3); }
.btn-critical { background: var(--signal); color: var(--on-signal); }
.btn-critical:hover { opacity: 0.88; }
.btn-danger { background: var(--danger); color: var(--on-danger); }
.btn:focus-visible, .nav-item:focus-visible, .footer-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.rail-group-toggle:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* Inputs: grey fill, ink left-edge accent on focus (design-guide rule) */
.input {
    width: 100%;
    border: 0;
    border-left: 2px solid transparent;
    background: var(--surface-1);
    border-radius: var(--r-field);
    padding: 0.75rem 1rem;
    font: inherit;
    color: var(--ink);
    transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.input::placeholder { color: var(--ink-faint); }
.input:focus { outline: none; border-left-color: var(--ink); background: var(--surface-2); }
.input.shake { animation: input-shake 280ms var(--ease); }
@keyframes input-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    55% { transform: translateX(4px); }
    80% { transform: translateX(-2px); }
}

.add-bar { display: flex; gap: 1rem; margin-bottom: 3rem; align-items: center; }
.add-bar .input { flex: 1; }
.add-hint { color: var(--ink-faint); white-space: nowrap; }

/* Summary bar — beacon: hollow ring when local-only, solid acid when live */
.summary-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
    margin-bottom: 2rem;
    color: var(--ink-mute);
}
.summary-bar .dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--r-pill);
    background: transparent;
    border: 2px solid var(--ink-faint);
    flex-shrink: 0;
}
.summary-bar .dot.live {
    background: var(--signal);
    border: 0;
    animation: dot-breathe 2.6s ease-in-out infinite;
}
/* Syncing: half-filled acid disc, slowly rotating. Shape carries the state. */
.summary-bar .dot.syncing {
    border: 1.5px solid var(--signal);
    background: conic-gradient(var(--signal) 0 180deg, transparent 180deg 360deg);
    animation: dot-spin 1.6s linear infinite;
}
/* Attention: a danger SQUARE — the one non-acid signal, reserved for trouble. */
.summary-bar .dot.attention {
    background: var(--danger);
    border: 0;
    border-radius: 2px;
}
@keyframes dot-breathe {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--signal) 45%, transparent); }
    60% { box-shadow: 0 0 0 6px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
}
@keyframes dot-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .summary-bar .dot.live, .summary-bar .dot.syncing { animation: none; }
}
/* The beacon is a real button (≥24px hit target) opening the devices popover. */
.beacon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; margin: -8px 0; border-radius: var(--r-pill);
    cursor: pointer; background: transparent;
}
.beacon-btn:hover { background: var(--surface-2); }

/* List view: no lines, tonal rows, done recedes (history is quiet) */
.category-section { margin-bottom: var(--section-gap); }
.category-heading {
    color: var(--ink-mute);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.2em;
    margin: 0 0 1.5rem;
    padding: 0 1rem;
}
.item-rows { display: flex; flex-direction: column; }

.item-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    margin-bottom: var(--element-gap);
    background: var(--card);
    border-radius: var(--r-row);
    cursor: pointer;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
    text-align: left;
    width: 100%;
}
.item-row:hover { background: var(--surface-2); }
.item-row:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.item-row .glyph {
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--ink-mute);
    transition: transform var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.item-row .item-toggle { border-radius: var(--r-field); }
.item-row .item-toggle:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.item-row:hover .glyph { transform: scale(1.15); }
.item-row.done { background: var(--surface-2); color: var(--ink-faint); }
.item-row.done:hover { background: var(--surface-3); }
.item-row.done .item-text { text-decoration: line-through; text-decoration-thickness: 1px; }
.item-row.done .glyph { opacity: 0.45; }
.item-row .item-meta { color: var(--ink-mute); }
.item-row.done .item-meta { color: var(--ink-faint); }
.item-row .row-delete { opacity: 0; color: inherit; padding: 0 0.25rem; display: inline-flex; align-items: center; transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease); }
.item-row:hover .row-delete, .item-row:focus-within .row-delete { opacity: 0.7; }
.item-row .row-delete:hover { opacity: 1; transform: scale(1.15); }

/* Manual reordering (drag-and-drop / Alt+↑↓): grab cursor, a faded ghost while
   dragging, and a drop line marking whether the item will land before (above /
   left) or after (below / right) the row under the cursor. */
.item-row[draggable="true"], .grid-card[draggable="true"] { cursor: grab; }
.item-row.dragging, .grid-card.dragging { opacity: 0.4; }
.item-row.drop-before, .ticket-card.drop-before { box-shadow: inset 0 2px 0 0 var(--signal); }
.item-row.drop-after, .ticket-card.drop-after { box-shadow: inset 0 -2px 0 0 var(--signal); }
.grid-card.drop-before { box-shadow: inset 2px 0 0 0 var(--signal); }
.grid-card.drop-after { box-shadow: inset -2px 0 0 0 var(--signal); }

/* Row choreography: enter, remote arrival, completion reward, exit */
.row-enter { animation: row-enter var(--t-base) var(--ease) backwards; }
@keyframes row-enter {
    from { opacity: 0; transform: translateY(8px) scale(0.99); }
}
.row-remote {
    animation: row-enter var(--t-base) var(--ease) backwards,
        row-remote-pulse 900ms var(--ease);
}
@keyframes row-remote-pulse {
    0% { box-shadow: inset 3px 0 0 var(--signal); background-color: color-mix(in srgb, var(--signal) 16%, var(--card)); }
    70% { box-shadow: inset 3px 0 0 var(--signal); }
    100% { box-shadow: inset 3px 0 0 transparent; }
}
.row-flash { animation: row-complete 480ms var(--ease); }
@keyframes row-complete {
    0% { background-color: var(--signal); color: var(--on-signal); }
    100% { }
}
.row-flash .glyph { animation: glyph-pop 420ms var(--ease); }
@keyframes glyph-pop {
    0% { transform: scale(1); }
    40% { transform: scale(1.4) rotate(-6deg); }
    100% { transform: scale(1); }
}
.row-unflash { animation: row-uncomplete 320ms var(--ease); }
@keyframes row-uncomplete {
    0% { background-color: var(--surface-3); }
    100% { }
}
.row-exit {
    opacity: 0 !important;
    transform: translateX(14px) scale(0.98) !important;
    height: 0 !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    margin-bottom: 0 !important;
    overflow: hidden;
    pointer-events: none;
    transition: all 150ms var(--ease) !important;
}

/* Grid view: denser desktop layout */
.grid-cards {
    display: grid;
    /* min(…, 100%) keeps the track from exceeding the column on very narrow
       windows, where a bare 180px floor would force a horizontal overflow. */
    grid-template-columns: repeat(auto-fill, minmax(min(180px, 100%), 1fr));
    gap: var(--element-gap);
}
@media (min-width: 1600px) { .grid-cards { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } }
.grid-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 96px;
    padding: 1.25rem 0.75rem;
    background: var(--card);
    border-radius: var(--r-row);
    cursor: pointer;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
    text-align: center;
}
.grid-card:hover { background: var(--surface-2); }
.grid-card:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.grid-card .glyph { color: var(--ink-mute); transition: transform var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease); }
.grid-card:hover .glyph { transform: scale(1.12); }
.grid-card.done { background: var(--surface-2); color: var(--ink-faint); }
.grid-card.done .item-text { text-decoration: line-through; text-decoration-thickness: 1px; }
.grid-card.done .glyph { opacity: 0.45; }
.grid-card.row-exit { height: auto !important; }

/* Empty state */
.empty-state { color: var(--ink-mute); padding: 2rem 1rem; animation: pane-enter var(--t-base) var(--ease) backwards; }
.empty-state h2 { font-family: var(--font-sans); font-size: 18px; margin: 0 0 0.5rem; color: var(--ink); }
.empty-state p { margin: 0.25rem 0; }

/* Peers / diagnostics panes */
.pane-section { margin-bottom: var(--section-gap); }
.kv-rows { display: flex; flex-direction: column; gap: var(--element-gap); }
.kv-row {
    display: grid;
    /* The key track gives ground before the value does, then the pair stacks. */
    grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
    gap: 0 1rem;
    align-items: center;
    padding: 0.75rem 1rem;
    background: var(--card);
    border-radius: var(--r-row);
}
@media (max-width: 640px) {
    .kv-row { grid-template-columns: minmax(0, 1fr); gap: 0.2rem; }
}
.kv-row .k { color: var(--ink-mute); }
.kv-row .v { overflow-wrap: anywhere; }
.member-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto auto;
    gap: 1rem;
    align-items: center;
    padding: 0.75rem 1rem;
    background: var(--card);
    border-radius: var(--r-row);
}
/* Narrow: the trailing chips (role, self-id, actions) drop under the identity
   instead of squeezing the name to a couple of characters. */
@media (max-width: 720px) {
    .member-row, .device-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
    /* 60% basis, not 100%: the identity still shares its line with the leading
       presence dot, and only the trailing chips wrap to a second line. */
    .member-row .who, .device-main { flex: 1 1 60%; }
    .self-id { margin-left: 0; }
}
.member-row .who { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.member-row .who-name { font-family: var(--font-sans); font-size: 14px; color: var(--ink); overflow-wrap: anywhere; }
.fingerprint-chip {
    display: inline-flex; align-items: center; gap: 0.35rem; align-self: flex-start;
    font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint);
    background: var(--surface-2); border-radius: var(--r-field);
    padding: 0.1rem 0.45rem; cursor: pointer; max-width: 100%;
}
.fingerprint-chip:hover { color: var(--ink-mute); background: var(--surface-3); }
.fingerprint-chip svg { flex-shrink: 0; }
.self-id { margin-left: auto; display: inline-flex; align-items: center; gap: 0.5rem; color: var(--ink-faint); }
.role-chip {
    font-family: var(--font-mono);
    font-size: 11px;
    text-transform: uppercase;
    padding: 0.125rem 0.5rem;
    border-radius: var(--r-pill);
    background: var(--surface-3);
    color: var(--ink-mute);
}
.role-chip.owner { background: var(--signal); color: var(--on-signal); }
.device-rows { display: flex; flex-direction: column; gap: 0.5rem; }
.device-row { display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto auto; gap: 0.75rem; align-items: center; padding: 0.5rem 0.25rem; }
.device-main { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.device-name { font-family: var(--font-sans); font-size: 14px; color: var(--ink); overflow-wrap: anywhere; }

/* Per-peer presence: a shape-encoded status dot (breathing acid = online, hollow
   ring = offline), a mono meta sub-line, and a right-aligned live status. The acid
   dot is the colour cue (--signal is theme-constant and illegible as text), so the
   status text uses primary vs faint ink rather than colour. */
.presence-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.presence-dot.live { background: var(--signal); box-shadow: 0 0 0 3px rgba(195, 244, 0, 0.22); animation: presence-breathe 2.6s ease-in-out infinite; }
.presence-dot.off { background: transparent; border: 1.5px solid var(--ink-faint); box-sizing: border-box; }
.presence-meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint); overflow-wrap: anywhere; }
.presence-status { font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint); white-space: nowrap; }
.presence-status.online { color: var(--ink); }
@keyframes presence-breathe { 0%, 100% { box-shadow: 0 0 0 3px rgba(195, 244, 0, 0.22); } 50% { box-shadow: 0 0 0 5px rgba(195, 244, 0, 0); } }

.event-log { display: flex; flex-direction: column; gap: 0.25rem; }
.event-line {
    display: grid;
    grid-template-columns: 90px minmax(0, 1fr);
    gap: 1rem;
    padding: 0.375rem 1rem;
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 16px;
    color: var(--ink-mute);
    border-radius: var(--r-field);
}
.event-line:nth-child(odd) { background: var(--surface-1); }
.event-line .ts { color: var(--ink-faint); }
/* Transport field names are longer than the log's timestamps; give the key
   track the room it needs so `relayConfigured` doesn't wrap under itself. */
.net-diagnostics .event-line { grid-template-columns: 150px minmax(0, 1fr); }

/* Muted helper text and a heading+action row, both inset to the 1rem content
   edge used by section headings and card text. */
.pane-note { color: var(--secondary); padding: 0 1rem; margin: 0; }
.pane-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding: 0 1rem;
    margin-bottom: 1.5rem;
}
.pane-section-head .category-heading { margin: 0; padding: 0; }

/* Peers & Devices pane: line the page title and connection summary up with the
   section headings + card text (a 1rem inset), and tighten the gap between the
   pane's several sections so the page doesn't sprawl. */
.main[data-view="peers"] .page-header { padding: 0 1rem; margin-bottom: 2rem; }
.main[data-view="peers"] .summary-bar { padding: 0 1rem; }
.main[data-view="peers"] .pane-section { margin-bottom: 2.5rem; }

/* Analytics pane: same insets as Peers & Devices, which it sits under in the
   sidebar. The congruency cards/legend already carry their own 1rem; only the
   title and the subtitle prose (shared with no pane of its own) need it here. */
.main[data-view="analytics"] .page-header { padding: 0 1rem; margin-bottom: 2rem; }
.main[data-view="analytics"] .analytics-note { padding: 0 1rem; }

/* Servers section (inside Peers & Devices): full-bleed cards on the same --card
   fill as the member rows above them (card edge at the pane margin, text at
   1rem); nested stat rows take the subtler --surface-1 tint for an inner lift. */
.server-cards { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1.5rem; }
.server-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1rem;
    background: var(--card);
    border-radius: var(--r-row);
}
.server-card-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.server-card-head .dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--r-pill);
    background: transparent;
    border: 2px solid var(--ink-faint);
    flex-shrink: 0;
}
.server-card-head .dot.live { background: var(--signal); border: 0; }
.server-name { font-weight: 600; }
.server-state { color: var(--secondary); margin-left: auto; }
.server-caps { color: var(--outline); }
.server-card .kv-row { background: var(--surface-1); padding: 0.5rem 0.75rem; }
.server-actions { flex-wrap: wrap; gap: 0.5rem; margin: 0; }
.server-pair .add-bar { padding: 0 1rem; margin-bottom: 0; }

/* Analytics block in the Settings dialog (Congruency + Activity renderers).
   The dialog body already pads, so drop the pane-level 1rem insets here. */
.analytics-subhead { color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.16em; margin: 1.5rem 0 0.75rem; }
.analytics-section .analytics-subhead:first-child { margin-top: 0; }
.analytics-note { color: var(--secondary); margin: 0 0 1rem; }
.analytics-section .congruency-cards,
.analytics-section .congruency-legend { padding: 0; }

/* Dialogs */
.dialog-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 50;
}
.dialog-backdrop.enter {
    animation: backdrop-in 150ms ease-out backwards;
}
@keyframes backdrop-in { from { opacity: 0; } }
.dialog {
    width: min(560px, calc(100vw - 4rem));
    /* Cap to the viewport and lay out as a column so a tall dialog (e.g.
       Settings) scrolls its body instead of overflowing the screen with the
       actions pushed out of reach. */
    max-height: calc(100vh - 4rem);
    display: flex;
    flex-direction: column;
    background: var(--surface-0);
    border-radius: var(--r-dialog);
    padding: 2rem;
}
.dialog-backdrop.enter > .dialog {
    animation: dialog-in var(--t-base) var(--ease) backwards;
}
@keyframes dialog-in {
    from { opacity: 0; transform: translateY(10px) scale(0.98); }
}
.dialog h2 { margin: 0 0 0.5rem; }
/* The scrollable region: min-height:0 lets it shrink below content height so
   overflow-y actually engages; title + actions stay pinned outside it. */
.dialog .dialog-body { color: var(--ink-mute); margin-bottom: 1.5rem; overflow-y: auto; min-height: 0; flex: 1 1 auto; }
.dialog .dialog-actions { display: flex; justify-content: flex-end; gap: 0.75rem; }
/* Trust register: irreversible surfaces feel like a mode (inverse surface +
   danger square marker). One variant, reused per destructive dialog kind. */
.trust-dialog { background: var(--inverse-surface); }
.trust-dialog h2 { color: var(--inverse-ink); }
.trust-dialog .dialog-body { color: var(--inverse-ink); }
.trust-marker {
    display: flex; align-items: center; gap: 0.5rem;
    text-transform: uppercase; letter-spacing: 0.12em;
    color: var(--danger-on-inverse); margin-bottom: 0.75rem;
}
.trust-square { width: 9px; height: 9px; flex-shrink: 0; background: var(--danger-on-inverse); border-radius: var(--r-field); }
.trust-reassure { display: flex; align-items: center; gap: 0.45rem; margin: 0.75rem 0 0; color: var(--inverse-ink); }
.trust-reassure svg { color: var(--signal); flex-shrink: 0; }
.trust-dialog .btn-secondary {
    background: transparent; color: var(--inverse-ink);
    border: 1px solid color-mix(in srgb, var(--inverse-ink) 25%, transparent);
}

/* ⌘K command palette. */
.command-palette { width: min(560px, calc(100vw - 4rem)); padding: 0; overflow: hidden; background: var(--card); }
.palette-search { display: flex; align-items: center; gap: 0.6rem; padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--line); }
.palette-search > svg { color: var(--ink-faint); flex-shrink: 0; }
.palette-input { flex: 1; min-width: 0; border: none; background: transparent; font-size: 16px; color: var(--ink); font-family: var(--font-sans); }
.palette-input:focus { outline: none; }
.palette-results { max-height: 50vh; overflow-y: auto; padding: 0.4rem; }
.palette-group { padding: 0.5rem 0.6rem 0.2rem; color: var(--ink-faint); }
.palette-row { display: flex; align-items: center; gap: 0.7rem; padding: 0.5rem 0.6rem; border-radius: var(--r-row); cursor: pointer; }
.palette-row > svg { color: var(--ink-mute); flex-shrink: 0; }
.palette-label { flex: 1; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.palette-tag { color: var(--ink-faint); flex-shrink: 0; }
.palette-row.selected { background: var(--ink-block); }
.palette-row.selected > svg, .palette-row.selected .palette-label, .palette-row.selected .palette-tag { color: var(--signal); }
.palette-empty { padding: 1.5rem 0.6rem; color: var(--ink-faint); text-align: center; }
.palette-footer { padding: 0.6rem 1.1rem; border-top: 1px solid var(--line); color: var(--ink-faint); font-family: var(--font-mono); }
.invite-code {
    font-family: var(--font-mono);
    font-size: 12px;
    background: var(--surface-1);
    padding: 1rem;
    border-radius: var(--r-field);
    overflow-wrap: anywhere;
    user-select: all;
}
.invite-share-layout {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 1rem;
}
.invite-share-copy {
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
}
/* QR contrast is machine-readable data, so its white field stays constant in
   both themes and square; rounding/clipping would eat into the quiet zone. */
.invite-qr-frame {
    display: flex;
    align-items: center;
    justify-content: center;
    width: fit-content;
    max-width: 100%;
    background: #ffffff;
}
.invite-qr {
    display: block;
    width: auto;
    max-width: 100%;
    height: auto;
}
.dialog .invite-share-layout { margin-top: 0.25rem; }
.dialog .invite-share-layout .invite-code { align-self: stretch; }
/* The invite's terms sit UNDER the code, not beside it: "works once" and the
   countdown are what the sharer has to read before handing the code over, and a
   sidelong caption reads as decoration. */
.invite-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 0.9rem;
    color: var(--secondary);
    margin-top: -0.5rem;
}
.invite-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.invite-dialog { width: min(42rem, calc(100vw - 2rem)); }
.invite-panel { display: grid; gap: 1rem; min-width: 0; }
.invite-panel p { margin: 0; }
.invite-controls { display: flex; flex-wrap: wrap; align-items: end; gap: 0.75rem; }
.invite-field { display: grid; gap: 0.5rem; flex: 1 1 9rem; min-width: 0; }
.invite-panel-hint { color: var(--ink-mute); }
.invite-panel-error { color: var(--danger); }
.invite-code-list { display: grid; gap: 1rem; }
.invite-code-row { display: grid; gap: 0.75rem; padding: 1rem; background: var(--surface-1); border-radius: var(--r-row); min-width: 0; }
.invite-code-heading { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; align-items: baseline; justify-content: space-between; }
.invite-code-heading h3 { margin: 0; }
.invite-code-heading span { color: var(--ink-mute); }
.invite-code-row .invite-code { padding: 0; background: transparent; font-size: 0.75rem; min-width: 0; overflow-wrap: anywhere; }
.invite-code-row .invite-qr-frame { justify-self: start; }
.invite-share-layout.is-expired { grid-template-columns: minmax(0, 1fr); }
/* Live-code count next to the section heading. Only rendered above 1, where it
   is the only sign that the code handed to the previous person still works.
   The pill is styled here rather than reused from .nav-item .badge, which is
   scoped to the rail and carries its own margin-left:auto. */
.category-heading.with-badge { display: flex; align-items: center; gap: 0.5rem; }
.category-heading.with-badge .badge {
    background: var(--signal);
    color: var(--on-signal);
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 14px;
    letter-spacing: 0;
    padding: 0.125rem 0.5rem;
    border-radius: var(--r-pill);
}
.dialog .warning { color: var(--on-danger-container); background: var(--danger-container); padding: 0.75rem 1rem; border-radius: var(--r-field); }
.shortcut-rows { display: flex; flex-direction: column; gap: 0.5rem; }
.shortcut-rows .kv-row { grid-template-columns: 120px 1fr; padding: 0.5rem 1rem; }
kbd {
    font-family: var(--font-mono);
    font-size: 11px;
    background: var(--surface-2);
    border-radius: var(--r-field);
    padding: 0.125rem 0.5rem;
}

/* Notices (snackbar parity) */
.notices {
    position: fixed;
    bottom: 1.5rem;
    left: calc(var(--sidebar-width) + var(--gutter-base));
    max-width: calc(100vw - var(--sidebar-width) - 2 * var(--gutter-base));
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
    z-index: 60;
}
.shell.has-hints ~ .notices { bottom: 3.75rem; }
.notice {
    background: var(--inverse-surface);
    color: var(--inverse-ink);
    padding: 0.625rem 1rem;
    border-radius: var(--r-row);
    font-family: var(--font-mono);
    font-size: 12px;
    max-width: 28rem;
    animation: notice-in var(--t-base) var(--ease) backwards;
    transition: opacity 180ms var(--ease), transform 180ms var(--ease);
}
@keyframes notice-in {
    from { opacity: 0; transform: translateY(10px); }
}
.notice.leaving { opacity: 0; transform: translateY(8px); }
.notice.success { background: var(--signal); color: var(--on-signal); }
.notice.error { background: var(--danger); color: var(--on-danger); }
.notice.update-ready {
    display: flex;
    align-items: center;
    gap: 1rem;
    background: var(--signal);
    color: var(--on-signal);
}
.notice-action {
    margin-left: auto;
    padding: 0.35rem 0.65rem;
    border-radius: var(--r-field);
    background: var(--ink);
    color: var(--card);
    font: inherit;
    cursor: pointer;
}
.notice-action:hover { background: var(--inverse-surface); color: var(--inverse-ink); }
.notice-action:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.notice-action:disabled { cursor: wait; opacity: 0.65; }

/* Shortcut hint bar: tonal strip, H or its ✕ to hide, sidebar toggle to show */
.key-hints {
    position: fixed;
    bottom: 0;
    left: var(--sidebar-width);
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Each hint is nowrap, so without wrapping the strip simply overflowed the
       window; it now stacks into extra rows and clears its own close button. */
    flex-wrap: wrap;
    gap: 0.35rem 1.5rem;
    padding: 0.5rem 2.5rem;
    background: var(--surface-1);
    color: var(--ink-mute);
    font-family: var(--font-mono);
    font-size: 11px;
    z-index: 30;
    animation: hints-in var(--t-base) var(--ease) backwards;
    transition: opacity 150ms var(--ease), transform 150ms var(--ease);
}
@keyframes hints-in {
    from { opacity: 0; transform: translateY(100%); }
}
.key-hints.leaving { opacity: 0; transform: translateY(100%); }
.key-hints .hint { display: inline-flex; align-items: center; gap: 0.375rem; white-space: nowrap; }
.key-hints kbd { background: var(--surface-2); color: var(--ink-mute); }
.key-hints .hints-close {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    padding: 0.125rem 0.5rem;
    color: var(--ink-faint);
    border-radius: var(--r-field);
    transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.key-hints .hints-close:hover { color: var(--ink); background: var(--surface-2); }
.key-hints .hints-close:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }

/* Joining overlay phases */
.join-overlay { text-align: center; }
.join-overlay .phase { color: var(--ink-mute); margin-top: 0.5rem; animation: phase-pulse 1.8s ease-in-out infinite; }
/* The elapsed counter and hints below it are the OPPOSITE of the pulsing phase
   line: they are read, not glanced at, so they hold still. */
.join-overlay .join-elapsed { color: var(--ink-faint); margin-top: 1.25rem; font-family: var(--font-mono); }
.join-overlay .join-hint {
    color: var(--ink-mute);
    max-width: 46ch;
    margin: 0.75rem auto 0;
}
.join-overlay .join-actions { display: flex; justify-content: center; margin-top: 1.5rem; }
@keyframes phase-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.45; }
}

/* ===== Window-size ladder ==============================================
   The shell keeps ONE structure at every size — a fixed rail plus a centred
   content column — and scales the tokens that drive it. The rail never becomes
   a horizontal strip (it used to below 900px, which reflowed the nav into a
   full-width row and pushed the content column off-screen entirely).

   Going up, the measure widens in two steps so a stretched window earns room.
   Going down, the rail and the vertical rhythm tighten, and the panes that are
   grids reorganise into fewer columns (see the per-pane rules below). */
@media (min-width: 1500px) { :root { --content-max: 74rem; } }
@media (min-width: 1800px) { :root { --content-max: 84rem; } }

@media (max-width: 1180px) {
    :root { --sidebar-width: 208px; --gutter-base: 1.5rem; --section-gap: 3rem; }
}
@media (max-width: 900px) {
    :root { --sidebar-width: 176px; --gutter-base: 1.15rem; --section-gap: 2.25rem; --element-gap: 0.65rem; }
    /* Title and its action cluster stack instead of colliding. */
    .page-header { flex-direction: column; align-items: stretch; gap: 0.85rem; margin-bottom: 2rem; }
    .header-actions { flex-wrap: wrap; gap: 0.5rem; }
    .add-bar { gap: 0.65rem; margin-bottom: 2rem; }
    .add-hint { display: none; }
    .category-heading { margin-bottom: 1rem; }
    .item-row, .kv-row, .member-row { padding: 0.6rem 0.75rem; }
    /* The hint strip wraps to two or three rows here — reserve room for it so
       the last content row is not parked underneath. */
    .key-hints { gap: 0.3rem 0.9rem; }
    .shell.has-hints .main { padding-bottom: calc(var(--section-gap) + 6.5rem); }
    .shell.has-hints ~ .notices { bottom: 7rem; }
    .pane-note, .pane-section-head, .category-heading,
    .main[data-view="peers"] .page-header, .main[data-view="peers"] .summary-bar { padding-inline: 0.75rem; }
}
@media (max-width: 720px) {
    :root { --sidebar-width: 150px; --gutter-base: 0.9rem; }
    .invite-share-layout { grid-template-columns: minmax(0, 1fr); justify-items: start; }
    .nav-item { padding-block: 0.6rem; }
}

/* Motion is feedback, not requirement */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}


/* ===== Board =========================================================== */
.board-grid {
    display: flex;
    gap: var(--element-gap);
    align-items: flex-start;
    overflow-x: auto;
    padding: 0.5rem 0 1rem;
}
.board-column {
    flex: 1 1 260px;
    min-width: 240px;
    max-width: 360px;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    border-radius: var(--r-row);
    padding: 0.5rem 0.5rem 1rem;
    transition: box-shadow var(--t-fast) var(--ease);
}
.board-column.drag-over { box-shadow: inset 0 0 0 2px var(--signal); }
/* The board keeps scrolling horizontally (that IS its design) — narrower
   columns just keep two of them on screen instead of one and a half. */
@media (max-width: 900px) {
    .board-grid { gap: 0.65rem; }
    .board-column { flex: 1 1 200px; min-width: 190px; }
}
.board-col-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.5rem;
    position: sticky;
    top: 0;
}
.board-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.board-col-name { flex: 1; }
.board-col-count { color: var(--ink-faint); font-family: var(--font-mono); }
.board-col-body { display: flex; flex-direction: column; gap: 0.75rem; }
.board-add {
    display: flex; align-items: center; gap: 0.4rem;
    border: none; background: transparent; cursor: pointer;
    color: var(--ink-faint); font-size: 0.8rem; padding: 0.4rem 0.5rem;
    border-radius: var(--r-field);
}
.board-add:hover { background: var(--surface-2); color: var(--ink-mute); }

/* Finished work recedes — the design-guide board screen dims its Done column
   too. Both flags come from the board CONFIG (state.isDone), not from a literal
   id, so a board that renamed its done state still recedes. */
.board-column.done .board-col-head { opacity: 0.6; }
.board-column.done:hover .board-col-head { opacity: 1; }
.ticket-card.done { opacity: 0.55; }
.ticket-card.done:hover,
.ticket-card.done:focus-visible,
.ticket-card.done.selected { opacity: 1; }

/* An empty column keeps its head, its count and its + Add at about half width.
   Five default states at 240px need 1264px and a 1440px window offers 1024, so
   what fell off the right edge was the Done column — while Blocked and Review
   each held a full 240px of nothing. */
.board-column.empty { flex: 0 1 124px; min-width: 124px; }
.board-column.empty .board-col-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 900px) {
    .board-column.empty { flex: 0 1 108px; min-width: 104px; }
}

.ticket-card {
    background: var(--card);
    /* The card edge is a drawn line, not a tonal step: --card sits only 1.08:1
       above the board behind it in the dark theme, so without a border the card
       edges all but vanish. The design-guide board screen draws one too. */
    border: 1px solid var(--line);
    border-radius: var(--r-row);
    padding: 0.625rem 0.75rem;
    display: flex; flex-direction: column; gap: 0.5rem;
    /* A click OPENS the ticket; dragging is the secondary gesture. `grab` here
       advertised the wrong one as primary. */
    cursor: pointer;
    transition: background var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.ticket-card:hover { background: var(--surface-2); }
/* Every other focusable row (.item-row, .grid-card, .note-row) draws this ring;
   the board card was the one that fell back to the browser default. */
.ticket-card:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.ticket-card.dragging { opacity: 0.4; cursor: grabbing; }

/* The card's single accent, so it can only ever mean one thing. The renderer
   picks it in order — unfinished draft, then blocked, then urgency — and
   medium/low get none at all, which is what keeps an ordinary board quiet. */
.ticket-card.edge-urgent { box-shadow: inset 3px 0 0 0 var(--danger); }
.ticket-card.edge-high { box-shadow: inset 3px 0 0 0 var(--ink-faint); }
.ticket-card.edge-blocked { box-shadow: inset 3px 0 0 0 var(--draft); }

/* An unfinished draft: created the moment the panel opened, never committed.
   A solid amber edge rather than a wash, so the card still reads as a card and
   the state is legible at board zoom without tinting the text underneath. */
.ticket-card.draft {
    background: var(--draft-tint);
    border-color: var(--draft);
    box-shadow: inset 3px 0 0 0 var(--draft);
}
.ticket-card.draft:hover { background: var(--draft-tint); box-shadow: inset 3px 0 0 0 var(--draft), inset 0 0 0 1px var(--draft); }
.ticket-card.draft.selected { box-shadow: inset 3px 0 0 0 var(--draft), inset 0 0 0 2px var(--ink); }
.draft-pill {
    font-size: 0.65rem; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.04em;
    line-height: 1;
    padding: 0.2rem 0.45rem; border-radius: var(--r-pill); white-space: nowrap;
    background: var(--draft); color: var(--ink-block);
}
.ticket-title.untitled { color: var(--ink-faint); font-style: italic; }
/* The panel's own "this is already saved" line, and the way out of it. */
.draft-hint { display: flex; align-items: center; gap: 0.35rem; }
.btn.btn-danger-text { color: var(--danger); }
.btn.btn-danger-text:hover { color: var(--danger); background: var(--danger-container); }
.ticket-title { font-size: 0.9rem; font-weight: 500; line-height: 1.3; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.ticket-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
/* One type style for the whole row. --ink-faint on --card is 4.47:1 in the
   light theme — under the AA floor at 11px — and the row previously mixed two
   greys (.value-badge used --ink-mute) and two sizes (11px vs 12.5px). The
   chips now read left to right in a fixed order: the old margin-left:auto on
   the timer dropped it alone at the right of a wrapped second line, so the
   same field sat in a different place on every card. */
.ticket-card-meta {
    display: flex; align-items: center; gap: 0.5rem; justify-content: space-between;
    color: var(--ink-mute);
}
/* Two fixed groups — who on the left, when on the right — and NO wrapping, so
   a field is in the same place on every card. The left group clips rather than
   pushing the right one off; nothing ever lands alone on a second line. */
.ticket-card-meta .meta-left { display: flex; align-items: center; gap: 0.5rem; min-width: 0; overflow: hidden; }
.ticket-card-meta .meta-right { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; flex: none; }
.ticket-card-meta .ticket-avatar { width: 18px; height: 18px; }
/* In the row, priority is a word: the left edge carries the colour, and only
   urgent/high ever reach the row at all. */
.ticket-card-meta .priority-pill { padding: 0; background: none; box-shadow: none; text-transform: uppercase; letter-spacing: 0.04em; }
.ticket-card-meta .priority-pill.urgent { color: var(--danger); }
.ticket-card-meta .priority-pill.high { color: var(--ink); }

/* Due is plain text until it is late. Red on a card means exactly one thing. */
.ticket-due { display: inline-flex; align-items: center; gap: 0.25rem; font-family: var(--font-mono); white-space: nowrap; }
.ticket-due.today { color: var(--ink); }
.ticket-due.overdue { color: var(--danger); }

/* Progress is a 2px rule under the title rather than a number to decode; the
   n/n sits in the row below for anyone who wants the exact count. */
.ticket-progress { height: 2px; border-radius: 2px; background: var(--surface-3); overflow: hidden; }
.ticket-progress > span { display: block; height: 2px; background: var(--ink-faint); }

/* Why a blocked ticket is blocked, on the card. One line, clipped. */
.ticket-reason { display: flex; align-items: center; gap: 0.3rem; min-width: 0; color: var(--draft); font-family: var(--font-mono); }
.ticket-reason > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ticket-reason > svg { flex: none; }

/* The planning numbers are real but secondary, so they cost the resting card
   no height at all and appear while it is hovered, focused or selected. */
.ticket-extras { display: none; flex-wrap: wrap; gap: 0.5rem; align-items: center; color: var(--ink-faint); }
.ticket-extras .value-badge { font-size: inherit; color: inherit; }
.ticket-extras .value-badge svg { color: inherit; }
.ticket-card:hover .ticket-extras,
.ticket-card:focus-visible .ticket-extras,
.ticket-card.selected .ticket-extras { display: flex; }
.ticket-meta { display: inline-flex; align-items: center; gap: 0.25rem; font-family: var(--font-mono); }

/* Priority is a ramp of WEIGHT, not of hue: filled for urgent, outlined for
   high, plain text below that. The old fills were hardcoded light hex with no
   dark counterpart, so in the dark theme a near-white chip out-shouted the
   title above it — while `low`, on --surface-2, was the only one you couldn't
   see. The line-height was inherited (24px on a 10.4px font), which made this
   one-word row 27px tall: taller than the title line it sat under. */
.priority-pill {
    font-size: 0.65rem; font-family: var(--font-mono); line-height: 1;
    padding: 0.2rem 0.45rem; border-radius: var(--r-pill); white-space: nowrap;
}
.priority-pill.urgent { background: var(--danger-container); color: var(--on-danger-container); }
.priority-pill.high { color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
/* Below `high` the label is plain text — the word itself carries the value.
   Both steps stay on --ink-mute: --ink-faint is 4.47:1 on --card in the light
   theme, the very floor the meta row above just moved off. */
.priority-pill.medium, .priority-pill.low { color: var(--ink-mute); padding-left: 0; padding-right: 0; }

.ticket-avatar {
    width: 20px; height: 20px; border-radius: 50%;
    background: var(--surface-3); color: var(--ink-mute);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 0.6rem; font-weight: 500; flex-shrink: 0;
}
.ticket-avatar.is-self { background: var(--signal); color: var(--on-signal); }
.prop-person { display: inline-flex; align-items: center; gap: 0.4rem; }
.ticket-timer { display: inline-flex; align-items: center; gap: 0.3rem; font-family: var(--font-mono); }
.timer-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--signal); }
/* Outline chips: a finished ticket should not be the loudest thing on the
   board. on_time was filled with --signal, the brightest token in the system,
   so completed work out-shouted work in flight. Only the bad outcome keeps a
   colour — on-time and undertime differ by their label text, not by hue, and
   the old undertime hex (#FAEEDA/#633806) had no dark counterpart. */
.timeliness-badge {
    font-size: 0.65rem; font-family: var(--font-mono); line-height: 1;
    padding: 0.2rem 0.45rem; border-radius: var(--r-pill); white-space: nowrap;
    box-shadow: inset 0 0 0 1px currentColor;
}
.timeliness-badge.on_time { color: var(--ink-mute); }
.timeliness-badge.overtime { color: var(--danger); }
.timeliness-badge.undertime { color: var(--ink-mute); }

/* ----- rigor create dialog ----- */
.field-label { display: block; margin: 0.75rem 0 0.35rem; color: var(--ink-mute); }
.field-label .req { color: var(--danger); }
.rigor-badge {
    display: inline-flex; align-items: center; gap: 0.35rem;
    font-size: 0.7rem; font-family: var(--font-mono);
    background: var(--surface-2); color: var(--ink-mute);
    padding: 0.2rem 0.6rem; border-radius: var(--r-pill); margin-bottom: 0.5rem;
}
.rigor-tasks { display: flex; flex-direction: column; gap: 0.4rem; }
.rigor-task-row { display: flex; align-items: center; gap: 0.4rem; }
.rigor-task-row .input { flex: 1; }
.add-task { margin-top: 0.5rem; display: inline-flex; align-items: center; gap: 0.35rem; }
.rigor-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.complexity-row { display: flex; align-items: center; gap: 0.6rem; }
.complexity-slider { flex: 1; accent-color: var(--ink); }
.complexity-readout { min-width: 3ch; text-align: right; font-family: var(--font-mono); }

/* ----- ticket detail ----- */
.ticket-detail-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin-bottom: 0.75rem; }
.detail-checklist { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; }
.detail-task { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; font-size: 0.9rem; }
.detail-task-main { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; min-width: 0; flex: 1; }
.detail-task.done .detail-task-main span { text-decoration: line-through; color: var(--ink-faint); }
.detail-task-remove {
    background: none; border: none; padding: 2px; cursor: pointer; flex-shrink: 0;
    display: inline-flex; align-items: center; color: var(--ink-faint); opacity: 0;
    transition: opacity var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.detail-task:hover .detail-task-remove, .detail-task-remove:focus-visible { opacity: 1; }
.detail-task-remove:hover { color: var(--danger); }
.detail-task-addrow { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; color: var(--ink-faint); }
.detail-task-add { flex: 1; }

/* ----- congruency ----- */
.congruency-cards { display: flex; flex-direction: column; gap: 0.75rem; padding: 0 1rem; }
.congruency-card {
    display: grid; grid-template-columns: 150px 1fr 96px; gap: 1rem; align-items: center;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-row); padding: 0.9rem 1rem;
}
.congruency-user { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
.congruency-user .ticket-avatar { width: 30px; height: 30px; font-size: 0.7rem; }
.congruency-mid { min-width: 0; }
.congruency-bar { display: flex; height: 9px; border-radius: var(--r-pill); overflow: hidden; background: var(--surface-2); }
.congruency-bar .seg { height: 100%; }
.congruency-bar .seg.on-time { background: var(--signal); }
.congruency-bar .seg.over { background: var(--danger); }
.congruency-bar .seg.under { background: #EF9F27; }
.congruency-math { color: var(--ink-mute); font-family: var(--font-mono); margin-top: 0.5rem; }
.congruency-score-box { text-align: right; }
.congruency-score { font-size: 1.6rem; font-weight: 600; letter-spacing: -0.02em; }
.tendency {
    display: inline-block; margin-top: 0.3rem; font-size: 0.6rem; font-family: var(--font-mono);
    padding: 0.15rem 0.5rem; border-radius: var(--r-pill); white-space: nowrap;
}
.tendency.calibrated { background: var(--signal); color: var(--on-signal); }
.tendency.underestimates { background: var(--danger-container); color: var(--on-danger-container); }
.tendency.overestimates { background: #FAEEDA; color: #633806; }
.congruency-legend { display: flex; gap: 1rem; margin-top: 1rem; padding: 0 1rem; color: var(--ink-mute); }
.congruency-legend .lg { display: inline-flex; align-items: center; gap: 0.35rem; }
.congruency-legend .sw { width: 10px; height: 10px; border-radius: 3px; }
.congruency-legend .sw.on-time { background: var(--signal); }
.congruency-legend .sw.over { background: var(--danger); }
.congruency-legend .sw.under { background: #EF9F27; }

/* ===== Ticket detail: right drawer + full document ===================== */
/* Drawer: the board stays visible on the left; the ticket slides in from the
   right edge over half the viewport and scrolls independently. A transparent
   click-away scrim sits behind it (no dimming, so it doesn't read as a modal)
   and closes the drawer on an outside click. Rendered in a top-level host so the
   fixed positioning is viewport-relative. Below dialogs (z 50). */
.detail-scrim {
    position: fixed;
    inset: 0;
    /* Below the titlebar (z 40) so the window drag region + traffic lights stay
       usable; above the board content so an outside click lands here and closes. */
    z-index: 39;
    background: transparent;
}
.detail-drawer {
    position: fixed;
    /* Start below the 44px titlebar so the toolbar (close ✕) is NOT inside the
       window-drag region, which would otherwise swallow real clicks in the Pear
       frameless window. */
    top: 44px;
    right: 0;
    width: min(62vw, 1100px);
    height: calc(100vh - 44px);
    z-index: 45;
    display: flex;
    flex-direction: column;
    background: var(--card);
    border-left: 1px solid var(--line);
    box-shadow: -12px 0 40px rgba(0, 0, 0, 0.18);
    /* The drawer's top strip overlaps the titlebar's window-drag region; without
       this, real clicks on the toolbar (incl. the close ✕) are swallowed by the
       OS drag region and never reach the button — the drawer wouldn't close. */
    -webkit-app-region: no-drag;
}
@keyframes drawer-in { from { transform: translateX(100%); } }
/* Slide in only when the drawer OPENS. Every re-render rebuilds the DOM node;
   replaying the animation on each store update reads as a glitch and moves
   the hover targets under the cursor. renderDrawerHost adds .enter once. */
.detail-drawer.enter, .item-inspector.enter { animation: drawer-in var(--t-base) var(--ease); }
/* While the drawer is open the page itself does not scroll — only the drawer's
   own scroll region does. This stops the board behind it from moving when you
   scroll the drawer (wheel anywhere, not just at the scroll boundary). */
html.board-drawer-open { overflow: hidden; }
.detail-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--line);
    flex-shrink: 0;
}
.detail-toolbar-title {
    font-weight: 500;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.detail-toolbar-actions { display: flex; gap: 0.5rem; flex-shrink: 0; }

/* List-item Inspector — non-modal right panel (toggle I), narrower than the
   board drawer and below it in z-order. Overlays the right gutter the list
   leaves empty, so it hides almost no content. */
.item-inspector {
    position: fixed; right: 0; top: 44px;
    width: 380px; height: calc(100vh - 44px);
    z-index: 40;
    display: flex; flex-direction: column;
    background: var(--surface-1);
    border-left: 1px solid var(--line);
    -webkit-app-region: no-drag;
}
@media (max-width: 920px) { .item-inspector { width: 100vw; } }
.inspector-head {
    display: flex; flex-direction: column; gap: 0.4rem;
    padding: 0.9rem 1.25rem 0.35rem; flex-shrink: 0;
}
.inspector-kicker-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.inspector-kicker { text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-mute); }
.inspector-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The title doubles as the rename field: looks like plain title text until focused. */
.inspector-title-field {
    min-width: 0; flex: 1; border: 0; background: transparent; color: var(--ink);
    padding: 0.15rem 0.3rem; margin: -0.15rem -0.3rem; border-radius: var(--r-field);
}
.inspector-title-field:hover { background: var(--surface-2); }
.inspector-title-field:focus { outline: none; background: var(--card); box-shadow: inset 0 0 0 2px var(--ink); }
.inspector-body { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 1.25rem; display: flex; flex-direction: column; gap: 1.5rem; }
.inspector-section { display: flex; flex-direction: column; gap: 0.5rem; }
/* Mockup footer: edited-time left, sync beacon right; the only rule line. */
.inspector-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    padding: 0.7rem 1.25rem; border-top: 1px solid var(--line);
    color: var(--ink-mute); flex-shrink: 0; text-transform: uppercase; letter-spacing: 0.08em;
}
.inspector-foot-status { display: inline-flex; align-items: center; gap: 0.4rem; }
.inspector-foot .dot { width: 0.5rem; height: 0.5rem; border-radius: var(--r-pill); background: transparent; border: 2px solid var(--ink-faint); flex-shrink: 0; }
.inspector-foot .dot.live { background: var(--signal); border: 0; }
/* Blocks-only body affordances (Option B mockup): a single inline hint line
   and, on a fresh item, a plain-text invitation plus ghost type chips. */
.block-addhint {
    border: 0; background: transparent; color: var(--ink-faint);
    padding: 0.35rem 0; cursor: pointer; text-align: left; letter-spacing: 0.05em;
}
.block-addhint:hover { color: var(--ink); }
/* The inspector body fills the drawer so any empty-space click starts a text
   block (cursor: text signals it); children keep their own cursors. */
.ticket-body.inspector-variant { flex: 1; cursor: text; }
.inspector-empty { display: flex; flex-direction: column; gap: 0.7rem; align-items: flex-start; }
.inspector-empty-hint {
    border: 0; background: transparent; color: var(--ink-faint);
    font-family: var(--font-sans); font-size: 0.9rem; padding: 0; cursor: pointer; text-align: left;
}
.inspector-empty-hint:hover { color: var(--ink-mute); }
.inspector-empty-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.ghost-chip {
    display: inline-flex; align-items: center; gap: 0.35rem;
    border: 1px dashed var(--line-strong); background: transparent; color: var(--ink-mute);
    border-radius: var(--r-field); padding: 0.28rem 0.55rem; cursor: pointer;
    text-transform: uppercase; letter-spacing: 0.06em;
}
.ghost-chip:hover { color: var(--ink); border-color: var(--ink); border-style: solid; }
.inspector-value { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.inspector-value .value-badges { display: inline-flex; gap: 0.5rem; }
.category-pill { display: inline-flex; align-items: center; padding: 0.2rem 0.6rem; background: var(--surface-2); border-radius: var(--r-pill); color: var(--ink-mute); width: fit-content; }
.item-row.inspecting { box-shadow: inset 2px 0 0 0 var(--signal); background: var(--surface-1); }
.detail-split-scroll {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}
/* Drawer as a small full view: tasks + notes prominent on the left, the
   property rail on the right, sticky while the left column scrolls. */
.detail-split-scroll.two-col {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 260px;
    grid-template-rows: auto 1fr;
    grid-template-areas: "summary rail" "body rail";
    gap: 1.25rem 1.75rem;
    align-items: start;
}
.detail-split-scroll.two-col .ticket-summary { grid-area: summary; }
.detail-split-scroll.two-col > .ticket-body { grid-area: body; }
.detail-split-scroll.two-col > .prop-rail { grid-area: rail; position: sticky; top: 0; }
@media (max-width: 920px) {
    /* align-items:start comes from the grid rule above; in a COLUMN flexbox it
       is the cross (horizontal) axis, so without this reset the stacked
       summary/body/rail shrink-wrap their text and leave the drawer half empty. */
    .detail-split-scroll.two-col { display: flex; flex-direction: column; align-items: stretch; }
    .detail-split-scroll.two-col > .prop-rail { position: static; }
}
.detail-drawer .prop-rail.compact .prop-row { grid-template-columns: 96px 1fr; }
.ticket-card.selected { box-shadow: inset 0 0 0 2px var(--ink); background: var(--surface-2); }

/* Full document: summary top-left, block body below it, property rail right. */
.ticket-doc-header { align-items: center; }
.ticket-doc {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-rows: auto 1fr;
    grid-template-areas: "summary rail" "body rail";
    gap: 2rem 2.5rem;
    align-items: start;
}
.ticket-doc-summary { grid-area: summary; min-width: 0; }
.ticket-doc-body { grid-area: body; min-width: 0; }
.ticket-doc-rail { grid-area: rail; position: sticky; top: 1rem; }

/* Summary (shared by both presentations) */
.ticket-summary { display: flex; flex-direction: column; gap: 0.6rem; }
.ticket-summary-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.ticket-title-field {
    width: 100%; border: 0; background: transparent; color: var(--ink);
    font-family: var(--font-sans); font-weight: 600; font-size: 1.15rem; line-height: 1.3;
    padding: 0.25rem 0.35rem; margin: 0 -0.35rem; border-radius: var(--r-field);
}
.ticket-summary.full .ticket-title-field { font-size: 1.6rem; line-height: 1.2; }
.ticket-title-field:focus { outline: none; background: var(--surface-1); }
.ticket-summary-meta { display: flex; flex-wrap: wrap; gap: 0.75rem; color: var(--ink-mute); }
.sm-meta { font-family: var(--font-mono); }
.ticket-summary-tasks { margin-top: 0.5rem; }
.ticket-summary-tasks .category-heading { padding: 0; margin-bottom: 0.5rem; }

/* Property rail */
.prop-rail-inner { display: flex; flex-direction: column; gap: 1.5rem; }
.prop-shared .category-heading, .prop-section .category-heading {
    display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.75rem; padding: 0;
}
.prop-avatars { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.prop-empty { color: var(--ink-faint); padding: 0; }
.prop-rows { display: flex; flex-direction: column; gap: 0.5rem; }
.prop-row { display: grid; grid-template-columns: 110px 1fr; align-items: center; gap: 0.5rem; }
.prop-key { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--ink-mute); }
.prop-key svg { color: var(--ink-faint); flex-shrink: 0; }
.prop-val { min-width: 0; }
.prop-select, .prop-input {
    width: 100%; border: 0; background: var(--surface-1); border-radius: var(--r-field);
    padding: 0.4rem 0.5rem; font: inherit; font-size: 0.85rem; color: var(--ink);
}
.prop-select:focus, .prop-input:focus { outline: none; box-shadow: inset 0 0 0 2px var(--ink); background: var(--surface-2); }
.prop-readonly { font-family: var(--font-mono); font-size: 0.8rem; color: var(--ink-mute); }

/* ===== Block-based body editor ========================================= */
.ticket-body { display: flex; flex-direction: column; }
.ticket-body-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; }
.ticket-body-head .category-heading { margin: 0; padding: 0; }
.block-hint { color: var(--ink-faint); }
.ticket-blocks { display: flex; flex-direction: column; gap: 0.1rem; }

/* Blocks are quiet: no hover/editing highlight, content flush left. The
   six-dot handle floats in the container's left padding on hover only, so it
   never indents or reflows the content; all block actions live in its menu. */
.block {
    position: relative;
    border-radius: var(--r-field);
}
.block.block-drop { box-shadow: inset 0 2px 0 var(--signal); }
.block.block-dragging { opacity: 0.4; }
.block-handle {
    position: absolute; left: -1.25rem; top: 0.25rem;
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.15rem; height: 1.15rem; padding: 0; border: 0; background: transparent;
    border-radius: var(--r-field); color: var(--ink-faint);
    opacity: 0; transition: opacity var(--t-fast) var(--ease);
    cursor: grab;
}
.block:hover .block-handle { opacity: 1; }
.block-handle:active { cursor: grabbing; }
.block-handle:hover { background: var(--surface-2); color: var(--ink); }
.block-content { min-width: 0; padding: 0.3rem 0; font-size: 0.95rem; color: var(--ink); }

/* Block content views */
.block-md { line-height: 1.5; cursor: text; white-space: pre-wrap; word-break: break-word; }
.block-md.muted { color: var(--ink-faint); font-style: italic; }
.block-md code { font-family: var(--font-mono); background: var(--surface-2); padding: 0.05rem 0.3rem; border-radius: var(--r-field); font-size: 0.85em; }
.block-md a { color: var(--ink); text-decoration: underline; }
.block-md h1, .block-md h2, .block-md h3 { margin: 0.4em 0 0.2em; font-weight: 700; line-height: 1.25; }
.block-md h1:first-child, .block-md h2:first-child, .block-md h3:first-child { margin-top: 0; }
.block-md h1 { font-size: 1.4em; }
.block-md h2 { font-size: 1.2em; }
.block-md h3 { font-size: 1.05em; }
/* First-class heading + divider blocks. */
.block-heading { margin: 0; font-weight: 700; line-height: 1.25; cursor: text; word-break: break-word; color: var(--ink); }
.block-heading-l2 { font-size: 1.2em; }
.block-heading-l3 { font-size: 1.05em; }
.block-divider { border: none; border-top: 1px solid var(--line); margin: 0.6rem 0; width: 100%; }
.block-heading-wrap { display: flex; align-items: center; gap: 0.6rem; }
.block-heading-input {
    flex: 1; min-width: 0; border: none; background: var(--surface-1);
    border-radius: var(--r-field); padding: 0.2rem 0.4rem;
    font-family: var(--font-sans); font-weight: 700; color: var(--ink);
}
.block-heading-input:focus { outline: none; }
.heading-level { flex-shrink: 0; }
.heading-level .seg { padding: 0.2rem 0.55rem; }
.block-content .block-callout { display: flex; gap: 0.6rem; background: var(--surface-2); border-left: 3px solid var(--signal); border-radius: var(--r-field); padding: 0.6rem 0.8rem; cursor: text; }
.block-callout > svg { color: var(--ink-mute); flex-shrink: 0; margin-top: 2px; }
.block-code { font-family: var(--font-mono); font-size: 0.82rem; background: var(--surface-2); border-radius: var(--r-field); padding: 0.7rem 0.9rem; overflow-x: auto; white-space: pre; cursor: text; margin: 0; }
.block-checklist, .block-numbered, .block-links { margin: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.block-numbered { padding-left: 1.25rem; }
.block-checklist, .block-links { list-style: none; padding-left: 0; }
.block-checklist li { display: flex; align-items: center; gap: 0.5rem; cursor: text; }
.block-checklist li.done span { text-decoration: line-through; color: var(--ink-faint); }
.block-checklist input { accent-color: var(--ink); flex-shrink: 0; }
.block-links a { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--ink); text-decoration: none; }
.block-links a:hover { text-decoration: underline; }
.block-links svg { color: var(--ink-faint); }
.block-image { max-width: 100%; border-radius: var(--r-field); display: block; cursor: pointer; }
.block-table { border-collapse: collapse; width: 100%; font-size: 0.85rem; cursor: text; }
.block-table th, .block-table td { border: 1px solid var(--line); padding: 0.35rem 0.6rem; text-align: left; }
.block-table th { background: var(--surface-2); font-weight: 600; }

/* Table block grid editor — a table edits AS a table: one input per cell, with a
   leading control column (delete row) and control row (delete column), plus an
   add-row / add-column toolbar. Negative right/bottom cell margins overlap the
   1px borders so seams stay hairline. */
.block-table-editor { display: flex; flex-direction: column; gap: 0.5rem; }
.block-table-grid { display: grid; width: 100%; overflow-x: auto; align-items: stretch; padding-bottom: 1px; }
.block-table-corner { display: block; }
.block-table-cell {
    width: 100%; min-width: 0; margin: 0 -1px -1px 0; border: 1px solid var(--line);
    background: var(--card); color: var(--ink); font: inherit; font-size: 0.85rem;
    padding: 0.35rem 0.5rem; outline: none;
}
.block-table-cell.block-table-head { background: var(--surface-2); font-weight: 600; }
.block-table-cell:focus { box-shadow: inset 0 0 0 2px var(--ink); position: relative; z-index: 1; }
.block-table-btn {
    display: inline-flex; align-items: center; justify-content: center; width: 100%;
    min-height: 1.5rem; border: 0; background: transparent; color: var(--ink-faint);
    cursor: pointer; padding: 0.1rem 0.2rem;
}
.block-table-btn:hover { color: var(--signal); }
.block-table-toolbar { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.block-table-add {
    display: inline-flex; align-items: center; gap: 0.3rem; border: 1px dashed var(--line);
    background: transparent; color: var(--ink-mute); cursor: pointer; font-family: var(--font-mono);
    font-size: 0.75rem; padding: 0.3rem 0.55rem; border-radius: var(--r-field);
}
.block-table-add:hover { background: var(--surface-2); color: var(--ink); }
.block-table-hint { color: var(--ink-faint); margin-left: auto; }

/* Block editor */
.block-editor-wrap { position: relative; }
/* Editing is seamless: no box, no border, no background — the caret is the
   only signal, and the text sits exactly where the view rendered it. */
.block-editor {
    width: 100%; border: 0; border-radius: 0; background: transparent;
    box-shadow: none; padding: 0; font: inherit;
    font-size: 0.95rem; color: var(--ink); resize: vertical; line-height: 1.5;
}
.block-editor:focus { outline: none; box-shadow: none; }
.block-code .block-editor { font-family: var(--font-mono); font-size: 0.85rem; }

/* WYSIWYG (contentEditable) markdown + callout editor. Shares .block-md so the
   view and the editor render identically — clicking a block just drops a caret
   in. Paragraphs are zero-margin so multi-line blocks read as plain lines. */
.block-rich p { margin: 0; }
.block-rich p:empty::after { content: "\200b"; } /* keep empty lines selectable */
.block-rich.block-editor { white-space: pre-wrap; cursor: text; min-height: 1.6em; }
.block-rich.block-editor:focus { outline: none; box-shadow: none; }
/* The callout editor keeps only the callout's own visual identity. */
.block-rich-callout.block-editor { border-left: 3px solid var(--signal); background: var(--surface-2); border-radius: var(--r-field); padding: 0.6rem 0.8rem; }
.block-rich.is-empty::before {
    content: attr(data-placeholder); color: var(--ink-faint); font-style: italic;
    pointer-events: none; position: absolute; padding: inherit;
}
.block-rich.is-empty { position: relative; }

/* Insert affordances + "/" command menu */
.block-add-row { position: relative; margin-top: 0.4rem; }
.block-add, .block-empty {
    display: inline-flex; align-items: center; gap: 0.4rem; color: var(--ink-faint);
    font-family: var(--font-mono); font-size: 0.75rem; padding: 0.4rem 0.5rem; border-radius: var(--r-field);
}
.block-add:hover, .block-empty:hover { background: var(--surface-2); color: var(--ink-mute); }
.block-empty { width: 100%; justify-content: flex-start; border: 1px dashed var(--line); padding: 0.85rem; }
.block-menu-anchor { position: relative; }
.block-menu-scrim { position: fixed; inset: 0; z-index: 40; }
/* Compact command menu (mockup): sticky mono header showing the filter, a
   keyboard-driven selection (black row, acid icon), sticky hint footer. */
.block-slash-menu {
    position: absolute; z-index: 41; top: 100%; left: 0; margin-top: 4px; min-width: 220px; max-width: 260px;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-row);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); padding: 0;
    display: flex; flex-direction: column; max-height: 300px; overflow-y: auto; outline: none;
}
.slash-head, .slash-foot {
    position: sticky; z-index: 1; background: var(--surface-2); color: var(--ink-mute);
    padding: 0.35rem 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; flex-shrink: 0;
}
.slash-head { top: 0; }
.slash-foot { bottom: 0; }
.slash-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.45rem 0.7rem; border-radius: 0; color: var(--ink); text-align: left; }
.slash-item:hover { background: var(--surface-2); }
.slash-item.sel { background: var(--ink); color: var(--card); }
.slash-item.sel .label-md { color: inherit; }
.slash-item.sel svg { color: var(--signal); }
.slash-item svg { color: var(--ink-mute); flex-shrink: 0; }
.block-slash-menu.hidden { display: none; }
/* Left-edge grip for resizing the right-anchored drawers. */
.drawer-resizer { position: absolute; left: -2px; top: 0; bottom: 0; width: 7px; cursor: col-resize; z-index: 46; }
.drawer-resizer:hover { background: var(--line); }

@media (max-width: 920px) {
    .detail-drawer { width: 100vw; }
    .ticket-doc { grid-template-columns: minmax(0, 1fr); grid-template-areas: "summary" "rail" "body"; }
    .ticket-doc-rail { position: static; }
    /* The drawer is the whole viewport here, so its own resize grip is inert
       and only steals the leftmost 7px of every click. */
    .detail-drawer .drawer-resizer { display: none; }
}

/* ---- Overview (the cross-list day plan) -------------------------------- */
/* Pinned Overview entry sits above the list rail. */
.rail-pinned { padding: 0 0.5rem 0.25rem; }

/* Row action cluster (flag + day-picker + move + delete) — one grid cell so the
   item-row stays a clean 3-column grid. */
.row-actions { display: inline-flex; align-items: center; gap: 0.125rem; justify-self: end; }
.item-row .row-flag {
    opacity: 0;
    color: inherit;
    background: none;
    border: none;
    padding: 0 0.25rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.item-row:hover .row-flag, .item-row:focus-within .row-flag { opacity: 0.7; }
.item-row .row-flag:hover { opacity: 1; transform: scale(1.15); }
.item-row .row-flag.flagged { opacity: 1; color: var(--on-signal); background: var(--signal); border-radius: var(--r-field); }
.row-flag-more { margin-left: -0.125rem; }
/* List-flag header button in the active state. */
.btn-icon.active { background: var(--signal); color: var(--on-signal); border-color: var(--signal); }

/* Header sub-line + Focus/Planner segmented toggle. */
.plan-subhead { margin-top: 0.35rem; }
.plan-viewtoggle, .segmented { display: inline-flex; background: var(--surface-2); border-radius: var(--r-pill); padding: 2px; }
.segmented { flex-wrap: wrap; }
.plan-viewtoggle .seg, .segmented .seg {
    font-family: var(--font-mono);
    font-size: 11px;
    border: none;
    background: none;
    color: var(--ink-mute);
    padding: 0.3rem 0.8rem;
    border-radius: var(--r-pill);
    cursor: pointer;
    transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.plan-viewtoggle .seg.active, .segmented .seg.active { background: var(--ink-block); color: var(--signal); }
/* Many-option pickers (e.g. Language) wrap to multiple rows: a stadium radius on
   a tall box looks bad, so this variant is a clean rounded rectangle with gaps. */
.segmented-wrap { border-radius: var(--r-row); gap: 4px; padding: 8px; }

/* Settings dialog rhythm: headings align flush with their controls (no indent),
   and a consistent gap sits above each heading regardless of the control before
   it (the old per-control bottom margins drifted once segmented/switch landed). */
.settings .category-heading { padding: 0; margin: 1.6rem 0 0.7rem; }
.settings > .category-heading:first-child { margin-top: 0; }
.settings .label-md { margin: 0.5rem 0 0; }
.settings .choice-row { margin: 0; }
/* Body text has no bottom margin and a control row has no top one, so a section
   shaped heading → prose → button (Flatten history, Diagnostics, the seed
   export) used to sit its button flush against the last line of text. Restore
   the gap on that adjacency only, so heading → control stays tight. */
.settings .label-md + .choice-row { margin-top: 0.9rem; }
.settings .switch-row { margin: 0; }
.settings .analytics-section { margin-top: 0.6rem; }
.list-identity dl { margin: 0; display: grid; gap: 0.35rem; }
.list-identity-row { display: grid; grid-template-columns: minmax(7rem, auto) 1fr; gap: 1rem; font-family: var(--font-mono); font-size: 11px; line-height: 16px; }
.list-identity-row dt { color: var(--ink-faint); }
.list-identity-row dd { margin: 0; color: var(--ink-mute); overflow-wrap: anywhere; user-select: text; }

/* The 7-day strip: Today + Tomorrow + the next five days.
   The pills share the row equally while there is room and WRAP onto a second
   row when there isn't — the day cells stay legible at every width instead of
   being crushed under their own "TOMORROW" label. 5rem is the basis, so all
   seven fit one row down to ~36rem of content. */
.plan-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(5rem, 100%), 1fr));
    gap: 0.4rem;
    margin: 0 0 var(--section-gap);
}
.plan-pill {
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 0.5rem 0.25rem;
    background: var(--surface-2);
    border: 1px solid transparent;
    border-radius: var(--r-row);
    cursor: pointer;
    transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.plan-pill:hover { background: var(--surface-3); }
.plan-pill .pill-dow {
    color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.08em;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.plan-pill .pill-day { font-size: 1.05rem; font-weight: 600; color: var(--ink); }
.plan-pill .pill-count { color: var(--ink-faint); }
.plan-pill .pill-count.zero { opacity: 0.6; }
.plan-pill.selected { background: var(--ink-block); }
.plan-pill.selected .pill-dow { color: var(--signal); }
.plan-pill.selected .pill-day, .plan-pill.selected .pill-count { color: var(--inverse-ink); }
.plan-pill.drop { border-color: var(--signal); background: var(--surface-3); }

/* Week paging above the day strip (reach past days / future weeks). */
.plan-strip-wrap { margin: 0 0 var(--section-gap); }
.plan-strip-wrap .plan-strip { margin: 0; }
.plan-strip-nav { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 0.5rem; }
.plan-week-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.7rem; height: 1.7rem; padding: 0;
    background: var(--surface-2); border: 1px solid transparent; border-radius: var(--r-field);
    color: var(--ink-mute); cursor: pointer;
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.plan-week-btn:hover { background: var(--surface-3); color: var(--ink); }
.plan-week-range { color: var(--ink-mute); min-width: 8.5rem; }
.plan-today-btn {
    margin-left: auto;
    background: var(--surface-2); border: 1px solid transparent; border-radius: var(--r-pill);
    color: var(--ink); cursor: pointer; padding: 0.25rem 0.7rem;
    transition: background-color var(--t-fast) var(--ease);
}
.plan-today-btn:hover { background: var(--surface-3); }

/* Plan rows + spotlight. */
.plan-section-label { color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.1em; margin: 0 0 0.6rem; }
.plan-rows { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: var(--section-gap); }
.plan-row {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem 0.85rem;
    background: var(--card);
    border-radius: var(--r-row);
    transition: background-color var(--t-fast) var(--ease);
}
.plan-row:hover { background: var(--surface-2); }
.plan-row .plan-check { background: none; border: none; cursor: pointer; color: var(--ink); display: inline-flex; align-items: center; padding: 0; }
.plan-row .glyph { color: var(--ink-mute); display: inline-flex; }
.plan-row .plan-text { flex: 1; min-width: 0; cursor: text; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-row .plan-chip {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-mute);
    background: var(--surface-2);
    padding: 0.1rem 0.5rem;
    border-radius: var(--r-pill);
    /* Shrinkable, but never past a readable stub — the row text yields first. */
    flex: 0 1 auto;
    min-width: 2.5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.plan-row .plan-remove, .plan-row .plan-open {
    opacity: 0; background: none; border: none; cursor: pointer; color: var(--ink-faint);
    display: inline-flex; align-items: center; padding: 0 0.15rem;
    transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.plan-row:hover .plan-remove, .plan-row:hover .plan-open, .plan-row:focus-within .plan-remove { opacity: 0.7; }
.plan-row .plan-remove:hover, .plan-row .plan-open:hover { opacity: 1; color: var(--ink); }
.plan-row.done { background: var(--surface-2); color: var(--ink-faint); }
.plan-row.done .plan-text { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--ink-faint); }
.plan-row.done .plan-check { color: var(--ink-faint); }
.plan-row[draggable="true"] { cursor: grab; }
.plan-row.dragging { opacity: 0.4; }
.plan-row.drop-before { box-shadow: inset 0 2px 0 0 var(--signal); }
.plan-row.drop-after { box-shadow: inset 0 -2px 0 0 var(--signal); }

/* Carried-over tray: overdue-but-unfinished work surfaced under Today. */
.plan-carry-label { display: flex; align-items: center; gap: 0.35rem; color: var(--ink-mute); }
.plan-carry-rows { margin-bottom: var(--section-gap); }
.plan-row.plan-carry { border-left: 2px solid var(--signal); }
.plan-row.plan-carry .plan-carry-badge {
    display: inline-flex; align-items: center; gap: 0.25rem;
    font-family: var(--font-mono); font-size: 11px; color: var(--ink-mute); flex: none; white-space: nowrap;
}
.plan-row.plan-carry .plan-carry-move {
    opacity: 0; background: none; border: none; cursor: pointer; color: var(--ink-faint);
    display: inline-flex; align-items: center; padding: 0 0.15rem;
    transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.plan-row.plan-carry:hover .plan-carry-move, .plan-row.plan-carry:focus-within .plan-carry-move { opacity: 0.7; }
.plan-row.plan-carry .plan-carry-move:hover { opacity: 1; color: var(--ink); }

.plan-spotlight-wrap { margin-bottom: var(--section-gap); }
.plan-row.spotlight {
    padding: 0.95rem 1rem 0.95rem 0.85rem;
    border-left: 3px solid var(--signal);
    border-radius: 0 var(--r-row) var(--r-row) 0;
    background: var(--card);
}
.plan-row.spotlight .plan-text { font-size: 1.1rem; font-weight: 600; white-space: normal; }

/* List-card: a flagged whole list (open-only, not editable). */
.plan-list-card { background: var(--surface-1); }
.plan-list-card .plan-text { font-weight: 500; cursor: pointer; }

/* Done tray. */
.plan-done-tray { display: flex; align-items: center; gap: 0.5rem; margin: 0.5rem 0; color: var(--ink-mute); }
.plan-done-tray .glyph.done { color: var(--on-signal); background: var(--signal); border-radius: var(--r-field); padding: 1px; display: inline-flex; }
.plan-done-rows { opacity: 0.75; }

/* Receded Tomorrow preview (Focus layout). */
.plan-tomorrow { opacity: 0.62; }
.plan-tomorrow .plan-row { background: transparent; padding: 0.4rem 0.85rem; }

/* Empty + all-clear. */
.plan-empty { padding: 2.5rem 0; color: var(--ink-mute); max-width: 32rem; }
.plan-empty-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: var(--r-row);
    background: var(--surface-2); color: var(--ink-faint); margin-bottom: 0.9rem;
}
.plan-empty-title { font-size: 1.1rem; color: var(--ink); margin-bottom: 0.4rem; }
.plan-empty-tip {
    display: inline-flex; align-items: center; gap: 0.5rem;
    margin-top: 1rem; padding: 0.4rem 0.7rem;
    background: var(--surface-2); border-radius: var(--r-pill); color: var(--ink-mute);
}
.plan-empty-tip svg { color: var(--ink-faint); }
.plan-empty-tip-or { color: var(--ink-faint); }

/* Planner layout: today agenda on the left, the week rail on the right. */
/* minmax(0, …) rather than bare fr: an fr track floors at its content's min
   size, so a long agenda row would otherwise blow the grid past the column. */
.plan-planner { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: var(--element-gap) var(--section-gap); align-items: start; }
.plan-week-rail { display: flex; flex-direction: column; gap: 0.5rem; }
.plan-week-day {
    background: var(--card);
    border: 1px solid transparent;
    border-radius: var(--r-row);
    padding: 0.6rem 0.75rem;
    cursor: pointer;
    transition: border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.plan-week-day:hover { background: var(--surface-2); }
.plan-week-day.selected { box-shadow: inset 0 0 0 1px var(--ink-faint); }
.plan-week-day.today .plan-week-head { color: var(--ink); }
.plan-week-day.drop { border-color: var(--signal); background: var(--surface-2); }
.plan-week-head { display: flex; justify-content: space-between; align-items: baseline; font-weight: 600; font-size: 0.9rem; margin-bottom: 0.35rem; }
.plan-week-empty { color: var(--ink-faint); }
.plan-week-rows { display: flex; flex-direction: column; gap: 0.3rem; }
.plan-week-row { display: flex; align-items: center; gap: 0.45rem; font-size: 0.85rem; color: var(--ink-mute); }
.plan-week-row .ti { color: var(--ink-faint); flex: none; }
.plan-week-check { display: inline-flex; align-items: center; justify-content: center; flex: none; min-width: 24px; min-height: 24px; padding: 0; border: none; border-radius: 0; background: transparent; color: var(--ink); cursor: pointer; }
.plan-week-check:hover { background: var(--surface-2); }
.plan-week-check:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.plan-week-check .ti { color: inherit; }
.plan-week-row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-week-row.done span { text-decoration: line-through; color: var(--ink-faint); }

/* Day-picker dialog. */
.plan-day-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-bottom: 0.75rem; }
.plan-day-opt { display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem; }
.plan-day-custom { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }

.plan-calendar-dialog { width: min(420px, calc(100vw - 2rem)); }
.plan-calendar-entry { margin: 0.5rem 0 1.25rem; overflow-wrap: anywhere; }
.plan-calendar-nav { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; }
.plan-calendar-month { flex: 1; min-width: 0; text-align: center; }
.plan-calendar-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.25rem; }
.plan-calendar-weekday { padding: 0.5rem 0; text-align: center; color: var(--ink-mute); }
.plan-calendar-day {
    position: relative;
    min-height: 40px;
    padding: 0.5rem 0;
    border: 0;
    border-radius: var(--r-field);
    background: transparent;
    color: var(--ink);
    cursor: pointer;
}
.plan-calendar-day:hover { background: var(--surface-2); }
.plan-calendar-day.selected { background: var(--ink); color: var(--surface-0); }
.plan-calendar-day.today::after {
    content: '';
    position: absolute;
    bottom: 3px;
    left: calc(50% - 2px);
    width: 4px;
    height: 4px;
    background: var(--ink);
}
.plan-calendar-day.selected.today::after { background: var(--signal); }

@media (max-width: 980px) {
    .plan-planner { grid-template-columns: minmax(0, 1fr); }
}

/* Click to choose a date, or drag to move/reorder the plan row. */
.plan-grip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    min-width: 24px;
    min-height: 24px;
    padding: 0;
    border: 0;
    border-radius: var(--r-field);
    background: transparent;
    color: var(--ink-faint);
    cursor: grab;
    opacity: 0;
    transition: opacity var(--t-fast) var(--ease);
    margin-left: -0.25rem;
}
.plan-row:hover .plan-grip, .plan-row:focus-within .plan-grip { opacity: 0.7; }
.plan-row .plan-grip:hover, .plan-row .plan-grip:focus-visible { opacity: 1; color: var(--ink); background: var(--surface-2); }

/* Flag a board ticket into the day plan — hover-revealed, acid when flagged. */
.ticket-card { position: relative; }
.ticket-flag {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    display: inline-flex;
    align-items: center;
    padding: 0.15rem;
    border-radius: var(--r-field);
    color: var(--ink-faint);
    opacity: 0;
    transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.ticket-card:hover .ticket-flag, .ticket-card:focus-within .ticket-flag { opacity: 0.7; }
.ticket-flag:hover { opacity: 1; transform: scale(1.12); }
.ticket-flag.flagged { opacity: 1; color: var(--on-signal); background: var(--signal); }
.ticket-title { padding-right: 1.25rem; }

/* Flag a grocery item into the plan from grid (shopping-mode) cards too. */
.grid-card .row-flag {
    opacity: 0;
    color: var(--ink-faint);
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.1rem;
    border-radius: var(--r-field);
    display: inline-flex;
    transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.grid-card:hover .row-flag, .grid-card:focus-within .row-flag { opacity: 0.7; }
.grid-card .row-flag:hover { opacity: 1; transform: scale(1.12); }
.grid-card .row-flag.flagged { opacity: 1; color: var(--on-signal); background: var(--signal); }

/* ===== Value Return ==================================================== */
/* Per-item value/delay badges (board card meta, todo row, plan rows). */
.value-badge { display: inline-flex; align-items: center; gap: 0.2rem; font-size: 0.78rem; color: var(--ink-mute); font-family: var(--font-mono); }
.value-badge svg { color: var(--signal); flex-shrink: 0; }
.value-badge.delay svg { color: #EF9F27; }
.row-value { display: inline-flex; align-items: center; gap: 0.5rem; margin-left: 0.5rem; cursor: pointer; }
.row-value:hover .value-badge { color: var(--ink); }

/* Settings toggle */
.value-toggle { margin-top: 0.5rem; }
.value-toggle .category-heading { display: flex; align-items: center; gap: 0.4rem; padding: 0; margin: 0 0 0.5rem; }
.value-toggle-row { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; color: var(--ink-mute); }

/* Rate inputs (create dialog + value-rate dialog) */
.value-input-row { display: inline-flex; align-items: center; gap: 0.4rem; }
.value-input-row svg { color: var(--ink-faint); flex-shrink: 0; }

/* Overview rollups (per-day pill / planner head / week bar) */
.value-rollup { display: inline-flex; align-items: center; gap: 0.15rem; color: var(--ink-faint); font-family: var(--font-mono); }
.value-rollup svg { flex-shrink: 0; }
.value-rollup svg:first-child { color: var(--signal); }
.value-rollup svg:nth-of-type(2) { color: #EF9F27; margin-left: 0.3rem; }
.plan-weekbar { display: flex; justify-content: flex-end; padding: 0.1rem 0.25rem 0; }
.plan-week-head-right { display: inline-flex; align-items: center; gap: 0.5rem; }
.plan-pill .value-rollup { font-size: 0.62rem; margin-top: 0.1rem; }
.plan-text-open { cursor: pointer; }
.plan-text-open:hover { text-decoration: underline; }

/* Value tab: chart + period stat cards */
.value-pane { display: flex; flex-direction: column; gap: 1.5rem; }
.value-chart-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-dialog); padding: 1.25rem; }
.value-chart { width: 100%; }
.value-chart-svg { width: 100%; height: 200px; display: block; }
.vc-axis { stroke: var(--line); stroke-width: 1; }
.vc-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.vc-value { stroke: var(--signal); }
.vc-delay { stroke: #EF9F27; }
.value-legend { display: flex; gap: 1.25rem; margin-bottom: 0.75rem; color: var(--ink-mute); }
.value-legend-item { display: inline-flex; align-items: center; gap: 0.35rem; }
.value-legend-item svg { color: var(--ink-faint); }
.value-swatch { width: 14px; height: 3px; border-radius: 2px; }
.value-swatch.value { background: var(--signal); }
.value-swatch.delay { background: #EF9F27; }
.value-axis { display: flex; justify-content: space-between; color: var(--ink-faint); margin-top: 0.4rem; }
.value-empty { color: var(--ink-faint); text-align: center; padding: 2rem 0; }
/* auto-fit rather than a fixed 4: the cards re-flow 4 → 3 → 2 → 1 on their own
   as the column narrows, with no breakpoint to keep in sync. */
.value-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr)); gap: 1rem; }
.value-stat-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-row); padding: 1rem; display: flex; flex-direction: column; gap: 0.4rem; }
.value-stat-label { color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.04em; }
.value-stat-num { font-size: 1.8rem; font-weight: 600; letter-spacing: -0.02em; display: inline-flex; align-items: center; gap: 0.4rem; }
.value-stat-num svg { color: var(--signal); }
.value-stat-sub { color: var(--ink-mute); display: inline-flex; align-items: center; gap: 0.3rem; }
.value-stat-sub svg { color: #EF9F27; }
.value-legend { flex-wrap: wrap; row-gap: 0.35rem; }

/* Value-return enable switch (list/board settings) */
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; cursor: pointer; margin-top: 0.5rem; }
.switch-text { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.switch-title { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--ink); }
.switch-title svg { color: var(--signal); flex-shrink: 0; }
.switch-hint { color: var(--ink-mute); }
.switch { position: relative; flex-shrink: 0; width: 32px; height: 18px; border-radius: var(--r-pill); background: var(--surface-3); transition: background var(--t-base) var(--ease); }
.switch.on { background: var(--signal); }
.switch-input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.switch-knob { position: absolute; top: 2px; left: 2px; box-sizing: border-box; width: 14px; height: 14px; border-radius: 50%; background: var(--card); border: 1px solid var(--line); transition: transform var(--t-base) var(--ease); pointer-events: none; }
.switch.on .switch-knob { border-color: transparent; }
.switch.on .switch-knob { transform: translateX(14px); }

/* Drawer property boxes: Congruency (estimate + complexity) + Value Return */
.prop-box { background: var(--surface-1); border: 1px solid var(--line); border-radius: var(--r-row); padding: 0.85rem 0.9rem; }
.prop-box .category-heading { display: flex; align-items: center; gap: 0.4rem; padding: 0; margin: 0 0 0.6rem; }
.prop-box .category-heading svg { color: var(--ink-faint); }
.prop-pair { display: flex; gap: 0.75rem; }
.prop-field { display: flex; flex-direction: column; gap: 0.25rem; flex: 1; min-width: 0; }
.prop-field-label { color: var(--ink-mute); }
.prop-rail-inner > .prop-box { margin-top: 1.25rem; }

/* Minimalist 1–10 rate sliders (value + delay) */
.rate-row { display: flex; align-items: center; gap: 0.5rem; }
.rate-row + .rate-row { margin-top: 0.6rem; }
.rate-row > svg { flex-shrink: 0; color: var(--ink-faint); }
.rate-label { color: var(--ink-mute); flex-shrink: 0; min-width: 3.2rem; }
.rate-slider { flex: 1; accent-color: var(--signal); cursor: pointer; height: 3px; }
.rate-readout { min-width: 1.4rem; text-align: right; font-family: var(--font-mono); color: var(--ink); }

/* ===== Notes =========================================================== */
/* Two panes inside the content column: a fixed index and the document. A note
   row is NOT a row to tick — there is no checkbox anywhere on this surface —
   and an archived note recedes WITHOUT a strikethrough, because isDone on a
   note means filed, not finished. */
.notes-head { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; flex: 1; }
.notes-crumb { color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.12em; }
/* The h1 doubles as the note's rename field, the same trick the inspector
   title uses: plain title text until it takes focus. */
.notes-title-field {
    width: 100%; min-width: 0; border: 0; background: transparent; color: var(--ink);
    padding: 0.15rem 0.3rem; margin: -0.15rem -0.3rem; border-radius: var(--r-field);
}
.notes-title-field:hover { background: var(--surface-2); }
.notes-title-field:focus { outline: none; background: var(--card); box-shadow: inset 0 0 0 2px var(--ink); }
.notes-title-field::placeholder { color: var(--ink-faint); }

.notes-split { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 2rem; align-items: start; }
.notes-index { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
.notes-groups { display: flex; flex-direction: column; gap: 1.5rem; }
.notes-group .category-heading { margin: 0 0 0.5rem; padding: 0 0.75rem; }
.notes-index-empty { color: var(--ink-faint); padding: 0 0.75rem; margin: 0; }

/* A plain note is title + timestamp; a note that grew a body adds the excerpt
   and its block count. No rule between rows (the no-line rule): the active one
   is marked by an inset acid edge on a tonal shift, never by a border. */
.note-row {
    display: flex; flex-direction: column; gap: 0.2rem;
    width: 100%; text-align: left; cursor: pointer;
    padding: 0.6rem 0.75rem; border-radius: var(--r-row);
    background: transparent; color: var(--ink);
    transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.note-row:hover { background: var(--surface-1); }
.note-row:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.note-row.active { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--signal); }
.note-row-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-row-title.untitled { color: var(--ink-faint); }
.note-row-excerpt { color: var(--ink-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-row-meta { display: flex; gap: 0.75rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.06em; }
/* Archived rows recede. Deliberately no line-through: nothing was completed. */
.note-row.archived { background: var(--surface-2); color: var(--ink-faint); }
.note-row.archived .note-row-title, .note-row.archived .note-row-excerpt { color: var(--ink-faint); }
.note-row.archived:hover { background: var(--surface-3); }

/* The document column: a mono meta strip, then the shared block body. */
.notes-doc { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
.notes-doc-meta { display: flex; flex-wrap: wrap; gap: 0.35rem 1rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.08em; }
.notes-doc-empty { padding-top: 0; }

@media (max-width: 920px) {
    /* The index stops being a column of its own and stacks over the document. */
    .notes-split { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
}

/* List options: quiet metadata disclosures and readable, borderless sections. */
.list-settings-dialog { width: min(600px, calc(100vw - 2rem)); }
.list-settings-dialog > .dialog-body { display: flex; flex-direction: column; gap: 1rem; padding-top: 1rem; }
.list-settings-dialog .category-heading { padding: 0; margin: 0; color: var(--ink); }
.list-settings-dialog > .dialog-body > .category-heading { margin-bottom: -0.5rem; }
.list-settings-dialog .input, .list-settings-dialog .prop-select { width: 100%; margin: 0; min-height: 44px; box-sizing: border-box; }
.list-settings-dialog .list-settings-hint { font-family: var(--font-sans); font-size: 14px; line-height: 1.5; color: var(--ink-mute); margin: 0.5rem 0 0; overflow-wrap: anywhere; }
.list-info-button { min-height: 44px; justify-content: flex-start; text-align: left; white-space: normal; }
.list-info-panel { margin-top: 0.75rem; padding: 1rem; background: var(--surface-2); border-radius: var(--r-field); }
.list-info-panel[hidden] { display: none; }
.list-settings-dialog .list-identity { margin: 0; display: grid; gap: 0.75rem; }
.list-settings-dialog .list-identity-row { grid-template-columns: minmax(6rem, 1fr) minmax(0, 2fr); font-size: 12px; line-height: 1.5; }
.list-settings-dialog .list-identity-row dt { color: var(--ink-mute); }
.list-settings-dialog .list-identity-row dd { color: var(--ink); }
.list-members, .list-rigor { padding-top: 0.5rem; }
.list-members-rows { display: grid; gap: 0.75rem; list-style: none; padding: 0; margin: 1rem 0 0; }
.list-member { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-width: 0; }
.list-member-identity { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; min-width: 0; }
.list-member-name { color: var(--ink); font-size: 16px; overflow-wrap: anywhere; }
.list-member .fingerprint-chip { min-height: 36px; max-width: 100%; font-size: 12px; }
.list-member .role-chip { flex-shrink: 0; }
.list-sharing { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.75rem; }
.list-sharing .list-info { flex: 1 1 14rem; }
.list-sharing > .btn { min-height: 44px; }
.list-settings-dialog .switch-row { align-items: flex-start; margin: 0; }
.list-rigor-switch { display: flex; align-items: center; justify-content: center; width: 44px; min-height: 44px; flex-shrink: 0; border: 0; padding: 0; background: transparent; cursor: pointer; border-radius: var(--r-field); }
.list-rigor-switch:disabled { cursor: not-allowed; opacity: 0.6; }
.list-settings-dialog .value-toggle { margin: 0; }
.list-settings-dialog .switch-hint { font-family: var(--font-sans); font-size: 14px; line-height: 1.5; }
.list-settings-dialog button:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.list-settings-dialog .dialog-actions { justify-content: space-between; flex-wrap: wrap; }
@media (max-width: 480px) {
    .list-settings-dialog { padding: 1.25rem; max-height: calc(100dvh - 2rem); }
    .list-settings-dialog .list-identity-row { grid-template-columns: 1fr; gap: 0.15rem; }
    .list-settings-dialog .dialog-actions .btn { min-height: 44px; }
}
