/**
 * Harbour - Stylesheet
 *
 * Design: CLI/terminal character — box-drawing scaffolding and a typographic
 *   split (mono for machine facts, sans for human structure/labels). Not a pure
 *   monospace aesthetic any more (LIN-785 / LIN-782).
 * Theme: Light is the default (:root); dark is an opt-in `.theme-dark` hook on
 *   `<html>`, applied pre-paint by the shared shell from a persisted preference.
 * Layout: Single-column, max 120ch wide for readability.
 */

/* =============================================================================
   CSS Custom Properties (Theme Colors)
   =============================================================================
   Using CSS variables for consistent theming and easy customization.
   - bg/fg: Background and foreground (text) colors
   - fg-dim: Muted text for secondary information
   - green/yellow/blue: Status and accent colors matching Linear's palette
*/
:root {
  --bg: #ffffff;
  --bg-muted: #f8f9fa;  /* Muted background for containers */
  --fg: #1a1a1a;
  --fg-dim: #666666;
  --fg-vdim: #eeeeee;  /* Very dim - box-drawing characters, structural elements */
  --fg-mid: #bbbbbb;   /* Mid neutral accent — e.g. unstarted-state marker, distinct from --border (LIN-510) */
  --green: #16a34a;    /* Completed status (de-tealed from #16a085 so it stays distinct from --teal; LIN-724) */
  --yellow: #d4a600;   /* In-progress status */
  --blue: #2563eb;     /* Links and project headers */
  --purple: #7c3aed;   /* Periodicals (synthetic maintenance group) */
  --red: #cc0000;      /* Danger actions (remove) */
  --red-hover: #990000;

  /* Harbour brand accent (LIN-724). Teal is the product's accent hue, inherited
     from harbour.cat. The status hues above are kept visibly distinct from it
     (notably --green, nudged off its old teal-green #16a085). Consumed by the
     wordmark (LIN-725) and landing (LIN-726). The per-theme teal value is
     defined in .theme-dark below. */
  --teal: #0d9488;          /* Brand accent (light) */
  --teal-soft: #0d948820;   /* Low-alpha teal tint for accent fills/hovers */

  /* Font families: Separate structural (mono, for machine facts) from content
     (sans, for human structure/labels). LIN-785 loads both faces self-hosted
     (see @font-face below): JetBrains Mono heads the structural stack, Inter the
     content stack — both with robust system fallbacks so a failed/blocked font
     load degrades to the prior look rather than a layout shift. */
  --font-structural: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Consolas', monospace;
  --font-content: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* LIN-456: tokens previously referenced via var() fallback only — now defined
     at their exact prior fallback values so fallback removal stays a visual no-op.
     --accent/--ok are distinct hues from --red/--green; do not remap them. */
  --accent: #c33;                        /* Roadmap failed-state accent (≠ --red) */
  --ok: #2a8f4e;                          /* Roadmap done-state green (≠ --green) */
  --bg-alt: rgba(0, 0, 0, 0.03);         /* Subtle alternating-row background tint */
  --border: #ddd;                        /* Default hairline border */
  --border-strong: #ccc;                 /* Higher-contrast border for hover/focus emphasis (LIN-510) */
  --border-subtle: rgba(0, 0, 0, 0.08);  /* Lower-contrast border over light bg */
  --font-mono: monospace;                /* Generic monospace fallback */

  /* LIN-456: completed token groups (definitions only — not yet wired to sites,
     so adding them changes nothing; they give 0C's /styleguide a home and let
     later phases tokenize spacing/elevation deliberately rather than by sweep). */

  /* Spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2rem;

  /* Border-radius scale (theme spec §6 sanctioned values 5/8/14/999px; the
     bespoke 2/7/9px sanctioned radii stay as per-element literals — LIN-863) */
  --radius-sm: 5px;
  --radius-md: 8px;
  --radius-lg: 14px;
  --radius-full: 999px;

  /* Elevation / shadow scale */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);

  /* Z-index scale */
  --z-base: 1;
  --z-sticky: 100;
  --z-overlay: 1000;
  --z-modal: 2000;

  /* Sticky shared-header clearance (LIN-984). The reserved height used as the
     `scroll-margin-top` for interactive controls so scroll-into-view settles
     them below the pinned `.nav-bar`, and as the offset for page-level fixed
     controls that must clear the header (e.g. the ship mode toggle). Sized for
     the tallest wrap of the two-row nav (filters/actions + the views row). */
  --nav-sticky-h: 6rem;

  /* Font-size scale */
  --font-size-sm: 0.85em;
  --font-size-base: 1em;
  --font-size-lg: 1.25em;

  /* ===========================================================================
     Semantic theme vocabulary (LIN-785 S1, per the LIN-782 locked direction)
     ===========================================================================
     The named layer S2 primitives consume. In light (:root) these are ALIASES
     over the existing token system, so adding them changes nothing visually for
     un-migrated pages (the stability guardrail). `.theme-dark` re-binds the same
     names below. Defining the aliases as `var(--existing)` keeps the two layers
     from drifting — change the raw token and the semantic name follows. */
  --text: var(--fg);            /* primary body text */
  --muted: var(--fg-dim);       /* secondary text */
  --faint: var(--fg-vdim);      /* structural hairlines / box-drawing */
  --line: var(--border);        /* default hairline */
  --line-soft: var(--border-subtle);  /* lower-contrast hairline */
  --card: var(--bg-muted);      /* card / container surface */
  --card-h: var(--bg-alt);      /* card hover surface */
  --raised: var(--bg);          /* raised surface (sits on the page bg) */
  --inset: var(--bg-alt);       /* inset / well surface */
  --brand: var(--teal);         /* product accent (teal, per LIN-782) — fill/dot/border */
  --brand-dim: #0f766e;         /* AA-safe brand text on --card (LIN-786 §11: --teal #0d9488 on #f8f9fa ≈3.6:1 fails AA; darken for TEXT only, leave --brand fills intact) */
  --shadow: var(--shadow-md);   /* default elevation */

  /* Status palette — keep --green/--red; add running (--amber) + neutral
     (--slate). Each ships an AA-safe `-dim` companion for use as TEXT on the
     theme's surface (the raw hue is for fills / dots / accents only). --amber
     #FFB224 in particular fails AA as text on white, hence --amber-dim. --yellow
     stays the existing in-progress hue; --amber is the new running-status token. */
  --green-dim: #15803d;   /* AA-safe green text on light */
  --amber: #FFB224;       /* running status (fill/dot) */
  --amber-dim: #8a5a00;   /* AA-safe amber text on light */
  --red-dim: #b91c1c;     /* AA-safe red text on light */
  --slate: #64748b;       /* neutral / idle status (fill/dot) */
  --slate-dim: #475569;   /* AA-safe slate text on light */

  /* a11y: focus-visible ring colour (AA-safe against light surfaces). */
  --focus: #0d6efd;

  /* Motion baseline (LIN-785 §7). Tokenized durations so the reduced-motion
     media query can neutralize them in one place; keyframes defined below. */
  --motion-fast: 120ms;
  --motion-base: 240ms;
  --motion-slow: 480ms;
  /* Running-dot pulse cadence (theme spec §7). Distinct from the motion scale
     above so repointing it never corrupts fast/base/slow. */
  --motion-pulse: 1.6s;
}

/* =============================================================================
   Self-hosted fonts (LIN-785)
   =============================================================================
   Vendored latin-subset woff2 under /public/fonts (no build step). `swap` shows
   the fallback immediately and swaps when the face arrives, so a slow/blocked
   load never blanks text. The families head their respective --font-* stacks. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/inter-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/inter-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/inter-700.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-500.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-600.woff2') format('woff2');
}

/* =============================================================================
   Theme hooks (LIN-484, extended for the global toggle in LIN-785)
   =============================================================================
   Alternate themes are pure overrides of the color tokens above — the structural
   tokens (spacing/radius/font/z/motion) are theme-neutral and stay shared. The
   default light theme is `:root`, so applying NO class is the light theme and
   every existing page renders byte-identically; only the token *values* change
   under a theme class, so no new token names are introduced by a theme. The
   class is applied to `<html>` pre-paint by the shared shell (lib/components/
   page.js) from the persisted preference, but each theme class is also
   self-painting (sets bg/fg) so it can still wrap any subtree — e.g. the
   side-by-side panel on /styleguide. (The legacy amber variant was removed in
   LIN-785.) */
.theme-dark {
  --bg: #16181d;
  --bg-muted: #1f222a;
  --bg-alt: rgba(255, 255, 255, 0.05);
  --fg: #e6e6e6;
  --fg-dim: #9aa0a6;
  --fg-vdim: #2c313a;        /* box-drawing / hairline structure on dark */
  --fg-mid: #5c6370;         /* mid neutral accent on dark — between --border and --fg-dim */
  --green: #2ee65f;          /* de-tealed from #2ee6a6 so it stays distinct from --teal/--ok on dark (LIN-724) */
  --yellow: #e6c14a;
  --blue: #6ea8fe;
  --purple: #b794f6;
  --red: #ff6b6b;
  --red-hover: #ff8787;
  --teal: #2dd4bf;           /* Brand accent (dark) — brighter teal for contrast on dark bg */
  --teal-soft: #2dd4bf20;
  --accent: #ff6b6b;
  --ok: #4ade80;
  --border: #2c313a;
  --border-strong: #3d4450;  /* stronger hover/focus border on dark (more visible than --border) */
  --border-subtle: rgba(255, 255, 255, 0.12);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.6);

  /* LIN-785 semantic vocabulary re-bound for dark. Re-declaring the aliases here
     (rather than relying on the :root `var(--fg)` cascade) keeps them correct
     even when `.theme-dark` wraps a SUBTREE rather than `<html>` (the styleguide
     panel). Values resolve against this block's raw tokens. */
  --text: var(--fg);
  --muted: var(--fg-dim);
  --faint: var(--fg-vdim);
  --line: var(--border);
  --line-soft: var(--border-subtle);
  --card: var(--bg-muted);
  --card-h: var(--bg-alt);
  --raised: var(--bg-muted);
  --inset: var(--bg-alt);
  --brand: var(--teal);
  --brand-dim: #2dd4bf;      /* AA-safe brand text on dark --card (--teal already ≈8.5:1 here; restated so the token resolves in dark) */
  --shadow: var(--shadow-md);

  /* Status palette on dark — raw hues for fills/dots, `-dim` companions are the
     AA-safe text colours on the dark surface. */
  --green-dim: #4ade80;
  --amber: #ffc04d;          /* running status (fill/dot) on dark */
  --amber-dim: #ffd27a;      /* AA-safe amber text on dark */
  --red-dim: #ff9b9b;        /* AA-safe red text on dark */
  --slate: #94a3b8;          /* neutral / idle status on dark */
  --slate-dim: #cbd5e1;      /* AA-safe slate text on dark */
  --focus: #6ea8fe;          /* focus ring on dark (AA-safe) */

  background: var(--bg);
  color: var(--fg);
}

/* =============================================================================
   Base Reset & Typography
   ============================================================================= */
* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  /* `clip` (NOT `hidden`) clips horizontal overflow WITHOUT establishing a scroll
     container. `overflow-x: hidden` here makes the root/`body` a scroll-container
     ancestor of the sticky header (`.nav-bar` below), which sticks it relative to
     that non-scrolling ancestor instead of the viewport — the classic
     overflow-breaks-sticky interaction that left the header inert (LIN-1068). Keep
     this `clip`: reverting to `hidden` silently re-breaks `position: sticky` for the
     header and any sticky element under the root. */
  overflow-x: clip;
  scrollbar-gutter: stable;
}

/* Terminal-style typography: structural base is monospace for alignment */
body {
  font-family: var(--font-structural);
  font-size: 16px;
  line-height: 1.6;
  background: var(--bg);
  color: var(--fg);
  padding: var(--space-5);
  max-width: 120ch;  /* Optimal reading width */
  margin-left: auto;
  margin-right: auto;
  /* `clip`, not `hidden`, for the same reason as the `html` reset above: `hidden`
     would make `body` a scroll container and defeat the sticky header (LIN-1068).
     Swim pages deliberately re-establish a horizontal scroll container via
     `body:has(.swim-page) { overflow-x: auto }` (swim.css) — that override is
     intentional and orthogonal to this base clip. */
  overflow-x: clip;
}

