﻿/* styles.css
   ----------------------------------------------------------------
   Phase 2: most layout and visual styling now lives in Tailwind
   utility classes (in index.html). This file is for things Tailwind
   can't express cleanly:

     1. The PDF canvas (custom shadow + bitmap rendering hints).
     2. The comment marker dots (transitions, selected state).
     3. The done-state visual (translucent dots and grayed thread rows).

   Anything that's plain spacing, color, or layout should use a
   Tailwind class in the HTML instead.
   ---------------------------------------------------------------- */

/* ── Design system tokens (see DESIGN.md §2) ──────────────────────── */

:root {
  --surface-app:          #f5f5f4;
  --surface-viewer:       #fafaf9;
  --surface-card:         rgba(255,255,255,0.72);
  --surface-card-hover:   rgba(255,255,255,0.9);
  --surface-card-selected:#ffffff;
  --surface-raised:       #ffffff;
  --border-subtle:        rgba(0,0,0,0.06);
  --border-strong:        rgba(0,0,0,0.14);
  --text-primary:         #1f2937;
  --text-muted:           #6b7280;
  --text-faint:           #9ca3af;
  --accent-active:        #1f2937;
  --accent-danger:        #ef4444;
  --accent-warning:       #f59e0b;
  --accent-success:       #16a34a;
  --radius-card:          8px;
  --radius-control:       6px;
  --shadow-card:          0 1px 3px rgba(0,0,0,0.05);
  --shadow-card-raised:   0 3px 10px rgba(0,0,0,0.10);
  --shadow-lift:          0 8px 24px rgba(0,0,0,0.14), 0 2px 6px rgba(0,0,0,0.08);
  --shadow-menu:          0 4px 12px rgba(0,0,0,0.10);
  --shadow-modal:         0 8px 40px rgba(0,0,0,0.18);  /* canonical modal-card elevation (see DESIGN.md §2) */

  /* Keyboard focus ring — one indigo box-shadow ring reused by every
     :focus-visible rule (DESIGN.md §2). Kept as a soft indigo tint so it reads
     on the light card surfaces without shouting. */
  --accent-focus-ring:    rgba(99,102,241,0.18);
  /* Recessed input/well surface (search fields, webhook URL box). */
  --surface-sunken:       rgba(0,0,0,0.04);

  /* App-shell layout vars for the LSB↔header merge. */
  --header-h: 42px;   /* fixed header band height; hamburger + LSB tabs match it */
  --ham-w: 44px;      /* hamburger overlay width = far-left reserved gutter */
  --rsb-corner-w: 88px; /* far-right reserved gutter for the pinned bell + RSB toggle */

  /* Type scale (see DESIGN.md §2a). 2 text sizes; titles = body + 600 weight.
     Mirrored in tailwind.config.js fontSize (React reads from there).
     Glyph/icon sizes are local (not part of the text scale) — see DESIGN.md §4. */
  --fs-caption: 13px;  /* dense chrome: toolbar, pills, badges, timestamps, small buttons, secondary labels */
  --fs-body:    15px;  /* content: comments, layer names, menu items, rows, inputs, titles (bold), header band */
  --fs-glyph:   15px;  /* button glyphs (NOT text): dropdown carets, ×/close, source-icon — one coherent size */

  /* One monospace stack for all signal/code values. */
  --font-mono: ui-monospace, 'Cascadia Code', 'Fira Code', monospace;

  /* Interactive indigo (focus ring, favourites, primary buttons, links).
     Distinct from --accent-active (#1f2937 dark = active/open-in-set hairline). */
  --accent-primary:        #6366f1;
  --accent-primary-strong: #4f46e5;  /* hover/active darker indigo */
  /* Darker danger for hover/active (base stays --accent-danger). */
  --accent-danger-strong:  #dc2626;
}

/* ── Keyboard focus ring (see DESIGN.md §2 / §5) ──────────────────────────
   One shared :focus-visible ring for every interactive family, so keyboard
   users get a consistent indigo halo. :focus-visible only — a mouse click
   never rings (the browser suppresses :focus-visible on pointer activation).
   The ring is an inset-safe box-shadow referencing --accent-focus-ring; it
   layers over each control's own resting shadow via the comma list. */
.layer-row__header:focus-visible,
.home-row__header:focus-visible,
.split-btn__main:focus-visible,
.split-btn__caret:focus-visible,
.symbol-btn:focus-visible,
.rsb-tool-btn:focus-visible,
.left-sidebar-tab-btn:focus-visible,
.thread-card:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-focus-ring);
}

/* Hamburger: account/settings trigger, pinned far-left above the LSB.
   Mount node is #settings-dropdown-root (React SettingsDropdown).
   Fixed so it never moves on LSB toggle; z above the LSB overlay (20). */
#settings-dropdown-root {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--ham-w);
  height: var(--header-h);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* RSB corner: notification bell + RSB toggle, pinned far-right above the RSB.
   Mirrors the pinned hamburger. Fixed so it never moves on RSB toggle; z above
   the RSB overlay (20). Its parent #toolbar right <div> stays a flex:1 spacer. */
#rsb-corner {
  position: fixed;
  top: 0;
  right: 0;
  width: var(--rsb-corner-w);
  height: var(--header-h);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

/* Header band: fixed height so the hamburger and LSB tabs align to it.
   #toolbar already carries Tailwind px-3/py-1.5; the ID rule wins on
   specificity, so set the height and the left offset here. */
#toolbar {
  height: var(--header-h);
  box-sizing: border-box;
  /* Closed default: clear the hamburger gutter (left) and the corner cluster (right). */
  padding-left: var(--ham-w);
  padding-right: var(--rsb-corner-w);
}

/* Open: push the header's controls past the LSB so nothing hides under it
   and the LSB toggle button lands just past the LSB's right edge. */
body.lsb-open #toolbar {
  padding-left: var(--sidebar-w, 256px);
}

/* Closed: hide the LSB overlay entirely (tabs included). */
body:not(.lsb-open) #left-sidebar {
  display: none;
}

/* Open: push the header's controls left so nothing hides under the RSB. */
body.rsb-open #toolbar {
  padding-right: var(--right-sidebar-w, 384px);
}

/* Closed: hide the RSB overlay entirely (page-label + filters included). */
body:not(.rsb-open) #sidebar {
  display: none;
}

/* ── Toolbar centre group — shrink gracefully so the body never scrolls ──
   The header is left-spacer(flex:1) / centre group / right-spacer(flex:1).
   Flex items default to min-width:auto, so the centre group refused to
   compress and pushed the Res slider off-screen at narrow widths (≤1000px),
   overlapping the pinned #rsb-corner cluster. Allow the group and its
   subgroups to shrink below their content width, trim the inter-group gap
   as space runs out, and drop the least-essential control (Res slider)
   before anything overlaps. Body must NEVER scroll horizontally. */
#toolbar-center {
  min-width: 0;
  flex-shrink: 1;
}
#toolbar-center > div {
  min-width: 0;
}
/* Narrow: tighten the gap between the three centre subgroups. */
@media (max-width: 1100px) {
  #toolbar-center { gap: 0.75rem; }
}
/* Narrower: the Res slider is the least-essential control (resolution is a
   power-user tweak); hide it so Prev/Next + Zoom keep their room and the
   far-right notification bell + comments toggle never overlap. */
@media (max-width: 1000px) {
  #toolbar-res { display: none; }
}

/* ── Custom scrollbars — transparent track, faint gray thumb ─────── */

::-webkit-scrollbar { width: 7px; height: 7px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(156,163,175,0.55); /* --text-faint at 55% */
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover { background: rgba(156,163,175,0.8); }
::-webkit-scrollbar-button { display: none; }

/* ── Split button primitive (see DESIGN.md §3.2) ───────────────────── */

.split-btn {
  position: relative;
  display: inline-flex;
  align-items: stretch;
  /* Taller than the prior content-derived ~20px height (DESIGN.md §3.2). */
  min-height: 26px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  background: var(--surface-raised);
  overflow: visible;
  flex-shrink: 0;
}

.split-btn__main {
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-caption);
  font-family: inherit;
  font-weight: 500;
  color: var(--text-primary);
  padding: 0 6px;
  white-space: nowrap;
  border-radius: var(--radius-control) 0 0 var(--radius-control);
  line-height: 1.2;
}
.split-btn__main:hover { background: #f9fafb; }

.split-btn__caret {
  background: none;
  border: none;
  border-left: 1px solid var(--border-strong);
  cursor: pointer;
  font-size: var(--fs-glyph);
  color: var(--text-muted);
  padding: 0 5px;
  border-radius: 0 var(--radius-control) var(--radius-control) 0;
  line-height: 1;
  display: flex;
  align-items: center;
}
.split-btn__caret:hover { background: #f9fafb; color: var(--text-primary); }

.split-btn__close {
  background: #111;
  color: #fff;
  border: none;
  border-left: 1px solid #111;
  cursor: pointer;
  font-size: var(--fs-glyph);
  padding: 0 5px;
  border-radius: 0 var(--radius-control) var(--radius-control) 0;
  line-height: 1;
  display: flex;
  align-items: center;
}
.split-btn__close:hover { background: #333; }
/* When the close button is present, remove right rounding from the caret */
.split-btn:has(.split-btn__close) .split-btn__caret { border-radius: 0; }

/* Caret-only variant (set members in Navigate tab): no primary label button.
   The caret fills the left edge, so it gets full left rounding and no left border. */
.split-btn--caret-only .split-btn__caret {
  border-left: none;
  border-radius: var(--radius-control);
}
.split-btn--caret-only:has(.split-btn__close) .split-btn__caret {
  border-radius: var(--radius-control) 0 0 var(--radius-control);
}

/* No-PDF variant — yellow close button */
.split-btn--nopdf .split-btn__close {
  background: var(--accent-warning);
  border-left-color: var(--accent-warning);
}
.split-btn--nopdf .split-btn__close:hover { background: #d97706; }

/* Empty-acetate variant — a call-to-action to give a brand-new empty acetate a
   file. Purple OUTLINE style (not a solid fill): the whole shell takes the
   --accent-primary border and the "Attach PDF" main reads as accent-coloured
   text on a transparent ground, mirroring the .lai-attach attach button (the
   purple that appears when an opened PDF doesn't match — see §Attach|Inspect).
   Was solid --accent-warning yellow through 2026-06-27; recoloured to the
   attach-purple outline 2026-07-10 (Hugo: read it as "Find pdf, but purple"). */
.split-btn--empty {
  border-color: var(--accent-primary);
}
.split-btn--empty .split-btn__main {
  background: transparent;
  color: var(--accent-primary);
  border-radius: var(--radius-control) 0 0 var(--radius-control); /* round left only */
  padding: 0 12px;
}
.split-btn--empty .split-btn__main:hover { background: #eef2ff; }
/* Caret shares the accent outline so the split reads as one purple control. */
.split-btn--empty .split-btn__caret {
  border-left-color: var(--accent-primary);
  color: var(--accent-primary);
}
.split-btn--empty .split-btn__caret:hover { background: #eef2ff; color: var(--accent-primary-strong); }

/* The row itself reads as a placeholder: a dashed edge sets it apart from
   acetates that already carry a file. */
.layer-row--empty {
  border-style: dashed;
  border-color: var(--border-strong);
}

/* Brief highlight when the + button selects a row — especially a reused
   untouched empty acetate, so the click reads as "here it is", not a no-op. */
@keyframes layer-row-flash {
  0%   { background: color-mix(in srgb, var(--accent-primary) 22%, transparent); }
  100% { background: transparent; }
}
.layer-row.is-flash {
  animation: layer-row-flash 600ms ease-out;
}

/* Cloud source icon inside split button main label */
.split-btn__source-icon {
  font-size: var(--fs-glyph);
  line-height: 1;
  vertical-align: -2px;
  margin-right: 4px;
  color: var(--text-muted);
}

/* Paperclip attach button — left of split button, shown when file is open but unattached */
.layer-row__attach-btn {
  background: var(--accent-primary);
  color: #fff;
  border: none;
  border-radius: var(--radius-control);
  cursor: pointer;
  padding: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.layer-row__attach-btn:hover { background: var(--accent-primary-strong); }
.layer-row__attach-btn:disabled { opacity: 0.5; cursor: default; }
.layer-row__attach-btn svg { width: 11px; height: 14px; }

/* ── Attach | Inspect detail bar ──────────────────────────────────── */
.layer-detail-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.layer-attach-inspect {
  display: flex;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--accent-primary);
  flex-shrink: 0;
}
.layer-attach-inspect button {
  display: flex;
  align-items: center;
  background: transparent;
  color: var(--accent-primary);
  border: none;
  padding: 2px 8px;
  font-size: var(--fs-caption);
  font-weight: 500;
  cursor: pointer;
  line-height: 18px;
  transition: background 0.1s;
}
.layer-attach-inspect button:hover { background: #eef2ff; }
.layer-attach-inspect .lai-attach,
.layer-attach-inspect .lai-refresh { border-right: 1px solid var(--accent-primary); gap: 4px; }
.layer-attach-inspect .lai-attach svg,
.layer-attach-inspect .lai-refresh svg { width: 8px; height: 11px; flex-shrink: 0; }
.layer-attach-inspect .lai-attach:disabled,
.layer-attach-inspect .lai-refresh:disabled { opacity: 0.5; cursor: default; }
.layer-attach-inspect .lai-inspect.is-active {
  background: var(--accent-primary);
  color: #fff;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
}
.layer-attach-inspect .lai-inspect.is-active:hover { background: var(--accent-primary-strong); }

/* ── Symbol button primitive (see DESIGN.md §3.4) ──────────────────
   Small icon-only interactive element. Transparent at rest; subtle
   square background on hover. Apply to any clickable glyph or SVG
   that isn't a full button control.                                  */
.symbol-btn,
.layer-row__peek-btn,
.layer-row__edit-icon {
  border-radius: var(--radius-control);
  transition: background 80ms ease, color 80ms ease;
}
.symbol-btn:hover,
.layer-row__peek-btn:hover,
.layer-row__edit-icon:hover {
  background: rgba(0,0,0,0.06);
  color: var(--text-muted);
}

/* Expand / center-peek button. Lives inside the action cluster
   (.layer-row__header-btns / .home-row__actions) alongside the ★ and split.
   order:-1 pins it to the LEFT of the ★ so the cluster reads `peek · ★ · split`
   (split rightmost). Because peek is the leftmost cluster child, it appears /
   disappears on hover into the flexible name — the ★ and split (pinned to the
   right edge) never move, so a favorited ★ doesn't "jump" (regression 2026-07-01). */
.layer-row__peek-btn {
  display: none;
  order: -1;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  flex-shrink: 0;
}
.layer-row__peek-btn svg { width: 10px; height: 10px; }

/* Show on hover — > scopes to this row's own header, not nested member rows */
.layer-row:hover > .layer-row__header .layer-row__peek-btn { display: flex; }

/* CSS :hover propagates to all ancestors, so hovering a member row also marks
   the parent set row as :hover. Override: hide the set's peek button when a
   member row inside it is the actual hovered element. */
.layer-row--set:not(.is-detail-expanded):has(.layer-set-members:hover) > .layer-row__header .layer-row__peek-btn { display: none; }

/* Always show when selected, or when THIS row's own detail is open.
   Sets keep is-layer-selected through all steps, so we must exclude them
   when is-expanded is also set (step 2: sublayers visible, no detail = hover only). */
.layer-row.is-layer-selected:not(.layer-row--set) > .layer-row__header .layer-row__peek-btn,
.layer-row--set.is-layer-selected:not(.is-expanded) > .layer-row__header .layer-row__peek-btn,
.layer-row:not(.layer-row--set).is-expanded > .layer-row__header .layer-row__peek-btn,
.layer-row.is-detail-expanded > .layer-row__header .layer-row__peek-btn { display: flex; }

/* Member sort toggle — set headers only. Flat symbol-btn family, hover-revealed
   like peek (order:-1, LEFT of peek so DOM order reads sort · peek · ★ · split);
   filled/visible-at-rest when the set's member_sort_auto is ON, mirroring the
   favourited ★ (same --accent-primary). See DESIGN.md §3.4. */
.layer-row__sort-btn {
  display: none;
  order: -1;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  flex-shrink: 0;
  border-radius: var(--radius-control);
  transition: background 80ms ease, color 80ms ease;
}
.layer-row__sort-btn svg { width: 12px; height: 12px; }
.layer-row__sort-btn:hover { background: rgba(0,0,0,0.06); color: var(--text-muted); }
.layer-row__sort-btn.is-active { display: flex; color: var(--accent-primary); }
.layer-row__sort-btn.is-active:hover { color: var(--accent-primary-strong); background: rgba(99,102,241,0.08); }

/* Show on set-header hover / selection / expansion — same rules as peek, but
   scoped to set rows only (leaf rows never render this button). */
.layer-row--set:hover > .layer-row__header .layer-row__sort-btn,
.layer-row--set.is-layer-selected:not(.is-expanded) > .layer-row__header .layer-row__sort-btn,
.layer-row--set.is-expanded > .layer-row__header .layer-row__sort-btn,
.layer-row--set.is-detail-expanded > .layer-row__header .layer-row__sort-btn { display: flex; }
/* Hide the set's sort button when a member row inside it is the hovered element
   (CSS :hover propagates to ancestors — same guard as peek/★). */
.layer-row--set:not(.is-detail-expanded):has(.layer-set-members:hover) > .layer-row__header .layer-row__sort-btn { display: none; }
/* But an ACTIVE toggle stays visible even at rest (like a favourited ★). */
.layer-row--set > .layer-row__header .layer-row__sort-btn.is-active { display: flex; }

/* Favorite star button — hidden at rest; shown on row hover/selection */
.layer-fav-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  flex-shrink: 0;
  border-radius: var(--radius-control);
  transition: background 80ms ease, color 80ms ease;
}
.layer-fav-btn:hover { background: rgba(0,0,0,0.06); color: var(--text-muted); }
.layer-fav-btn.is-favorite { color: var(--accent-primary); display: flex; }
.layer-fav-btn.is-favorite:hover { color: var(--accent-primary-strong); background: rgba(99,102,241,0.08); }
.layer-row:hover > .layer-row__header .layer-fav-btn,
.layer-row.is-layer-selected > .layer-row__header .layer-fav-btn,
.layer-row.is-expanded > .layer-row__header .layer-fav-btn { display: flex; }
/* Hovering a member row also marks the parent set :hover (CSS propagation) —
   hide the set header's own ★ in that case, mirroring the peek-button guard. */
.layer-row--set:has(.layer-set-members:hover) > .layer-row__header .layer-fav-btn { display: none; }

/* Home rows reuse the SAME .layer-row__peek-btn / .layer-fav-btn markup (ADR
   0025) but live under .home-row / .home-row__main / .home-row__actions, not
   .layer-row / .layer-row__header — so the Acetates hover-reveal rules above
   never match. Mirror them for Home so the peek (open-in-modal) button and the
   ★ appear on hover. The ★ lives in .home-row__main; the peek in
   .home-row__actions. Direct-child combinators (`> .home-row__main` /
   `> .home-row__actions`) keep a row's reveal to its OWN buttons, never a
   nested member's — so hovering a set card does not flash every member's ★. The
   ★ now lives in .home-row__actions (left of the split, peek floats in on its
   left — regression 2026-07-01), so reveal it there, not in .home-row__main. */
/* Leaf and set rows both wrap __main/__actions in a `.home-row__header` now, so
   reveal the ★/peek on HEADER hover via a direct-child combinator — this keeps a
   row's reveal to its OWN header buttons, never a nested member's (hovering a set
   card must not flash every member's ★). */
.home-row > .home-row__header:hover > .home-row__actions .layer-fav-btn,
.home-row > .home-row__header:hover > .home-row__actions .layer-row__peek-btn { display: flex; }
/* Member auto-sort toggle (Task 8) — Home set headers use .home-row__actions,
   not .layer-row__header, so the Acetates .layer-row--set hover rule never
   matches; mirror it here. .is-active already stays visible at rest (global
   rule above), this only handles the hover-reveal-when-off case. */
.home-row > .home-row__header:hover > .home-row__actions .layer-row__sort-btn { display: flex; }

/* Danger variant — missing-file primary */
.split-btn--danger .split-btn__main {
  color: var(--accent-danger);
}
.split-btn--danger {
  border-color: #fca5a5;
}
.split-btn--danger .split-btn__caret {
  border-left-color: #fca5a5;
}

/* Dropdown menu */
.split-btn__menu {
  position: absolute;
  top: calc(100% + 3px);
  right: 0;
  min-width: 140px;
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  box-shadow: 0 4px 12px rgba(0,0,0,0.10);
  z-index: 200;
  padding: 3px 0;
}
.split-btn__menu[hidden] { display: none; }
.split-btn__menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 6px 12px;
  font-size: var(--fs-caption);
  font-family: inherit;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-primary);
  white-space: nowrap;
}
.split-btn__menu button:hover { background: #f3f4f6; }
.split-btn__menu button:disabled { color: var(--text-faint); cursor: default; }
.split-btn__menu button:disabled:hover { background: none; }
.split-btn__menu .layer-row__delete {
  color: var(--accent-danger);
  border-top: 1px solid var(--border-strong);
}
.split-btn__menu .layer-row__delete:hover {
  background: var(--accent-danger);
  color: #fff;
}

/* ── Toolbar shared classes ────────────────────────────────────────── */
/* ~80% of original size: xs text, tighter padding.                    */

.toolbar-btn {
  padding: 2px 8px;
  font-size: var(--fs-caption);
  font-family: inherit;
  background: #fff;
  border: 1px solid #d1d5db;
  border-radius: 4px;
  cursor: pointer;
  color: var(--text-primary);
  transition: background 80ms ease;
  white-space: nowrap;
}
.toolbar-btn:hover:not(:disabled) { background: #f9fafb; }
.toolbar-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.toolbar-btn--icon {
  min-width: 1.6rem;
  padding: 2px 4px;
  font-weight: 600;
  text-align: center;
}

.toolbar-label {
  font-size: var(--fs-caption);
  font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
}

.toolbar-text {
  font-size: var(--fs-caption);
  color: var(--text-primary);
}

.toolbar-zoom-input {
  width: 2.6rem;
  padding: 2px 4px;
  font-size: var(--fs-caption);
  text-align: right;
  border: 1px solid #d1d5db;
  border-radius: 4px;
  tabular-nums: true;
}
.toolbar-zoom-input:disabled { opacity: 0.4; }

/* Icon-only button (LSB toggle). */
.toolbar-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 5px;
  cursor: pointer;
  color: var(--text-muted);
  transition: background 80ms ease, color 80ms ease, border-color 80ms ease;
}
.toolbar-icon-btn:hover {
  background: #f3f4f6;
  border-color: #e5e7eb;
  color: var(--text-primary);
}
.toolbar-icon-btn[aria-pressed="true"] {
  color: var(--accent-primary-strong);
}

/* ---------- Request / Bug report (modal opened from the account dropdown) ---------- */

/* Small centered card inside the shared .ws-modal__backdrop overlay. */
.feedback-modal__card {
  background: var(--surface-raised);
  border-radius: var(--radius-card);
  box-shadow: 0 8px 40px rgba(0,0,0,0.18);
  width: min(92vw, 440px);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.feedback-modal__title {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
}
.feedback-modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.feedback-modal__sent {
  padding: 16px 4px;
  text-align: center;
  font-size: var(--fs-body);
  color: var(--text-muted);
}
.feedback-composer__input {
  width: 100%;
  min-height: 110px;
  resize: vertical;
  padding: 8px 10px;
  font-size: var(--fs-body);
  font-family: inherit;
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  outline: none;
  box-sizing: border-box;
}
.feedback-composer__input:focus {
  border-color: var(--text-faint);
}
.feedback-composer__error {
  font-size: var(--fs-caption);
  color: var(--accent-danger-strong);
}

/* ---------- Global: make [hidden] always win over display utility classes ---------- */

/* Tailwind's .flex/.grid etc. override the native [hidden] attribute because
   they share the same specificity and utilities are declared later. This rule
   restores the expected behaviour throughout the app. */
[hidden] { display: none !important; }

/* ---------- The PDF canvas ---------- */

/* canvas-wrapper is a block element; JS sets its margin dynamically to
   create 50% extra pan room on each side. This rule is the baseline
   before any PDF is loaded — JS overrides margin once a page renders. */
#canvas-wrapper {
  display: block;
  width: fit-content;
  margin: 20px auto;
}

/* The bitmap size is set in JS (canvas.width/height); the CSS size is
   also set in JS to match the current zoom level. We add the soft
   drop shadow here, plus a hint to the browser to keep the bitmap
   sharp when up- or down-scaled. */
#page-canvas {
  background: #ffffff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
  display: block;
  height: auto;
  /* Slightly nicer scaling at extreme zoom levels. */
  image-rendering: -webkit-optimize-contrast;
}

/* ---------- Markers overlay ---------- */

/*
  The overlay sits *on top of* the canvas (same parent: #canvas-wrapper).
  It's positioned absolutely to cover the canvas exactly. The overlay
  itself never receives clicks (pointer-events: none) — only the
  individual marker buttons do. This way:

    • Double-clicks on empty space land on the canvas-wrapper, where
      app.js listens for them to create new comments.
    • Clicks on existing markers are caught by the buttons themselves
      (which set pointer-events: auto, see below).
*/
#markers-overlay {
  position: absolute;
  inset: 0;            /* shorthand for top:0 right:0 bottom:0 left:0 */
  pointer-events: none;
}

/*
  A single marker dot. JS sets `style.left` and `style.top` in CSS
  pixels (= PDF point × current zoom) and adds class names for state.

  We use translate(-50%, -50%) so the (left, top) coordinate is the
  *center* of the dot, not its top-left corner — that matches what
  users expect when clicking on a drawing.
*/
/* The marker element is the transparent hit area; the visual dot is
   rendered by ::after so clickable size and visual size are independent. */
.marker {
  pointer-events: auto;
  position: absolute;
  width: 18px;       /* hit area — 2 px larger than visual on each side */
  height: 18px;
  border-radius: 50%;
  border: none;
  background: transparent;
  transform: translate(-50%, -50%);
  transition: width 120ms ease, height 120ms ease;
  cursor: pointer;
  padding: 0;
}

/* Visual dot — sized and styled independently from the hit area. */
.marker::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.4);
  background: var(--dot-color, #DB2777);
  transition: width 120ms ease, height 120ms ease, opacity 120ms ease;
}

/* Selected state — visual grows to 22 px; hit area matches. */
.marker.is-selected {
  width: 22px;
  height: 22px;
}
.marker.is-selected::after {
  width: 22px;
  height: 22px;
  box-shadow: 0 0 0 2px #5B21B6, 0 0 0 4px rgba(91, 33, 182, 0.3);
}

/* Priority dot colour — inherited by ::after via --dot-color. markers-overlay.ts
   sets this inline from the workspace priority option colour (--notion-c-*-dot);
   these class rules are the fallback before definitions load and match the
   default option colours (High=red, Medium=yellow, Low=blue). */
.marker.priority-high   { --dot-color: var(--notion-c-red-dot); }
.marker.priority-medium { --dot-color: var(--notion-c-yellow-dot); }
.marker.priority-low    { --dot-color: var(--notion-c-blue-dot); }

/* Done state — fade the visual dot only. */
.marker.is-done::after {
  opacity: 0.25;
}

/* Dragging state — shrink the dot slightly to give "picked up" feedback. */
.marker.is-dragging {
  width: 14px;
  height: 14px;
}
.marker.is-dragging::after {
  width: 10px;
  height: 10px;
  box-shadow: 0 0 0 1px #5B21B6, 0 0 0 3px rgba(91, 33, 182, 0.2), 0 2px 8px rgba(0, 0, 0, 0.3);
}

/* ---------- Sidebar — done thread treatment ---------- */

/* Threads marked done appear grayed out in the list and in the
   thread view. Tailwind doesn't have a one-class shortcut for this
   combination, so we keep it here. */
.thread-card.is-done,
.thread-view.is-done #thread-messages,
.thread-view.is-done #thread-messages * {
  color: var(--text-muted);
}
.thread-card.is-done { opacity: 0.7; }

.thread-card.is-done .priority-pill,
.thread-view.is-done .priority-pill {
  filter: grayscale(0.7);
  opacity: 0.7;
}

/* Priority pill — used in thread rows and the Thread View priority button. */
.priority-pill {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 9999px;
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  min-width: 80px;       /* fixed width so pills line up regardless of label length */
  text-align: center;
  box-sizing: border-box;
}
/* When a pill is a <button>, strip browser button defaults. */
button.priority-pill {
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.02em;
}
/* Pill background/text colours come from the workspace priority option colours,
   applied inline via --notion-c-*-bg / --notion-c-*-text (see PriorityPicker). */

/* ── Thread cards (right sidebar list) ─────────────────────────────────
   Each comment is a card on the --surface-app panel, mirroring the
   left sidebar's .layer-card language: white sheet, subtle edge, soft
   shadow; selection raises the card and adds a 3 px accent left border. */
.thread-card {
  display: flex;
  gap: 8px;
  padding: 10px 12px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid transparent;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  user-select: none;
  transition: background 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}
.thread-card:hover {
  background: var(--surface-card-hover);
}
.thread-card.is-selected {
  background: var(--surface-card-selected);
  border-left-color: var(--accent-active);
  box-shadow: var(--shadow-card-raised);
}

/* Reply card (thread detail view): the reply text sits in a card with the same
   sheet + resting shadow as an unselected thread card; the author/date/actions
   meta row sits below it, outside the card (see .thread-reply-meta). */
.thread-reply-card {
  padding: 8px 10px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}
.thread-reply-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 3px 4px 0;   /* sits close under its card */
}
/* In the detail meta row the author reads as a label alongside Edit/Copy:
   faint grey like those actions, but bold. (The list card author stays
   --text-primary / normal weight — this override is scoped to the meta row.) */