/* =============================================================================
   Motion + a11y baseline (LIN-785 §7 / §11)
   =============================================================================
   Reusable keyframes for status affordances S2 will attach to (a pulsing dot for
   "running", an indeterminate shimmer for "loading"), a focus-visible ring driven
   by the --focus token, and a prefers-reduced-motion baseline that neutralizes
   animation/transition/smooth-scroll for users who ask for less motion. */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* Indeterminate shimmer: a narrow bright band swept across an overflow-clipped
   track (theme §7 — "38% band sweeping a track, 1.4s"). Consumers clip a ~38%
   `:after` band and translate it with this keyframe (see `.livebar`, LIN-933);
   the translateX form (not background-position) is what yields the spec band. */
@keyframes shimmer {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(360%); }
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =============================================================================
   Header Layout
   ============================================================================= */
header {
  text-align: center;
  margin-bottom: var(--space-5);
}

h1 {
  color: var(--fg);
  font-weight: normal;
  font-family: var(--font-content);
}

.header-link {
  color: inherit;
  text-decoration: none;
}

.header-link:hover {
  color: var(--blue);
}

/* =============================================================================
   Navigation Bar
   =============================================================================
   Text-based navigation with label: value pattern.
   Filters (workspace, team) on left, actions (reset, logout) on right.
*/
.nav-bar {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
  gap: var(--space-2) 2ch;
  /* Header nav carrying the view switcher (LIN-978). It leads every workspace
     page, so cross-view navigation is reachable from the top without scrolling
     to the footer (the audit's Finding 1). Pinned/translucent while scrolling
     (LIN-984 — the deferred aesthetic follow-up that restores the retired
     obs-appbar treatment onto the shared header): sticky at the top, a
     translucent page-bg wash with a backdrop blur, and a hairline underline.
     The interception hazard that backed this out originally — a pinned header
     overlays content and swallows pointer events on controls that scroll up
     beneath it (it broke the ship-orientation mode toggle) — is solved with
     PER-INTERACTION `scroll-margin-top` on interactive controls (see the
     `--nav-sticky-h` rule below), NOT `scroll-padding-top` on the container.
     A sticky element still occupies normal flow, so the layout fallback (the
     header simply sitting at the top on pages that don't scroll) is intact.
     DEPENDENCY: this pins only because the root/`body` use `overflow-x: clip`
     (not `hidden`) — `hidden` would make an ancestor a scroll container and leave
     this header inert (LIN-1068). See the `html`/`body` resets above. */
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  padding: var(--space-2) 0;
  /* Solid --bg fallback first, then the translucent wash for browsers with
     color-mix support; the backdrop blur keeps scrolled content legible under
     the wash. */
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line-soft);
}

/* Two-row header scaffold (LIN-1058, structural beat). Row 1 groups
   brand/selectors/actions; the view tab strip (.nav-views, flex-basis:100%)
   wraps to row 2. Structural scaffolding ONLY — the tab-strip look, active-tab
   treatment (▸ marker + underline), panel cards, and the responsive `⋯ more`
   collapse land in the styling pass. Mirrors the prior `.nav-bar` row-1 flex so
   the intermediate renders as a coherent two-row header, not stacked. */
.nav-primary-row {
  flex: 1 1 100%;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2) 2ch;
  flex-wrap: wrap;
}

/* Per-interaction clearance for the pinned header (LIN-984). When a control is
   scrolled into view (a Playwright click's auto-scroll, an in-page anchor jump,
   a focus-driven scroll), the browser stops it flush at the viewport top —
   directly under the sticky `.nav-bar`, which then intercepts its pointer
   events. `scroll-margin-top` reserves the header's height per element so the
   scroll settles the control BELOW the header, keeping it clickable. This is
   deliberately on the elements (per-interaction) rather than `scroll-padding-top`
   on the scroll container, which the retired attempt proved does not fix it.
   `:where()` keeps specificity at 0 so any page-specific override still wins. */
:where(a, button, input, select, textarea, summary, label, [role="button"], [tabindex]) {
  scroll-margin-top: var(--nav-sticky-h);
}

/* Header-level view switcher (LIN-978 / LIN-1058 "Confident CLI tab strip").
   Row 2 of the two-row header: a full-width tab strip, separated from row 1 by a
   hairline so the nav reads as an intentional object. Laid out as a single
   non-wrapping row; horizontal-scroll is kept as a graceful FALLBACK (the
   primary+`⋯ more` overflow split is the real density solution — beat 4). */
.nav-views {
  flex: 0 0 100%;
  display: flex;
  align-items: baseline;
  gap: 2ch;
  flex-wrap: nowrap;
  overflow-x: auto;
  white-space: nowrap;
  margin-top: var(--space-1);
  padding-top: var(--space-1);
  border-top: 1px solid var(--line-soft);  /* zone separation from row 1 */
  /* Thin scrollbar, unobtrusive on desktop; momentum scroll on touch. */
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.nav-view {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.9em;
  font-family: var(--font-content);  /* human label — sans */
  flex: 0 0 auto;
  padding: var(--space-1) 0;
  border-bottom: 2px solid transparent;  /* reserve the tab-underline slot */
}

.nav-view:hover {
  color: var(--text);
  border-bottom-color: var(--line);
}

/* Active view — a confident tab, deliberately past bold-only: a brand underline
   (border-bottom:2px) PLUS a leading ▸ marker in --brand (redundant
   shape+color+glyph). The marker is a CSS ::before so the markup stays
   byte-identical to the LIN-978 `<strong.nav-view-current>` contract; the ▸ is
   the machine-structure accent, so it takes the mono structural face. */
.nav-view-current {
  color: var(--text);
  font-weight: 600;
  border-bottom-color: var(--brand);
}

.nav-view-current::before {
  content: '\25B8';  /* ▸ */
  color: var(--brand);
  margin-right: 0.5ch;
  font-family: var(--font-structural);
}

/* `⋯ more` overflow (LIN-1058). At desktop the flag-gated links flow inline on
   the strip (`display:contents`) and the toggle is hidden; the collapse to a
   `⋯ more` expander happens at narrow widths (beat 4). The expander, when open,
   is an ELEVATED, IN-FLOW card below the strip — it pushes content down, never
   floats over it, and has no backdrop (LIN-984 safe by construction). */
.nav-views-overflow {
  display: contents;
}

.nav-more-toggle {
  display: none;  /* revealed only when the strip collapses (beat 4) */
  flex: 0 0 auto;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: var(--space-1) 0;
  font-family: var(--font-structural);  /* ⋯ is structural character */
  font-size: 0.9em;
  color: var(--muted);
  cursor: pointer;
}

.nav-more-toggle:hover {
  color: var(--text);
}

.nav-views-overflow--open {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  flex-basis: 100%;
  margin-top: var(--space-1);
  padding: var(--space-2) var(--space-3);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

/* Desktop measured overflow (LIN-1286). The `⋯ more` collapse machinery now also
   serves desktop, but ONLY when the JS width-measuring routine (setupNavViewsOverflow
   in common.js) marks the strip `.nav-views--collapsed` because the row does not fit
   at this width. Absent JS — or when everything fits — the strip keeps the historical
   inline `display:contents` + horizontal-scroll fallback above, so the no-JS first
   paint is sane and a strip that fits never over-collapses. When collapsed, the group
   hides behind the revealed toggle exactly like mobile; JS has already inlined the
   items that fit, so only the genuine excess lives in the group. */
.nav-views--collapsed .nav-views-overflow {
  display: none;
}

.nav-views--collapsed .nav-more-toggle {
  display: inline-flex;
}

/* The opened card must win over the collapse-hide above (equal specificity, later
   source). Its card look (var(--card)/--line/--radius-md/--shadow-lg, column layout)
   comes from the base `.nav-views-overflow--open` rule. */
.nav-views--collapsed .nav-views-overflow--open {
  display: flex;
}

/* When the desktop card is open, let the strip wrap so the full-width card drops to
   its own row (like mobile) rather than fighting the nowrap strip. The collapsed
   primary row already fits at this width, so enabling wrap only moves the
   flex-basis:100% card below; `overflow-x:visible` drops the (now unneeded) scroll
   fallback so the open card is not clipped. */
.nav-views--collapsed.nav-views--open {
  flex-wrap: wrap;
  overflow-x: visible;
}

/* Neutralize a persisted open state above the mobile breakpoint (LIN-1058), EXCEPT
   when the desktop measuring routine is managing a genuine collapse (LIN-1286). The
   `⋯ more` toggle historically only existed at ≤640px, so a `--open` class set on
   mobile could survive a resize past 640px where the toggle was `display:none` and
   couldn't re-close it — the card would then override the desktop `display:contents`
   and render as a stuck-open panel. Forcing `display:contents` here re-inlines the
   views. Scoped to `:not(.nav-views--collapsed)` so it no longer fires when JS has
   deliberately collapsed the desktop overflow, where an open group IS a real card. */
@media (min-width: 641px) {
  .nav-views:not(.nav-views--collapsed) .nav-views-overflow--open {
    display: contents;
  }
}

.nav-filters {
  display: flex;
  align-items: baseline;
  gap: 2ch;
  flex-wrap: wrap;
}

.nav-item {
  display: flex;
  align-items: baseline;
  gap: 0.5ch;
}

.nav-label {
  color: var(--fg-dim);
  font-family: var(--font-content);
  font-size: 0.9em;
}

.nav-value {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-content);
  font-size: 0.9em;
  color: var(--fg);
  cursor: pointer;
}

.nav-value:hover {
  color: var(--blue);
}

.nav-value[aria-expanded="true"] {
  color: var(--blue);
}

.nav-actions {
  display: flex;
  align-items: baseline;
  gap: 1.5ch;
}

.nav-action {
  color: var(--fg-dim);
  text-decoration: none;
  font-size: 0.9em;
  font-family: var(--font-content);
}

.nav-action:hover {
  color: var(--fg);
}

/* Button variant of nav-action (e.g. the +local workspace form submit) */
button.nav-action {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

/* Harbour brand wordmark (LIN-725, brand S3).
   Shared by the nav bar and the footer via lib/components/wordmark.js. Stays on
   the CLI mono stack (--font-structural) — NOT a serif — with a trailing accent
   dot painted in the brand --teal token (LIN-724). */
.wordmark {
  font-family: var(--font-structural);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--fg);
  text-decoration: none;
}

.wordmark-accent {
  color: var(--teal);
}

a.wordmark:hover {
  color: var(--teal);
}

/* Nav bar: leads the filters; nudge it slightly larger than the label/value
   text and add breathing room before "workspace:". */
.wordmark-nav {
  font-size: 1em;
  margin-right: 1ch;
}

/* Footer: a quieter brand label sitting under the deploy line. */
.footer-brand {
  margin-top: var(--space-2);
}

.wordmark-footer {
  color: var(--fg-dim);
  font-weight: 600;
  font-size: 0.95em;
}

/* Queue badge in nav bar */
.queue-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.2rem var(--space-2);
  background: var(--blue);
  color: white;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 0.8em;
  font-family: var(--font-content);
  cursor: pointer;
}

.queue-badge:hover {
  background: color-mix(in srgb, var(--blue) 85%, black);
}

.queue-badge.hidden {
  display: none;
}

.queue-count {
  font-weight: 600;
}

/* Queue panel (modal-style overlay) */
.queue-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(400px, 90vw);
  max-height: 70vh;
  background: var(--bg);
  border: 1px solid var(--fg-vdim);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  z-index: var(--z-overlay);
  overflow: hidden;
}

.queue-panel.hidden {
  display: none;
}

.queue-panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem var(--space-3);
  background: var(--bg-muted);
  border-bottom: 1px solid var(--fg-vdim);
  font-family: var(--font-content);
  font-weight: 500;
}

.queue-panel-close {
  background: none;
  border: none;
  font-size: 1.2em;
  color: var(--fg-dim);
  cursor: pointer;
  padding: 0 var(--space-1);
}

.queue-panel-close:hover {
  color: var(--fg);
}