.thread-reply-meta .thread-card__author {
  color: var(--text-faint);
  font-weight: 600;
}

/* Thread detail message list: each <li> is a comment block (card + meta row).
   Generous gap between blocks so the meta row reads as belonging to the card
   above it, not the card below. */
.thread-message-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Author line: bare avatar + full name (no chip), muted timestamp. */
.thread-card__author {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption);
  font-weight: 400;
  color: var(--text-primary);
  white-space: nowrap;
}

/* ── RSB toolbar: Sort / Filter / Isolate dropdown triggers ── */
.rsb-tool-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 12px;
  border-radius: 9999px;
  font-size: var(--fs-caption);
  font-weight: 500;
  font-family: inherit;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid #d1d5db;
  background: transparent;
  color: var(--text-muted);
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.rsb-tool-btn:hover {
  background: #f3f4f6;
  color: var(--text-primary);
}
.rsb-tool-btn.is-open,
.rsb-tool-btn.is-active {
  background: #e5e7eb;
  color: var(--text-primary);
  border-color: #e5e7eb;
}
.rsb-tool-btn__arrow {
  font-size: var(--fs-caption);
  color: var(--text-primary);
}
/* Leading icon — inherits the button's text/active colour via currentColor. */
.rsb-tool-btn__icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}
.rsb-tool-btn__label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* ── RSB search: expanding search bar ──
   The search trigger is a circular icon-only button at the right end of the
   toolbar row. Clicking it sets `.is-searching` on the row, which (a) collapses
   the Sort/Filter/Isolate/All buttons to icon-only circles and (b) lets the
   search field grow to fill the freed space. The magnifier sits at the field's
   right end and closes the search. */
/* Icon-only state — applied while the search field is expanded
   (`.is-searching`) and, responsively, when the row is too narrow to fit the
   labelled buttons (`[data-compact]`, set by the fit measurement in
   SidebarHeader.tsx). */
.rsb-toolbar.is-searching .rsb-tool-btn__label,
.rsb-toolbar[data-compact] .rsb-tool-btn__label {
  display: none;
}
.rsb-toolbar.is-searching .rsb-tool-btn:not(.rsb-search-btn),
.rsb-toolbar[data-compact] .rsb-tool-btn:not(.rsb-search-btn) {
  width: 28px;
  height: 28px;
  padding: 0;
  justify-content: center;
  flex-shrink: 0;
}
/* The collapsed search trigger is always a circle (icon only). */
.rsb-search-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  justify-content: center;
  flex-shrink: 0;
}
/* Icon-only circular variant — used by the thread-detail nav buttons so they
   match the 28px search-trigger height. Keeps the toolbar row (and the divider)
   the exact same height in the list and detail views, so nothing shifts on enter. */
.rsb-tool-btn--circle {
  width: 28px;
  height: 28px;
  padding: 0;
  justify-content: center;
  flex-shrink: 0;
  font-size: var(--fs-body);
}
.rsb-search.is-open {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  border: 1px solid #d1d5db;
  border-radius: 9999px;
  padding: 2px 4px 2px 12px;
}
.rsb-search.is-open input {
  all: unset;
  flex: 1;
  min-width: 0;
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.rsb-search.is-open input::placeholder {
  color: var(--text-faint);
}
/* Search trigger inside the open field keeps the circular button shape but
   needs no border (the field already draws one). */
.rsb-search.is-open .rsb-search-btn {
  border-color: transparent;
}

/* ── Thread group header (DESIGN.md §3.22) ──
   Muted separator between thread-card groups in the "All comments" scope.
   Lighter than .page-group-header: it delimits groups inside the list rather
   than anchoring a top-level section. */
.thread-group-header {
  padding: 10px 12px 2px;
  margin-top: 8px;
  border-top: 1px solid var(--border-subtle);
  font-size: var(--fs-caption);
  font-weight: 500;
  color: var(--text-muted);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* No top border / margin on the very first group so it doesn't float. */
.thread-group-header:first-child {
  margin-top: 0;
  border-top: none;
}

/* Acetates list group header — shown above each project/owner group under the
   project / owner sorts. Mirrors .thread-group-header (centered, gray, caption
   size) so the two sidebars read consistently. */
.layer-group-header {
  padding: 10px 12px 4px;
  margin-top: 8px;
  font-size: var(--fs-caption);
  font-weight: 500;
  color: var(--text-muted);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.layer-group-header:first-child {
  margin-top: 0;
}

/* ── RSB dropdown menus (rendered inside a Popover panel) ── */
.rsb-menu {
  display: flex;
  flex-direction: column;
  padding: 4px;
  min-width: 168px;
}
.rsb-menu__item {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 10px;
  border-radius: 6px;
  font-size: var(--fs-body);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 80ms ease;
}
.rsb-menu__item:hover {
  background: #f3f4f6;
}
.rsb-menu__item.is-active {
  background: #e5e7eb;
  color: var(--text-primary);
  font-weight: 600;
}
.rsb-menu__label {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rsb-menu__arrow {
  font-size: var(--fs-caption);
  color: var(--text-primary);
}
/* Checkbox marker for the Filter and Isolate menus. */
.rsb-menu__check {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  border: 1px solid #d1d5db;
  background: #fff;
  position: relative;
  transition: background 80ms ease, border-color 80ms ease;
}
.rsb-menu__check.is-checked {
  background: #4b5563;
  border-color: #4b5563;
}
.rsb-menu__check.is-checked::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 3px;
  height: 7px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* Assignee pill — placeholder for unassigned threads. */
.assignee-pill {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 56px;
  height: 20px;
  border-radius: 9999px;
  font-size: var(--fs-caption);
  font-family: inherit;
  background: transparent;
  border: 0.5px solid #e5e7eb;
  color: var(--text-faint);
  box-sizing: border-box;
  transition: background 120ms ease;
}
.assignee-pill:hover {
  background: #e5e7eb;
}

.assignee-pill--sm {
  min-width: 36px;
  height: 14px;
  font-size: var(--fs-caption);
}

/* ---- Me-chip (settings trigger) ---- */
.me-chip {
  cursor: pointer;
}
.me-chip:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}

/* ---- Settings dropdown ---- */
.settings-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 234px;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.10);
  display: flex;
  flex-direction: column;
  padding: 4px 0;
  z-index: 100;
}
.settings-dropdown__item {
  all: unset;
  display: block;
  padding: 7px 14px;
  font-size: var(--fs-body);
  color: var(--text-primary);
  cursor: pointer;
  box-sizing: border-box;
  width: 100%;
  transition: background 80ms ease;
}
.settings-dropdown__item:hover {
  background: #f3f4f6;
}
.settings-dropdown__item:disabled {
  color: var(--text-faint);
  opacity: 0.55;
  cursor: default;
}
.settings-dropdown__item:disabled:hover {
  background: transparent;
}

/* Hamburger trigger button fills the pinned mount. */
.me-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 0;
  color: var(--text-primary);
  border-radius: 6px;
}
.me-chip:hover { background: #f3f4f6; }
.me-chip__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- Settings dropdown: signed-in user block (Slice 3e) ---- */
.settings-auth__user {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 14px;
  border-bottom: 1px solid #f1f1f3;
}
.settings-auth__meta {
  min-width: 0;
}
.settings-auth__name {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-auth__email,
.settings-auth__since {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-auth__disconnect {
  display: inline;
  padding: 0;
  background: none;
  border: none;
  font-size: var(--fs-caption);
  color: var(--text-muted);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}

/* ---- Settings: actions row (Disconnect + drive root link) ---- */
.settings-auth__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}
.settings-auth__drive-root {
  display: inline;
  padding: 0;
  background: none;
  border: none;
  font-size: var(--fs-caption);
  color: var(--text-muted);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.settings-auth__drive-root-input {
  font-size: var(--fs-caption);
  font-family: inherit;
  padding: 1px 4px;
  border: 1px solid var(--accent-primary);
  border-radius: 3px;
  outline: none;
  color: var(--text-primary);
  background: #fff;
  width: 90px;
}
.settings-auth__drive-root-input.is-invalid {
  border-color: var(--accent-danger-strong);
  color: var(--accent-danger-strong);
}

.settings-dropdown__divider {
  border: none;
  border-top: 1px solid #e5e7eb;
  margin: 4px 0;
}

.settings-workspace-block {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 8px 14px 4px 18px;
}
.settings-workspace-block__logo {
  width: 24px;
  height: 24px;
  border-radius: 4px;
  object-fit: cover;
  flex-shrink: 0;
}
.settings-workspace-block__logo--fallback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-primary);
  color: #fff;
  font-size: var(--fs-caption);
  font-weight: 600;
  border-radius: 4px;
}
.settings-workspace-block__name {
  flex: 1;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-workspace-block__gear {
  all: unset;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  color: var(--text-faint);
  cursor: pointer;
  flex-shrink: 0;
}
.settings-workspace-block__gear:hover {
  background: #f3f4f6;
  color: var(--text-primary);
}

/* ---- Settings: workspace switcher (multi-workspace) ----
   The workspace row becomes clickable and gains a switch icon when 2+
   memberships exist; the membership list opens as a submenu panel anchored
   under the row. The degraded workspace-less session reuses the same row
   classes as a plain inline recovery list (no anchor). */

/* Anchor wrapper so the submenu can absolutely position under the row. */
.settings-ws-switch-anchor {
  position: relative;
}

/* The row itself is a click target when switching is available. */
.settings-workspace-block--switchable {
  cursor: pointer;
}
.settings-workspace-block--switchable:hover {
  background: #f3f4f6;
}
/* Keep the icon buttons visible against the hovered row background. */
.settings-workspace-block--switchable .settings-workspace-block__gear:hover,
.settings-workspace-block--switchable .settings-workspace-block__switch:hover {
  background: #e5e7eb;
}

/* Switch icon — same idiom as the gear beside it. */
.settings-workspace-block__switch {
  all: unset;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  color: var(--text-faint);
  cursor: pointer;
  flex-shrink: 0;
}
.settings-workspace-block__switch:hover {
  background: #f3f4f6;
  color: var(--text-primary);
}

/* Anchored membership submenu — the dropdown panel's own visual language
   (same background/border/shadow/radius as .settings-dropdown). */
.settings-ws-switch {
  position: absolute;
  top: calc(100% + 2px);
  left: 8px;
  right: 8px;
  display: flex;
  flex-direction: column;
  padding: 4px 0;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.10);
  z-index: 10;
}

.settings-ws-switch__label {
  padding: 6px 14px 2px;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.settings-ws-switch__status {
  padding: 4px 14px 6px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
/* Reuses .settings-dropdown__item for hover/padding; adds a leading check slot. */
.settings-ws-switch__item {
  display: flex;
  align-items: center;
  gap: 8px;
}
.settings-ws-switch__check {
  flex-shrink: 0;
  width: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent-primary);
}
.settings-ws-switch__name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-ws-switch__item[aria-current="true"] {
  font-weight: 600;
}
.settings-ws-switch__item:disabled {
  cursor: default;
  opacity: 1;
  color: var(--text-primary);
}
.settings-ws-switch__item:disabled:hover {
  background: transparent;
}
.settings-ws-switch__error {
  padding: 2px 14px 6px;
  font-size: var(--fs-caption);
  color: var(--accent-danger-strong);
}

.drive-chip {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 9999px;
  font-size: var(--fs-caption);
}
.drive-chip--none {
  background: #f1f1f3;
  color: var(--text-muted);
}
.drive-open-link {
  font-size: var(--fs-caption);
  color: var(--accent-primary);
  text-decoration: none;
}
.drive-open-link:hover { text-decoration: underline; }
.layer-row__drive-link {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-primary);
  text-decoration: none;
}
.layer-row__drive-link:hover { color: var(--text-muted); }
.layer-row__drive-links { display: flex; flex-direction: column; gap: 2px; }
.layer-row__drive-links-sep { display: none; }

/* ── Left sidebar ──────────────────────────────────────────────────── */

/* Sidebar width is driven by a CSS variable set (and persisted) in left-sidebar.js.
   The initial value here matches the old w-64 (256 px) Tailwind class. */
/* Sidebar width is driven by --sidebar-w (set + persisted in left-sidebar.ts).
   Fixed + top:0 so the LSB runs the full viewport height and merges with the
   header band; its right border is the divider that splits the header. Stays
   an overlay (not in flow) so the PDF viewer never reflows on toggle. */
#left-sidebar {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  width: var(--sidebar-w, 256px);
  min-width: 256px;
  border-right: 1px solid #d1d5db;
  z-index: 20;
}

/* Drag handle — thin hit target on the right edge */
#left-sidebar-resize-handle {
  position: absolute;
  top: 0;
  right: -3px; /* straddles the border */
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 10;
}
#left-sidebar-resize-handle:hover,
#left-sidebar-resize-handle.is-dragging {
  background: linear-gradient(to right, transparent 2px, #374151 2px, #374151 4px, transparent 4px);
  opacity: 0.5;
}

/* Tab bar = the LSB's slice of the header band. Same height and bottom border
   (gray-300, no shadow) as #toolbar so the two halves read as one continuous
   header bar split by the LSB divider. padding-left clears the pinned hamburger
   so the first tab never sits under it. */
.left-sidebar-tabs {
  display: flex;
  align-items: stretch;
  height: var(--header-h);
  flex-shrink: 0;
  padding-left: var(--ham-w);
  border-bottom: 1px solid #d1d5db;
}

/* Each tab button. */
.left-sidebar-tab-btn {
  flex: 1;
  padding: 6px 4px;
  font-size: var(--fs-body);
  font-family: inherit;
  font-weight: 600;
  color: var(--text-faint);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
  white-space: nowrap;
}
.left-sidebar-tab-btn:hover {
  color: var(--text-primary);
}
.left-sidebar-tab-btn--active {
  color: var(--text-primary);
  border-bottom-color: #111;
}

/* Tab indicator dots — shown when tab has noteworthy state. */
.tab-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  margin-left: 7px;
  vertical-align: middle;
  position: relative;
  top: -1px;
}
.tab-dot--yellow { background: var(--accent-warning); }
.tab-dot--purple { background: #c7d2fe; }

/* Count badge on a sidebar tab button (Home "Needs you" count). Reuses the
   same indigo + white as the toolbar notification bell's unread badge —
   --accent-primary is #6366f1 (indigo-500); no new colour. DESIGN.md §2. */
.tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  margin-left: 6px;
  border-radius: 8px;
  background: var(--accent-primary);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  vertical-align: middle;
  position: relative;
  top: -1px;
}

/* ---- Home tab — "my work" cards (DESIGN.md §3.27) ---- */
/* Panel + list share the Acetates gray field; rows are cards on top of it. */
.home-tab {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--surface-app);
}
#react-root { background: var(--surface-app); }
.home-list {
  display: flex;
  flex-direction: column;
  padding: 6px 8px;
}
.home-empty {
  padding: 24px 16px;
  text-align: center;
}
/* Centered gray section header — same language as .layer-group-header (§3.22a). */
.home-section-header {
  text-align: center;
  padding: 10px 12px 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* The single hard divider between Needs you and Recent. */
.home-divider {
  height: 1px;
  background: var(--border-subtle);
  margin: 8px 6px;
}
/* Card — reuses the Acetates .layer-row recipe (surface-card / border / radius /
   shadow / white-on-hover). DESIGN.md §3.27. */
.home-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 6px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  transition: background 120ms ease, box-shadow 120ms ease;
}
.home-row:hover {
  background: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
/* A LEAF row is a column: its `.home-row__header` carries the horizontal name +
   indicators + action cluster, and an optional `.home-row__detail` thread panel
   docks full-width BELOW it. The card padding moves to the header so the panel can
   sit flush to the card's bottom edge (its own chrome draws the seam). A collapsed
   leaf has only the header, so it reads identically to before. Sets keep their own
   `.home-row--set` column rules below and are unaffected (this excludes them). */
.home-row:not(.home-row--set) {
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  gap: 0;
}
.home-row:not(.home-row--set) > .home-row__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  cursor: pointer;
}
.home-row__main {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
/* Home rows reuse the Acetates name pill (.layer-row__name-text — bold, ellipsis)
   and ★ via the shared card header. Acetates only reveals the pen icon + pointer
   cursor when a card is expanded; Home has no expand step, so reveal them on row
   hover instead and always allow the rename pointer. */
.home-row .layer-row__name-text,
.home-row .layer-row__set-title { cursor: pointer; }
.home-row:hover .layer-row__edit-icon { display: flex; }
.home-row__indicators {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Was flex-shrink:0 — that starved the set name to ~36px ("By…") when the
     trailing "· N to do / N new / opened …" chips + set-count filled the row.
     Metadata must yield BEFORE the name: let the chip strip shrink and clip. */
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
}
.home-row__ind { white-space: nowrap; }
/* Set header "· N acetates" count — quiet caption; shrinks/clips before the
   name does (was flex-shrink:0, which pushed the starvation onto the name). */
.home-row__set-count {
  white-space: nowrap;
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Name keeps a readable share — a floor so the trailing metadata truncates
   first instead of collapsing the name (finding 2, 2026-07-20). Mirrors the
   Acetates row, which never crowds the name because its metadata lives in the
   detail body, not on the header line. */
.home-row__main > .layer-row__name,
.home-row__main > .layer-row__set-name {
  flex: 1 1 auto;
  min-width: 6rem;
}
.home-row__actions {
  margin-left: auto;
  flex-shrink: 0;
}
/* Loaded emphasis — the same accent the Acetates row uses (.is-open-in-set →
   --accent-active left border). Home cards carry the shared class; this is the
   .home-row-scoped style (the Acetates rule is .layer-row-scoped). */
.home-row {
  border-left-width: 3px;
  border-left-color: transparent;
}
.home-row.is-open-in-set {
  border-left-color: var(--accent-active);
}
/* Shadow-lift — expanded LEAF card only (v2 panel, mockup 07 primary). Crisp
   white fill + a prominent soft drop-shadow so the card floats above the rest
   of the list; content sits flush underneath (no inset panel, no gray wash).
   Scoped to :not(.home-row--set) so the set's own .is-expanded treatment
   (dissolve + standalone header card, above) is untouched. */
.home-row:not(.home-row--set).is-expanded {
  background: #fff;
  box-shadow: var(--shadow-lift);
}

/* Set card: header on its own row, qualifying members nested below. Styled to
   MATCH the Acetates set container exactly (.layer-row--set / .layer-set-members)
   — a ghost-card stack via box-shadow that dissolves when expanded, the header
   becoming a standalone card. No disclosure caret; the whole header is clickable
   (HomeTab toggles .is-expanded on header click). See DESIGN.md §3.27. */
.home-row--set {
  flex-direction: column;
  align-items: stretch;
  cursor: pointer;
  user-select: none;
  /* Ghost-card stack — two offset cards behind the real one (copied from
     .layer-row--set so both tabs read as "a stack of layers"). */
  box-shadow:
    var(--shadow-card),
    3px 4px 0 0 rgba(255,255,255,0.82),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 rgba(255,255,255,0.65),
    6px 8px 0 1px rgba(0,0,0,0.06);
  transition: box-shadow 80ms ease, background 80ms ease;
  margin-bottom: 14px;
}
/* Hover: keep the ghost cards (override the generic .home-row:hover shadow). */
.home-row--set:hover {
  box-shadow:
    0 2px 6px rgba(0,0,0,0.08),
    3px 4px 0 0 rgba(255,255,255,0.82),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 rgba(255,255,255,0.65),
    6px 8px 0 1px rgba(0,0,0,0.06);
}
.home-row--set > .home-row__header {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}
/* Expanded: keep the "stack of layers" ghost cards when expanded — Home only
   (spec 2026-07-08, decision 6/A). The collapsed set already draws this stack;
   persisting it on expand keeps the set visually distinct from a single leaf now
   that member cards are replaced by the merged panel. Reuses the SAME offset
   white/black card shadows as the collapsed .home-row--set — no new colour/shadow
   token. The Acetates .layer-row--set.is-expanded rule is a separate selector and
   stays a dissolve (untouched). */
.home-row--set.is-expanded {
  /* Keep the card FILL when expanded so the ghost-card stack sits behind one
     solid white card (header + merged panel together), not behind a transparent
     hole showing the app background. Same surface/border/radius as the base
     .home-row and the collapsed set — no new colour. The header + panel below
     draw flush on this fill (both transparent), so padding stays 0. */
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow:
    var(--shadow-card),
    3px 4px 0 0 rgba(255,255,255,0.82),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 rgba(255,255,255,0.65),
    6px 8px 0 1px rgba(0,0,0,0.06);
  padding: 0;
  margin-bottom: 14px;
}
.home-row--set.is-expanded > .home-row__header {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  /* Top/side padding matches the leaf / collapsed-set header (8px 10px). Bottom
     padding is ZEROed on expand: the docked panel's first section
     (.home-row__sec) already contributes 8px of top padding + a hairline, so an
     8px header-bottom padding stacked on top of that read as a doubled 16px gap
     below the name/button — the "extra space below when expanded" (2026-07-08).
     Zeroing it leaves a single clean seam. */
  padding: 8px 10px 0;
  margin-bottom: 0;
}
/* Members collapse by default; revealed on .is-expanded. Each is a full card
   (individual, like the Acetates .layer-set-members), indented under the header. */
.home-set-members {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding: 0 0 4px 16px;
}
.home-row--set.is-expanded > .home-set-members {
  display: flex;
}
/* Member-list expanded set (no tasks/mentions panel — the Recent case): render
   EXACTLY like the Acetates set card, NOT the panel case. The ghost-card stack
   must NOT sit behind the whole card (that wrapped the member cards in a stack
   background — the "strange look"). Instead the CONTAINER dissolves (transparent,
   no stack, no fill/border) and the HEADER becomes the stacked standalone card;
   members sit on the app background below (they carry their own card chrome).
   Mirrors .layer-row--set.is-expanded > .layer-row__header (line ~3065).
   The PANEL case (Needs-you set, .home-row--set.is-expanded WITHOUT this class)
   keeps its stack-behind-everything look — untouched. (2026-07-13) */
.home-row--set.is-expanded.home-row--set-memberlist {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}
.home-row--set.is-expanded.home-row--set-memberlist > .home-row__header {
  /* Stacked standalone card, matching the Acetates expanded set header. Keeps
     the ghost-card stack offsets here (the "stack of layers" cue moves onto the
     header) + a raised shadow. Border/radius/fill = the collapsed-set card. */
  background: var(--surface-card-selected);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow:
    var(--shadow-card-raised),
    3px 4px 0 0 var(--surface-app),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 var(--surface-app),
    6px 8px 0 1px rgba(0,0,0,0.06);
  padding: 8px 10px;
  margin-bottom: 12px;
}
/* ── Recent cards: toned down to recede into the panel ── (2026-07-13)
   The Recent list is secondary to Needs you, so its cards blend into the app
   surface at rest — resting FILL = --surface-app (the panel background) instead
   of the raised --surface-card, and the card drop-shadow is dropped. Emphasis on
   interaction is UNCHANGED: hover still lifts to white (`:not(:hover)` excludes
   the hovered card so the existing .home-row:hover rule keeps winning), the
   selected/open accent (.is-open-in-set left border) is untouched, and an
   expanded leaf still floats white (.home-row:not(.home-row--set).is-expanded,
   higher specificity, wins). No new colour — every value is an existing token. */
/* LEAF: blend the fill + drop the shadow, but KEEP the outline (border) so a
   single acetate still reads as a card, just a quiet one. */
.home-recent > .home-row:not(:hover):not(.home-row--set):not(.is-expanded) {
  background: var(--surface-app);
  box-shadow: none;
}
/* SET: blend the fill, and recolour the ghost-card STACK so its offset cards are
   the panel surface too — only their outline hairlines (rgba(0,0,0,0.06)) stay,
   reading as bare stacked outlines behind the header rather than white cards.
   The main card drop-shadow (var(--shadow-card)) is dropped; the two black
   hairlines are kept verbatim; the two white fills become --surface-app. */
.home-recent > .home-row--set:not(:hover):not(.is-expanded) {
  background: var(--surface-app);
  box-shadow:
    3px 4px 0 0 var(--surface-app),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 var(--surface-app),
    6px 8px 0 1px rgba(0,0,0,0.06);
}
/* Quiet the ★ and the name in Recent's resting state: a favorited ★ shows a
   filled LIGHT gray (--text-faint, close to the card outline) instead of the
   indigo accent, and the layer/set name is dark gray (--text-muted) instead of
   near-black (--text-primary) — both only at rest; hover/selected/expanded keep
   their existing emphasis (the `:not(:hover)` and the higher-specificity state
   rules win over these). */
/* One uniform light gray (--text-faint) for EVERY foreground mark in a resting
   Recent row — the favorited ★, the layer/set name, the split-button label +
   caret, and the "opened …" / "· N acetates" meta — so the whole card reads as a
   single quiet tone (close to the card outline), not a mix of blacks and accents.
   The name is painted by EditableLabel's INNER `.editable-label__text` span,
   which carries its own --text-primary; the outer wrapper alone was overridden by
   it (why the name looked black). Target the inner span. */
.home-recent > .home-row:not(:hover):not(.is-expanded) .layer-fav-btn.is-favorite,
.home-recent > .home-row:not(:hover):not(.is-expanded) .layer-row__sort-btn.is-active,
.home-recent > .home-row:not(:hover):not(.is-expanded) .layer-row__name-text,
.home-recent > .home-row:not(:hover):not(.is-expanded) .layer-row__set-title,
.home-recent > .home-row:not(:hover):not(.is-expanded) .editable-label__text,
.home-recent > .home-row:not(:hover):not(.is-expanded) .home-row__set-count,
.home-recent > .home-row:not(:hover):not(.is-expanded) .home-row__ind--opened,
.home-recent > .home-row:not(:hover):not(.is-expanded) .split-btn__main,
.home-recent > .home-row:not(:hover):not(.is-expanded) .split-btn__caret {
  color: var(--text-faint);
}
/* SPLIT BUTTON: blend its raised white fill into the panel surface at rest so it
   doesn't pop against the toned card. Its border stays (the outline reads as a
   quiet control); hover still lightens via the existing .split-btn__*:hover. The
   danger/nopdf/empty variants keep their own fills (more specific selectors). */
.home-recent > .home-row:not(:hover):not(.is-expanded) .split-btn:not(.split-btn--danger):not(.split-btn--empty) {
  background: var(--surface-app);
}
/* Member rows keep their full .home-row card chrome (border/shadow/background) —
   they read as individual cards, matching Acetates. Just zero the stray margin. */
.home-set-members .home-row {
  margin-bottom: 0;
}

/* ── Home: docked thread-detail panel (Task 10; v2 rewrite Task 8/9) ── */
/* Superseded content language: the v1 "raised banner" look (--surface-raised
   fill + border seam) is replaced by FLUSH content — the panel is just more
   of the same shadow-lift card (mockup 07: "content flush on the card").
   Sections below (.home-row__sec / .home-mentions / .home-row-item) each draw
   their own hairline top border, so no separate panel background/border is
   needed here; the card's own --shadow-lift + white fill (above) already reads
   as "raised". Bottom corners keep --radius-card so the card silhouette stays
   rounded. (Task 9 removes the old `.home-thread-row` markup below; those
   rules are left in place until then.) */
.home-row__detail {
  background: #fff;
  border-radius: 0 0 var(--radius-card) var(--radius-card);
  overflow: hidden;
}

/* Section label — small uppercase gray caption with a hairline top border,
   e.g. "Assigned tasks" (mockup 06/07/08 .sec). Reuses --text-faint / caption
   type scale + --border-subtle; no new colour/size token. */
.home-row__sec {
  font-size: var(--fs-caption);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  padding: 8px 11px 3px;
  border-top: 1px solid var(--border-subtle);
}
/* Status footer that closes an expanded card (ThreadDetailPanel StatusFooter):
   a quiet "{N} open tasks · opened {when}" line with a hairline top seam (reusing
   the .home-row__sec seam language) that gives the panel a visual BASE instead of
   ending abruptly on the last task. Faint caption, no new colour/token. A touch
   more bottom padding than a section label so it reads as breathing room at the
   card's foot; inherits the card's rounded bottom via .home-row__detail. */
.home-row__status {
  padding: 7px 11px 9px;
  border-top: 1px solid var(--border-subtle);
}

/* ── Mentions accordion (MentionAccordion.tsx) — mockups 05/06/07/08 ── */
.home-mentions__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 11px;
  background: none;
  border: none;
  border-top: 1px solid var(--border-subtle);
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.home-mentions__label {
  flex: 1;
  min-width: 0;
}
.home-mentions__caret {
  margin-left: auto;
  color: var(--text-faint);
  flex-shrink: 0;
}
/* Expanded mention list — a faint indigo wash via color-mix on the existing
   --accent-primary token (matches the established color-mix pattern used
   elsewhere in this file, e.g. the layer-row-selected keyframe above); no new
   colour. */
.home-mentions__list {
  background: color-mix(in srgb, var(--accent-primary) 3%, transparent);
  border-top: 1px solid var(--border-subtle);
}

/* ── Two-list row grammar shared by TaskRow / MentionRow (mockup 08) ── */
.home-row-item {
  padding: 5px 11px;
  cursor: pointer;
}
.home-row-item + .home-row-item {
  border-top: 1px solid var(--border-subtle);
}
.home-row-item:hover {
  background: var(--surface-card-hover);
}
/* 5-col grid: page · badge/avatar · text(1fr) · time · action (reserved,
   empty this session — Session 3 fills ✓/✕). */
.home-row-line {
  display: grid;
  /* page · badge · text · meta(time·author) · action.
     - Text track is minmax(0, 1fr): a bare 1fr track has min-width:auto, so a
       long unbroken preview forces the track to expand and overrun the row
       instead of clipping — text-overflow:ellipsis never fires. minmax(0, …)
       lets the track shrink below its content min so the ellipsis engages.
     - Meta track is `auto` (was a fixed 46px): the meta cell holds
       "{time} · {author}" (e.g. "1w · Hugo Franklin"), which is far wider than
       46px, so with white-space:nowrap it overflowed its fixed track and
       collided with the preview text on its left. `auto` sizes the track to the
       meta's real width and the 1fr text track shrinks around it — so the text
       ellipsis-crops right before the time, at any window width
       (Home preview overrun + time/text tangle, fixed 2026-07-07).
     - Badge track is a fixed 34px (was `auto`): the second line (.home-row-sub)
       has no badge, so an `auto` badge track collapsed to 0px there and column 3
       (the text) started further left than the task title above — the reply
       avatar/text didn't align under the title. A fixed track keeps column 3 at
       the same x on both lines. 34px fits every priority pill (High/Med/Low are
       ≤32px) with no clipping (aligned reply column, 2026-07-13). */
  grid-template-columns: 28px 34px minmax(0, 1fr) auto 26px;
  gap: 8px;
  align-items: center;
}
.home-row-pg {
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-caption);
}
/* Sub-layer name in a SET panel's task/mention leading cell (spec 2026-07-08).
   Same thin gray as the page (--text-faint / caption), single-line ellipsis so
   long acetate names clip (full name via title=). Occupies the leading grid cell
   in place of the page; the page relocates to the reply/second line. */
.home-row-sublayer {
  color: var(--text-faint);
  font-size: var(--fs-caption);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Relocated page on the second line (.home-row-sub) — sits in the leading
   grid cell (column 1, empty today) beside the avatar. Reuses the page look.
   No grid-template change: __av/__txt/__time keep their explicit grid-column
   placement; this fills the otherwise-empty col 1. */
.home-row-sub__pg {
  grid-column: 1;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-caption);
}
.home-row-txt {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.home-row-txt--task {
  font-weight: 500;
}
/* Mentions carry no priority pill, so their avatar+text span the badge+text
   columns (col 2/3), reclaiming the fixed 34px badge track that the task-reply
   alignment introduced — the mention sits right after the page, as before
   (2026-07-13). Tasks keep the badge column for their pill. */
.home-row-txt--mention {
  grid-column: 2 / span 2;
}
.home-row-txt .avatar {
  margin-right: 6px;
  vertical-align: middle;
}
/* Keep the actor avatar on the SAME line as the actor text. The photo variant
   is display:block globally (.avatar:not(.avatar--initials), specificity 0,2,0);
   .home-row-txt .avatar (also 0,2,0) tied and LOST on source order, so a photo
   avatar stayed block and pushed the text onto its own line (mention row rendered
   as 2–3 lines). Raise specificity to 0,3,0 so both variants sit inline
   (avatar-on-own-line, fixed 2026-07-13). */
.home-mention .home-row-txt .avatar,
.home-row-item .home-row-txt .avatar {
  display: inline-block;
}
.home-row-meta {
  color: var(--text-faint);
  font-size: var(--fs-caption);
  white-space: nowrap;
  text-align: right;
  justify-self: end;
}
/* Reserved action-cell — sized so columns stay aligned. Holds the hover-reveal
   action: a native "done" checkbox on tasks (identical to the RSB thread box),
   a ✕ "dismiss" button on mentions. */
.home-row-act {
  justify-self: center;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.home-row-act > button,
.home-row-act > input[type="checkbox"] {
  visibility: hidden;
  cursor: pointer;
}
.home-row-act > button {
  width: 22px;
  height: 22px;
  border: none;
  border-radius: var(--radius-sm, 4px);
  background: transparent;
  font-size: var(--fs-caption);
  line-height: 22px;
  text-align: center;
  padding: 0;
}
/* Always reachable by keyboard/touch even though hidden by default. */
.home-row-act > button:focus-visible,
.home-row-act > input[type="checkbox"]:focus-visible {
  visibility: visible;
  outline: 2px solid var(--accent-primary);
  outline-offset: 1px;
}
.home-row-item:hover .home-row-act > button,
.home-row-item:hover .home-row-act > input[type="checkbox"] {
  visibility: visible;
}
/* A ticked task checkbox stays visible through the brief "checked" beat before
   the row optimistically removes (else it'd vanish the instant it's checked). */
.home-row-act > input[type="checkbox"]:checked {
  visibility: visible;
}
/* Mention ✕ — faint by default, darkens on hover. */
.home-mention .home-row-act > button {
  color: var(--text-faint);
}
.home-mention .home-row-act > button:hover,
.home-mention .home-row-act > button.is-pressed {
  color: var(--text-muted);
  background: color-mix(in srgb, var(--text-faint) 15%, transparent);
}

/* Second (reply / thread-header) line — same 5-col grid, sub-cells placed
   under their line-1 counterparts. */
.home-row-sub {
  display: grid;
  /* Same track sizing as .home-row-line (fixed 34px badge track so column 3
     lines up under line 1's text even though the sub line has no badge; auto
     meta so the reply/time never tangles) — 2026-07-07 / 2026-07-13. */
  grid-template-columns: 28px 34px minmax(0, 1fr) auto 26px;
  gap: 8px;
  align-items: center;
  margin-top: 3px;
}
/* Reply avatar sits in the badge column (col 2, under the priority pill); the
   reply TEXT sits in the text column (col 3) so it aligns under the task title
   above it. The fixed 34px badge track (shared with .home-row-line) keeps col 3
   at the same x on both lines (aligned reply text, 2026-07-13). */
.home-row-sub__av {
  grid-column: 2;
  justify-self: start;
  align-self: center;
}
.home-row-sub__txt {
  grid-column: 3;
  color: var(--text-faint);
  font-size: var(--fs-caption);
  min-width: 0; /* clip within the (possibly spanned) grid area — see .home-row-line */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.home-row-sub__time {
  grid-column: 4;
  color: var(--text-faint);
  font-size: var(--fs-caption);
  text-align: right;
  justify-self: end;
}
/* Mention "on “…”" thread-header line spans under the badge+text columns
   (mockup: grid-column:2 / span 2). */
.home-row-sub__on {
  grid-column: 2 / span 2;
}

/* Priority pill (TaskRow). Soft tint of the matching accent token + darker
   accent text — matches the established badge tint pattern elsewhere in this
   file (e.g. .int-badge--*, .notion-webhook__chip--*) via color-mix, so no new
   colour is introduced. */
.home-task__badge {
  font-size: 10px;
  font-weight: 600;
  padding: 1px 5px;
  border-radius: 4px;
  text-align: center;
}
.home-task__badge--high {
  background: color-mix(in srgb, var(--accent-danger) 16%, white);
  color: var(--accent-danger-strong);
}
.home-task__badge--med {
  background: color-mix(in srgb, var(--accent-warning) 20%, white);
  color: var(--accent-warning);
}
.home-task__badge--low {
  background: color-mix(in srgb, var(--text-faint) 20%, white);
  color: var(--text-muted);
}

/* ── Avatar stack (AvatarStack.tsx) — Notion-style overlap, mockups 06/07/08 ── */
.avatar-stack {
  display: inline-flex;
  align-items: center;
}
.avatar-stack .avatar {
  margin-left: -6px;
  /* Ring so overlapping circles read as separated; matches the card fill
     behind them (white, since avatar stacks only ever sit on white/flush
     panel content in this surface). */
  border: 1.5px solid var(--surface-raised);
}
.avatar-stack .avatar:first-child {
  margin-left: 0;
}
.avatar-stack__more {
  width: 18px;
  height: 18px;
  margin-left: -6px;
  border-radius: 50%;
  /* Plain neutral gray fill — matches the established un-tokenized gray-100/200
     pattern already used throughout this file (e.g. #f3f4f6/#e5e7eb chip fills
     below), not a new accent colour. */
  background: #e5e7eb;
  color: var(--text-muted);
  font-size: 9px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--surface-raised);
}

/* One thread entry inside the docked panel. */
.home-thread-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  cursor: pointer;
  border-top: 1px solid var(--border-subtle);
}
.home-thread-row:first-child {
  border-top: none;
}
.home-thread-row:hover {
  background: var(--surface-card-hover);
}
/* Type marker — fixed-width so preview text aligns across rows. */
.home-thread-row__type {
  flex-shrink: 0;
  min-width: 48px;
}
/* Preview — grows to fill available space; truncate with ellipsis. */
.home-thread-row__preview {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Page label — right-aligned, does not shrink below its content. */
.home-thread-row__page {
  flex-shrink: 0;
}

/* Placeholder text in empty tabs. */
.left-sidebar-placeholder {
  font-size: var(--fs-body);
  color: var(--text-faint);
  line-height: 1.5;
}

/* Inline button inside the no-match empty state. */
.left-sidebar-clear-filters {
  display: inline;
  background: none;
  border: none;
  padding: 0;
  font-size: inherit;
  color: var(--text-muted);
  text-decoration: underline;
  cursor: pointer;
}
.left-sidebar-clear-filters:hover {
  color: var(--text-primary);
}

/* Layer rows are draggable under any sort/filter — show the grab affordance. */
.flex-1.overflow-y-auto .layer-row[draggable="true"] {
  cursor: grab;
}
.flex-1.overflow-y-auto .layer-row[draggable="true"]:active {
  cursor: grabbing;
}

/* ── Pages tab ─────────────────────────────────────────────────────── */
.pages-grid {
  display: flex;
  flex-direction: column;
  padding: 8px 0;
}

.page-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
  cursor: default;
  user-select: none;
  border-left: 2px solid transparent;
  transition: background 0.1s, border-color 0.1s;
}

.page-card:hover {
  background: #f9fafb;
}

.page-card.is-current {
  border-left-color: var(--accent-primary);
  background: #eef2ff;
}

.page-card.is-all-done {
  opacity: 0.45;
}

.page-card.is-drop-target {
  background: #fef9c3;
  border-left-color: var(--accent-warning);
  outline: 1px dashed var(--accent-warning);
}

.page-card__number {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-primary);
  min-width: 28px;
}

.page-card__stats {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

.page-card__selected {
  font-size: var(--fs-caption);
  color: var(--text-primary);
  background: #d1d5db;
  border-radius: 9999px;
  padding: 0 5px;
  cursor: grab;
}
.page-card__selected:active {
  cursor: grabbing;
}
.page-card__selected.is-dragging {
  background: #a855f7;
  color: #fff;
}

.page-card__open {
  font-size: var(--fs-caption);
  color: var(--accent-primary);
  cursor: pointer;
  border-radius: 3px;
  padding: 0 2px;
}
.page-card__open:hover {
  background: #e0e7ff;
}

.page-card__done {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  cursor: pointer;
  border-radius: 3px;
  padding: 0 2px;
}
.page-card__done:hover {
  background: #f3f4f6;
}

/* Display-only page cards (non-active file-set groups): counts must not look
   clickable — the active file owns the selection state, and clicking these
   bubbles up to the card handler so it navigates to that file instead. */
.page-card.is-static .page-card__open,
.page-card.is-static .page-card__done {
  cursor: default;
}
.page-card.is-static .page-card__open:hover,
.page-card.is-static .page-card__done:hover {
  background: transparent;
}

/* Ghost pages — comments that exist on pages beyond the PDF's page count. */
.page-card--ghost {
  opacity: 0.6;
  font-style: italic;
}
.page-card--ghost.is-current {
  opacity: 1;
  background: #fef3c7;
}
.page-card--ghost .page-card__ghost-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-warning);
  flex-shrink: 0;
}
.page-card__ghost-label {
  font-size: var(--fs-caption);
  font-weight: 400;
  color: var(--text-faint);
}