.queue-panel-items {
  max-height: calc(70vh - 170px);
  overflow-y: auto;
  padding: 0;
}

.queue-item {
  padding: 0.75rem var(--space-3);
  border-bottom: 1px solid var(--fg-vdim);
}

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

.queue-item-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-2);
}

.queue-item-title {
  font-family: var(--font-content);
  font-weight: 500;
  font-size: 0.9em;
  color: var(--fg);
  flex: 1;
}

.queue-item-remove {
  background: none;
  border: none;
  color: var(--red);
  cursor: pointer;
  font-size: 0.8em;
  padding: 0;
}

.queue-item-remove:hover {
  text-decoration: underline;
}

.queue-item-meta {
  font-size: 0.8em;
  color: var(--fg-dim);
  margin-top: var(--space-1);
}

/* Richer default queue-row content (LIN-1244). Global file → both surfaces:
   the nav-badge popover (app.js) and the /dispatch Queue list (dispatch.js). */
.queue-item-issue {
  color: var(--blue);
  text-decoration: none;
}

.queue-item-issue:hover {
  text-decoration: underline;
}

.queue-item-snippet {
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--fg-dim);
  margin-top: var(--space-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.queue-item-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-2);
}

.queue-item-chip {
  font-size: 0.7em;
}

.queue-item-flag {
  color: var(--brand);
  border-color: var(--brand);
}

.queue-panel-empty {
  padding: var(--space-5) var(--space-3);
  text-align: center;
  color: var(--fg-dim);
  font-family: var(--font-content);
}

.queue-panel-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0,0,0,0.3);
  z-index: 999;
}

.queue-panel-overlay.hidden {
  display: none;
}

/* =============================================================================
   Options Panel (appears below nav bar when selector is expanded)
   ============================================================================= */
/* Selector dropdown — an ELEVATED, CONTAINED card in NORMAL FLOW (LIN-1058).
   It opens as an in-flow block below the nav that pushes content down; it never
   floats over content and has no backdrop/click-catcher (LIN-984 safe). The S1
   theme-aware --shadow-lg gives it real elevation in both themes; --card/--line/
   --radius-md keep it on-token. Box-drawing rows stay inside the card. */
.nav-options-panel {
  padding: var(--space-2) var(--space-3);
  margin-left: 3ch;  /* Indent for visual containment */
  margin-bottom: var(--space-2);
  max-width: 32ch;  /* Keep compact on desktop */
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.nav-options-panel.hidden {
  display: none;
}

.nav-options-row {
  display: flex;
  align-items: baseline;
  gap: 0.5ch;
}

.option-prefix {
  color: var(--fg-vdim);
  white-space: pre;
  flex-shrink: 0;
}

.nav-option-form,
.nav-action-form {
  display: contents;
}

.nav-option {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-content);
  font-size: 0.9em;
  color: var(--fg);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: baseline;
  gap: 0.25ch;
}

.nav-option:hover {
  color: var(--blue);
}

.nav-option.selected {
  font-weight: 500;
}

.option-marker {
  color: var(--blue);
  font-size: var(--font-size-sm);
}

.option-marker-placeholder {
  display: inline-block;
  width: 1.1ch;  /* Same width as marker */
}

.nav-option-add,
.nav-option-add-local {
  color: var(--fg-dim);
}

.nav-option-danger {
  margin-left: auto;  /* Right-align remove button */
  color: var(--red);
}

.nav-option-danger:hover {
  color: var(--red-hover);
}

/* Mobile dropdown backdrop - hidden on desktop */
.nav-dropdown-overlay {
  display: none;
}

/* =============================================================================
   Search Panel
   =============================================================================
   Search input below the page title for filtering issues by keyword (LIN-1512).
   The `search` toggle that opens it stays in the nav bar's .nav-actions.
*/
/* Search panel — same elevated in-flow card treatment as the selector dropdown
   (LIN-1058): --card surface, --line border, --radius-md, --shadow-lg; stays in
   normal flow below the title, no floating overlay, no backdrop. */
.search-panel {
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
  max-width: 48ch;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.search-panel.hidden {
  display: none;
}

.search-input-row {
  display: flex;
  align-items: center;
  gap: 1ch;
}

.search-input {
  font-family: var(--font-structural);  /* the query is machine input — mono */
  font-size: 0.9em;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
  color: var(--fg);
  background: var(--bg);
  flex: 1;
  max-width: 40ch;
}

.search-input:focus {
  outline: 1px solid var(--blue);
  border-color: var(--blue);
}

.search-clear {
  font-family: var(--font-structural);
  font-size: 0.9em;
  color: var(--fg-dim);
  background: none;
  border: none;
  cursor: pointer;
}

.search-clear:hover {
  color: var(--fg);
}

.search-no-results {
  color: var(--fg-dim);
  font-size: 0.9em;
  padding: var(--space-2) 0;
}

.search-no-results.hidden {
  display: none;
}

/* =============================================================================
   "In Progress" Section
   =============================================================================
   Displays all in-progress issues across projects in a single collapsible list.
   Yellow accent color matches Linear's "started" state.
*/
.in-progress-section {
  margin-bottom: 2.5rem;
}

.in-progress-header {
  color: var(--yellow);
  font-weight: 600;
  cursor: pointer;
  padding: var(--space-2) 0;
  font-family: var(--font-content);
}

/* Project name badge shown after issue title in In Progress section */
.in-progress-project {
  color: var(--fg-dim);
  margin-left: 1ch;
  font-size: 0.9em;
  font-family: var(--font-content);
}

/* Activity time/kind shown in the Recent activity section */
.completed-time {
  color: var(--fg-dim);
  margin-left: 1ch;
  font-size: 0.9em;
  font-family: var(--font-content);
}

/* Per-task source flag, shown only when a workspace merges >1 provider (LIN-544) */
.source-badge {
  color: var(--fg-dim);
  margin-left: 1ch;
  padding: 0 0.5ch;
  font-size: 0.8em;
  font-family: var(--font-content);
  border: 1px solid var(--border);
  border-radius: 3px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* =============================================================================
   "Recent activity" Section
   =============================================================================
   Displays recent task activity (completed/created/edited) across projects in a
   single collapsible list. Green accent color matches Linear's "completed" state.
*/
.recent-activity-section {
  margin-bottom: 2.5rem;
}

.recent-activity-header {
  color: var(--green);
  font-weight: 600;
  cursor: pointer;
  padding: var(--space-2) 0;
  font-family: var(--font-content);
}

/* =============================================================================
   Project Sections
   =============================================================================
   Each project is a collapsible section with header, description, and issue tree.
*/
.project {
  margin-bottom: 2.5rem;
  overflow: hidden;
}

.project-header {
  color: var(--blue);
  font-weight: 600;
  cursor: pointer;
  padding: var(--space-2) 0;
  font-family: var(--font-content);
}

/* Periodicals synthetic group (LIN-341) — distinct purple accent so the
   maintenance group reads as visually separate from real Linear projects. */
.project[data-project-type="periodicals"] {
  border-left: 3px solid var(--purple);
  padding-left: 1ch;
}

.project[data-project-type="periodicals"] .project-header {
  color: var(--purple);
}

.project[data-project-type="periodicals"] .periodical-state {
  color: var(--purple);
}

.project[data-project-type="periodicals"] .periodical-mode {
  color: var(--fg-dim);
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
  margin-left: 0.75ch;
}

.project-description {
  color: var(--fg-dim);
  margin-left: 3ch;  /* Indent under project header */
  margin-bottom: var(--space-2);
  word-wrap: break-word;
  overflow-wrap: break-word;
  cursor: pointer;
  font-family: var(--font-content);
}

.project-description:hover {
  color: var(--fg);
}

.desc-toggle {
  background: none;
  border: none;
  color: var(--blue);
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  padding: 0;
}

.desc-toggle:hover {
  text-decoration: underline;
}

/* =============================================================================
   Issue Tree Lines
   =============================================================================
   Each issue is rendered as a "line" with:
   - state: Status indicator (✓ ◐ ○)
   - title: Issue title (clickable to expand details)
   - toggle: Collapse/expand arrow for parent issues

   Tree structure is rendered with CSS pseudo-elements (::before/::after).
*/
.line {
  display: flex;
  align-items: baseline;
  min-height: 1.6em;
  padding: 4px 0;
}

/* Parent items (with children) get bolder weight for visual hierarchy */
.line.has-children .title {
  font-weight: 500;
}

/* Status indicators: ✓ (done), ◐ (in-progress), ○ (todo), ◌ (backlog) */
.state {
  flex-shrink: 0;
  margin-right: 0.5ch;
  font-size: 0.9em;
  opacity: 0.85;
}
.state.done { color: var(--green); }
.state.in-progress { color: var(--yellow); }
.state.todo { color: var(--fg-dim); }
/* Idle/backlog rides the semantic slate idle token (LIN-850), not a magic
   opacity. --slate-dim is the AA-safe slate text companion. */
.state.backlog { color: var(--slate-dim); }

/* Issue title - fills remaining space, wraps on overflow */
.title {
  flex: 1;
  min-width: 0;  /* Allow flex item to shrink below content size */
  word-wrap: break-word;
  overflow-wrap: break-word;
  font-family: var(--font-content);
}
.title.done { color: var(--fg-dim); }

/* Collapse/expand toggle arrow (▼/▶) */
.toggle {
  color: var(--fg);
  cursor: pointer;
  margin-left: 1ch;
  flex-shrink: 0;
  padding: var(--space-1) var(--space-2);
  margin-right: -0.5rem;
  font-size: var(--font-size-sm);
}

.toggle:hover {
  color: var(--blue);
}

.detail-link {
  color: var(--fg-dim);
  text-decoration: none;
  font-size: 0.8em;
  font-family: var(--font-content);
  display: inline-block;
  margin-top: 0.3rem;
}

.detail-link:hover {
  color: var(--blue);
}

/* =============================================================================
   Detail Section Toggles (Details ▶ / Prompts ▶)
   =============================================================================
   Styled like .detail-link but with cursor pointer and toggle behavior.
   Content sections start collapsed (hidden) by default.
*/
.detail-toggle {
  color: var(--fg-dim);
  cursor: pointer;
  font-size: 0.8em;
  font-family: var(--font-content);
  padding: var(--space-1) 0;
  display: block;
}

.detail-toggle:hover {
  color: var(--blue);
}

/* LIN-158: Nested toggle (e.g., Comments inside Details) */
.detail-toggle.nested-toggle {
  margin-top: var(--space-2);
  padding-left: var(--space-3);
  /* LIN-456: --border was undefined here (no fallback), so this resolved to the
     initial border color (currentColor). Locked to currentColor to preserve that
     exact rendering now that --border is defined. (Latent: likely intended #ddd —
     a deliberate change belongs to a later visual phase, not this no-op pass.) */
  border-left: 1px solid currentColor;
}

/* Content wrapper for toggleable sections */
.detail-content {
  padding-left: 1.5ch;
  margin-bottom: var(--space-1);
}

/* LIN-158: Nested content (comments inside details) inherits border styling */
.detail-content .detail-content {
  margin-left: var(--space-3);
  /* LIN-456: see .nested-toggle above — preserve currentColor rendering. */
  border-left: 1px solid currentColor;
}

/* Prompt buttons row within Prompts section */
.detail-prompts {
  color: var(--fg-dim);
  font-size: 0.9em;
  font-family: var(--font-content);
}

/* Mobile: larger tap targets for toggles */
@media (max-width: 640px) {
  .detail-toggle {
    padding: var(--space-2) 0;
  }
}

.project-meta {
  color: var(--fg-dim);
  margin-left: 3ch;
  margin-bottom: 0.75rem;
  font-family: var(--font-content);
}

/* "Show N completed" toggle link */
.completed-toggle {
  color: var(--fg-dim);
  cursor: pointer;
  margin-left: 4ch;
  padding: var(--space-2) 0;
  font-family: var(--font-content);
}

/* "+ Add task" link at end of task list */
.add-task-link {
  color: var(--fg-dim);
  padding: var(--space-1) 0;
  padding-left: 1.5em;
  font-family: var(--font-content);
}

.add-task-link a {
  color: var(--fg-dim);
  text-decoration: none;
}

.add-task-link a:hover {
  color: var(--blue);
}

/* Utility class for hiding elements (used by JS for expand/collapse) */
.hidden { display: none !important; }

/* =============================================================================
   Node Container Structure & CSS Tree Lines
   =============================================================================
   Each issue is wrapped in a .node container that holds:
   - .line (the clickable row)
   - .details (expandable details panel)
   - .children (nested child nodes)

   Tree lines are drawn with CSS pseudo-elements instead of box-drawing chars.
   This provides cleaner HTML, easier theming, and better accessibility.
*/

/* Tree containers provide the context for tree lines */
.tree,
.children {
  position: relative;
}

/* All nodes inside tree containers get indentation and tree lines */
:is(.tree, .children) > .node {
  position: relative;
  padding-left: 1.5em;
}

/* Branch connector: draws the ├── or └── shape */
:is(.tree, .children) > .node::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  height: 0.9em;  /* Align with middle of first line */
  width: 0.8em;
  border-left: 1px solid var(--fg-vdim);
  border-bottom: 1px solid var(--fg-vdim);
}

/* Vertical line continuation for non-last nodes */
:is(.tree, .children) > .node:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-left: 1px solid var(--fg-vdim);
}

/* Hover highlighting for issue groups */
.node:hover > .line {
  background-color: rgba(0, 0, 0, 0.02);
}

.child-count {
  color: var(--fg-dim);
  font-size: 12px;
  font-family: var(--font-content);
}

/* =============================================================================
   Expandable Items & Details Panel
   =============================================================================
   Clicking an issue line expands a details panel showing description,
   assignee, dates, labels, and Linear link.
*/
.line.expandable {
  cursor: pointer;
}

/* LIN-566: visible keyboard focus for expandable rows (WCAG 2.4.7). These rows
   are role=button + tabindex=0 controls, so keyboard users need a focus cue. */
.line.expandable:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
}

.line.expandable:hover .title {
  text-decoration: underline;
  text-decoration-color: var(--fg-dim);
  text-underline-offset: 2px;
}

/* Details panel shown when issue is expanded */
.details {
  line-height: 1.4;
  padding: 0.2rem 0;
  padding-left: 1.5ch;
  overflow: hidden;
}

.detail-line {
  display: flex;
  align-items: baseline;
  margin-bottom: 0.2rem;
}

.detail-line:last-of-type {
  margin-bottom: 0;
}

.detail-text {
  color: var(--fg-dim);
  flex: 1;
  min-width: 0;
  word-wrap: break-word;
  overflow-wrap: break-word;
  font-family: var(--font-content);
}

.detail-meta {
  color: var(--fg-dim);
  font-size: 0.9em;
  flex: 1;
  min-width: 0;
  word-wrap: break-word;
  overflow-wrap: break-word;
  font-family: var(--font-content);
}

/* =============================================================================
   Issue Description Expansion (LIN-156)
   =============================================================================
   Expandable descriptions with markdown rendering and image support.
*/
.issue-description {
  color: var(--fg-dim);
  font-family: var(--font-content);
  font-size: 0.9em;
  margin-bottom: var(--space-2);
}

.issue-description .desc-truncated {
  display: block;
}

.issue-description .desc-full {
  display: block;
  max-height: 60vh;
  overflow-y: auto;
  padding-right: var(--space-2);
}

.issue-description .desc-full-content {
  display: block;
}

/* Markdown content styling within expanded descriptions */
.issue-description .desc-full-content p {
  margin: 0.5em 0;
}

.issue-description .desc-full-content p:first-child {
  margin-top: 0;
}

.issue-description .desc-full-content ul,
.issue-description .desc-full-content ol {
  margin: 0.5em 0;
  padding-left: 1.5em;
}

.issue-description .desc-full-content li {
  margin: 0.25em 0;
}

.issue-description .desc-full-content code {
  background: var(--bg-muted);
  padding: 0.1em 0.3em;
  border-radius: var(--radius-sm);
  font-family: var(--font-structural);
  font-size: 0.9em;
}

.issue-description .desc-full-content pre {
  background: var(--bg-muted);
  padding: 0.75em;
  border-radius: 4px;
  overflow-x: auto;
  margin: 0.5em 0;
}

.issue-description .desc-full-content pre code {
  background: none;
  padding: 0;
}

.issue-description .desc-full-content blockquote {
  border-left: 3px solid var(--fg-vdim);
  margin: 0.5em 0;
  padding-left: 1em;
  color: var(--fg-dim);
}

.issue-description .desc-full-content a {
  color: var(--blue);
}

/* Images in expanded descriptions */
.issue-description img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  margin: var(--space-2) 0;
  display: block;
}

.issue-description .img-error {
  color: var(--fg-dim);
  font-style: italic;
  font-size: var(--font-size-sm);
}

/* Toggle button for description expand/collapse */
.issue-desc-toggle {
  color: var(--blue);
  background: none;
  border: none;
  cursor: pointer;
  font-size: inherit;
  font-family: inherit;
  padding: 0;
  text-decoration: none;
}

.issue-desc-toggle:hover {
  text-decoration: underline;
}

/* =============================================================================
   Attachments Section (LIN-652)
   =============================================================================
   Hidden-by-default gallery of an issue's images (issue + comment), each fetched
   through the /api/image relay. Thumbnails wrap responsively; click opens the
   relayed image in a new tab.
*/
.attachments-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

.attachment-item {
  display: inline-block;
  line-height: 0;
}

.attachment-image {
  max-width: 160px;
  max-height: 160px;
  width: auto;
  height: auto;
  border: 1px solid var(--fg-vdim);
  border-radius: 4px;
  object-fit: cover;
}

.attachment-item .img-error {
  color: var(--fg-dim);
  font-style: italic;
  font-size: var(--font-size-sm);
  line-height: 1.4;
}

/* =============================================================================
   Comments Section (LIN-156)
   =============================================================================
   Collapsible comments list fetched on-demand.
*/
.comments-loading,
.comments-error,
.comments-empty {
  color: var(--fg-dim);
  font-style: italic;
  font-size: 0.9em;
  padding: var(--space-1) 0;
}

.comments-error {
  color: var(--red);
}

.comments-list {
  margin-top: var(--space-1);
}

.comment {
  margin: var(--space-2) 0;
  padding: var(--space-2) 0 var(--space-2) 0.75rem;
  border-left: 2px solid var(--fg-vdim);
}

.comment:first-child {
  margin-top: 0;
}

.comment-meta {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
  margin-bottom: var(--space-1);
  font-family: var(--font-content);
}

.comment-body {
  font-family: var(--font-content);
  font-size: 0.9em;
  color: var(--fg);
}

.comment-body p {
  margin: 0.25em 0;
}

.comment-body p:first-child {
  margin-top: 0;
}

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

.comment-body code {
  background: var(--bg-muted);
  padding: 0.1em 0.3em;
  border-radius: var(--radius-sm);
  font-family: var(--font-structural);
  font-size: 0.9em;
}

.comment-body a {
  color: var(--blue);
}

/* =============================================================================
   Responsive Breakpoints
   =============================================================================
   640px: Tablets and large phones - reduce padding, increase tap targets
   400px: Small phones - further reduce padding and font sizes
*/