/* File/layer group header — sits above each file's pages. */
.page-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 14px 6px;
  border-bottom: 1px solid #e5e7eb;
  margin-bottom: 2px;
  position: sticky;
  top: 0;
  background: #f9fafb;
  z-index: 1;
}

.page-group-header__filename {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Greyed out when a layer is open without a PDF file. */
.page-group-header.is-no-pdf {
  opacity: 0.45;
}

.page-group-header__layer-pill {
  align-self: center;
  flex-shrink: 0;
}

/* ── Navigate tab (layer cards with page rows) ──────────────────────── */

/* Panel background matches Layers tab */
[data-tab-panel="navigate"] {
  background: var(--surface-app);
  padding: 6px 2px;
  overflow-y: auto;
}

/* Grid container: cards stacked vertically */
.navigate-grid {
  display: flex;
  flex-direction: column;
  padding: 2px 0;
}

/* Navigate-tab cards: detail body always visible (overrides Layers-tab display:none rule) */
.layer-row[data-detail-kind="pages"] > .layer-row__detail {
  display: block;
  padding: 0;
  border-top: none;
  /* max-height transition powers the drag-collapse animation */
  max-height: 4000px;
  overflow: hidden;
  transition: max-height 120ms ease-out;
}

/* Drag mode: all multi-page detail bodies collapse simultaneously.
   Single-page merged rows have no detail body so are unaffected.
   overflow:hidden is required — without it max-height:0 collapses the layout box
   but page rows still overflow visually, covering the set blocks below and hiding
   the drop indicator (bug: standalone card at top couldn't be dragged downward). */
.navigate-grid.is-dragging-mode .layer-row[data-detail-kind="pages"] > .layer-row__detail {
  max-height: 0;
  overflow: hidden;
  transition: max-height 120ms ease-in;
}
/* Drag mode: set containers contract to header-only. The draggable element is now
   .navigate-set-header (not the set-list), so the set-list can safely shrink. */
.navigate-grid.is-dragging-mode .navigate-set-list > .layer-row {
  display: none;
}
/* Within-set sub-layer drag: collapse only detail bodies of members in this set.
   Rows stay visible (not display:none) so the user can see drop targets.
   JS adds is-dragging-mode to the specific .navigate-set-list, not the grid. */
.navigate-set-list.is-dragging-mode .layer-row[data-detail-kind="pages"] > .layer-row__detail {
  max-height: 0;
  overflow: hidden;
  transition: max-height 120ms ease-in;
}

/* ── Binder: single-page merged row ─────────────────────────────────── */

/* Compressed header height — matches a standalone page-card row */
.layer-row.is-single-page-merged > .layer-row__header {
  padding-top: 4px;
  padding-bottom: 4px;
  min-height: 30px;
}

/* Inline stats (selected/open/done) — sits between peek and the ▾+✕ split-btn */
.layer-row__merged-stats {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
  margin-left: auto;
  margin-right: 4px;
}

/* Active-page highlight for a merged row: whole card gets indigo tint */
.layer-row.is-single-page-merged.is-current-layer {
  background: #eef2ff;
  border-left-color: var(--accent-primary);
}
/* Ensure no-pdf yellow overrides current-layer indigo */
.layer-row.is-single-page-merged.is-current-layer.is-no-pdf {
  background: #fef3c7;
  border-left-color: var(--accent-warning);
}

/* Optimistic highlight: applied immediately when the user triggers navigation,
   before the async file load completes. Wiped on the next full re-render. */
.layer-row[data-detail-kind="pages"].is-nav-pending {
  border-left-color: var(--accent-active);
}
.layer-row.is-single-page-merged.is-nav-pending {
  border-left-color: var(--accent-active);
}

/* ── Navigate layer-set container ───────────────────────────────────── */

.navigate-set-list {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-left-width: 3px;
  border-left-color: transparent;
  border-radius: var(--radius-card);
  margin-bottom: 6px;
  overflow: hidden;
}
.navigate-set-list:hover {
  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}

/* Set header: full layer-row header chrome — peek button hidden until hover */
.navigate-set-header .layer-row__peek-btn { display: none; }
.navigate-set-header:hover .layer-row__peek-btn { display: flex; }
/* Member auto-sort toggle (Task 8) — Navigate's set header is .navigate-set-header,
   not .layer-row--set, so the Acetates hover rule never matches; mirror it here.
   .is-active already stays visible at rest (global rule), this only handles the
   hover-reveal-when-off case. */
.navigate-set-header:hover .layer-row__sort-btn { display: flex; }
/* Empty ★ reveal on hover. A favorited ★ is always shown (.is-favorite rule),
   but the empty ★'s reveal rules are keyed on `.layer-row:hover` — the set header
   is `.navigate-set-header`, not `.layer-row`, so without this the empty star is
   invisible on set headers (favorited ones still showed). Mirrors the peek rule. */
.navigate-set-header:hover .layer-fav-btn { display: flex; }
/* Edit pencil always visible on the set header (no is-expanded gating) */
.navigate-set-edit { opacity: 0; transition: opacity 80ms; }
.navigate-set-list:hover .navigate-set-edit { opacity: 1; }

/* Member cards sit flush inside the set container */
.navigate-set-list > .layer-row {
  border-radius: 0;
  border: none;
  border-left: 3px solid transparent; /* slot for current-layer accent */
  border-top: 1px solid var(--border-subtle);
  margin-bottom: 0;
  box-shadow: none;
}
/* Indent member headers to match page-card left padding */
.navigate-set-list > .layer-row > .layer-row__header {
  padding-left: 14px;
}
/* First member directly after the set header needs no top border */
.navigate-set-list > .navigate-set-header + .layer-row {
  border-top: none;
}
.navigate-set-list > .layer-row:hover {
  background: #f9fafb;
  box-shadow: none;
}
/* Current-layer highlight on merged row matches page-card.is-current */
.navigate-set-list > .layer-row.is-single-page-merged.is-current-layer {
  background: #eef2ff;
  border-left-color: var(--accent-primary);
}
/* No-PDF override: amber wins over indigo (higher specificity than the rules above) */
.navigate-set-list > .layer-row.is-single-page-merged.is-current-layer.is-no-pdf {
  background: #fef3c7;
  border-left-color: var(--accent-warning);
}

/* Standalone navigate cards: suppress is-open-in-set dark accent (every card
   here is open, so the accent adds no information; is-nav-pending re-asserts it) */
.navigate-grid > .layer-row.is-open-in-set {
  border-left-color: transparent;
}
/* Set member rows: same suppression — member rows already have a reserved
   border-left slot and manage colour via is-current-layer only */
.navigate-set-list > .layer-row.is-open-in-set {
  border-left-color: transparent;
}

/* Drop indicator line (Navigate card drag) */
.chip-drop-line {
  height: 2px;
  margin: 4px 0;
  background: var(--accent-primary);
  border-radius: 2px;
  pointer-events: none;
}

/* Drop line inside navigate set: flush, no margin */
.navigate-set-list > .chip-drop-line {
  margin: 0;
  border-radius: 0;
}

/* Dragged card/set opacity */
.navigate-grid > .layer-row.is-dragging,
.navigate-set-list > .layer-row.is-dragging,
.navigate-set-list.is-dragging {
  opacity: 0.4;
}

/* Light ✕ button for sub-layer merged rows — matches the caret button,
   not the black Layers-tab close. JS uses position:fixed when opening
   these menus to escape the overflow:hidden of .navigate-set-list.
   Excluded for no-PDF members: their ✕ stays yellow (see below), matching
   the Acetates tab — a lost no-PDF member reads red button body + yellow ✕. */
.navigate-set-list > .layer-row .split-btn:not(.split-btn--nopdf) .split-btn__close {
  background: #fff;
  color: var(--text-muted);
  border-left-color: var(--border-subtle);
}
.navigate-set-list > .layer-row .split-btn:not(.split-btn--nopdf) .split-btn__close:hover {
  background: #fee2e2;
  color: var(--accent-danger-strong);
  border-left-color: #fca5a5;
}
/* No-PDF member ✕ in the Navigate set list — yellow, mirroring the global
   .split-btn--nopdf rule so "loaded without a PDF" reads the same in every tab. */
.navigate-set-list > .layer-row .split-btn--nopdf .split-btn__close {
  background: var(--accent-warning);
  color: #fff;
  border-left-color: var(--accent-warning);
}
.navigate-set-list > .layer-row .split-btn--nopdf .split-btn__close:hover { background: #d97706; }

/* ── Sidebar file picker ───────────────────────────────────────────── */
/* Sticky at top of Files tab; always visible regardless of scroll.    */
.sidebar-file-picker {
  display: flex;
  gap: 8px;
  padding: 6px 8px;
  border-bottom: 1px solid #e5e7eb;
  background: #fff;
  flex-shrink: 0;
}
.sidebar-file-picker__label {
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.sidebar-file-picker__btn-wrap {
  cursor: pointer;
}
.sidebar-file-picker__btn {
  display: inline-block;
  padding: 4px 10px;
  font-size: var(--fs-caption);
  font-family: inherit;
  font-weight: 500;
  color: var(--text-primary);
  background: #f9fafb;
  border: 1px solid #d1d5db;
  border-radius: 5px;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.sidebar-file-picker__btn-wrap:hover .sidebar-file-picker__btn {
  background: #f3f4f6;
  border-color: var(--text-faint);
}

/* ── Layers tab toolbar ────────────────────────────────────────────── */
/* Header bar with "Open PDF" split button (left) + search bar (right). */
.layers-tab-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Side padding matches the layer list below (.flex-1 ... p-4 = 16px). */
  padding: 6px 16px;
  border-bottom: 1px solid var(--border-subtle);
  flex-shrink: 0;
}

/* ── Layers toolbar (React) ─────────────────────────────────────────
   The React LayersToolbar mounts into #layers-toolbar-root inside
   .layers-tab-toolbar. It reuses the RSB toolbar classes verbatim
   (rsb-tool-btn / rsb-search / rsb-menu — see SidebarHeader.tsx) so the
   two toolbars are visually identical. The root wrapper grows to fill
   the space left of the New-layer button. */
.layers-toolbar-root {
  flex: 1;
  min-width: 0;
}

/* Group heading (Owner / Project) inside the shared rsb-menu filter popover. */
.rsb-menu__group-label {
  padding: 6px 10px 2px;
  font-size: var(--fs-caption);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

/* New-layer button: circular "+" at the right end of the toolbar row —
   light fill, dark glyph, matching the flat grayscale of .rsb-tool-btn. */
/* "New acetate" split button — primary (+ New) joined to a caret. Both halves
   are gray rsb-tool-btns; the shared edge is flattened so they read as one
   control matching Sort/Filter. (Replaced the old standalone .layers-new-btn
   when the + moved into the React toolbar — empty-acetate work, 2026-06-22.) */
.rsb-new-split {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.rsb-new-split__main {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.rsb-new-split__caret {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left-width: 0;
  padding-left: 6px;
  padding-right: 8px;
}
/* Compact / search-expanded: collapse to just the + circle, drop the caret
   (its menu actions are secondary and return when the row widens). */
.rsb-toolbar.is-searching .rsb-new-split__caret,
.rsb-toolbar[data-compact] .rsb-new-split__caret {
  display: none;
}
.rsb-toolbar.is-searching .rsb-new-split__main,
.rsb-toolbar[data-compact] .rsb-new-split__main {
  border-radius: 9999px;
}

/* ── Right sidebar ─────────────────────────────────────────────────── */

/* Floats over the viewer from the right edge; width driven by --right-sidebar-w.
   Fixed + top:0 so the RSB runs the full viewport height and merges with the
   header band; its left border is the divider that splits the header. Stays an
   overlay (not in flow) so the PDF viewer never reflows on toggle. display:flex
   is the open state; Task 3 hides it via body:not(.rsb-open). Background matches
   the left sidebar's app surface so thread cards read as sheets on a neutral panel. */
#sidebar {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: var(--right-sidebar-w, 384px);
  min-width: 300px;
  background: var(--surface-app);
  border-left: 1px solid #d1d5db;
  z-index: 20;
  display: flex;
  flex-direction: column;
}

/* RSB page-label row = the RSB's slice of the header band. White background,
   same height, bottom border (gray-300) and shadow as #toolbar so both halves
   read as one continuous header bar split by the RSB divider. Symmetric padding
   so the page-label centres across the full RSB width, disregarding the pinned
   #rsb-corner cluster (centred short text sits clear of the far-right buttons). */
.rsb-header-band {
  display: flex;
  align-items: center;
  height: var(--header-h);
  box-sizing: border-box;
  flex-shrink: 0;
  padding: 0 16px;
  background: #fff;
  /* Clean 1px border only (no shadow) so the header-bottom edge reads identically
     across viewer / LSB / RSB — a faint shadow is invisible on white but visible
     on the grey RSB body, which looked inconsistent. */
  border-bottom: 1px solid #d1d5db;
}

/* Drag handle — thin hit target on the left edge */
#right-sidebar-resize-handle {
  position: absolute;
  top: 0;
  left: -3px; /* straddles the border */
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 10;
}
#right-sidebar-resize-handle:hover,
#right-sidebar-resize-handle.is-dragging {
  background: linear-gradient(to right, transparent 2px, #374151 2px, #374151 4px, transparent 4px);
  opacity: 0.5;
}

/* ── Signal bag rows ───────────────────────────────────────────────── */
.signal-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 2px 0;
}
.signal-label {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--text-muted);
  white-space: nowrap;
  min-width: 96px;
  flex-shrink: 0;
}
.signal-value {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--text-primary);
  word-break: break-all;
}
.signal-value--placeholder {
  font-size: var(--fs-caption);
  color: #d1d5db;
  font-style: italic;
}
.signal-value--empty {
  font-size: var(--fs-caption);
  color: #d1d5db;
}
/* Greyed row — signal concept doesn't exist on this side of the toggle */
.signal-row--greyed .signal-label { color: var(--text-faint); }
.signal-value--greyed             { font-size: var(--fs-caption); color: var(--text-faint); }

/* ── PARA selector (SP3, ADR 0022) ─────────────────────────────────── */
.para-selector { display: flex; align-items: center; }
.para-selector--saving { opacity: 0.6; pointer-events: none; }
.para-selector-root { min-height: 24px; flex: 1; min-width: 0; }

/* Copyable value — click to copy full text to clipboard */
.signal-value--copyable {
  cursor: pointer;
  text-decoration: underline dotted #a5b4fc;
}
.signal-value--copyable:hover {
  color: var(--accent-primary-strong);
  text-decoration-color: var(--accent-primary);
}
.signal-value--copied {
  color: var(--accent-success);
  transition: color 0s;
}

/* ── Page mismatch warning banner ──────────────────────────────────── */
#pdf-warning-banner {
  padding: 5px 16px;
  background: #fef3c7;
  color: #78350f;
  font-size: var(--fs-body);
  line-height: 1.5;
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  flex-shrink: 0;
}
#pdf-warning-banner.hidden { display: none; }
#pdf-warning-banner .banner-dismiss {
  margin-left: auto;
  cursor: pointer;
  font-size: var(--fs-glyph);
  line-height: 1;
  opacity: 0.5;
  flex-shrink: 0;
}
#pdf-warning-banner .banner-dismiss:hover { opacity: 1; }
#pdf-warning-banner .banner-pages-link {
  color: #78350f;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