/* Tablets and large phones */
@media (max-width: 640px) {
  /* Mobile sticky-header clearance (LIN-1058). The two-row header is taller at
     ≤640px — this PR's 44px tap targets grow the view-strip row, so the pinned
     header measures ~122px vs the desktop ~93px. The base `--nav-sticky-h: 6rem`
     (96px) under-shoots, leaving auto-scrolled/anchored/focused controls ~26px
     tucked under the header (a partial mobile regression of the LIN-984 clearance
     that `scroll-margin-top: var(--nav-sticky-h)` provides). Bump the token here
     so mobile clearance ≥ the real 122px header; the desktop value is untouched. */
  :root {
    --nav-sticky-h: 7.75rem;  /* 124px ≥ measured 122px mobile header */
  }

  body {
    padding: var(--space-3);
    font-size: 14px;
  }

  /* Mobile nav: compact horizontal layout */
  .nav-bar {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-2) 1ch;
  }

  .nav-filters {
    flex-direction: row;
    gap: 1.5ch;
  }

  .nav-actions {
    gap: 1ch;
  }

  .nav-item {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  .nav-value {
    max-width: 12ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Search input: full width on mobile */
  .search-input {
    max-width: none;
  }

  /* Mobile dropdown panels: inline dropdown below nav item */
  .nav-options-panel {
    max-width: none;
    margin-left: 1ch;
  }

  /* Hide backdrop overlay on mobile - not needed for inline dropdown */
  .nav-dropdown-overlay {
    display: none;
  }

  /* Mobile-390 view tab strip (LIN-1058). Density fix: keep the first-class four
     (+ any hoisted active view) inline; collapse the flag-gated/power-user views
     behind the in-flow `⋯ more` expander. Comfortable 44px tap targets on every
     link and the toggle. The expander opens as an IN-FLOW card BELOW the strip
     (flex-wrap lets it drop to its own row) — it displaces content down, never
     floats over it, and there is no backdrop (LIN-984 safe by construction). */
  .nav-views {
    flex-wrap: wrap;      /* let the opened `⋯ more` card drop to its own row */
    overflow-x: visible;  /* wrap replaces the desktop horizontal-scroll fallback */
    gap: 0.25ch 1.5ch;
    align-items: center;
  }

  /* 44px comfortable tap targets; revealing the toggle (overrides its desktop
     display:none) and collapsing the overflow group (overrides display:contents). */
  .nav-view,
  .nav-more-toggle {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  .nav-views-overflow {
    display: none;
  }

  /* Re-assert the opened expander AFTER the collapse rule above (equal
     specificity, later source) so `⋯ more` reveals the in-flow card; the card
     look (var(--card)/--line/--radius-md/--shadow-lg) comes from the base
     `.nav-views-overflow--open` rule. */
  .nav-views-overflow--open {
    display: flex;
  }

  .nav-views-overflow--open .nav-view {
    min-height: 44px;
    width: 100%;
  }

  header {
    margin-bottom: var(--space-4);
  }

  h1 {
    font-size: 1.3rem;
  }

  /* Reduce vertical spacing on issue lines */
  .line {
    padding: 2px 0;
    /* Allow trailing badges to wrap to their own line instead of squeezing
       the flex:1 title into a narrow column (LIN-423). */
    flex-wrap: wrap;
  }

  /* On narrow screens the project name badge drops to its own line beneath the
     title rather than competing for horizontal space (LIN-423). flex-basis:100%
     forces the wrap; order:1 moves it past the toggle so the toggle stays on the
     title's row. The left indent aligns it under the title text (past the state
     icon). Applies to both In Progress and Recent activity rows. */
  .in-progress-project {
    flex-basis: 100%;
    order: 1;
    margin-left: 2.4ch;
  }

  .project {
    margin-bottom: var(--space-5);
  }

  .in-progress-section {
    margin-bottom: var(--space-5);
  }

  .recent-activity-section {
    margin-bottom: var(--space-5);
  }

  .project-header {
    padding: 0.4rem 0;
  }

  .project-description {
    margin-bottom: var(--space-2);
  }

  /* Larger tap targets for touch devices (44px minimum recommended) */
  .toggle {
    padding: var(--space-2) 0.75rem;
    margin: -0.5rem -0.75rem -0.5rem var(--space-2);
    font-size: 1rem;
  }

  .completed-toggle {
    margin-left: 3ch;
    padding: var(--space-2) 0;
    display: block;
  }

  .details {
    padding-left: 0.75rem;
  }

  /* Disable hover underline on touch devices (no hover state) */
  .line.expandable:hover .title {
    text-decoration: none;
  }
}

/* Small phones */
@media (max-width: 400px) {
  body {
    padding: 0.75rem;
    font-size: 13px;
  }

  header {
    margin-bottom: var(--space-3);
  }

  h1 {
    font-size: 1.2rem;
  }

  .project {
    margin-bottom: var(--space-4);
  }

  .in-progress-section {
    margin-bottom: var(--space-4);
  }

  .recent-activity-section {
    margin-bottom: var(--space-4);
  }

  /* Tighter nav spacing on very small phones */
  .nav-filters {
    gap: 1ch;
  }

  .nav-value {
    max-width: 10ch;
  }

  /* View switcher stays a single scrolling row at 390px (LIN-978) — tighter
     gaps, never a multi-line wrap. */
  .nav-views {
    gap: 1.25ch;
  }
}

/* =============================================================================
   Setup Notice (Local Development)
   =============================================================================
   Shown on landing page when running locally with no auth configured.
   Uses box-drawing characters to match CLI aesthetic.
*/
.setup-notice {
  margin: var(--space-4) auto;
  max-width: 60ch;
  padding: 0.75rem var(--space-3);
  background: var(--bg-muted);
  border-radius: 4px;
  font-size: 0.85rem;
  line-height: 1.6;
}

.setup-notice p {
  margin: 0;
  color: var(--fg-dim);
}

.setup-notice code {
  background: var(--bg);
  padding: 0.1rem 0.3rem;
  border-radius: 2px;
  font-size: 0.8rem;
}

.setup-notice a {
  color: var(--blue);
}

/* =============================================================================
   Login Page (Landing Page CTA)
   =============================================================================
   Shown at the bottom of the landing page for unauthenticated users.
   Primary CTA is a filled brand action (mirrors the primary button idiom); the
   GitHub CTA is a differentiated outline variant so the two never read alike.
*/
.login-container {
  text-align: center;
  margin-top: 4rem;
}

.login-container p {
  color: var(--fg-dim);
  margin-bottom: var(--space-4);
  font-family: var(--font-content);
}

.login-button {
  display: inline-block;
  padding: 0.75rem var(--space-4);
  background: var(--brand);  /* product accent (teal, LIN-860) — no more hard-coded purple */
  color: var(--bg);
  text-decoration: none;
  border: 1px solid var(--brand);
  border-radius: 4px;
  font-family: var(--font-content);
}

.login-button:hover {
  background: color-mix(in srgb, var(--brand) 85%, black);
  border-color: color-mix(in srgb, var(--brand) 85%, black);
}

/* GitHub CTA — differentiated from the primary brand action: a chromeless
   outline that sits below the filled Linear CTA so the two never look alike. */
.login-button-github {
  background: transparent;
  color: var(--fg);
  border-color: var(--border);
}

.login-button-github:hover {
  background: var(--card-h);
  border-color: var(--border-strong);
}

/* Local-workspace CTA — secondary action below the Linear login button.
   No Linear account required; backed by the local provider (LIN-377). */
.local-workspace-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75ch;
  margin-top: var(--space-4);
  font-family: var(--font-content);
}

.local-workspace-cta-sep {
  color: var(--fg-vdim);
  font-size: 0.9em;
}

.local-workspace-name {
  padding: 0.4rem 0.6rem;
  font-family: var(--font-structural);
  font-size: 0.9em;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
}

.local-workspace-button {
  padding: 0.4rem 0.9rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-family: var(--font-content);
  font-size: 0.9em;
  color: var(--fg-dim);
  cursor: pointer;
}

.local-workspace-button:hover {
  color: var(--fg);
  border-color: var(--fg-dim);
}

/* =============================================================================
   Landing (LIN-726, Harbour brand S4)
   =============================================================================
   The unauthenticated landing adopts the Harbour visual system: the anchor mark
   + lowercase wordmark in teal, a faint teal grid background, and a genuine
   light/dark response. Everything is brand-tokened (--teal / --teal-soft from
   S2), and typography stays the product's mono (--font-structural) + sans
   (--font-content) stacks — no serif (DM Serif is explicitly rejected).

   Scope is `body.is-landing` only, so the authenticated app's default-light
   chrome is untouched. */

body.is-landing {
  /* Faint teal grid. Two layered gradients draw a 1px line on a 32px cell;
     --teal-soft keeps them barely-there so the CLI tree stays legible on top.
     `fixed` so the grid sits still while the page scrolls. */
  background-image:
    linear-gradient(to right, var(--teal-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--teal-soft) 1px, transparent 1px);
  background-size: 32px 32px;
  background-position: -1px -1px;
  background-attachment: fixed;
}

/* Genuine light/dark for the landing only. Remaps the same token values the
   .theme-dark sample uses (style.css top), so the grid, hero, CTAs, and the
   showcase (LIN-980) all flip together — no switcher, and nothing outside the
   landing is affected. Mirrors the FULL `.theme-dark` vocabulary — raw tokens,
   the LIN-785 semantic layer (--text/--muted/--card/--line …), and the status
   palette (--amber/--slate/-dim companions) — so token-driven landing CSS is
   dark-safe here exactly as it is under the global `.theme-dark` toggle. The
   semantic aliases MUST be re-declared at this scope: they are defined on :root
   as `var(--fg)` etc. and inherit their light computed value otherwise (the raw
   `--fg` override alone does not reach them). */
@media (prefers-color-scheme: dark) {
  body.is-landing {
    --bg: #16181d;
    --bg-muted: #1f222a;
    --bg-alt: rgba(255, 255, 255, 0.05);
    --fg: #e6e6e6;
    --fg-dim: #9aa0a6;
    --fg-vdim: #2c313a;
    --fg-mid: #5c6370;
    --green: #2ee65f;
    --yellow: #e6c14a;
    --blue: #6ea8fe;
    --purple: #b794f6;
    --red: #ff6b6b;
    --teal: #2dd4bf;
    --teal-soft: #2dd4bf20;
    --border: #2c313a;
    --border-strong: #3d4450;
    --border-subtle: rgba(255, 255, 255, 0.12);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.6);

    /* Semantic layer (LIN-785) re-bound for the landing's dark path. */
    --text: var(--fg);
    --muted: var(--fg-dim);
    --faint: var(--fg-vdim);
    --line: var(--border);
    --line-soft: var(--border-subtle);
    --card: var(--bg-muted);
    --card-h: var(--bg-alt);
    --raised: var(--bg-muted);
    --inset: var(--bg-alt);
    --brand: var(--teal);
    --brand-dim: #2dd4bf;
    --shadow: var(--shadow-md);

    /* Status palette on dark — raw hues for fills/dots, `-dim` for text. */
    --green-dim: #4ade80;
    --amber: #ffc04d;
    --amber-dim: #ffd27a;
    --red-dim: #ff9b9b;
    --slate: #94a3b8;
    --slate-dim: #cbd5e1;
    --focus: #6ea8fe;

    background-color: var(--bg);
    color: var(--fg);
  }
}

.landing-hero {
  text-align: center;
  margin: 3rem 0 4rem;
}

/* Anchor mark (S1) painted in the brand teal token; sizing the inline SVG. */
.landing-mark {
  color: var(--teal);
  line-height: 0;
  margin-bottom: var(--space-3);
}

.landing-mark svg {
  width: 56px;
  height: 56px;
}

/* Lowercase wordmark on the CLI mono stack — the page's single <h1>. */
.landing-wordmark {
  font-family: var(--font-structural);
  font-size: 2.75rem;
  font-weight: normal;
  letter-spacing: -0.02em;
  color: var(--fg);
  margin: 0;
}

.landing-wordmark-accent {
  color: var(--teal);
}

.landing-tagline {
  font-family: var(--font-content);
  color: var(--fg-dim);
  margin: var(--space-2) 0 var(--space-4);
}

.landing-ctas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}

.landing-cta {
  display: inline-block;
  padding: 0.75rem 1.5rem;
  border-radius: 4px;
  text-decoration: none;
  font-family: var(--font-content);
  font-size: 1.05rem;
  border: 1px solid transparent;
}

/* Linear: the filled, primary action. Dark teal-tinted text reads on the teal
   fill in every theme (light/dark/amber all keep --teal saturated). */
.landing-cta-linear {
  background: var(--teal);
  color: #04231f;
  font-weight: 500;
}

.landing-cta-linear:hover {
  background: color-mix(in srgb, var(--teal) 85%, black);
}

/* GitHub: a co-equal CTA, visually quieter as a teal outline. */
.landing-cta-github {
  background: transparent;
  color: var(--teal);
  border-color: var(--teal);
}

.landing-cta-github:hover {
  background: var(--teal-soft);
}

@media (max-width: 480px) {
  .landing-ctas {
    flex-direction: column;
  }
  .landing-cta {
    width: 100%;
    max-width: 18rem;
  }
  .landing-wordmark {
    font-size: 2.25rem;
  }
}

/* =============================================================================
   Error Page
   =============================================================================
   Friendly error display for OAuth and other errors.
*/
.error-container {
  text-align: center;
  margin-top: 4rem;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

.error-title {
  font-family: var(--font-content);
  font-size: 1.25rem;
  font-weight: 500;
  color: var(--fg);
  margin-bottom: var(--space-3);
}

.error-message {
  color: var(--fg-dim);
  margin-bottom: var(--space-5);
  font-family: var(--font-content);
  line-height: 1.5;
}

/* Self-diagnosing detail block (reason/type/code/time) on error pages. Subdued,
   monospace, terminal-aesthetic — safe to quote in a bug report. */
.error-details {
  text-align: left;
  display: inline-block;
  margin: 0 auto var(--space-5);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg-subtle, rgba(0, 0, 0, 0.02));
  font-family: var(--font-structural);
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--fg-dim);
}

.error-detail-key {
  display: inline-block;
  min-width: 4.5rem;
  color: var(--fg-muted, var(--fg-dim));
  opacity: 0.8;
}

.error-detail-value {
  color: var(--fg);
}

.error-home-link {
  display: block;
  margin-top: var(--space-3);
  color: var(--fg-dim);
  font-family: var(--font-content);
  font-size: 0.9em;
}

.error-home-link:hover {
  color: var(--fg);
}

/* =============================================================================
   Promptable Labels & Prompt Container
   =============================================================================
   Labels with AI prompts are rendered as clickable links.
   Clicking opens a prompt container with copyable text.
*/

/* Promptable label buttons within metadata */
.label-prompt {
  display: inline-block;
  background: none;
  border: 1px solid var(--border);
  padding: 0.15rem 0.4rem;
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--fg-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  text-decoration: none;
  margin-right: 0.3rem;
  margin-bottom: 0.4rem;
  white-space: nowrap;
  transition: all 0.15s ease;
}

.label-prompt:hover {
  border-color: var(--fg-dim);
  color: var(--fg);
  background: rgba(0, 0, 0, 0.03);
}

/* "more" link keeps simple link styling */
.label-prompt.more-toggle {
  background: none;
  border: none;
  padding: 0;
  margin-right: 0;
  color: var(--fg-dim);
  font-size: inherit;
}

.label-prompt.more-toggle:hover {
  text-decoration: underline;
  border-color: transparent;
}

/* Custom prompt buttons - dashed border to distinguish from built-in */
.label-prompt.custom-prompt-btn {
  border-style: dashed;
}

/* Prompt container - appears below metadata when label is clicked */
.prompt-container {
  margin-top: 0.75rem;
  padding: 0.75rem;
  background: var(--bg-muted);
  border: 1px solid var(--fg-vdim);
  border-radius: 4px;
}

.prompt-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-2);
}

.prompt-name {
  font-family: var(--font-content);
  font-weight: 500;
  font-size: 0.9em;
  color: var(--fg);
}

.prompt-copy,
.prompt-download {
  background: none;
  border: 1px solid var(--fg-vdim);
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--fg-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.prompt-copy:hover,
.prompt-download:hover {
  border-color: var(--fg-dim);
  color: var(--fg);
}

/* Prompt action buttons container */
.prompt-actions {
  display: flex;
  gap: 0.35rem;
  align-items: center;
}

/* Dispatch disclosure trigger — mirrors .prompt-dispatch (blue accent) so the
   single "Dispatch ▾" entry point reads as the primary dispatch affordance. */
.dispatch-disclosure {
  background: none;
  border: 1px solid var(--blue);
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--blue);
  cursor: pointer;
  border-radius: var(--radius-sm);
  user-select: none;
}

.dispatch-disclosure:hover {
  background: var(--blue);
  color: white;
}

/* Inline options panel revealed by the disclosure trigger. Show/hide is driven
   by the global .hidden class; when shown it lays its targets out in a row. */
.prompt-options {
  display: flex;
  gap: 0.35rem;
  align-items: center;
  flex-wrap: wrap;
}

/* Dispatch button - similar to copy but with accent color */
.prompt-dispatch {
  background: none;
  border: 1px solid var(--blue);
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--blue);
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.prompt-dispatch:hover {
  background: var(--blue);
  color: white;
}

.prompt-dispatch.dispatched {
  border-color: var(--green);
  color: var(--green);
}

/* Proxy toggle button - toggles proxy API instructions in prompts */
/* LIN-738: resting state uses --fg-dim (readable interactive control), not the
   structural box-drawing token --fg-vdim, which rendered the button effectively
   invisible at rest (1.16:1). Same token-misuse class LIN-570 fixed. Hover lifts
   to full --fg so the affordance is still distinguishable from rest. */
.prompt-proxy-toggle {
  background: none;
  border: 1px solid var(--fg-dim);
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--fg-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  margin-left: var(--space-1);
}

.prompt-proxy-toggle:hover {
  border-color: var(--fg);
  color: var(--fg);
}

/* LIN-525 #1: active look is driven by a single body attribute, not a per-button
   class — so +proxy buttons injected after page load (lazy issue-detail blocks,
   swipe re-renders) inherit the state automatically and can't render "off" while
   the toggle is functionally on. */
body[data-proxy-active="true"] .prompt-proxy-toggle {
  border-color: var(--green);
  color: var(--green);
}

/* LIN-191: Disabled state for prompt action buttons during loading/streaming */
.prompt-actions button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* =============================================================================
   Dispatch Execution Controls (model/harness) — LIN-1096

   Shared across every dispatch-time surface (Dispatch page, dashboard tree,
   prompt-compose section, Suggested-next-run) via window.renderDispatchExecControls
   (public/common.js) — this is the one stylesheet all four surfaces load.
   Mirrors the settings page's dispatch-defaults control shape (settings.css,
   LIN-1095) with its own class names, since settings.css isn't loaded here.
   ============================================================================= */
.dispatch-exec-controls {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-wrap: wrap;
}

.dispatch-exec-harness-select,
.dispatch-exec-model {
  font-family: var(--font-content);
  font-size: 0.8em;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--fg-vdim);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--fg);
}

.dispatch-exec-harness-select {
  min-width: 9ch;
}

.dispatch-exec-model {
  min-width: 12ch;
}

.dispatch-exec-harness-select:focus,
.dispatch-exec-model:focus {
  outline: none;
  border-color: var(--blue);
}

.dispatch-exec-model::placeholder {
  color: var(--fg-dim);
}

/* Preset picker (LIN-1391 S9) — same control shape as the exec controls above,
   rendered client-side into .dispatch-preset-picker-container. */
.dispatch-preset-select {
  font-family: var(--font-content);
  font-size: 0.8em;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--fg-vdim);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--fg);
  min-width: 12ch;
}

.dispatch-preset-select:focus {
  outline: none;
  border-color: var(--blue);
}

/* Prompt text - monospace, preserves formatting */
.prompt-text {
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
  line-height: 1.5;
  color: var(--fg);
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: break-word;
  margin: 0;
  max-height: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;
}

/* Mobile adjustments for prompt container */
@media (max-width: 640px) {
  .prompt-container {
    padding: var(--space-2);
  }

  .prompt-text {
    font-size: 0.8em;
    max-height: 300px;
  }
}

/* =============================================================================
   Markdown Rendering in Prompts and Reasoning
   =============================================================================
   Styles for rendered markdown content (via marked.js library).
   Applied to both .prompt-text and .recommend-reasoning containers.
*/

/* Shared markdown container styles */
.prompt-text,
.recommend-reasoning {
  font-family: var(--font-content);
  font-size: 0.9em;
  line-height: 1.5;
}

/* Headers - h1 rendered as h2 size, h2 as h3, etc. */
.prompt-text h1,
.prompt-text h2,
.recommend-reasoning h1,
.recommend-reasoning h2 {
  font-family: var(--font-content);
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--fg);
  margin: 1em 0 0.5em 0;
  padding-bottom: 0.25em;
  border-bottom: 1px solid var(--fg-vdim);
}

.prompt-text h1:first-child,
.prompt-text h2:first-child,
.recommend-reasoning h1:first-child,
.recommend-reasoning h2:first-child {
  margin-top: 0;
}

.prompt-text h3,
.prompt-text h4,
.recommend-reasoning h3,
.recommend-reasoning h4 {
  font-family: var(--font-content);
  font-size: 0.95em;
  font-weight: 600;
  color: var(--fg);
  margin: 0.75em 0 0.4em 0;
}

/* Paragraphs */
.prompt-text p,
.recommend-reasoning p {
  margin: 0.5em 0;
}

.prompt-text p:first-child,
.recommend-reasoning p:first-child {
  margin-top: 0;
}

.prompt-text p:last-child,
.recommend-reasoning p:last-child {
  margin-bottom: 0;
}

/* Bullet and numbered lists */
.prompt-text ul,
.prompt-text ol,
.recommend-reasoning ul,
.recommend-reasoning ol {
  margin: 0.5em 0;
  padding-left: 1.5em;
}

.prompt-text li,
.recommend-reasoning li {
  margin: 0.2em 0;
  line-height: 1.4;
}

/* Code blocks */
.prompt-text pre,
.recommend-reasoning pre {
  background: var(--bg);
  border: 1px solid var(--fg-vdim);
  border-radius: var(--radius-sm);
  padding: 0.75em;
  margin: 0.5em 0;
  overflow-x: auto;
}

.prompt-text pre code,
.recommend-reasoning pre code {
  font-family: var(--font-structural);
  font-size: 0.9em;
  color: var(--fg);
  background: none;
  border: none;
  padding: 0;
}

/* Inline code */
.prompt-text code,
.recommend-reasoning code {
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
  background: var(--bg);
  border: 1px solid var(--fg-vdim);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.3em;
}

/* Bold text */
.prompt-text strong,
.recommend-reasoning strong {
  font-weight: 600;
}

/* Links */
.prompt-text a,
.recommend-reasoning a {
  color: var(--blue);
  text-decoration: none;
}

.prompt-text a:hover,
.recommend-reasoning a:hover {
  text-decoration: underline;
}

/* =============================================================================
   More Prompts Inline Expansion
   ============================================================================= */

.more-prompts.hidden {
  display: none;
}

/* =============================================================================
   AI Recommendation Feature
   =============================================================================
   "AI suggest" button and recommendation container for AI-powered suggestions.
*/

/* Suggest button - inherits layout from .label-prompt, only overrides colors */
.label-prompt.suggest-btn {
  border-color: var(--blue);
  color: var(--blue);
}

.label-prompt.suggest-btn:hover {
  background: var(--blue);
  border-color: var(--blue);
  color: white;
}

.label-prompt.suggest-btn.loading {
  background: var(--blue);
  border-color: var(--blue);
  color: white;
  opacity: 0.7;
}

/* Recommendation container */
.recommend-container {
  margin-top: 0.75rem;
  padding: 0.75rem;
  background: linear-gradient(135deg, #f0f7ff 0%, var(--bg-muted) 100%);
  border: 1px solid var(--blue);
  border-left: 3px solid var(--blue);
  border-radius: 4px;
}

/* Streaming phase indicator (LIN-185) */
.streaming-phase {
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--blue);
  padding: var(--space-1) 0;
  margin-bottom: var(--space-1);
}

.streaming-phase::before {
  content: '● ';
  animation: phase-pulse 1.2s ease-in-out infinite;
}

@keyframes phase-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

.recommend-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-2);
}

.recommend-title {
  font-family: var(--font-content);
  font-weight: 500;
  font-size: 0.9em;
  color: var(--blue);
}

.recommend-close {
  background: none;
  border: none;
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--fg-dim);
  cursor: pointer;
}

.recommend-close:hover {
  color: var(--fg);
}

.recommend-header-actions {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.reasoning-toggle {
  background: none;
  border: none;
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-content);
  font-size: 0.8em;
  color: var(--blue);
  cursor: pointer;
}

.reasoning-toggle:hover {
  text-decoration: underline;
}

.recommend-reasoning {
  font-family: var(--font-content);
  font-size: 0.9em;
  line-height: 1.5;
  color: var(--fg);
  margin: 0 0 0.75rem 0;
}

.recommend-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Generated prompt display within recommendation */
.recommend-prompt {
  margin-top: var(--space-2);
}

.recommend-prompt .prompt-header {
  margin-bottom: var(--space-1);
}

.recommend-prompt .prompt-text {
  background: white;
  padding: 0.75rem;
  border-radius: var(--radius-sm);
  max-height: 300px;
  overflow-y: auto;
  white-space: pre-wrap;
  font-size: var(--font-size-sm);
  line-height: 1.5;
}

.free-tier-info {
  margin-top: var(--space-2);
  padding: var(--space-1) 0;
  font-size: 0.8em;
  color: var(--fg-dim);
}

.free-tier-info .free-tier-limit-reached {
  color: var(--red);
}

.free-tier-info a {
  color: var(--blue);
}

/* Mobile adjustments */
@media (max-width: 640px) {
  .recommend-container {
    padding: var(--space-2);
  }

  .recommend-reasoning {
    font-size: var(--font-size-sm);
  }

  .recommend-prompt .prompt-text {
    max-height: 200px;
  }
}

/* =============================================================================
   Page Footer
   =============================================================================
   Displays reset/audit actions and deploy information.
   CLI aesthetic: monospace for technical info, dim colors.
*/
.page-footer {
  margin-top: 3rem;
  padding-top: var(--space-3);
  border-top: 1px solid var(--fg-vdim);
  text-align: center;
  font-family: var(--font-content);
  font-size: var(--font-size-sm);
}

.footer-actions {
  margin-bottom: var(--space-2);
}

.footer-action {
  color: var(--fg-dim);
  text-decoration: none;
}

.footer-action:hover {
  color: var(--fg);
}

.footer-current {
  color: var(--fg-dim);
  font-weight: bold;
}

.footer-deploy {
  color: var(--fg-dim);
  font-family: var(--font-structural);
  font-size: 0.9em;
}

.footer-link {
  color: var(--fg-dim);
  text-decoration: none;
}

.footer-link:hover {
  color: var(--blue);
  text-decoration: underline;
}

.footer-legal {
  color: var(--fg-dim);
  text-decoration: none;
}

.footer-legal:hover {
  color: var(--fg);
}

.footer-ai-status {
  text-decoration: none;
}

.footer-ai-status.connected {
  color: var(--green);
}

.footer-ai-status.env {
  color: var(--fg-dim);
}

.footer-ai-status.free {
  color: var(--yellow);
}

.footer-ai-status.disconnected {
  color: var(--fg-dim);
}

.footer-ai-status:hover {
  color: var(--blue);
}

/* Workspace model indicator, populated client-side next to the AI status. */
.footer-ai-model {
  color: var(--fg-dim);
}

.footer-ai-model:not(:empty)::before {
  content: " · ";
  color: var(--fg-dim);
}

/* Mobile adjustments for footer */
@media (max-width: 640px) {
  .page-footer {
    margin-top: var(--space-5);
    padding-top: 0.75rem;
  }
}

/* =========================================================================
   Section component (LIN-461, Phase A.1)

   Canonical in-card section wrapper + heading, built on Phase-0 tokens. This
   is the single owner of section styling: the per-page variant rules
   (.dispatch-section / .proxy-section / .settings-section / .prompts-section /
   .custom-prompts-section / .legal-section box+margin) were retired into it.
   A few of those class NAMES survive on the markup as pure behavioural/test
   hooks (dispatch.js `.closest()`, `.X-section .line` descendant scoping, and
   E2E selectors) — they carry no styling of their own.

   `--boxed` is the dispatch/proxy/settings light box; its move to --radius-sm
   (3px, was 4px) and --bg-alt (0.03, was 0.02) is the deliberate, reviewed
   token-convergence pixel shift on those three pages + /styleguide.
   ========================================================================= */
.section {
  margin-bottom: var(--space-5);
}

.section--boxed {
  padding: var(--space-3);
  background: var(--bg-alt);
  border-radius: var(--radius-sm);
}