#pdf-warning-banner .banner-pages-link:hover { opacity: 0.75; }

/* ── Unattached-layer warning banner ───────────────────────────────── */
#pdf-unattached-banner {
  background: #f5f3ff;
  border-left: 3px solid var(--accent-primary);
  border-bottom: 1px solid #e0e7ff;
  color: var(--accent-primary-strong);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 16px;
  font-size: var(--fs-body);
  flex-wrap: wrap;
  flex-shrink: 0;
}
#pdf-unattached-banner.hidden { display: none; }
#pdf-unattached-banner .banner-diffs {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
#pdf-unattached-banner .banner-diff-item {
  font-size: var(--fs-caption);
  opacity: 0.85;
}
#pdf-unattached-banner .banner-attach-btn {
  background: transparent;
  color: var(--accent-primary-strong);
  border: 1px solid var(--accent-primary);
  border-radius: 4px;
  padding: 2px 9px;
  font-size: var(--fs-caption);
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
#pdf-unattached-banner .banner-attach-btn:hover { background: #ede9fe; }
#pdf-unattached-banner .banner-attach-btn:disabled { opacity: 0.5; cursor: default; }
#pdf-unattached-banner .banner-dismiss-btn {
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--accent-primary);
  font-size: var(--fs-glyph);
  line-height: 1;
  opacity: 0.55;
  padding: 0 0 0 10px;
  flex-shrink: 0;
}
#pdf-unattached-banner .banner-dismiss-btn:hover { opacity: 1; }

/* ── Layer conflict modal ───────────────────────────────────────────── */
#layer-conflict-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
}
#layer-conflict-modal.hidden { display: none; }
.lc-modal__box {
  background: #fff;
  border-radius: 8px;
  padding: 24px 28px;
  max-width: 420px;
  width: 90%;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
}
.lc-modal__heading {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--accent-primary-strong);
  margin: 0 0 10px;
}
.lc-modal__body {
  font-size: var(--fs-body);
  color: var(--text-muted);
  margin: 0 0 20px;
  line-height: 1.6;
}
.lc-modal__buttons {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
.lc-modal__btn {
  border: none;
  border-radius: 5px;
  padding: 7px 14px;
  font-size: var(--fs-body);
  cursor: pointer;
  white-space: nowrap;
}
.lc-modal__btn--primary { background: var(--accent-primary); color: #fff; }
.lc-modal__btn--primary:hover { background: var(--accent-primary-strong); }
.lc-modal__btn--secondary { background: #f3f4f6; color: var(--text-primary); border: 1px solid #d1d5db; }
.lc-modal__btn--secondary:hover { background: #e5e7eb; }

/* ---- Layers tab: card-based layer rows (see DESIGN.md §3.1) ------- */

/* Panel background — cards sit on a slightly darker surface */
[data-tab-panel="layers"] {
  background: var(--surface-app);
  padding: 6px 2px;
}

/* Layer card container */
.layer-row {
  position: relative;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  margin-bottom: 6px;
  transition: background 120ms ease, box-shadow 120ms ease;
  /* left accent border for selected state (zero-width by default) */
  border-left-width: 3px;
  border-left-color: transparent;
}
.layer-row:hover {
  background: #ffffff;
  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}

/* Selected (first click) — raises card, adds accent left border */
.layer-row.is-layer-selected,
.layer-row.is-expanded {
  background: var(--surface-card-selected);
  border-left-color: var(--accent-active);
  box-shadow: var(--shadow-card-raised);
}

/* No-PDF state — yellow left accent (overrides is-open-in-set and is-expanded black) */
.layer-row.is-no-pdf,
.layer-row.is-open-in-set.is-no-pdf,
.layer-row.is-expanded.is-no-pdf { border-left-color: var(--accent-warning); }

/* Header row: name on the left, split-btn on the right */
.layer-row__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 8px;
  cursor: default;
}

/* Layer name — click to rename when expanded */
.layer-row__name {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 4px;
  overflow: hidden;
}

.layer-row__name-text {
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: default;
  user-select: none;
  min-width: 0;
}

/* Pen icon button — hidden until detail is visible */
.layer-row__edit-icon {
  display: none;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  color: var(--text-faint);
  cursor: pointer;
  margin-left: 2px;
  background: transparent;
}
.layer-row__edit-icon svg {
  width: 10px;
  height: 10px;
  overflow: visible;
  flex-shrink: 0;
}

.layer-row:not(.layer-row--set).is-expanded > .layer-row__header .layer-row__edit-icon,
.layer-row--set.is-detail-expanded > .layer-row__header .layer-row__edit-icon {
  display: flex;
}

/* Name text becomes pointer when rename is available */
.layer-row:not(.layer-row--set).is-expanded > .layer-row__header .layer-row__name-text,
.layer-row--set.is-detail-expanded > .layer-row__header .layer-row__set-title {
  cursor: pointer;
}

.layer-row__name-text--italic {
  font-style: italic;
  color: var(--text-muted);
}

/* Timestamp under the name */
.layer-row__ts {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  white-space: nowrap;
}

/* Right side: split button */
.layer-row__header-btns {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Cluster order: peek (-1, already set on the base rule) · attach (0, default) ·
   ★ (1) · split (2). The ★ is the FIRST DOM child (prepended by the header
   composers) but must sit immediately LEFT of the split button — so order pins
   the split rightmost and the ★ just before it, while the peek floats in on the
   far left. The whole cluster is flex-shrink:0 (pinned to the card's right edge),
   so peek toggling on hover only moves the cluster's LEFT edge into the flexible
   name — the ★ and split hold their x-position (regression 2026-07-01). Mirrored
   for Home's .home-row__actions (it also carries .layer-row__header-btns). */
.layer-row__header-btns .layer-fav-btn { order: 1; }
.layer-row__header-btns .split-btn { order: 2; }

/* Detail section — hidden by default.
   Regular rows: visible when .is-expanded.
   Set rows: visible only when .is-detail-expanded (step 3 of the 3-click cycle). */
.layer-row__detail {
  display: none;
  padding: 0 10px 10px 10px;
  border-top: 1px solid var(--border-subtle);
}

.layer-row:not(.layer-row--set).is-expanded > .layer-row__detail,
.layer-row--set.is-detail-expanded > .layer-row__detail {
  display: block;
}

/* Header loses bottom radius + bottom border when detail is open — detail connects flush below */
.layer-row--set.is-expanded.is-detail-expanded > .layer-row__header {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  border-bottom-color: transparent;
  margin-bottom: 0;
}

/* Detail is the bottom half of the same visual card as the header */
.layer-row--set.is-detail-expanded > .layer-row__detail {
  background: var(--surface-card-selected);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--accent-active);
  border-top: 1px solid var(--border-subtle);
  border-bottom-left-radius: var(--radius-card);
  border-bottom-right-radius: var(--radius-card);
  box-shadow: var(--shadow-card-raised);
  padding: 0 10px 10px 10px;
  margin-bottom: 6px;
}

/* Inline rename input — replaces the name text in place */
.layer-row__rename-input {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-body);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 1px 5px;
  outline: none;
  font-family: inherit;
  color: var(--text-primary);
}

/* Inline notice for file-gone / permission-denied outcomes */
.layer-row__notice {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  font-style: italic;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}

/* Expand / collapse all sections toggle */
.layer-row__expand-all {
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-faint);
  padding: 4px 0 2px;
  display: block;
}
.layer-row__expand-all:hover { color: var(--text-muted); }

/* Open-in-set highlight — accent border only; no fill tint */
.layer-row.is-open-in-set {
  border-left-color: var(--accent-active);
}

/* ---- Layer sets ---------------------------------------------------- */

/* Set container: ghost-card stack via box-shadow.
   Semi-transparent fills so the panel background shows through slightly.
   Ghost card borders match the main card's --border-subtle (rgba(0,0,0,0.06)).
   box-shadow inherits border-radius automatically. */
.layer-row--set {
  box-shadow:
    var(--shadow-card),
    3px 4px 0 0 rgba(255,255,255,0.82),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 rgba(255,255,255,0.65),
    6px 8px 0 1px rgba(0,0,0,0.06);
  transition: box-shadow 80ms ease, background 80ms ease;
  margin-bottom: 14px;
}

/* Hover: keep ghost cards (override the generic .layer-row:hover shadow) */
.layer-row--set:hover {
  box-shadow:
    0 2px 6px rgba(0,0,0,0.08),
    3px 4px 0 0 rgba(255,255,255,0.82),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 rgba(255,255,255,0.65),
    6px 8px 0 1px rgba(0,0,0,0.06);
}

/* Selected: stack lifts as one; ghost borders stay consistent with the header border */
.layer-row--set.is-layer-selected {
  box-shadow:
    3px 4px 0 0 rgba(255,255,255,0.82),
    3px 4px 0 1px rgba(0,0,0,0.06),
    6px 8px 0 0 rgba(255,255,255,0.65),
    6px 8px 0 1px rgba(0,0,0,0.06),
    8px 14px 18px rgba(0,0,0,0.13);
}

/* When expanded: dissolve the container; header gets its own card chrome */
.layer-row--set.is-expanded {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  border-left-color: transparent;
  margin-bottom: 2px;
}

/* Header becomes a standalone card when the set is expanded */
.layer-row--set.is-expanded > .layer-row__header {
  background: var(--surface-card-selected);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--accent-active);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card-raised);
  margin-bottom: 6px;
}

/* Set container header — clickable, no pill */
.layer-row--set > .layer-row__header {
  cursor: pointer;
  user-select: none;
}

/* Set container name wrapper — mirrors .layer-row__name layout */
.layer-row__set-name {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 4px;
  overflow: hidden;
}

/* Set container title text */
.layer-row__set-title {
  min-width: 0;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Member cards — hidden by default; revealed on step 2 of the set's 3-click cycle */
.layer-set-members {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding: 0 0 4px 16px;
}

.layer-row--set.is-expanded > .layer-set-members {
  display: flex;
}

.layer-set-members > .layer-row {
  margin-bottom: 0;
}

/* Shown ~100ms after hovering a row; removed when merge or set-expand triggers */
.layer-row--merge-target {
  outline: 2px solid var(--accent-active);
  outline-offset: 2px;
  border-radius: var(--radius-card);
}

/* Virtual set: suppress ghost-card pseudo-elements while the set hasn't been created yet */
.layer-row--virtual-set::before,
.layer-row--virtual-set::after { display: none; }

/* Drop indicator line inside the layers list */
.chip-drop-line--layers {
  height: 2px;
  background: var(--accent-active);
  border-radius: 1px;
  margin: 2px 0;
  pointer-events: none;
}

.layer-row.is-dragging {
  opacity: 0.4;
}

/* Drive link inside detail */
.layer-row__drive-link {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  text-decoration: none;
}
.layer-row__drive-link:hover { color: var(--text-primary); }
.layer-row__drive-links { display: flex; flex-direction: column; gap: 2px; }
.layer-row__drive-links-sep { display: none; }

/* Prevent layout shift when scrollbar appears inside left sidebar panels */
#left-sidebar [data-tab-panel] .overflow-y-auto,
#left-sidebar [data-tab-panel].overflow-y-auto {
  scrollbar-gutter: stable;
}

/* ---- Layer detail: favorites strip -------------------------------- */

.layer-favorites {
  padding: 4px 0 6px;
}

/* ---- Layer detail: notes field ------------------------------------ */

.layer-notes-row {
  align-items: flex-start;
  margin-top: 5px;
}

.layer-notes-wrapper {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.layer-notes {
  width: 100%;
  min-height: 24px;
  resize: none;
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--text-primary);
  border: 1px solid #e5e7eb;
  border-radius: 3px;
  padding: 3px 5px;
  box-sizing: border-box;
  background: #fafafa;
  line-height: 1.4;
  outline: none;
}

.layer-notes:focus {
  border-color: var(--accent-primary);
  background: #fff;
}

.layer-notes::placeholder {
  color: #d1d5db;
}

.layer-notes-status {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  min-height: 12px;
  line-height: 1;
}

/* ---- Layer detail: collapsible sections --------------------------- */

.layer-detail-section {
  background: #f3f4f6;
  border-radius: 4px;
  margin-top: 5px;
  overflow: hidden;
}

.layer-detail-section__toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  padding: 5px 6px;
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-faint);
  letter-spacing: 0.05em;
  text-align: left;
}

.layer-detail-section__toggle:hover {
  color: var(--text-muted);
}

/* FILE section in inspect mode — matches the unattached banner (#f5f3ff) */
.layer-detail-section.is-inspecting-section {
  background: #f5f3ff;
}
.layer-detail-section.is-inspecting-section .layer-detail-section__toggle {
  color: var(--accent-primary);
}
.layer-detail-section.is-inspecting-section .layer-detail-section__toggle:hover {
  color: var(--accent-primary-strong);
}

/* Signal label + value highlighted when the live file value differs from the layer value */
.signal-label--differs {
  color: var(--accent-primary);
  font-weight: 600;
}
.signal-value--differs {
  color: var(--accent-primary);
}

/* Thin rule between signal groups inside a layer detail section */
.signal-divider {
  border: none;
  border-top: 1px solid #e5e7eb;
  margin: 5px 0;
}

/* Filled triangle arrow — right-pointing (collapsed), rotates down (expanded) */
.layer-detail-section__arrow {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 3px 0 3px 5px;
  border-color: transparent transparent transparent currentColor;
  transition: transform 0.15s ease;
  flex-shrink: 0;
}

.layer-detail-section__toggle[aria-expanded="true"] .layer-detail-section__arrow {
  transform: rotate(90deg);
}

.layer-detail-section__body {
  padding: 0 6px 6px;
}

/* ---- Layer detail: history log ------------------------------------ */

.layer-history {
  flex: 1;
  min-width: 0;
}

.layer-history__entry {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--text-primary);
  word-break: break-word;
  line-height: 1.5;
}

/* Loading state for comments count */
.signal-value--loading {
  color: #d1d5db;
  font-style: italic;
}

/* ---- Center Peek overlay ------------------------------------------- */

#center-peek-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 300;
}

#center-peek-card {
  background: var(--surface-raised);
  border-radius: var(--radius-card);
  box-shadow: 0 8px 40px rgba(0,0,0,0.18);
  width: min(80vw, 900px);
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.cp-header {
  display: flex;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-subtle);
  gap: 12px;
  flex-shrink: 0;
}

.cp-title {
  flex: 1;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Rich peek header: name span + edit icon + action area (split button) */
.cp-header-content {
  display: flex;
  align-items: center;
  flex: 1;
  gap: 6px;
  min-width: 0;
}
.cp-peek-layer-name {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex-shrink: 1;
}
.cp-header-content .layer-row__rename-input {
  font-size: var(--fs-body);
  font-weight: 600;
}
/* Edit icon always visible in peek header — it's a focused editing context */
.cp-header-content .layer-row__edit-icon { display: flex; }
.cp-header-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  margin-left: auto;
}

.cp-close {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-size: var(--fs-glyph);
  padding: 4px 8px;
  border-radius: var(--radius-control);
  flex-shrink: 0;
  line-height: 1;
}
.cp-close:hover { background: rgba(0,0,0,0.06); }

.cp-body {
  overflow-y: auto;
  padding: 20px;
  flex: 1;
  min-height: 0;
}

/* Top-level layer row in peek: no card chrome — the peek card is the container.
   Member rows inside .layer-set-members restore card appearance (overridden below). */
.cp-body > .layer-row {
  border: none;
  box-shadow: none;
  background: transparent;
  border-left: none;
  margin-bottom: 0;
}
.cp-body > .layer-row:hover {
  background: transparent;
  box-shadow: none;
}

/* Hide the layer-row header — name + buttons moved to the peek card header */
.cp-body > .layer-row > .layer-row__header { display: none; }

/* Force the detail section always visible in peek */
.cp-body > .layer-row > .layer-row__detail {
  display: block;
  border-top: none;
  padding-top: 0;
}

/* Force all collapsible section bodies open in peek */
.cp-body .layer-detail-section__body {
  display: block;
}

/* Hide peek buttons everywhere inside the peek card — no nested peeks */
#center-peek-card .layer-row__peek-btn {
  display: none !important;
}

/* Member rows inside a set peek — always visible, no left indent, sidebar-style cards */
.cp-body .layer-set-members {
  display: flex;
  padding: 0;
  gap: 8px;
  margin-top: 12px;
}
.cp-body .layer-set-members > .layer-row {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-left-width: 3px;
  border-left-color: transparent;
  box-shadow: var(--shadow-card);
}
.cp-body .layer-set-members > .layer-row:hover {
  background: #ffffff;
  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}
.cp-body .layer-set-members > .layer-row.is-layer-selected,
.cp-body .layer-set-members > .layer-row.is-expanded {
  background: var(--surface-card-selected);
  border-left-color: var(--accent-active);
  box-shadow: var(--shadow-card-raised);
}
.cp-body .layer-set-members > .layer-row > .layer-row__header {
  cursor: pointer;
  user-select: none;
}

/* ---- Integration badge (integration-badge.js) ---- */
.int-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-caption);
  line-height: 1;
  color: var(--text-muted);
}
.int-badge--connected    { color: var(--accent-success); }
.int-badge--disconnected { color: var(--text-faint); }
.int-badge--setup-needed { color: var(--accent-warning); }
.int-badge--error        { color: var(--accent-danger); }

/* ---- Settings: integration badge row ---- */
.settings-auth__badge-row {
  margin-top: 2px;
  margin-bottom: 2px;
}

/* ── Auth overlay (shown until the user signs in) ──────────────────── */

.auth-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  backdrop-filter: blur(8px);
  background: rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
}
.auth-overlay[hidden] { display: none; }

.auth-overlay__card {
  background: var(--surface-raised);
  border-radius: 12px;
  padding: 40px;
  min-width: 320px;
  text-align: center;
  box-shadow: var(--shadow-card-raised);
}

.auth-overlay__title {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 6px;
}

.auth-overlay__sub {
  font-size: var(--fs-body);
  color: var(--text-muted);
  margin: 0 0 24px;
}

.auth-overlay__google-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 20px;
  font-size: var(--fs-body);
  font-family: inherit;
  font-weight: 500;
  color: var(--text-primary);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  cursor: pointer;
  white-space: nowrap;
}
.auth-overlay__google-btn:hover {
  background: #f9fafb;
  border-color: rgba(0, 0, 0, 0.22);
}

/* ── Workspace Settings Modal ─────────────────────────────────────────── */

.ws-modal__backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 400;
}

.ws-modal__card {
  background: var(--surface-raised);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  width: min(80vw, 900px);
  height: 85vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.ws-modal__header {
  display: flex;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-subtle);
  flex-shrink: 0;
}

.ws-modal__title {
  flex: 1;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
}

.ws-modal__close {
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
  color: var(--text-muted);
  border-radius: var(--radius-control);
  display: flex;
  align-items: center;
}
.ws-modal__close:hover { background: rgba(0,0,0,0.06); color: var(--text-primary); }

.ws-modal__tabs {
  display: flex;
  border-bottom: 1px solid var(--border-subtle);
  padding: 0 20px;
  flex-shrink: 0;
}

.ws-modal__tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 14px;
  font-size: var(--fs-body);
  color: var(--text-muted);
  cursor: pointer;
  margin-bottom: -1px;
}
.ws-modal__tab:hover { color: var(--text-primary); }
.ws-modal__tab--active {
  color: var(--text-primary);
  border-bottom-color: var(--accent-primary);
  font-weight: 500;
}

.ws-modal__body {
  overflow-y: auto;
  flex: 1;
}

.ws-modal__section {
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Compact modal — a short reference card that sizes to its content instead of
   the settings modal's fixed 85vh (used by the keyboard-shortcuts modal). */
.ws-modal__card--compact {
  height: auto;
  max-height: 85vh;
  width: min(90vw, 560px);
}

/* ── Keyboard shortcuts list (KeyboardShortcutsModal) ── */
.kbd-list { gap: 24px; }

.kbd-group__title {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 10px;
}

.kbd-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 7px 0;
}
.kbd-row + .kbd-row { border-top: 1px solid var(--border-subtle); }

.kbd-row__desc {
  font-size: var(--fs-body);
  color: var(--text-primary);
  min-width: 0;
}

.kbd-row__keys {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.kbd-plus, .kbd-or {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  margin: 0 2px;
}

/* Keyboard-key chip. Token-only (surface-sunken / border-subtle / radius-control
   / fs-caption / text-muted) — no new colour or radius. See DESIGN.md §3.15c. */
.kbd {
  display: inline-block;
  min-width: 20px;
  padding: 2px 7px;
  font-family: inherit;
  font-size: var(--fs-caption);
  line-height: 1.4;
  text-align: center;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
}

/* ── Form primitives ── */

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

.ws-form-label {
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-primary);
}

.ws-form-hint {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  margin: 0;
}

.ws-form-inline {
  display: flex;
  gap: 8px;
  align-items: center;
}

.ws-form-input {
  flex: 1;
  height: 32px;
  padding: 0 10px;
  font-size: var(--fs-body);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  background: var(--surface-base);
  color: var(--text-primary);
  outline: none;
}
.ws-form-input:focus { border-color: var(--accent-primary); box-shadow: 0 0 0 2px rgba(99,102,241,0.15); }

.ws-form-value {
  font-size: var(--fs-body);
  color: var(--text-primary);
}

.ws-form-error {
  font-size: var(--fs-caption);
  color: var(--accent-danger);
  margin: 0;
}

/* ── Buttons ── */

@keyframes ws-spin { to { transform: rotate(360deg); } }
.ws-spinner {
  display: inline-block;
  border: 2px solid var(--border-subtle);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: ws-spin 0.7s linear infinite;
  vertical-align: middle;
  flex-shrink: 0;
}

.ws-btn {
  height: 30px;
  padding: 0 12px;
  font-size: var(--fs-body);
  border-radius: var(--radius-control);
  cursor: pointer;
  white-space: nowrap;
  border: 1px solid transparent;
}
.ws-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.ws-btn--primary {
  background: var(--accent-primary);
  color: #fff;
}
.ws-btn--primary:hover:not(:disabled) { background: var(--accent-primary-strong); }

.ws-btn--secondary {
  background: var(--surface-base);
  color: var(--text-primary);
  border-color: var(--border-subtle);
}
.ws-btn--secondary:hover:not(:disabled) { background: rgba(0,0,0,0.04); }

.ws-btn--warning {
  background: var(--surface-base);
  color: var(--accent-warning);
  border-color: var(--accent-warning);
}
.ws-btn--warning:hover:not(:disabled) { background: rgba(245,158,11,0.08); }

.ws-btn--danger {
  background: var(--accent-danger);
  color: #fff;
  border-color: transparent;
}
.ws-btn--danger:hover:not(:disabled) { background: #b91c1c; }

/* ── Logo area ── */

.ws-logo-area {
  display: flex;
  align-items: center;
  gap: 14px;
}

.ws-logo-area__preview {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-card);
  object-fit: cover;
  border: 1px solid var(--border-subtle);
  flex-shrink: 0;
}

.ws-logo-area__initials {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-card);
  background: rgba(99,102,241,0.12);
  color: var(--accent-primary);
  font-size: 20px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border: 1px solid var(--border-subtle);
}

.ws-logo-area__controls {
  display: flex;
  gap: 8px;
  align-items: center;
}

/* ── Member list ── */

.ws-member-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ws-member-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-control);
}
.ws-member-row:hover { background: rgba(0,0,0,0.03); }

/* ws-member-row__avatar replaced by .avatar.avatar--md — see Avatar component */