/* Scoped under .section so the generic `.section-header` name can't collide
   with the unrelated `.section-header` on the audit dashboard (audit.js), which
   has its own baseline and never sits inside a `.section`. */
.section .section-header {
  font-family: var(--font-content);
  font-size: var(--font-size-base);
  font-weight: 500;
  color: var(--fg);
  margin: 0 0 0.75rem 0;
}

/* Underlined heading — prompts catalog. Additive over .section-header. */
.section .section-header--ruled {
  font-size: 1.1em;
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--fg-vdim);
}

/* =============================================================================
   Page Header (LIN-462, Phase A.2)
   Canonical home for the page-level <h1> + optional subtitle, rendered by
   renderPageHeader() (lib/components/page-header.js). The <header> layout
   (centering, margin) comes from the global `header` rule above; this block
   owns only the subtitle, absorbing the former per-page `*-subtitle` rules.
   Load-bearing test-hook classes (.dashboard-subtitle, .dispatch-subtitle,
   .prompts-subtitle) ride alongside `.page-header__subtitle` as no-style hooks
   (passed via subtitleClass). `0.9em` has no exact Phase-0 token, so it stays a
   literal — preserving the prior look byte-for-byte (LIN-461 convention).
   ============================================================================= */
.page-header__subtitle {
  color: var(--fg-dim);
  font-family: var(--font-content);
  font-size: 0.9em;
  margin-top: var(--space-2);
}

/* =============================================================================
   Field (LIN-463, Phase A.3)
   Canonical horizontal dim-label + value row, rendered by renderField()
   (lib/components/field.js). Absorbs the byte-identical per-page label/value
   rules — .dispatch-label/.dispatch-value, .proxy-label, and (Session 2) the
   settings .settings-label/.settings-value. The settings semantic value variants
   (.connected/.disconnected/… colours) survive as no-style hooks on
   `settings-value`, not as duplicated row CSS. The row (.field) carries the same
   flex/spacing/font as the shared `.X-section .line` rule it converges, so a
   migrated field row renders byte-for-byte; the `.line` NAME stays on the
   non-field rows in those sections.
   ============================================================================= */
.field {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4rem 0;
  font-family: var(--font-content);
  font-size: 0.9em;
}

.field-label {
  color: var(--fg-dim);
  min-width: 10ch;
  flex-shrink: 0;
}

.field-value {
  color: var(--fg);
}

/* =========================================================================
   Card (LIN-468, Phase A.4)
   Canonical slot-based content card, rendered by renderCard()
   (lib/components/card.js). Absorbs the near-duplicate per-page card chrome —
   the .prompt-card container + .prompt-header row and the .custom-prompt-card
   container + .custom-prompt-header row. Those variant class NAMES survive on
   the markup as no-style semantic/E2E hooks (referenced by E2E specs and
   client JS), riding alongside `.card`/`.card-header`; the styling lives here.
   Tokenised throughout: the move to --space-3 padding, --bg-alt fill,
   --border-subtle hairline and --radius-sm (3px) corners is the deliberate,
   reviewed pixel shift on /prompts, /prompts/custom and /styleguide. Tag/chip
   styling inside .card-labels is intentionally NOT here — LIN-465 owns it.
   ========================================================================= */
.card {
  margin-bottom: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-alt);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}

/* State accent — a thicker colored left border over the base hairline. */
.card-accent {
  border-left-width: 3px;
}

.card-accent--in-progress { border-left-color: var(--yellow); }
.card-accent--done { border-left-color: var(--green); }
.card-accent--todo { border-left-color: var(--fg-dim); }
.card-accent--backlog { border-left-color: var(--fg-vdim); }
.card-accent--failed { border-left-color: var(--red); }

.card-header {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.card-title {
  font-family: var(--font-content);
  font-weight: 500;
  color: var(--fg);
}

.card-meta {
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
  color: var(--fg-dim);
  margin-left: auto;
}

.card-labels {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
}

/*
   Status Pill (LIN-465, Phase A.5)
   The canonical state→glyph+label pill. Shares card-accent's state vocabulary
   and Phase-0 color tokens so a pill and a card accent compose on one interface.
   Coexists with the per-page pill CSS (.foreman-state, swipe .state.*, …) until
   Phase B points the client JS at this class.
*/
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.1rem 0.45rem;
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
  line-height: 1.4;
  color: var(--fg-dim);
  background: var(--bg-alt);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-full);
}

.status-pill__char {
  display: inline-block;
  min-width: 1em;
  text-align: center;
}

/* State color modifiers — same state→token mapping as .card-accent--*. */
.status-pill--in-progress { color: var(--yellow); }
.status-pill--done { color: var(--green); }
.status-pill--todo { color: var(--fg-dim); }
/* Idle/backlog on the semantic slate idle token (LIN-850) — the AA-safe slate
   text companion, replacing the too-faint --fg-vdim. */
.status-pill--backlog { color: var(--slate-dim); }
.status-pill--failed { color: var(--red); }

/* Neutral, stateless label/tag chip (no state color). */
.status-pill--tag { color: var(--fg-dim); }

/*
   Bare variant (LIN-850) — a box-less status pill that renders pixel-identically
   to the legacy `.state` inline glyph, so the project-tree rows (lib/render.js)
   route through the shared status primitive WITHOUT gaining the locked-forbidden
   pill/card chrome. It strips the chip (padding/bg/border/radius/gap) and
   reproduces `.state` exactly: 0.9em glyph, 0.5ch trailing gutter, 0.85 opacity.
   The color still comes from the `.status-pill--<state>` modifiers above, so the
   tree keeps its per-state vocabulary. Box-drawing scaffolding is separate DOM
   and stays byte-for-byte (LIN-782 lock).
*/
.status-pill--bare {
  padding: 0;
  gap: 0;
  background: none;
  border: none;
  border-radius: 0;
  font-size: 0.9em;
  line-height: inherit;
  margin-right: 0.5ch;
  opacity: 0.85;
  flex-shrink: 0;
}
.status-pill--bare .status-pill__char {
  min-width: 0;
  text-align: inherit;
}

/*
   Run-status extension (LIN-786, Theme S2)
   The autopilot run-status vocabulary on the SAME seam: a leading dot (the
   bright fill token) + an AA-safe `-dim` label colour. State is dot + label,
   never colour alone. `done` is shared with the issue-state vocabulary above
   and keeps its --green text; `running/error/queued` are additive.
*/
.status-pill__dot {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: currentColor;  /* overridden per-state below; --tag falls back to text colour */
  flex-shrink: 0;
}

.status-pill--running { color: var(--amber-dim); }
.status-pill--error   { color: var(--red-dim); }
.status-pill--queued  { color: var(--slate-dim); }

/* The dot uses the bright fill token (decorative, ≥3:1 non-text), while the
   label stays on the AA-safe `-dim` text colour above. */
.status-pill--running .status-pill__dot { background: var(--amber); }
.status-pill--done    .status-pill__dot { background: var(--green); }
.status-pill--error   .status-pill__dot { background: var(--red); }
.status-pill--queued  .status-pill__dot { background: var(--slate); }

/* The running dot pulses to read as "live"; it inherits the global
   prefers-reduced-motion neutralizer (no new animation machinery). */
.status-pill--running .status-pill__dot {
  animation: pulse var(--motion-pulse) ease-in-out infinite;
}

/* =============================================================================
   Theme S2 Primitives (LIN-786)
   =============================================================================
   Theme-owned, page-agnostic primitives composed by LIN-783 (Observation) and
   reusable anywhere. Every rule reads the semantic token layer ONLY (no raw hex,
   no page logic). Interactive members inherit the global S1 `:focus-visible`
   ring (none suppresses it); primary Button + IconButton meet the 40px
   touch-target floor. Server render helpers live in lib/components/*.js. */

/* --- Button (default / primary / ghost) ------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: 32px;
  padding: var(--space-1) var(--space-3);
  font-family: var(--font-content);
  font-size: var(--font-size-sm);
  line-height: 1.4;
  color: var(--text);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
}

.btn:hover { background: var(--card-h); border-color: var(--border-strong); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Primary: filled brand action. ≥40px touch target (a11y floor §11). */
.btn--primary {
  min-height: 40px;
  padding: var(--space-2) var(--space-4);
  color: var(--bg);
  background: var(--brand);
  border-color: var(--brand);
}
.btn--primary:hover {
  background: color-mix(in srgb, var(--brand) 85%, black);
  border-color: color-mix(in srgb, var(--brand) 85%, black);
}

/* Ghost: chromeless until hover (toolbar-dense rows). */
.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
}
.btn--ghost:hover { background: var(--card-h); color: var(--text); border-color: transparent; }

.btn__icon { display: inline-flex; }

/* --- Icon Button ----------------------------------------------------------- */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;   /* §11 touch-target floor */
  min-height: 40px;
  padding: var(--space-1);
  color: var(--muted);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.icon-btn:hover { color: var(--text); background: var(--card-h); border-color: var(--border-strong); }
.icon-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.icon-btn--ghost { background: transparent; border-color: transparent; }
.icon-btn--ghost:hover { background: var(--card-h); border-color: transparent; }
.icon-btn__icon { display: inline-flex; }

/* --- Icon ------------------------------------------------------------------ */
.icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;  /* optically centre against text */
  flex-shrink: 0;
}

/* --- Tag (soft sans label) + Chip (mono data) ------------------------------ */
.tag {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5ch;
  padding: 0.1rem var(--space-2);
  font-family: var(--font-content);
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text);
  background: var(--card-h);
  border-radius: var(--radius-sm);
}
.tag__count { font-family: var(--font-structural); color: var(--muted); }
.tag--brand   { color: var(--brand-dim); background: var(--teal-soft); }
.tag--running { color: var(--amber-dim); }
.tag--done    { color: var(--green-dim); }
.tag--error   { color: var(--red-dim); }
.tag--queued  { color: var(--slate-dim); }

.chip {
  display: inline-block;
  padding: 0.05rem 0.4rem;
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
  color: var(--text);
  background: var(--inset);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}

/* --- Accent Bar (3px status stripe) ---------------------------------------- */
.accent-bar {
  display: block;
  width: 100%;
  height: 3px;
  border-radius: var(--radius-sm);
  background: var(--slate);
}
.accent-bar--vertical { width: 3px; height: auto; align-self: stretch; min-height: 1em; }

.accent-bar--running,    .accent-bar--in-progress { background: var(--amber); }
.accent-bar--done        { background: var(--green); }
.accent-bar--error,      .accent-bar--failed      { background: var(--red); }
.accent-bar--queued,     .accent-bar--todo,
.accent-bar--backlog     { background: var(--slate); }

/* --- Segment Bar (generic equal-cell track) -------------------------------- */
.segment-bar {
  display: flex;
  gap: 2px;
  width: 100%;
  height: 0.5rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.segment-bar__cell {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  font-family: var(--font-structural);
  font-size: 0.7em;
  color: var(--bg);
  background: var(--faint);
}
.segment-bar__cell--running { background: var(--amber); }
.segment-bar__cell--done    { background: var(--green); }
.segment-bar__cell--error   { background: var(--red); }
.segment-bar__cell--queued  { background: var(--slate); }
.segment-bar__cell--empty   { background: var(--faint); }
.segment-bar__count { color: inherit; }

/* --- Disclosure (native <details>) ----------------------------------------- */
.disclosure { border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--card); }
.disclosure__summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-content);
  color: var(--text);
  cursor: pointer;
  list-style: none;
}
.disclosure__summary::-webkit-details-marker { display: none; }
.disclosure__caret {
  width: 0;
  height: 0;
  border-left: 5px solid var(--muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--motion-fast) ease;
  flex-shrink: 0;
}
.disclosure[open] > .disclosure__summary .disclosure__caret { transform: rotate(90deg); }
.disclosure__body {
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-top: 1px solid var(--line-soft);
  color: var(--text);
}