.ws-member-row__info {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ws-member-row__name {
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ws-member-row__email {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ws-member-row__role-select {
  font-size: var(--fs-body);
  padding: 2px 6px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  background: var(--surface-base);
  color: var(--text-primary);
  cursor: pointer;
  flex-shrink: 0;
}
.ws-member-row__role-select:disabled { opacity: 0.5; cursor: not-allowed; }

.ws-member-row__role-badge {
  font-size: var(--fs-caption);
  padding: 2px 7px;
  border-radius: 99px;
  font-weight: 500;
  flex-shrink: 0;
}
.ws-member-row__role-badge--admin  { background: rgba(99,102,241,0.12); color: var(--accent-primary); }
.ws-member-row__role-badge--member { background: rgba(0,0,0,0.06); color: var(--text-muted); }

.ws-member-row__remove {
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
  color: var(--text-muted);
  border-radius: var(--radius-control);
  display: flex;
  align-items: center;
  flex-shrink: 0;
  opacity: 0;
}
.ws-member-row:hover .ws-member-row__remove { opacity: 1; }
.ws-member-row__remove:hover { color: var(--accent-danger); background: rgba(239,68,68,0.08); }
.ws-member-row__remove:disabled { opacity: 0.3; cursor: not-allowed; }

.ws-loading {
  font-size: var(--fs-body);
  color: var(--text-muted);
  margin: 0;
}

/* ── API Keys tab ── */
.ws-token-create-row {
  display: flex;
  gap: 8px;
  align-items: center;
}
.ws-token-create-row .ws-form-input { flex: 1; }

.ws-token-reveal {
  background: rgba(99,102,241,0.06);
  border: 1px solid rgba(99,102,241,0.2);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ws-token-reveal__label {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--accent-primary);
  margin: 0;
}
.ws-token-reveal__value {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ws-token-reveal__value code {
  flex: 1;
  font-family: var(--font-mono);
  font-size: var(--fs-body);
  background: rgba(0,0,0,0.05);
  padding: 6px 8px;
  border-radius: 4px;
  word-break: break-all;
  color: var(--text-primary);
}

.ws-token-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
}
.ws-token-table th {
  text-align: left;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0 8px 8px 0;
  border-bottom: 1px solid var(--border-color);
}
.ws-token-table td {
  padding: 8px 8px 8px 0;
  border-bottom: 1px solid rgba(0,0,0,0.04);
  color: var(--text-primary);
  vertical-align: middle;
}
.ws-token-table tr:last-child td { border-bottom: none; }
/* Revoke button uses ws-member-row__remove (opacity:0 default); needs its own hover trigger in table context. */
.ws-token-table tr:hover .ws-member-row__remove { opacity: 1; }

.ws-token-scope {
  font-size: var(--fs-caption);
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
}
.ws-token-scope--read      { background: rgba(0,0,0,0.06); color: var(--text-muted); }
.ws-token-scope--readwrite { background: rgba(99,102,241,0.12); color: var(--accent-primary); }

.ws-token-prefix {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  background: rgba(0,0,0,0.04);
  padding: 2px 5px;
  border-radius: 3px;
}

/* ── Danger button (confirm dialog) ── */
.ws-btn--danger {
  background: var(--accent-danger);
  color: #fff;
  border: 1px solid var(--accent-danger);
  border-radius: var(--radius-control);
  font-size: var(--fs-body);
  font-weight: 500;
  padding: 5px 14px;
  cursor: pointer;
}
.ws-btn--danger:hover:not(:disabled) { background: var(--accent-danger-strong); border-color: var(--accent-danger-strong); }
.ws-btn--danger:disabled { opacity: 0.5; cursor: default; }

/* Stacks ws-form-row items inside an ExpandCard body with consistent gap */
.ws-expand-section { display: flex; flex-direction: column; gap: 16px; }

/* ── Master Admin tab ── */
/* Reuses .ws-member-row/.ws-form-* primitives + DESIGN.md §2 tokens only — no new colour. */
.admin-row { cursor: pointer; }
.admin-row__count { flex-shrink: 0; white-space: nowrap; }
.admin-row__chevron {
  display: flex;
  align-items: center;
  color: var(--text-faint);
  flex-shrink: 0;
}
.admin-invite-row--muted { opacity: 0.55; }
.admin-master-badge { color: var(--accent-primary); font-weight: 500; }
.admin-user-row__workspaces { margin: 0; }
.admin-back-btn { align-self: flex-start; }
.admin-autojoin-check {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
.admin-add-member-results { max-height: 180px; overflow-y: auto; }
/* Muted text toggle for the inactive-invite section — not a filled button. */
.admin-inactive-toggle {
  align-self: flex-start;
  background: none;
  border: none;
  padding: 0;
  font-size: var(--fs-caption);
  color: var(--text-muted);
  cursor: pointer;
  text-decoration: underline;
}
.admin-inactive-toggle:hover { color: var(--text-primary); }
/* Danger zone sits at the bottom of the workspace-detail view, set apart. */
.admin-danger-zone {
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}

/* ── ImagePicker — clickable image with hover pen overlay ── */
.img-picker {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: var(--radius-card);
  overflow: hidden;
  flex-shrink: 0;
  user-select: none;
  outline: none;
}
.img-picker:focus-visible { box-shadow: 0 0 0 2px var(--accent-active); }
.img-picker--round {
  border-radius: 50%;
  overflow: visible; /* pen badge sits outside the circle */
}
/* Image and hover overlay must self-clip to the circle when overflow is visible */
.img-picker--round .img-picker__img,
.img-picker--round .img-picker__initials { border-radius: 50%; overflow: hidden; }
.img-picker--round::after { border-radius: 50%; }
/* Pen badge sits at the bottom-right edge, partially outside the circle */
.img-picker--round .img-picker__pen {
  bottom: 4px;
  right: 4px;
  transform: translate(35%, 35%);
  border-radius: 50%;
}
.img-picker__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.img-picker__initials {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: rgba(99,102,241,0.12);
  color: var(--accent-primary);
  font-weight: 600;
}
/* Gray overlay beneath the pen icon — appears on hover */
.img-picker::after {
  content: '';
  position: absolute;
  inset: 0;
  background: transparent;
  transition: background 0.15s;
  pointer-events: none;
  z-index: 1;
}
.img-picker:hover::after { background: rgba(0, 0, 0, 0.18); }
.img-picker__pen {
  position: absolute;
  bottom: 5px;
  right: 5px;
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 4px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.12s;
  pointer-events: none;
  color: var(--text-primary);
  z-index: 2;
}
.img-picker__pen-svg { width: 9px; height: 9px; }
.img-picker:hover .img-picker__pen { opacity: 1; }

/* ── Workspace logo block — image + name stacked left ── */
.ws-logo-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.editable-label.ws-workspace-name-label {
  font-size: var(--fs-body);
  font-weight: 600;
  /* padding-left offsets the invisible pen icon (10px svg + 8px gap) so
     the visible text is optically centred, not the text+pen span */
  padding-left: 18px;
}

/* ── Scrollable member list (max 6 rows) ── */
.ws-member-list--scrollable {
  max-height: calc(6 * 52px);
  overflow-y: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  padding: 4px 0;
}

/* ── Avatar — unified person avatar (photo or initials circle) ── */
/* Used in: MemberChip (sm), workspace member rows (md), and any future person surface. */
.avatar {
  border-radius: 50%;
  flex-shrink: 0;
}
/* Photo variant — no background behind the image */
.avatar:not(.avatar--initials) {
  object-fit: cover;
  display: block;
}
/* Initials variant — solid light-grey circle with a white letter. */
.avatar--initials {
  background: #b6bcc6;
  color: #fff;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.avatar--sm { width: 18px; height: 18px; font-size: var(--fs-caption); }
.avatar--md { width: 30px; height: 30px; font-size: var(--fs-body); }

/* ── MemberChip — inline avatar + name pill ── */
.member-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: rgba(0,0,0,0.04);
  border-radius: 99px;
  padding: 2px 7px 2px 2px;
  font-size: var(--fs-caption);
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
  vertical-align: middle;
}
.member-chip__name {
  line-height: 1;
}

/* ── Round profile photo preview ── */
.ws-logo-area__preview--round { border-radius: 50%; }

/* ── ServiceAvatarButton — clickable avatar for connected services ── */
.svc-avatar-btn {
  position: relative;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  background: none;
  border-radius: 50%;
  cursor: pointer;
  flex-shrink: 0;
  overflow: visible; /* tooltip must escape the circle boundary */
}
/* Gray dim overlay — clips to the circle via border-radius, not overflow */
.svc-avatar-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.32);
  opacity: 0;
  transition: opacity 0.14s;
  pointer-events: none;
  z-index: 1;
}
.svc-avatar-btn:hover::before { opacity: 1; }
.svc-avatar-btn__img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
/* Tooltip appears to the right of the avatar, bottom-aligned */
.svc-avatar-btn__tooltip {
  position: absolute;
  left: calc(100% + 6px);
  bottom: 0;
  transform: none;
  background: rgba(0, 0, 0, 0.82);
  color: #fff;
  font-size: var(--fs-caption);
  font-weight: 500;
  white-space: nowrap;
  padding: 4px 8px;
  border-radius: 4px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.14s;
  z-index: 100;
}
.svc-avatar-btn:hover .svc-avatar-btn__tooltip { opacity: 1; }

/* ── Section divider ── */
.ws-section-divider {
  height: 1px;
  background: var(--border-subtle);
  margin: 16px 0;
}

/* ── ExpandCard ─────────────────────────────────────────────────── */

.expand-card {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  background: var(--surface-card);
  overflow: hidden;
  flex-shrink: 0;
}

.expand-card__header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  cursor: pointer;
  transition: background 80ms ease;
  border-radius: var(--radius-card);
}

.expand-card__header:hover {
  background: var(--surface-card-hover);
}

.expand-card--expanded .expand-card__header {
  border-bottom: 1px solid var(--border-subtle);
  border-radius: var(--radius-card) var(--radius-card) 0 0;
}

.expand-card__body {
  max-height: 0;
  overflow: hidden;
  transition: max-height 220ms ease, padding 220ms ease;
  padding: 0 12px;
}

.expand-card--expanded .expand-card__body {
  max-height: 4000px;
  padding: 12px;
}
/* Inner content wrapper — scrollable by default; override with noScroll prop */
.expand-card__body-inner {
  max-height: 300px;
  overflow-y: auto;
}

/* ── EditableLabel — inline editable text primitive ── */
.editable-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  min-width: 0;
  max-width: 100%;
  font-size: var(--fs-body);
}
.editable-label__text {
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.editable-label__empty {
  color: var(--text-faint);
}
.editable-label__pen {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  opacity: 0;
  transition: opacity 0.1s;
  color: var(--text-faint);
}
.editable-label__pen-svg {
  width: 10px;
  height: 10px;
}
.editable-label:hover .editable-label__pen {
  opacity: 1;
  color: var(--text-muted);
}
.editable-label--editing {
  cursor: default;
}
.editable-label__input {
  flex: 1;
  min-width: 60px;
  font: inherit;
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 1px 5px;
  outline: none;
  background: none;
}
.editable-label__input:focus {
  border-color: var(--accent-active);
}

/* ── Card-name EditableLabel bridge (ADR 0026) ──
   The card name is a React EditableLabel island mounted inside the existing
   `.layer-row__name` / `.layer-row__set-name` / `.cp-peek-layer-name` wrapper.
   The island's root replaces the old `.layer-row__name-text` / set-title span,
   so re-home the bold/ellipsis/tooltip styling onto its `.editable-label__text`
   child and let the label flex to fill the wrapper (so ellipsis still clips). */
.layer-row__name .editable-label,
.layer-row__set-name .editable-label,
.cp-peek-layer-name .editable-label {
  flex: 1;
  min-width: 0;
  gap: 4px;                 /* tighter than the modal default so the pen hugs the name */
}
/* The peek-name wrapper must flex so its island child can ellipsis-clip. */
.cp-peek-layer-name {
  display: flex;
  align-items: center;
}
/* Click-to-edit binds on `.editable-label__text` (see EditableLabel.tsx), so only
   the name text triggers rename on every surface — no per-surface shrink-wrap is
   needed. The Navigate-only `flex: 0 1 auto; max-width: 100%` workaround that used
   to live here was removed once the click target moved onto the text span
   (2026-07-05). The root stays `flex: 1` above for clean ellipsis-clipping. */
.layer-row__name .editable-label__text {
  font-weight: 500;
  color: var(--text-primary);
}
.layer-row__set-name .editable-label__text,
.cp-peek-layer-name .editable-label__text {
  font-weight: 600;
  color: var(--text-primary);
}
/* The pen only renders on an expanded/peek card (hidePen gates it), so reveal it
   on hover of the name wrapper — matching the old `.layer-row:hover` edit icon. */
.layer-row__name:hover .editable-label__pen,
.layer-row__set-name:hover .editable-label__pen,
.cp-peek-layer-name:hover .editable-label__pen {
  opacity: 1;
  color: var(--text-muted);
}
/* Alias the island's input to the old `.layer-row__rename-input` look. */
.layer-row__name .editable-label__input,
.layer-row__set-name .editable-label__input,
.cp-peek-layer-name .editable-label__input {
  min-width: 0;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 1px 5px;
}

/* ── Integration row (Connect tab) ── */
.ws-integration-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
}
.ws-integration-row__icon {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.ws-integration-row__name {
  font-size: var(--fs-body);
  font-weight: 500;
  flex: 1;
}

/* ── Notion connector: token-input form (disconnected state) ── */
.notion-connect-form {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0 8px;
}
.notion-connect-form__input {
  flex: 1;
  min-width: 0;
  height: 30px;
  padding: 0 8px;
  font-size: var(--fs-body);
  font-family: var(--font-mono);
  background: var(--surface-input, var(--surface-raised));
  border: 1px solid var(--border-input, var(--border-subtle));
  border-radius: var(--radius-sm, 4px);
  color: var(--text-primary);
  outline: none;
}
.notion-connect-form__input:focus {
  border-color: var(--accent-focus, var(--accent));
  box-shadow: 0 0 0 2px var(--accent-focus-ring);
}
.notion-connect-form__error {
  margin: 0 0 8px;
  font-size: var(--fs-caption);
  color: var(--accent-danger);
}

/* ── Notion property mapper ── */
.notion-mapper {
  padding: 4px 0 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Normalize all action buttons inside the mapper to the Mapping-button text size */
.notion-mapper .ws-btn {
  font-size: var(--fs-caption);
  height: 26px;
}

/* Header row: section title + optional db picker + optional refresh button */
.notion-mapper__db-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  margin-bottom: 2px;
}
.notion-mapper__db-header:first-child {
  margin-top: 0;
}
.notion-mapper__db-title {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  flex-shrink: 0;
  width: 160px; /* shared left-column width — keeps all pickers left-aligned */
}
.notion-mapper__section-heading {
  margin: 8px 0 2px 0;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.notion-mapper__col-headers {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 20px 0 0 0;
}
.notion-mapper__col-headers + .notion-mapper__db-divider {
  margin-top: 2px;
  margin-bottom: 2px;
}
.notion-mapper__col-header {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.notion-mapper__col-headers .notion-mapper__col-header:first-child {
  flex-shrink: 0;
  width: 160px;
}
.notion-disconnect-btn {
  font-size: var(--fs-caption);
  height: 26px;
}

.notion-mapper__field-block {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.notion-mapper__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.notion-mapper__label {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 160px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
.notion-mapper__label .editable-label {
  font-size: var(--fs-body);
}
.notion-mapper__required {
  color: var(--text-primary);
}
.notion-mapper__select {
  flex: 1;
  min-width: 0;
  height: 28px;
  padding: 0 6px;
  font-size: var(--fs-body);
  background: var(--surface-input, var(--surface-raised));
  border: 1px solid var(--border-input, var(--border-subtle));
  border-radius: var(--radius-sm, 4px);
  color: var(--text-primary);
  cursor: pointer;
}

/* "Mapping" toggle button — 120px matches the ~Refresh schema button width so the db picker
   and property selects share the same right edge */
.notion-mapper__mapping-btn {
  flex-shrink: 0;
  width: 120px;
  height: 26px;
  padding: 0 8px;
  font-size: var(--fs-caption);
  text-align: center;
}
.notion-mapper__mapping-btn--active {
  background: #f3f4f6;
  border-color: #d1d5db;
}
/* Invisible spacer in rows without a Mapping button — keeps select widths consistent */
.notion-mapper__mapping-btn-spacer {
  flex-shrink: 0;
  width: 120px;
}

/* Expanded mapping content — gray card.
   Negative margins extend it 8px past each side of the row:
   - left: breathing room before the label text
   - right: just past the Mapping button's right edge
   Padding restores the inner content position so label text aligns with the row above. */
.notion-mapper__mapping-content {
  margin: 3px -8px 10px;
  padding: 6px 8px;
  background: #f3f4f6;
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 4px;
  box-shadow: 0 1px 4px rgba(0,0,0,.10);
}
.prop-row__type-tag {
  color: var(--text-muted);
  font-style: italic;
}

/* ── Property config (Customize tab) ── */
.prop-config {
  padding: 4px 0 8px;
}
.prop-config__section-heading {
  margin: 8px 0 2px;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.prop-config__list {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-bottom: 8px;
}
.prop-config__coming-soon {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  padding: 2px 0;
}

/* ── prop-row header layout ── */
.prop-row__header {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Left column: fixed-width label matching notion-mapper__label */
.prop-row__name-label {
  flex-shrink: 0;
  width: 160px;
  min-width: 0;
  overflow: hidden;
  font-size: var(--fs-body);
  gap: 4px;
}
.prop-row__dead-name {
  flex-shrink: 0;
  width: 160px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}

/* Middle column: type select — narrow by default (custom rows), fills space in default property rows */
.prop-row__type-select {
  flex-shrink: 0;
  width: 84px;
  height: 26px;
  font-size: var(--fs-body);
}
/* default property rows use AppSelect (flex:1 built-in) — no native select overrides needed */

/* Legacy chip class — kept for any residual usage */
.prop-row__type-chip {
  flex-shrink: 0;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}

.prop-row__label-input,
.prop-row__name-input {
  flex: 1;
  min-width: 0;
  height: 26px;
  font-size: var(--fs-body);
}

/* ── Options panel ── */
.prop-row__options {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.prop-row__option-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.prop-row__option-label {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-body);
  gap: 4px;
}
.prop-row__option-input {
  flex: 1;
  min-width: 0;
  height: 26px;
  font-size: var(--fs-body);
}
.prop-row__remove-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  background: none;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  color: var(--text-faint);
  flex-shrink: 0;
}
.prop-row__remove-btn:hover { color: var(--accent-danger); background: rgba(239,68,68,0.08); }
.prop-row__add-option-btn {
  font-size: var(--fs-caption);
  color: var(--text-faint);
  background: none;
  border: none;
  padding: 2px 0;
  cursor: pointer;
  text-align: left;
}
.prop-row__add-option-btn:hover { color: var(--text-muted); }

/* ── Colour swatch per option ── */
.prop-row__option-color-wrap {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}
.prop-row__option-color {
  display: block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  cursor: pointer;
  border: 1px solid rgba(0,0,0,0.1);
  flex-shrink: 0;
}
.prop-row__option-color:hover { opacity: 0.85; }
/* Picker renders via portal (Popover) — card styling only, no position */
.prop-row__color-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  width: 124px;
  padding: 6px;
  background: var(--surface-base);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.prop-row__color-swatch {
  position: relative;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
  transition: transform 0.1s;
}
.prop-row__color-swatch:hover { transform: scale(1.15); }
.prop-row__color-swatch--active { border-color: rgba(0,0,0,0.45); }
/* Delayed tooltip label on colour swatches */
.prop-row__color-swatch::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--text-primary);
  color: #fff;
  font-size: var(--fs-caption);
  padding: 2px 6px;
  border-radius: 3px;
  white-space: nowrap;
  text-transform: capitalize;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.1s;
  transition-delay: 0s;
  z-index: 600;
}
.prop-row__color-swatch:hover::after {
  opacity: 1;
  transition-delay: 0.55s;
}
/* Checkbox-style icon for status option rows */
.prop-row__status-icon {
  flex-shrink: 0;
  color: var(--text-faint);
  opacity: 0.5;
  margin-right: 2px;
}

/* Options card: constrained to middle column, picker allowed to overflow */
.prop-row .notion-mapper__mapping-content {
  margin-left: 168px;  /* 160px name-label + 8px gap */
  margin-right: 128px; /* 120px options-btn + 8px gap */
  overflow: visible;
}

/* Constrains user-mapper content to the same right edge as the property selects */
.notion-mapper__mapping-children {
  margin-right: 128px; /* 120px btn + 8px gap */
}

/* Divider between sections */
.notion-mapper__db-divider {
  border: none;
  border-top: 1px solid var(--border-subtle);
  margin: 8px 0 4px;
}

/* Option-level mapping rows — label matches property label width so selects align */
.notion-mapper__option-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.notion-mapper__option-label {
  font-size: var(--fs-body);
  color: var(--text-muted);
  width: 160px;
  flex-shrink: 0;
}
/* Invisible spacer mirrors the Mapping button width so option selects share the same right edge */
.notion-mapper__option-spacer {
  flex-shrink: 0;
  width: 120px;
}

/* Sync status value — flex:1 middle column of the sync row */
.notion-mapper__sync-value {
  flex: 1;
  font-size: var(--fs-body);
  color: var(--text-muted);
}
.notion-mapper__sync-error {
  color: var(--accent-danger);
}
.notion-mapper__sync-state {
  flex: 1;
  font-size: var(--fs-caption);
}
.notion-mapper__sync-state--active   { color: var(--accent-success); }
.notion-mapper__sync-state--issues   { color: var(--accent-warning); }
.notion-mapper__sync-state--inactive { color: var(--text-muted); }

/* ── Notion webhook setup — mapper row + gray-card panel ── */

/* flex:1 spacer between the "Webhook" label and the Set up / Manage button */
.notion-webhook__status { flex: 1; }

/* Gray panel — extra gap over the base mapping-content */
.notion-webhook__panel { gap: 6px; }

.notion-webhook__instructions {
  margin-top: 8px;
  align-items: flex-start;
}

.notion-webhook__learn-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  background: none;
  border: none;
  font-size: var(--fs-caption);
  color: var(--text-muted);
  cursor: pointer;
  font-weight: 500;
}
.notion-webhook__learn-toggle:hover { color: var(--text-primary); }

.notion-webhook__steps {
  margin: 4px 0 0;
  padding-left: 20px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
  list-style-type: decimal;
}
.notion-webhook__steps li { margin: 2px 0; }

.notion-webhook__field-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 26px;
}

.notion-webhook__label {
  flex-shrink: 0;
  width: 160px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}

.notion-webhook__url {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  white-space: nowrap;
  font-size: var(--fs-caption);
  padding: 4px 6px;
  border-radius: 4px;
  background: var(--surface-sunken);
  color: var(--text-muted);
}

.notion-webhook__copy { flex-shrink: 0; }

.notion-webhook__token-input {
  flex: 1;
  min-width: 0;
  height: 26px;
  padding: 0 6px;
  font-size: var(--fs-body);
  font-family: inherit;
  background: var(--surface-input, var(--surface-raised));
  border: 1px solid var(--border-input, var(--border-subtle));
  border-radius: var(--radius-sm, 4px);
  color: var(--text-primary);
  outline: none;
}
.notion-webhook__token-input:focus {
  border-color: var(--accent-focus, var(--accent));
  box-shadow: 0 0 0 2px var(--accent-focus-ring);
}

.notion-webhook__token-saved {
  flex: 1;
  font-size: var(--fs-caption);
  color: var(--accent-success);
}

.notion-webhook__token-status { flex: 1; font-size: var(--fs-caption); }
.notion-webhook__token-status--ok      { color: var(--accent-success); }
.notion-webhook__token-status--muted   { color: var(--text-faint, var(--text-muted)); }
.notion-webhook__token-status--bad     { color: var(--accent-danger); }
.notion-webhook__token-status--pending { color: var(--accent-warning); }

.notion-webhook__divider {
  border: none;
  border-top: 1px solid var(--border-subtle, var(--border));
  margin: 8px 0;
}

.notion-webhook__health-error {
  margin: 2px 0 0;
  font-size: var(--fs-caption);
  color: var(--accent-danger);
}

.notion-webhook__chip {
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-caption);
  padding: 2px 7px;
  border-radius: 10px;
  font-weight: 500;
  border: 1px solid transparent;
}
.notion-webhook__chip--ok      { color: var(--accent-success); border-color: var(--accent-success); }
.notion-webhook__chip--bad     { color: var(--accent-danger);  border-color: var(--accent-danger); }
.notion-webhook__chip--muted   { color: var(--text-muted); border-color: var(--border-strong); }
.notion-webhook__chip--pending { color: var(--accent-warning); border-color: var(--accent-warning); }

/* Direction ⓘ icon tooltip (Session 5) */
.notion-mapper__direction-icon {
  display: inline-block;
  margin-left: 4px;
  font-size: var(--fs-caption);
  color: var(--text-faint, var(--text-muted));
  cursor: pointer;
  vertical-align: middle;
  line-height: 1;
}
/* ⓘ inside a label cell or db-title — sits flush against the right edge, always thin */
.notion-mapper__label .notion-mapper__direction-icon,
.notion-mapper__db-title .notion-mapper__direction-icon {
  margin-left: auto;
  flex-shrink: 0;
  font-weight: 400;
}

.notion-mapper__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 4px;
}
/* Passive autosave indicator that replaced the Save/Cancel buttons. Muted; no new colour token. */
.notion-mapper__save-status {
  margin-right: auto;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
.notion-disconnect-confirm {
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
.notion-mapper__error {
  margin: 0;
  font-size: var(--fs-caption);
  color: var(--accent-danger);
}
.notion-mapper__error--inline {
  flex: 1;
}

/* Schema-health warnings (Session 3) — plain-language issues + inline repair action.
   Layout only; reuses --accent-danger (the same token the setup-needed badge uses), no new colour. */
.notion-mapper__health-item {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: var(--fs-caption);
  color: var(--accent-warning);
  margin: 2px 0;
  padding-left: 168px; /* align with column 2 (160px label + 8px gap) */
}
.notion-mapper__health-item .notion-mapper__health-msg { color: var(--text-muted); flex: 1; }
.notion-mapper__health-icon { flex-shrink: 0; }
.notion-mapper__health-badge {
  margin-left: 6px;
  font-size: var(--fs-caption);
  color: var(--accent-warning);
}

/* Pending-DB-change banner (ADR 0014) — automatic sync paused until an explicit Sync.
   Layout only; reuses --accent-warning (same token as schema-health warnings), no new colour. */
.notion-mapper__pending-banner {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
  border: 1px solid var(--accent-warning);
  border-radius: 6px;
  padding: 8px 10px;
  margin: 8px 0;
}
.notion-mapper__pending-icon { flex-shrink: 0; color: var(--accent-warning); }

/* ── Notion option chip ── */
/* shape: rect (select) or pill (status). Color applied via inline CSS vars. */
.notion-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 18px;
  white-space: nowrap;
}
.notion-chip--rect { border-radius: 3px; }
.notion-chip--pill { border-radius: 9999px; }

/* ── AppSelect — app-standard styled dropdown ── */
/* Replaces <select> throughout the app. See public/js/components/AppSelect.tsx. */
.app-select {
  position: relative;
  flex: 1;
  min-width: 0;
}
.app-select__trigger {
  position: relative;
  width: 100%;
  height: 28px;
  padding: 0 22px 0 8px; /* right padding reserves room for chevron */
  font-size: var(--fs-body);
  text-align: left;
  cursor: pointer;
  background: var(--surface-input, var(--surface-raised));
  border: 1px solid var(--border-input, var(--border-subtle));
  border-radius: var(--radius-sm, 4px);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Chevron arrow — absolute so it doesn't disrupt flex content alignment */
.app-select__trigger::after {
  content: '▾';
  position: absolute;
  right: 7px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-faint, #9ca3af);
  font-size: var(--fs-caption);
  line-height: 1;
  pointer-events: none;
}
.app-select__trigger:disabled { opacity: 0.5; cursor: default; }
/* Truncate long labels in the trigger; sublabel stays packed right after label text */
.app-select__trigger > span:not(.app-select__sublabel) {
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-select__trigger .app-select__sublabel {
  flex-shrink: 0;
  white-space: nowrap;
}
.app-select--no-arrow .app-select__trigger { padding-right: 8px; }
.app-select--no-arrow .app-select__trigger::after { display: none; }
/* Dropdown list — explicit font-size because it renders in a portal at document.body */
.app-select__list {
  padding: 4px 0;
  max-height: 240px;
  overflow-y: auto;
  font-size: var(--fs-caption);
  background: var(--surface-raised, #ffffff);
  color: var(--text-primary, #1f2937);
}
.app-select__option {
  padding: 5px 10px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-body);
  color: var(--text-primary, #1f2937);
}
.app-select__option[aria-selected="true"] { background: rgba(0,0,0,0.06); }
.app-select__option--active { background: rgba(0,0,0,0.04); }
.app-select__option[aria-selected="true"].app-select__option--active { background: rgba(0,0,0,0.09); }
/* Sublabel — gray secondary text after option label (e.g. "(Select)", "(Text)") */
.app-select__sublabel {
  color: var(--text-faint);
  font-size: var(--fs-caption);
}
.app-select__placeholder {
  color: var(--text-faint);
  font-size: var(--fs-caption);
}

/* ── Notion sub-sections (user mapper, webhook) ── */
.notion-section {
  padding: 4px 0 4px 38px;
}
.notion-section__toggle {
  background: none;
  border: none;
  padding: 2px 0;
  font-size: var(--fs-body);
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.notion-section__toggle:hover { color: var(--text-primary); }

/* ── Notion user mapper ── */
.notion-user-mapper {
  padding: 6px 0 4px 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.notion-user-mapper__hint {
  margin: 0 0 4px 0;
  font-size: var(--fs-caption);
  color: var(--text-faint);
}
.notion-user-mapper__table { display: flex; flex-direction: column; gap: 4px; }
.notion-user-mapper__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.notion-user-mapper__row--unmapped .notion-user-mapper__member-name {
  color: var(--text-faint);
}
/* 160px combined column = property label width → select left-aligns with property selects */
.notion-user-mapper__member-area {
  width: 160px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 4px;
}
.notion-user-mapper__member {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}
.notion-user-mapper__member-name { font-size: var(--fs-body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.notion-user-mapper__member-email { font-size: var(--fs-caption); color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.notion-user-mapper__auto-chip {
  font-size: var(--fs-caption);
  padding: 1px 5px;
  border-radius: 10px;
  background: #dcfce7;
  color: var(--accent-success);
  white-space: nowrap;
  flex-shrink: 0;
}
.notion-user-mapper__auto-chip--hidden { visibility: hidden; }
.notion-user-mapper__saved {
  font-size: var(--fs-caption);
  color: var(--accent-success);
}
.notion-user-mapper__loading,
.notion-user-mapper__empty {
  margin: 0;
  font-size: var(--fs-caption);
  color: var(--text-faint);
}
.notion-user-mapper__unmapped-warning {
  margin: 0;
  font-size: var(--fs-caption);
  color: var(--text-faint);
}

/* ── Thread sync status dot ── */
.thread-sync-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* ── "Unread for you" dot ── */
/* Leading inline dot before the comment text in an RSB thread row. Mirrors
   .thread-sync-dot's shape; sits to the LEFT of the text (small right margin)
   and uses the existing accent token — no new colour. Disappears when the
   thread's notifications are read (chokepoint or bell). */
.thread-unread-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-right: 6px;
  vertical-align: middle;
  background: var(--accent-primary);
}

/* ── Confirm dialog (centered over settings modal) ── */
.ws-confirm__backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 500;
}
.ws-confirm__card {
  background: var(--surface-raised);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  width: min(360px, 92vw);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ws-confirm__message {
  font-size: var(--fs-body);
  font-weight: 600;
  margin: 0;
  color: var(--text-primary);
}
.ws-confirm__subtext {
  font-size: var(--fs-body);
  color: var(--text-muted);
  margin: 0;
}
.ws-confirm__subtext p {
  margin: 0 0 8px 0;
}
.ws-confirm__subtext p:last-child {
  margin-bottom: 0;
}
.ws-confirm__actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 8px;
}

/* ── Choice-confirm dialog (ConfirmChoiceDialog) ──
   Reuses .ws-confirm__card so it matches the single-choice ConfirmDialog's
   look (surface, shadow, padding, type). The difference: two long-labelled
   danger choices don't fit a single right-aligned row (they overflowed and
   pushed Cancel out of the card), so the choices STACK full-width — most
   destructive (loud red) on top, safer option below — with a text Cancel and
   a top-right ✕ close. No new colour: reuses the .ws-btn family + tokens. */
.ws-choice__card {
  position: relative;              /* anchor for the ✕ close */
  gap: 12px;                       /* a touch more air than the base 8px */
  padding-top: 22px;               /* leave room beside the ✕ */
}
.ws-choice__card .ws-confirm__message {
  padding-right: 24px;             /* keep the title clear of the ✕ */
}
.ws-choice__close {
  position: absolute;
  top: 12px;
  right: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  border-radius: var(--radius-control);
  color: var(--text-faint);
  cursor: pointer;
}
.ws-choice__close:hover:not(:disabled) {
  background: rgba(0,0,0,0.05);
  color: var(--text-muted);
}
.ws-choice__close:disabled { opacity: 0.5; cursor: not-allowed; }

/* Stacked, full-width choices. */
.ws-choice__actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 4px;
}
.ws-choice__btn {
  width: 100%;
  height: 36px;                    /* a little taller than the 30px inline btn */
  font-weight: 600;
}

/* .ws-btn sets only a fixed height, so a <button>'s default baseline can leave
   its label a hair high. Flex-center the label inside every confirm-dialog
   button (choice buttons AND the single-choice dialog's Cancel/Delete row) so
   the text sits dead-centre. Scoped to the confirm dialogs — the global .ws-btn
   is used app-wide and left untouched. Also scoped to the Master Admin danger
   zone's inline confirm (same fixed-height .ws-btn, same baseline drift). */
.ws-choice__btn,
.ws-confirm__actions .ws-btn,
.admin-danger-zone .ws-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Cancel: a real (neutral) button matching .ws-btn--secondary, full-width and
   the same height as the choices above it. */
.ws-choice__cancel {
  width: 100%;
  height: 36px;
  margin-top: 2px;
  padding: 0 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  background: var(--surface-raised);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  font-size: var(--fs-body);
  cursor: pointer;
}
.ws-choice__cancel:hover:not(:disabled) {
  background: rgba(0,0,0,0.04);
}
.ws-choice__cancel:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Server-connection banner (ServerConnectionBanner) ──
   Slim fixed top strip shown while the app can't reach the server. Overlays
   (does not push layout) so nothing reflows when it appears. --accent-warning
   amber (existing token — no new colour); z-index 350 sits above the header
   (z 300) but below modals (z 400) so a dialog backdrop still covers it. */
.conn-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 350;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 28px;
  background: var(--accent-warning);
  color: #fff;
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.01em;
  box-shadow: 0 1px 6px rgba(0,0,0,0.18);
}
/* A small pulsing dot to convey "actively retrying". */
.conn-banner__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #fff;
  animation: conn-banner-pulse 1.2s ease-in-out infinite;
}
@keyframes conn-banner-pulse {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}

/* "First open" find-file modal (spec 2026-07-08). Backdrop/card reuse the
   standard modal shell scale; checkmark line uses --accent-success, primary
   button uses --accent-primary (DESIGN.md §2 — no new colour). No --fs-title
   token exists (DESIGN.md §2a — two sizes only): the filename uses
   --fs-body + 600 weight, same as every other title in the app. Card surface
   is --surface-raised (menus/modals/popovers, same as .ws-confirm__card) and
   the explain-block divider is --border-subtle (card-edge weight), matching
   the sibling confirm dialog's token usage. */
.ff-modal__backdrop {
  position: fixed; inset: 0; z-index: 400;
  background: rgba(0, 0, 0, 0.35);
  display: flex; align-items: center; justify-content: center;
}
.ff-modal__card {
  background: var(--surface-raised);
  width: min(92%, 500px);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  padding: 16px 22px 20px;
}
.ff-modal__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.ff-modal__eyebrow { font-size: var(--fs-caption); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); font-weight: 600; }
.ff-modal__x { border: none; background: none; cursor: pointer; color: var(--text-faint); font-size: 18px; line-height: 1; }
.ff-modal__filename { font-size: var(--fs-body); font-weight: 600; color: var(--text-primary); margin-bottom: 8px; word-break: break-all; }
.ff-modal__copied { font-size: var(--fs-caption); color: var(--accent-success); font-weight: 500; margin-bottom: 20px; }
.ff-modal__copied.is-manual { color: var(--text-muted); }
/* Drive-status prefix on the copy line: a coloured icon + word stating whether
   the file was found on Drive. Colour lives only in the icon + word (DESIGN §5
   "accent only at the edge") — the copy line's own text keeps its clipboard
   colour. Reuses existing tokens: green found / red not-found. */
.ff-modal__drive { font-weight: 500; }
.ff-modal__drive--found { color: var(--accent-success); }
.ff-modal__drive--not-found { color: var(--accent-danger); }
.ff-modal__drive-ic { margin-right: 4px; }
/* "Checking Drive…" state: neutral muted line + inline spinner (reuses ws-spin). */
.ff-modal__copied.is-loading { color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.ff-modal__spinner {
  display: inline-block;
  width: 11px;
  height: 11px;
  border: 2px solid var(--border-subtle);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: ws-spin 0.7s linear infinite;
  vertical-align: middle;
  flex-shrink: 0;
}
.ff-modal__check { margin-right: 4px; }
.ff-modal__btns { display: flex; flex-direction: column; gap: 9px; margin-bottom: 18px; }
.ff-modal__btn { border-radius: var(--radius-control); padding: 12px 16px; font-size: var(--fs-body); font-weight: 500; cursor: pointer; }
.ff-modal__btn--primary { background: var(--accent-primary); color: #fff; border: none; }
.ff-modal__btn--primary:hover { background: var(--accent-primary-strong); }
.ff-modal__btn--secondary { background: var(--surface-raised); border: 1px solid var(--border-strong); color: var(--text-primary); }
.ff-modal__explain { font-size: var(--fs-caption); color: var(--text-muted); line-height: 1.6; padding-top: 14px; border-top: 1px solid var(--border-subtle); }
.ff-modal__pathref { font-family: Consolas, "SF Mono", monospace; font-size: 10.5px; color: var(--text-faint); word-break: break-all; line-height: 1.5; margin-top: 10px; }
.ff-modal__tip { font-size: var(--fs-caption); color: var(--text-faint); margin-top: 12px; }
.ff-modal__kbd { background: #efeff1; border: 1px solid #dcdce0; border-bottom-width: 2px; border-radius: 4px; padding: 1px 5px; font-size: 10px; font-family: monospace; color: var(--text-muted); }

/* ── Set "Locate files" panel (spec 2026-07-11) ─────────────────────── */
/* Flex column + bounded height so a set with many members scrolls the ROW LIST
   internally (`.sl-list` flexes + scrolls) while the header (title/status/primary
   button) and the footer "Open set now" stay pinned and always reachable. Without
   this a tall card overflowed the viewport with no scroll region. */
.sl-card { background: var(--surface-raised); border-radius: var(--radius-card); box-shadow: var(--shadow-modal); padding: 22px 24px; width: 480px; max-width: 92vw; max-height: 88vh; display: flex; flex-direction: column; font-size: var(--fs-body); color: var(--text-primary); }
.sl-title { font-weight: 600; font-size: 17px; margin: 6px 0 3px; }
.sl-status { font-size: var(--fs-caption); color: var(--text-muted); margin-bottom: 14px; min-height: 17px; }
.sl-primary { width: 100%; background: var(--accent-primary); color: #fff; border: none; border-radius: var(--radius-control); padding: 12px; font-size: 15px; font-weight: 600; cursor: pointer; }
.sl-primary:hover { background: var(--accent-primary-strong); }
/* The one scroll region: flexes to fill the space between the pinned header and
   footer, scrolls its overflow. `min-height: 0` lets it shrink below content
   size inside the flex column (else it would push the footer off-card). The 2px
   right padding keeps the scrollbar off the row edges. */
.sl-list { display: flex; flex-direction: column; gap: 8px; margin: 14px 0 18px; flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 2px; }
.sl-row { display: flex; align-items: center; gap: 10px; padding: 9px 11px; border: 1px solid var(--border-subtle); border-radius: 8px; }
.sl-row.ok { border-color: color-mix(in srgb, var(--accent-success) 40%, #fff); background: color-mix(in srgb, var(--accent-success) 5%, #fff); }
.sl-ic { width: 16px; flex-shrink: 0; text-align: center; font-size: 13px; font-weight: 600; }
.sl-ic.missing { color: var(--text-faint); font-weight: 400; }
.sl-ic.ok { color: var(--accent-success); }
.sl-ic.warn { color: var(--accent-danger); }
.sl-name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sl-fn { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sl-meta { font-size: var(--fs-caption); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 4px; }
.sl-meta .sl-stat { font-weight: 600; color: var(--text-primary); }
.sl-meta .sl-stat.found { color: var(--accent-success); }
.sl-meta.attach { color: var(--accent-primary); }
.sl-meta.attach .sl-stat { color: var(--accent-primary); }
.sl-meta.gone .sl-stat { color: var(--accent-danger); }
.sl-meta.is-checking { color: var(--text-faint); font-style: italic; }
.sl-spin { width: 11px; height: 11px; border: 2px solid var(--border-subtle); border-top-color: var(--accent-primary); border-radius: 50%; display: inline-block; animation: ws-spin 0.7s linear infinite; flex-shrink: 0; }
.sl-act { font-size: var(--fs-caption); color: var(--accent-primary); font-weight: 500; border: 1px solid var(--accent-primary); background: #fff; border-radius: 6px; padding: 2px 10px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }
.sl-act.solid { background: var(--accent-primary); color: #fff; }
.sl-act:hover { background: #eef2ff; }
.sl-act.solid:hover { background: var(--accent-primary-strong); }
/* Path text is middle-truncated in JS (middleTruncate) with the full path on
   the title; keep it on one line. */
.sl-meta .sl-path { white-space: nowrap; }
/* Right action column: "Pick file" on top, the copy-button pair beneath it.
   The pair spans ~the same width as Pick file. Only rendered on Find rows. */
.sl-actions { display: flex; flex-direction: column; align-items: stretch; gap: 4px; flex-shrink: 0; }
.sl-actions .sl-act { width: 100%; }
/* Copy buttons — flat monochrome, peek/star (.symbol-btn) language: faint at
   rest, muted + subtle square hover bg on hover, rounded. Permanently visible
   (no hover-reveal). 26x26 to match the peek/star buttons; SVG ~16px inside —
   a larger glyph in the same box trims the empty space above/below it evenly. */
.sl-copy { display: flex; justify-content: center; gap: 4px; }
.sl-copy-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  border-radius: var(--radius-control);
  transition: background 80ms ease, color 80ms ease;
}
.sl-copy-btn svg { width: 16px; height: 16px; }
.sl-copy-btn:hover { background: rgba(0,0,0,0.06); color: var(--text-muted); }
.sl-open { width: 100%; background: #fff; color: var(--text-primary); border: 1px solid var(--border-strong); border-radius: var(--radius-control); padding: 11px; font-size: var(--fs-body); font-weight: 600; cursor: pointer; }
.sl-open:disabled { opacity: 0.5; cursor: default; }

/* ─── TipTap reply composer ─────────────────────────────────────────────── */

.reply-composer-wrap .ProseMirror {
  min-height: 48px;
  padding: 4px 8px;
  font-size: var(--fs-body); /* text-sm */
  border: 1px solid #d1d5db; /* border-gray-300 */
  border-radius: 4px;
  outline: none;
  line-height: 1.5;
  color: var(--text-primary);
}
.reply-composer-wrap .ProseMirror:focus {
  border-color: #60a5fa; /* blue-400 */
  box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.4);
}
.reply-composer-wrap .ProseMirror p {
  margin: 0;
}

/* Placeholder text (via data-placeholder attribute) */
.reply-composer-wrap .ProseMirror:empty::before,
.reply-composer-wrap .ProseMirror p:first-child:last-child:empty::before {
  content: attr(data-placeholder);
  color: var(--text-faint);
  pointer-events: none;
}

/* ─── @mention chip (inline) ────────────────────────────────────────────── */
/* Token: --mention-chip-bg. Pill appearance consistent with MemberChip. */

:root {
  --mention-chip-bg: #f3f4f6;   /* gray-100 — matches the assignee chip */
  --mention-chip-text: #4b5563; /* gray-600 */

  /* ── Notion option chip color palette (10 colors × bg + text) ── */
  --notion-c-default-bg: #e3e2e0; --notion-c-default-text: #787774;
  --notion-c-gray-bg:    #e3e3e2; --notion-c-gray-text:    #787774;
  --notion-c-brown-bg:   #eee0da; --notion-c-brown-text:   #64473a;
  --notion-c-orange-bg:  #fadec9; --notion-c-orange-text:  #c47822;
  --notion-c-yellow-bg:  #fdecc8; --notion-c-yellow-text:  #c29343;
  --notion-c-green-bg:   #dbeddb; --notion-c-green-text:   #448361;
  --notion-c-blue-bg:    #d3e5ef; --notion-c-blue-text:    #32729e;
  --notion-c-purple-bg:  #e8deee; --notion-c-purple-text:  #7b52a6;
  --notion-c-pink-bg:    #f5e0e9; --notion-c-pink-text:    #b8428a;
  --notion-c-red-bg:     #ffe2dd; --notion-c-red-text:     #c4534c;

  /* Saturated/dark "solid" variants of the same hues, tuned for small marker
     dots on top of a PDF — the pastel -bg is invisible and the -text is too
     soft at dot size, so dots use these. Same hue family, higher contrast. */
  --notion-c-default-dot: #5f5e5b;
  --notion-c-gray-dot:    #6e6e6b;
  --notion-c-brown-dot:   #8a5a2b;
  --notion-c-orange-dot:  #d9730d;
  --notion-c-yellow-dot:  #cb912f;
  --notion-c-green-dot:   #0f7b6c;
  --notion-c-blue-dot:    #0b6e99;
  --notion-c-purple-dot:  #6940a5;
  --notion-c-pink-dot:    #ad1a72;
  --notion-c-red-dot:     #e03e3e;
}

.mention-chip {
  display: inline-flex;
  align-items: center;
  background: var(--mention-chip-bg);
  color: var(--mention-chip-text);
  border-radius: 99px;
  padding: 1px 6px;
  font-size: var(--fs-body);
  font-weight: 600;
  white-space: nowrap;
  cursor: default;
  user-select: all;
}

/* Ghost typeahead text appended after cursor when @mention suggestion is active */
.mention-ghost-text {
  color: var(--text-faint);
  pointer-events: none;
  user-select: none;
}

/* ─── Projects modal ────────────────────────────────────────────────────── */

/* Backdrop reuses ws-modal__backdrop; this class bumps z-index above the
   workspace settings modal (z-index 400) it sits on top of. */
.projects-modal__backdrop { z-index: 450 !important; }

.projects-modal__card {
  background: var(--surface-raised);
  border-radius: var(--radius-card);
  box-shadow: 0 8px 40px rgba(0,0,0,0.18);
  width: min(90vw, 1100px);
  height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.projects-modal__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-subtle);
  flex-shrink: 0;
}
.projects-modal__title { font-size: var(--fs-body); font-weight: 600; margin: 0; color: var(--text-primary); }
.projects-modal__close {
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); padding: 4px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
}
.projects-modal__close:hover { background: var(--surface-card-hover); color: var(--text-primary); }
.projects-modal__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.projects-modal__footer {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 20px;
  border-top: 1px solid var(--border-subtle);
  flex-shrink: 0;
}
.projects-modal__error { color: var(--accent-danger); font-size: var(--fs-body); padding: 0 20px 8px; }
.projects-modal__notice { color: var(--text-muted); font-size: var(--fs-body); padding: 0 20px 8px; }

/* Auto-match / Add-all split button in the Notion card header — give it card-header height. */
.projects-modal__notion-actions .split-btn__main { padding: 4px 10px; font-size: var(--fs-caption); }
.projects-modal__notion-actions .split-btn__caret { font-size: var(--fs-glyph); padding: 0 5px; }

/* Unlinked Notion pages — ExpandCard wrapper */
.projects-modal__unlinked-list { padding: 4px 0; }
.projects-modal__unlinked-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 5px 12px; border-bottom: 1px solid var(--border-subtle);
}
.projects-modal__unlinked-row:last-child { border-bottom: none; }
.projects-modal__unlinked-name { font-size: var(--fs-body); color: var(--text-primary); }

/* Acetate projects section */
.projects-modal__section { margin-bottom: 0; }
.projects-modal__section-header {
  display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;
}
.projects-modal__section-label {
  font-size: var(--fs-caption); color: var(--text-muted); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.projects-modal__table-footer {
  display: flex; justify-content: space-between; align-items: center; padding-top: 8px;
}
.projects-modal__archived-toggle { font-size: var(--fs-caption); color: var(--text-muted); cursor: pointer; }

/* Table */
.projects-modal__table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: var(--fs-body); }
.projects-modal__table th {
  text-align: left; padding: 5px 8px; color: var(--text-muted);
  font-size: var(--fs-caption); font-weight: 500; border-bottom: 1px solid var(--border-subtle);
}
.projects-modal__table td { padding: 5px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: middle; }

/* Column widths — name gets most of the space */
.projects-modal__col--name    { width: auto; }
.projects-modal__col--layers  { width: 62px; }
.projects-modal__col--notion  { width: 180px; min-width: 180px; max-width: 180px; }
.projects-modal__col--actions { width: 150px; }

/* Name cell — optional Notion mark + locked text or editable label, on one row */
.projects-modal__name-cell { display: flex; align-items: center; gap: 6px; min-width: 0; }
.projects-modal__notion-mark {
  display: flex; align-items: center; flex-shrink: 0; color: var(--text-muted);
}
.projects-modal__name-editable { flex: 1; min-width: 0; font-size: var(--fs-body); padding: 3px 6px; }
/* Content-width so the Notion mark sits immediately to the RIGHT of the name (not the cell edge);
   still shrinks + ellipsises when the name is too long for the column. */
.projects-modal__name-locked {
  flex: 0 1 auto; min-width: 0;
  font-size: var(--fs-body); color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 3px 6px 3px 6px;
}

/* Notion selector — compact trigger; popup width controlled via dropdownMinWidth prop */
.projects-modal__notion-cell { display: flex; align-items: center; gap: 4px; }
.projects-modal__notion-cell .app-select { flex: 1; min-width: 0; }

.projects-modal__layer-count { color: var(--text-muted); text-align: right; }
.projects-modal__missing-badge {
  font-size: var(--fs-caption); color: var(--accent-danger); font-weight: 600; white-space: nowrap;
}
.projects-modal__row--new td { background: rgba(99, 102, 241, 0.04); }
.projects-modal__actions { white-space: nowrap; text-align: right; }
.projects-modal__empty { color: var(--text-faint); font-size: var(--fs-body); padding: 12px 8px; }

/* Action + add buttons */
.projects-modal__archive-btn,
.projects-modal__delete-btn,
.projects-modal__add-btn,
.projects-modal__add-all-btn,
.projects-modal__new-btn {
  background: none; border: 1px solid var(--border-strong); border-radius: var(--radius-control);
  padding: 2px 8px; font-size: var(--fs-caption); cursor: pointer; color: var(--text-muted);
  margin-left: 4px;
}
.projects-modal__archive-btn:hover,
.projects-modal__add-btn:hover,
.projects-modal__add-all-btn:hover,
.projects-modal__new-btn:hover { background: var(--surface-card-hover); color: var(--text-primary); }
.projects-modal__delete-btn:hover { color: var(--accent-danger); border-color: var(--accent-danger); }
/* Inline delete confirm — the second click is the destructive one, so it reads danger up front. */
.projects-modal__delete-btn--confirm,
.projects-modal__delete-btn--confirm:hover {
  color: var(--accent-danger); border-color: var(--accent-danger); background: var(--surface-card-hover);
}
.projects-modal__new-btn { margin-left: 0; color: var(--accent-active); border-color: var(--accent-active); }
@keyframes projects-modal-spin { to { transform: rotate(360deg); } }
.projects-modal__spinner {
  display: inline-block; width: 13px; height: 13px;
  border: 2px solid var(--border-subtle); border-top-color: var(--text-muted);
  border-radius: 50%; animation: projects-modal-spin 0.7s linear infinite;
  flex-shrink: 0; margin-left: auto; margin-right: 8px; align-self: center;
}
.notion-mapper__manage-projects-link {
  display: inline-block; margin-top: 6px; margin-left: 8px;
  font-size: var(--fs-caption); color: var(--accent-active); background: none; border: none;
  cursor: pointer; padding: 0; text-decoration: underline;
}
.notion-mapper__hint { font-size: var(--fs-caption); color: var(--text-faint); align-self: center; }

/* =====================================================================
   APPEND-ONLY BLOCK (owned by the app.ts/index.html copy-fix agent).
   Keep all rules for this agent's surfaces below this delimiter so the
   rest of styles.css stays owned by its primary maintainer.
   ===================================================================== */

/* ── FSA-unsupported browser notice banner ─────────────────────────────
   Slim amber top strip shown once per session when the File System Access
   API is missing (Firefox/Safari). Mirrors #pdf-warning-banner's amber
   language (same #fef3c7 warning surface, no new colour token). */
#fsa-support-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 16px;
  background: #fef3c7;
  color: #78350f;
  font-size: var(--fs-caption);
  border-bottom: 1px solid #fde68a;
  flex-shrink: 0;
}
#fsa-support-banner.hidden { display: none; }
#fsa-support-banner .banner-dismiss {
  margin-left: auto;
  cursor: pointer;
  opacity: 0.7;
  font-size: 15px;
  line-height: 1;
  flex-shrink: 0;
}
#fsa-support-banner .banner-dismiss:hover { opacity: 1; }

/* ===================================================================
   Product tour overlay (DESIGN.md section 3 primitive; tokens only)
   The tour owns the screen: a dimmed backdrop with a rounded-rect
   spotlight cut over the anchored control + a positioned tooltip card.
   z-index --z-product-tour (620) sits above .ws-choice (600) so nothing
   can paint over a running tour. Backdrop does NOT dismiss (spec section
   7) -- only Skip/Esc/Done end the tour. Reuses the confirm-scrim dim
   value and the modal card recipe; no new colour.
   =================================================================== */
:root { --z-product-tour: 620; }

.tour-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-product-tour);
}

/* SVG dim + spotlight hole. pointer-events:none so it never swallows a
   tooltip click; the sibling .tour-backdrop swallows app clicks. */
.tour-mask {
  position: fixed;
  inset: 0;
  pointer-events: none;
}
.tour-mask__dim {
  /* The existing confirm-scrim dim (DESIGN section 3.15) -- reused so the
     tour's "the world behind is inert" reads identically to every modal. */
  fill: rgba(0, 0, 0, 0.45);
  transition: none;
}

/* Swallows clicks to the app beneath; sits under the mask + card. The
   spotlight hole does NOT pass clicks through (this is a tour, not a
   coach-mark) -- the whole backdrop is click-inert to the app. */
.tour-backdrop {
  position: fixed;
  inset: 0;
  pointer-events: auto;
}

.tour-card {
  position: fixed;
  z-index: 1; /* above the mask/backdrop within the overlay stacking context */
  box-sizing: border-box;
  width: max-content;
  max-width: min(92vw, 360px);
  padding: 18px 20px 16px;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  transition: top 180ms ease, left 180ms ease;
}

.tour-card__meta {
  margin: 0 0 6px;
  font-size: var(--fs-caption);
  color: var(--text-faint);
}
.tour-card__headline {
  margin: 0 0 8px;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
}
.tour-card__body {
  margin: 0;
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.5;
  color: var(--text-primary);
  max-height: 40vh;
  overflow-y: auto;
}

/* Step dots -- filled = current, the same "filled dot = current" language as
   the favourite / member-sort toggle (DESIGN section 3.4). No new colour. */
.tour-card__dots {
  display: flex;
  gap: 6px;
  margin: 14px 0;
}
.tour-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--border-strong);
  cursor: pointer;
  transition: background 120ms ease, transform 120ms ease;
}
.tour-dot--active {
  background: var(--accent-primary);
  transform: scale(1.15);
}
.tour-dot:hover { background: var(--accent-primary-strong); }

.tour-card__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.tour-card__nav {
  display: flex;
  gap: 8px;
}

/* Buttons reuse the ws-btn token recipe (DESIGN section 5) -- no new primitive. */
.tour-btn {
  height: 30px;
  padding: 0 14px;
  font-size: var(--fs-body);
  border-radius: var(--radius-control);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
}
.tour-btn--primary {
  background: var(--accent-primary);
  color: #fff;
}
.tour-btn--primary:hover { background: var(--accent-primary-strong); }
.tour-btn--secondary {
  background: var(--surface-base);
  color: var(--text-primary);
  border-color: var(--border-subtle);
}
.tour-btn--secondary:hover { background: rgba(0, 0, 0, 0.04); }
.tour-btn--skip {
  background: transparent;
  color: var(--text-muted);
  padding: 0 4px;
}
.tour-btn--skip:hover { color: var(--text-primary); }

/* Keyboard focus ring -- the ONE shared indigo halo (DESIGN section 5),
   :focus-visible ONLY so a mouse click never rings. Scoped into the tour block
   (its buttons/dots live in a body-appended root outside the shared rule's
   list). No new colour -- reuses --accent-focus-ring. */
.tour-btn:focus-visible,
.tour-dot:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-focus-ring);
}

/* Reduced motion: drop the spotlight/tooltip transitions (quality floor). */
@media (prefers-reduced-motion: reduce) {
  .tour-card,
  .tour-dot,
  .tour-mask__dim {
    transition: none;
  }
  .tour-dot--active { transform: none; }
}