/* --- Surface / InsetPanel -------------------------------------------------- */
.surface {
  padding: var(--space-3);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.surface--inset { background: var(--inset); border-color: var(--line-soft); }
.surface--raised { background: var(--raised); box-shadow: var(--shadow); }

/*
   Badge convergence anchor (LIN-477, Phase B.2)
   Semantic hook emitted by client badge JS alongside each retained per-page
   hook class (label-tag, cell-stage-badge, loop-stage, overlay-badge,
   foreman-label-tag, swipe-label-tag, session-stage). The client badge family
   is heterogeneous — radii 3px→8px→10px, content/structural/11px-mono fonts,
   and two text-only members (loop-stage, session-stage with no chrome) — so
   .badge is INTENTIONALLY CHROMELESS in this phase: any shared chrome here
   would leak onto the text-only members and onto chrome badges for properties
   their hook rule doesn't re-set, regressing the rendering. Real style
   consolidation (collapsing hook rules into .badge + deltas) is the deferred,
   non-byte-careful Phase-B follow-on. Keep this rule inert. */
.badge {}

/*
   Empty State (LIN-466, Phase A.6)
   The canonical "nothing here yet" placeholder. The five per-page empty states
   genuinely diverge (foreman dashed border, roadmap structural font, swipe
   italic, distinct paddings/alignments) — only the dim foreground is universal,
   so the canonical class carries just that shared subset and composes with the
   RETAINED per-page variant class for byte-identical rendering. Convergence onto
   one canonical look is the tracked Phase-B seam.
*/
.emptyState {
  color: var(--fg-dim);
}

/*
   Legal Pages
   Styled to match the landing and projects page aesthetic.
*/
.legal-content {
  line-height: 1.6;
}

.legal-content h2 {
  color: var(--fg);
  font-family: var(--font-content);
  font-weight: normal;
  margin-bottom: var(--space-5);
  text-align: center;
}

.legal-content h3 {
  color: var(--blue);
  font-weight: 600;
  font-family: var(--font-content);
  padding: var(--space-2) 0;
  margin-bottom: var(--space-2);
}

.legal-content p {
  color: var(--fg-dim);
  margin-bottom: 0.75rem;
  margin-left: 3ch;
  font-family: var(--font-content);
}

.legal-content a {
  color: var(--blue);
}

/* =========================================================================
   Recap Section (LIN-261)
   Shared across swipe accordion and pipeline overlay.
   ========================================================================= */
.recap-section {
  font-family: var(--font-content);
  color: var(--fg);
  font-size: 0.9em;
}

.recap-header {
  display: flex;
  align-items: center;
  gap: 0.75ch;
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.recap-status-label {
  font-weight: 600;
  color: var(--fg);
}

.recap-meta {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
}

.recap-refresh {
  margin-left: auto;
  font-family: inherit;
  font-size: var(--font-size-sm);
  padding: 0.15rem 0.6rem;
  background: transparent;
  border: 1px solid var(--fg-dim);
  border-radius: var(--radius-sm);
  color: var(--fg);
  cursor: pointer;
}

.recap-refresh:hover {
  border-color: var(--fg);
  background: var(--bg-alt);
}

.recap-refresh:disabled {
  opacity: 0.5;
  cursor: default;
}

.recap-placeholder {
  color: var(--fg-dim);
  font-style: italic;
  padding: 0.35rem 0;
}

.recap-empty {
  color: var(--fg-dim);
  padding: 0.35rem 0;
}

.recap-error {
  color: var(--red);
  font-style: normal;
}

.recap-generating {
  display: inline-flex;
  align-items: center;
  gap: 0.5ch;
  color: var(--fg-dim);
  font-style: normal;
}

.recap-spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border: 2px solid var(--fg-dim);
  border-top-color: transparent;
  border-radius: 50%;
  animation: recap-spin 0.9s linear infinite;
}

@keyframes recap-spin {
  to { transform: rotate(360deg); }
}

.recap-list {
  list-style: none;
  padding: 0;
  margin: 0 0 0.75rem 0;
}

.recap-section-title {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: var(--space-1);
}

.recap-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75ch;
  padding: 0.2rem 0;
}

.recap-marker {
  flex-shrink: 0;
  width: 1.25ch;
  text-align: center;
  font-weight: 600;
}

.recap-marker-done { color: var(--green); }
.recap-marker-pending { color: var(--fg-dim); }
.recap-marker-deviation { color: var(--yellow); }

.recap-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.recap-item-title {
  color: var(--fg);
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.recap-item-evidence {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.recap-type {
  display: inline-block;
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.05rem 0.4rem;
  margin-left: 0.5ch;
  border: 1px solid var(--yellow);
  color: var(--yellow);
  border-radius: 2px;
  vertical-align: middle;
}

/* =========================================================================
   Context Section (LIN-572)
   Relationship diagram: blocker chains, parent/child, related. Custom
   box-drawing layout (not Mermaid) to match the CLI aesthetic. Shared by the
   main project page and the swipe view.
   ========================================================================= */
.context-section {
  font-family: var(--font-content);
  color: var(--fg);
  font-size: 0.9em;
}

.context-header {
  display: flex;
  align-items: baseline;
  gap: 0.75ch;
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.context-status-label {
  font-weight: 600;
  color: var(--fg);
}

.context-hint {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
}

.context-refresh {
  margin-left: auto;
  font-family: inherit;
  font-size: var(--font-size-sm);
  padding: 0.15rem 0.6rem;
  background: transparent;
  border: 1px solid var(--fg-dim);
  border-radius: var(--radius-sm);
  color: var(--fg);
  cursor: pointer;
}

.context-refresh:hover { border-color: var(--fg); background: var(--bg-alt); }

.context-placeholder {
  color: var(--fg-dim);
  font-style: italic;
  padding: 0.35rem 0;
}

.context-error { color: var(--red); font-style: normal; }

.context-loading {
  display: inline-flex;
  align-items: center;
  gap: 0.5ch;
  font-style: normal;
}

.context-spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border: 2px solid var(--fg-dim);
  border-top-color: transparent;
  border-radius: 50%;
  animation: recap-spin 0.9s linear infinite;
}

/* Flow row: blockers → root → blocked. Wraps to stacked lanes on narrow screens. */
.context-flow {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.context-lane {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.context-lane-label {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.context-lane-nodes {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.context-flow-arrow {
  align-self: center;
  color: var(--fg-dim);
  font-weight: 600;
  padding: 0 0.25ch;
}

/* A task node. Anchors so they're focusable + degrade to a real link. */
a.context-node { text-decoration: none; }

.context-node {
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.3rem 0.55rem;
  background: var(--bg-alt);
  color: var(--fg);
  transition: border-color 0.12s ease, background 0.12s ease;
}

a.context-node:hover { border-color: var(--fg); background: var(--bg); }

.context-node-head {
  display: flex;
  align-items: center;
  gap: 0.6ch;
}

.context-node-id {
  font-family: var(--font-structural);
  font-weight: 600;
  font-size: var(--font-size-sm);
}

.context-node-title {
  display: block;
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
  margin-top: 0.1rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 28ch;
}

.context-node--root {
  border-color: var(--fg);
  border-width: 2px;
  background: var(--bg);
}

.context-node--start {
  border-color: var(--green);
}

.context-node-tag {
  font-size: 0.7em;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--green);
  border: 1px solid var(--green);
  border-radius: var(--radius-sm);
  padding: 0 0.4ch;
}

/* Session-context provenance tags (LIN-593): seed / descended / spun-off. */
.context-node-tag--seed { color: var(--green); border-color: var(--green); }
.context-node-tag--descended { color: var(--fg-dim); border-color: var(--fg-dim); }
.context-node-tag--spun-off { color: var(--yellow); border-color: var(--yellow); }

/* Session view: a stack of touched-task blocks, each its own bordered panel. */
.context-session {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.context-session-task {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
}

.context-truncated {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
  font-style: italic;
  margin-top: var(--space-1);
}

/* Hierarchy + related bands below the flow. */
.context-hierarchy,
.context-related {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.context-rel-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.context-rel-nodes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
}

.context-sep { color: var(--fg-dim); padding: 0 0.25ch; }

@media (max-width: 600px) {
  .context-flow { flex-direction: column; align-items: stretch; }
  .context-flow-arrow { transform: rotate(90deg); align-self: flex-start; }
  .context-node-title { max-width: 100%; white-space: normal; }
}

/* =========================================================================
   Brief Section
   Current-state task brief (Markdown). Shares the recap header/spinner look.
   ========================================================================= */
.brief-section {
  font-family: var(--font-content);
  color: var(--fg);
  font-size: 0.9em;
}

.brief-header {
  display: flex;
  align-items: center;
  gap: 0.75ch;
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.brief-status-label {
  font-weight: 600;
  color: var(--fg);
}

.brief-meta {
  color: var(--fg-dim);
  font-size: var(--font-size-sm);
}

.brief-refresh {
  margin-left: auto;
  font-family: inherit;
  font-size: var(--font-size-sm);
  padding: 0.15rem 0.6rem;
  background: transparent;
  border: 1px solid var(--fg-dim);
  border-radius: var(--radius-sm);
  color: var(--fg);
  cursor: pointer;
}

.brief-refresh:hover {
  border-color: var(--fg);
  background: var(--bg-alt);
}

.brief-refresh:disabled {
  opacity: 0.5;
  cursor: default;
}

.brief-placeholder {
  color: var(--fg-dim);
  font-style: italic;
  padding: 0.35rem 0;
}

.brief-empty {
  color: var(--fg-dim);
  padding: 0.35rem 0;
}

.brief-error {
  color: var(--red);
  font-style: normal;
}

.brief-generating {
  display: inline-flex;
  align-items: center;
  gap: 0.5ch;
  color: var(--fg-dim);
  font-style: normal;
}

.brief-spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border: 2px solid var(--fg-dim);
  border-top-color: transparent;
  border-radius: 50%;
  animation: recap-spin 0.9s linear infinite;
}

.brief-content {
  color: var(--fg);
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.brief-content h2 {
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg-dim);
  margin: 0.75rem 0 var(--space-1);
}

.brief-content h2:first-child {
  margin-top: 0;
}

.brief-content ul {
  margin: var(--space-1) 0 var(--space-2);
  padding-left: 1.5ch;
}

.brief-content li {
  margin: 0.15rem 0;
}

.brief-content p {
  margin: var(--space-1) 0 var(--space-2);
}

/* ==========================================================================
   Dispatched Sessions section (LIN-522) — shared SessionsSection module.
   Mirrors the swipe view's session styling (public/swipe.css) so the tree
   view's on-card sessions list reads identically.
   ========================================================================== */
.sessions-section {
  font-family: var(--font-structural);
  font-size: var(--font-size-sm);
}

.sessions-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-bottom: 0.4rem;
  margin-bottom: 0.3rem;
  border-bottom: 1px solid var(--fg-vdim);
}

.sessions-status-label {
  font-weight: 500;
  color: var(--fg);
}

.sessions-count-meta {
  color: var(--fg-dim);
}

.sessions-refresh {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--fg-dim);
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  padding: 0;
}

.sessions-refresh:hover {
  color: var(--fg);
}

.sessions-empty {
  color: var(--fg-dim);
  padding: var(--space-2) 0;
}

.sessions-error {
  color: var(--red);
}

.session-entry {
  border-bottom: 1px solid var(--fg-vdim);
  padding: var(--space-2) 0;
}

.session-entry:last-child {
  border-bottom: none;
}

.session-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.session-state {
  flex-shrink: 0;
}

.session-state.state-queued { color: var(--fg-dim); }
.session-state.state-running { color: var(--green); }
.session-state.state-waiting { color: var(--yellow); }
.session-state.state-complete { color: var(--green); }
.session-state.state-error { color: var(--red); }

.session-stage {
  font-weight: 500;
  color: var(--fg);
}

.session-prompt-name {
  color: var(--fg-dim);
}

.session-iteration {
  color: var(--fg-dim);
}

.session-time {
  color: var(--fg-dim);
  margin-left: auto;
}

.session-summary {
  font-family: var(--font-content);
  font-size: 0.9em;
  color: var(--fg);
  padding: 0.3rem 0 0 1.25rem;
  line-height: 1.4;
}

.session-feedback {
  padding: var(--space-1) 0 0 1.25rem;
}

.session-feedback-entry {
  color: var(--fg-dim);
  padding: 0.1rem 0;
}

.session-feedback-time {
  color: var(--fg-dim);
  opacity: 0.7;
}
