/* ┌───────────────────────────────────────────────────────────────┐
 * │           GANTT CHART MAKER — styles.css  v3.5.0             │
 * │           Table of Contents                                  │
 * ├────────┬──────────────────────────────────────────────────────┤
 * │  LINE  │  SECTION                                            │
 * ├────────┼──────────────────────────────────────────────────────┤
 * │    31  │  Theme Variables (:root, [data-theme="dark"])       │
 * │   141  │  Layout (app-layout, sidebar, chart-section)        │
 * │   203  │  Compact Header                                    │
 * │   260  │  Feedback Button                                   │
 * │   291  │  Language Selector                                 │
 * │   389  │  Changelog Bell                                    │
 * │   545  │  Collapsible Panel                                 │
 * │   467  │  Cards                                             │
 * │   476  │  Inputs                                            │
 * │   512  │  Custom Date Picker                                │
 * │   725  │  Custom Color Picker                               │
 * │   880  │  Priority Chips                                    │
 * │   965  │  Buttons (.btn-primary, .btn-export, .btn-accent)    │
 * │  1026  │  Task creator (the Create Task card, 6.10.4)         │
 * │  1153  │  Compact Object Browser (.task-list-compact)       │
 * │  1460  │  Bulk Actions                                      │
 * │  1498  │  Checkbox Styling                                  │
 * │  1577  │  Icon Buttons                                      │
 * │  1610  │  Edit Input (inline)                               │
 * │  1621  │  Chart (.gantt-chart-container, SVG, grid, axis)   │
 * │  1769  │  Toast Notifications                               │
 * │  1800  │  Task Edit Dialog                                  │
 * │  1893  │  Responsive (@media queries)                       │
 * │  1974  │  Today Marker                                      │
 * │  1991  │  Row Hover Highlight                               │
 * │  1998  │  Chart Pan (cursor, panning class)                 │
 * │  2016  │  Zoom to Fit Button                                │
 * │  2044  │  Type Selector Chips                               │
 * │  2065  │  Progress Bar Overlay                              │
 * │  2071  │  Milestone Diamond                                 │
 * │  2076  │  Group Summary Bar                                 │
 * │  2084  │  Dependency Arrows                                 │
 * │  2090  │  Connect Mode                                      │
 * │  2119  │  Dialog Progress Slider                            │
 * │  2147  │  Dialog Select (parent dropdown)                   │
 * │  2153  │  Dialog Dependency List                            │
 * │  2201  │  Object Browser Hierarchy Indent                   │
 * │  2238  │  Connect Mode Toast                                │
 * │  2255  │  SEO Footer                                        │
 * │  2362  │  Share Button & Menu                               │
 * └────────┴──────────────────────────────────────────────────────┘
 */

/* ── Theme Variables ───────────────────────────────────────────── */
/* ===== styles.css MAP =====
   Orientation for a new maintainer. Numbers below (like the ones in the
   old Table of Contents above) drift the moment anyone inserts or deletes
   a line above the section they point at -- this file grew from the
   ~2,400 lines that box covers to 11,000+ lines across many feature
   sessions, and the box itself was never kept in sync. Treat every
   "~line" figure here as an approximate compass, not a promise; search
   for the quoted section heading text if the number is off.

   Roughly in file order:
   - Theme tokens (CSS custom properties): "Theme Variables" ~line 50 sets
     the light/default palette; the very next block, [data-theme=
     "default-dark"], redefines the SAME variable names for dark mode.
     IMPORTANT: the sepia/ocean/forest/rose palettes are NOT in this file
     -- they live in an inline <style> block inside index.html. See the
     note right below, above :root.
   - Base / reset: themed scrollbars + themed [data-tooltip] tooltips,
     right after the theme variables, ~line 207.
   - Page shell / layout: "Layout" ~line 285 -- .app-layout, .app-header
     (-compact), .app-footer, the draggable .side-panel-resizer.
   - Toolbar widgets: feedback button, language selector, changelog bell +
     notifications tabs, panel-collapse toggle, fullscreen button, ~line 517.
   - Form controls: cards, .input, custom Date Picker, custom Color Picker,
     priority chips, base buttons, ~line 1283.
   - Task-creation composer: the task creator card (design handoff "Task
     creator 1d", 6.10.4): the colour dot, name and notes over a tray of
     Dates / Priority / Type / Create task, four popovers, the keep pill,
     ~line 1850.
   - Object Browser (task list): ".task-list-compact" / ".task-row" /
     ".task-chip" rows, bulk actions, delete preview, the chart's
     right-click context menu, ~line 2417.
   - Chart TOOLBAR controls (date-range fields + the responsive
     collapse-to-popover cluster for Tools/Timeline/Zoom/Project) -- NOT
     the chart itself, ~line 3086.
   - Gantt CHART CANVAS: ".gantt-chart-container" and the SVG it holds --
     grid lines, axis text, .task-bar, .task-label, selection rubber-band
     -- the part that actually draws the chart, ~line 3837.
   - Toast notifications: ".message-box", ~line 3980.
   - Dialog shell + Task Edit/Create dialog: the shared ".dialog" /
     ".dialog-header" chrome every modal below reuses, plus GDPR
     self-service rows and the Export Image dialog, ~line 4054.
   - RESPONSIVE breakpoints: mobile (<=639px), tablet (640-1099px), tablet
     portrait, ultrawide (>=2400px), short/720p viewports, ~line 4691.
   - Chart annotations: today marker, hover guide line, zoom-to-fit,
     type chips, progress fill, milestone diamond, dependency arrows +
     link editing + lag/lead offsets, drag tooltip, tool tiles, ~line 5012.
   - SEO footer / About dialog, ~line 5828.
   - Share button + its dropdown menu, ~line 6082.
   - Export & format dialogs: export-as dropdown, the Excel/MSP/TJP
     spreadsheet dialogs (their shared content classes live much further
     down, ~line 7174), the MCP connector modal, the Universal Load-File
     picker, and the Tabbed Export modal, starting ~line 6251.
   - What's New modal: the feature-tour modal plus its many small CSS-only
     preview animations (".wn-*" classes), ~line 6494.
   - Templates modal: split across two far-apart spots -- the modal shell
     near the export dialogs (~line 6343) and the actual template gallery
     grid/cards ~680 lines later (~line 7023), added in a later session.
   - Import dialog: file / Google-Sheet import + column-mapping preview
     table, ~line 7318.
   - Project switching: the current inline "My Charts" bar + gallery modal;
     an OLDER dropdown (".project-picker") is still defined too -- see its
     legacy note, ~line 7476.
   - Collaboration UI: cloud-sync status badge, My Charts gallery, the
     Share/collaborator dialog, the role dropdown, remote cursors, presence
     avatars, ~line 7888.
   - Sign-in / Profile: toolbar auth button + menu, sign-in modal, Profile
     modal (identity card, live-cursor preview, colour picker, workspace
     stats), ~line 9493.
   - Theme palettes, cross-cutting pass: a "pill refresh" override block
     unifies border-radius across every dialog/input/button -- read its
     own comment before changing any corner radius, ~line 9987.
   - Touch / PWA / mobile layout: gesture plumbing, installed-app safe-area
     padding, and a separate coarse-pointer phone layout -- see the flagged
     contradiction at the "NOTE (final)" comment before trusting either
     description of how phones render, ~line 10206 onward.
   - Guided tour + ACCESSIBILITY: spotlight coachmarks, then the "#35"
     panel -- reduce-motion, enhanced focus ring, high-contrast palette
     override, colour-blind SVG filter, ~line 10744.

   Not present: a print stylesheet. There is no @media print anywhere in
   this file -- "export" always means the on-screen Export dialogs above,
   never browser printing.
   ===== */

/* Every surface in the app reads ONLY custom properties like the ones
   below -- never a hard-coded color -- so switching palettes is just
   re-pointing what these names resolve to. This file defines just the
   "default" light/dark pair (this block + the [data-theme="default-dark"]
   block right after it). The sepia/ocean/forest/rose palettes (light +
   dark) are NOT here -- they live in a separate inline <style> block
   inside index.html that redefines the same variable names for
   [data-theme="sepia-light"] etc. Check BOTH files when auditing or
   adding a theme. */
:root {
    /* Light theme (default) */
    --bg-body: #f8fafc;
    --bg-card: #ffffff;
    --bg-input: #f1f5f9;
    /* Hover wash — emerald-tinted mint (was slate #f1f5f9, identical to
       --bg-input): brand-coloured and ~1.5× more visible on white. */
    --bg-hover: #dcf4ea;
    --border-card: #e2e8f0;
    --border-input: #cbd5e1;
    --border-focus: #059669;
    --critical: #dc2626;
    --text-heading: #0f172a;
    --text-body: #334155;
    --text-muted: #64748b;
    --text-faint: #64748b;
    --text-input: #1e293b;
    --text-placeholder: #64748b;
    --chip-bg: #f1f5f9;
    --chip-text: #94a3b8;
    --icon-color: #94a3b8;
    --icon-hover-bg: #e2e8f0;
    --icon-hover-color: #059669;
    --delete-hover-bg: #fee2e2;
    --delete-hover-color: #dc2626;
    --grid-stroke: #cbd5e1;
    --month-band: rgba(0, 0, 0, 0.05);
    --axis-text: #94a3b8;
    --axis-stroke: #cbd5e1;
    --yaxis-text: #475569;
    --task-label-fill: white;
    --dialog-overlay: rgba(0, 0, 0, 0.25);
    --toast-success-bg: #dcfce7;
    --toast-success-text: #166534;
    --toast-success-border: #86efac;
    --toast-error-bg: #fee2e2;
    --toast-error-text: #991b1b;
    --toast-error-border: #fca5a5;
    --priority-high-text: #dc2626;
    --priority-high-bg: #fee2e2;
    --priority-high-border: #dc2626;
    --priority-med-text: #b45309;
    --priority-med-bg: #fef3c7;
    --priority-med-border: #b45309;
    --priority-low-text: #0369a1;
    --priority-low-bg: #e0f2fe;
    --priority-low-border: #0369a1;
    /* The user-named fourth level (violet). One palette for the list, the
       chooser, the chart's badges, the dialog and exports (6.10.1). */
    --priority-custom-text: #6d28d9;
    --priority-custom-bg: #ede9fe;
    --priority-custom-border: #6d28d9;
    --dialog-delete-border: #e2e8f0;
    --dialog-delete-hover-bg: #fee2e2;
    --dialog-delete-hover-text: #dc2626;
    --dialog-delete-hover-border: #dc2626;
    --note-dot: #d97706;
    --note-indicator: #d97706;
    --toggle-bg: #e2e8f0;
    --toggle-icon: #64748b;
    --title-accent: #059669;
    --glow-color: rgba(5, 150, 105, 0.5);
    --changelog-badge-bg: #f59e0b;
    --changelog-badge-text: #ffffff;
    --changelog-version-color: #059669;
    --scrollbar-thumb: #cbd5e1;
    --scrollbar-thumb-hover: #94a3b8;
    --scrollbar-track: #f1f5f9;
    --btn-add-bg: #059669; --btn-add-text: #ffffff;
    /* SVG chart-surface background (y-axis + x-axis mask rects). Must match .chart-section's computed bg. */
    --chart-bg: #ffffff;
    /* Surface tokens used by newer dialogs/dropdowns (project picker, New Project, Load Format cards).
       These must stay theme-aware — older rules used undefined vars and fell back to white. */
    --surface: #ffffff;
    --bg-elevated: #ffffff;
    --surface-hover: rgba(5, 150, 105, 0.06);
    --input-bg: #f1f5f9;
    --text: #1e293b;
    --accent: #059669;
    --accent-soft: #ecfdf5;
    --accent-dark: #047857;
}

[data-theme="default-dark"] {
    /* Obsidian dark theme (S196): near-black OLED shell (#0a0a0a) on a neutral charcoal
       ladder — no blue cast. The indigo brand accent is replaced with emerald (#34d399)
       for this theme. Semantic accents (priority/toast/delete) unchanged. */
    --bg-body: #0a0a0a;
    --bg-card: #151515;
    --bg-input: #1f1f1f;
    /* Hover wash — emerald-tinted charcoal (was #1f1f1f, identical to
       --bg-input): mirrors the light theme's mint hover. */
    --bg-hover: #223830;
    --border-card: #2b2b2b;
    --border-input: #3a3a3a;
    --border-focus: #34d399;
    --text-heading: #ffffff;
    --text-body: #ededed;
    --text-muted: #8c8c8c;
    --text-faint: #8c8c8c;
    --text-input: #ededed;
    --text-placeholder: #8c8c8c;
    --chip-bg: #1f1f1f;
    --chip-text: #707070;
    --icon-color: #707070;
    --icon-hover-bg: #2b2b2b;
    --icon-hover-color: #6ee7b7;
    --delete-hover-bg: #450a0a;
    --delete-hover-color: #fca5a5;
    --grid-stroke: #6f6f6f;
    --month-band: rgba(255, 255, 255, 0.06);
    --axis-text: #707070;
    --axis-stroke: #3a3a3a;
    --yaxis-text: #8c8c8c;
    --task-label-fill: white;
    --dialog-overlay: rgba(0, 0, 0, 0.6);
    --toast-success-bg: #052e16;
    --toast-success-text: #86efac;
    --toast-success-border: #16a34a;
    --toast-error-bg: #450a0a;
    --toast-error-text: #fca5a5;
    --toast-error-border: #dc2626;
    --priority-high-text: #fca5a5;
    --priority-high-bg: #450a0a;
    --priority-high-border: #dc2626;
    --priority-med-text: #fde68a;
    --priority-med-bg: #422006;
    --priority-med-border: #d97706;
    --priority-low-text: #7dd3fc;
    --priority-low-bg: #082f49;
    --priority-low-border: #0369a1;
    --priority-custom-text: #c4b5fd;
    --priority-custom-bg: #2e1065;
    --priority-custom-border: #7c3aed;
    --dialog-delete-border: #2b2b2b;
    --dialog-delete-hover-bg: #450a0a;
    --dialog-delete-hover-text: #fca5a5;
    --dialog-delete-hover-border: #dc2626;
    --note-dot: #fde68a;
    --note-indicator: #fde68a;
    --toggle-bg: #1f1f1f;
    --toggle-icon: #cccccc;
    --title-accent: #34d399;
    --glow-color: rgba(52, 211, 153, 0.6);
    --changelog-badge-bg: #f59e0b;
    --changelog-badge-text: #ffffff;
    --changelog-version-color: #34d399;
    --scrollbar-thumb: #3a3a3a;
    --scrollbar-thumb-hover: #4d4d4d;
    --scrollbar-track: #151515;
    --btn-add-bg: #34d399; --btn-add-text: #06281c;
    /* Dark theme — .chart-section is overridden to var(--bg-body) in the theming block */
    --chart-bg: #0a0a0a;
    /* Surface tokens for dark mode — elevated surfaces sit slightly above --bg-card so cards
       stay readable on the dark dialog background. Pure white was the previous fallback. */
    --surface: #1f1f1f;
    --bg-elevated: #1f1f1f;
    --surface-hover: rgba(52, 211, 153, 0.16);
    --input-bg: #1f1f1f;
    --text: #ededed;
    --accent: #34d399;
    --accent-soft: rgba(52, 211, 153, 0.18);
    --accent-dark: #6ee7b7;
}

/* Component rules further down that need MORE than a variable swap for
   dark mode (different shadow opacity, gradient stops, etc.) select on
   [data-theme$="-dark"] -- "ends with -dark" -- which matches every dark
   variant at once (default-dark, sepia-dark, ocean-dark, forest-dark,
   rose-dark), not just this default theme. */

/* ── Theme switch = ONE coordinated cross-fade ──────────────────────────
   theme-system.js adds .theme-switching to <html> for the duration of a
   theme change: every element-level transition is suppressed so surfaces
   can't each fade at their own speed (the old element-by-element stagger),
   and where the browser supports View Transitions the whole page cross-
   fades at once instead. Browsers without the API drop the unknown
   ::view-transition rules and just get the instant atomic swap. */
html.theme-switching,
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
    transition: none !important;
}
::view-transition-old(root) {
    animation-duration: 0.22s;
    animation-timing-function: ease;
}
::view-transition-new(root) {
    animation-duration: 0.22s;
    animation-timing-function: ease;
}
@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root) { animation: none; }
    ::view-transition-new(root) { animation: none; }
}

/* ── Themed Scrollbars ────────────────────────────────────────── */

/* Firefox */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}

/* Chromium / Safari */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--scrollbar-thumb-hover);
}

::-webkit-scrollbar-corner {
    background: var(--scrollbar-track);
}

/* ── Themed Tooltips ──────────────────────────────────────────── */
[data-tooltip] {
    position: relative;
}

[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    padding: 5px 10px;
    border-radius: 6px;
    font-size: 0.72rem;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    /* No delay on the base state so the tooltip vanishes the instant the
       pointer leaves; the show delay lives on :hover::after below. */
    transition: opacity 0.15s ease 0s;
    z-index: 9999;
    background: var(--bg-input);
    color: var(--text-body);
    border: 1px solid var(--border-input);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

[data-tooltip]:hover::after {
    opacity: 1;
    transition: opacity 0.15s ease 0.8s;
}

#exportBtn::after {
    transition: opacity 0.15s ease 0s;
}

#exportBtn:hover::after {
    transition: opacity 0.15s ease 7s;
}

/* Right-edge tooltip: align to right side instead of center */
[data-tooltip-align="right"]::after {
    left: auto;
    right: 0;
    transform: none;
}

/* ── Layout ────────────────────────────────────────────────────── */
/* Stacking note: z-index values throughout this file are hand-picked per
   component, not drawn from a shared scale or CSS variable. Rough bands
   in use: 60 (toolbar popovers) / 200-1000 (menus, dropdowns) / 1500-2000
   (dialogs, toasts) / 3000-3500 (date/color pickers, share menu) /
   9000-9999 (guided tour, cookie banner, tooltips) / 10000 (drag
   tooltip). Before adding a new overlay, grep this file for "z-index:"
   and pick a value that won't be buried by (or bury) a neighbour. */
body {
    background-color: var(--bg-body);
    color: var(--text-body);
    transition: background-color 0.2s, color 0.2s;
}

.app-layout {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
}

.top-panel {
    display: flex;
    flex-shrink: 0;
    /* --panel-x is the side gutter of the whole top region. It is 0 since the
       header redesign (Fotis, 2026-10-12): the bar, the task row, the object
       browser and the line beneath them all run edge to edge. The header still
       cancels it, so a gutter can be reintroduced here alone. */
    --panel-x: 0px;
    padding: 0 var(--panel-x);
}

.top-panel-left {
    flex: 1;
    min-width: 0;
}

.top-panel-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.5rem;
    flex-shrink: 0;
    padding: 0.75rem 0.5rem 0 0;
}

/* Original header (kept for reference) */
.app-header {
    display: flex;
    align-items: center;
    padding: clamp(0.5rem, 1vw, 0.75rem) 0;
}

.app-title {
    font-size: clamp(2rem, 4vw, 4.5rem);
    font-weight: 700;
    letter-spacing: -0.03em;
    color: var(--text-heading);
    background: linear-gradient(135deg, var(--text-heading) 0%, var(--title-accent) 50%, var(--text-heading) 100%);
    background-size: 200% 200%;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    transition: text-shadow 0.3s ease, filter 0.3s ease;
    text-shadow: 0 0 0px transparent;
    position: relative;
}

.app-title.glow {
    -webkit-text-fill-color: var(--text-heading);
    filter: drop-shadow(0 0 var(--glow-radius) var(--glow-color));
}

/* ── Top header bar (2026-10-11, design_handoff_header_bar option 2a) ───
   One 56px flex row, six groups 14px apart: brand (margin-right: auto) ·
   browse / learn / get (quiet text buttons) · divider · chart actions (the
   only bordered, segmented toolbar) · utilities pill (icon tiles) ·
   community (quiet; Donate keeps a red filled heart) · account (ink button
   or avatar) · collapse chevron. Green appears nowhere here, so Create Task
   stays the page's one primary. Tokens are the app's own and the dark theme
   follows. setupTopHeaderDensity() in app.js folds the quiet labels
   (.hdr-fold-labels) and then moves the browse group into the pill
   (.hdr-fold-browse) when the row runs out of room; below 900px the row
   may wrap instead. Markup: index.html <header class="app-header-compact">. */
.app-header-compact {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 14px;
    height: 56px;
    padding: 0 20px;
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-card);
    /* The bar runs edge to edge: it cancels the top panel's side gutter, then
       pads its own 20px, so its card surface never shows the page background
       as dark strips at either end. */
    margin: 0 calc(-1 * var(--panel-x, 1.5rem));
    position: relative;
    /* Nothing in the bar may widen the page; overflow-x: clip leaves
       overflow-y visible, so the dropdowns (lang / bell / account) still drop. */
    overflow-x: clip;
}
.app-header-compact > * { flex: none; }
.hdr-group { display: flex; align-items: center; gap: 2px; }
.hdr-community { gap: 4px; }
.hdr-divider { width: 1px; height: 22px; background: var(--border-card); }

/* Brand: wordmark (a link to the blog) with "Maker" in accent, date beside it. */
.title-clock-group {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-right: auto;
    min-width: 0;
}
.app-title-compact {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.2;
    color: var(--text-heading);
    white-space: nowrap;
}
.app-title-link { color: inherit; text-decoration: none; cursor: pointer; }
.app-title-link:hover { color: inherit; text-decoration: none; }
.app-title-accent { color: var(--accent); }
/* Read by screen readers and crawlers, not drawn: the app title's tagline. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.header-clock {
    font-size: 12px;
    font-weight: 400;
    color: var(--text-muted);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Quiet text buttons: browse / learn / get and community. */
.hdr-quiet {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 32px;
    padding: 0 10px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.15s ease, color 0.15s ease;
}
.hdr-quiet:hover { background: var(--bg-input); color: var(--text-heading); text-decoration: none; }
.hdr-quiet svg { width: 15px; height: 15px; flex: none; }
/* Donate: quiet like its neighbours, the heart stays red; hover warms to the
   error surface (the same red family in both themes). */
.hdr-donate:hover { background: var(--toast-error-bg); color: var(--toast-error-text); }
.hdr-donate:focus-visible { outline: 2px solid #dc2626; outline-offset: 2px; }

/* Chart actions: the one bordered, segmented toolbar. */
.hdr-seg {
    position: relative;
    display: flex;
    align-items: center;
    height: 34px;
    border: 1px solid var(--border-card);
    border-radius: 10px;
    background: var(--bg-card);
    overflow: hidden;
}
.hdr-seg-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 100%;
    padding: 0 13px;
    border: 0;
    background: transparent;
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    color: var(--text-body);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease;
}
.hdr-seg-btn:hover { background: var(--bg-input); }
.hdr-seg-btn svg { width: 15px; height: 15px; flex: none; }
.hdr-seg-divider { width: 1px; height: 18px; background: var(--border-card); flex: none; }

/* Utilities pill: language, accessibility, theme, share (anon) or bell. */
.hdr-pill {
    display: flex;
    align-items: center;
    gap: 2px;
    height: 34px;
    padding: 0 4px;
    background: var(--bg-input);
    border-radius: 10px;
}
.hdr-pill > * { position: relative; }
.hdr-tile {
    position: relative;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: inherit;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.hdr-tile:hover { background: var(--bg-card); color: var(--text-heading); }
.hdr-tile svg { width: 15px; height: 15px; }
.lang-wrapper { position: relative; }

/* Folded states, applied by the density ladder in app.js: first the quiet
   labels go (icon + tooltip, the aria-label keeps the name), then the browse
   group moves inside the pill and renders as tiles. */
.app-header-compact.hdr-fold-labels .hdr-quiet > span { display: none; }
.app-header-compact.hdr-fold-labels .hdr-quiet { padding: 0 8px; }
.app-header-compact.hdr-fold-browse .hdr-browse,
.app-header-compact.hdr-fold-browse .hdr-divider { display: none; }
.hdr-pill .hdr-quiet { width: 28px; height: 28px; padding: 0; justify-content: center; border-radius: 8px; }
.hdr-pill .hdr-quiet > span { display: none; }
.hdr-pill .hdr-quiet:hover { background: var(--bg-card); }
.hdr-pill .hdr-donate:hover { background: var(--toast-error-bg); }

/* Under 900px the row may wrap; the ladder has already folded the labels. */
@media (max-width: 899px) {
    .app-header-compact { height: auto; min-height: 56px; flex-wrap: wrap; row-gap: 6px; padding: 6px 12px; gap: 10px; }
    .hdr-divider { display: none; }
}

.lang-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.5rem;
    padding: 0.25rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
    z-index: 1500;
    animation: dialogIn 0.12s ease-out;
    min-width: min(8rem, 90vw);
}

.lang-dropdown.hidden { display: none; }

.lang-option {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    padding: 0.35rem 0.5rem;
    border: none;
    background: transparent;
    color: var(--text-body);
    font-size: 0.72rem;
    font-weight: 500;
    border-radius: 0.35rem;
    cursor: pointer;
    transition: background 0.1s;
    text-align: left;
}

.lang-option:hover {
    background: var(--bg-hover);
}

.lang-option.active {
    background: var(--bg-input);
    color: var(--text-heading);
    font-weight: 600;
}

.lang-flag {
    width: 22px;
    height: 15px;
    flex-shrink: 0;
    display: block;
    border-radius: 5px;
    overflow: hidden;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.14);
}
[data-theme$="-dark"] .lang-flag {
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}

/* ── Changelog Bell ────────────────────────────────────────────── */
.changelog-wrapper {
    position: relative;
}

/* Unread marker on the bell tile: a 7px accent dot ringed by the pill's
   background (the count itself lives in the panel header). JS writes a
   number into the span; font-size 0 keeps it a dot. */
.changelog-badge {
    position: absolute;
    top: 3px;
    right: 4px;
    width: 7px;
    height: 7px;
    padding: 0;
    min-width: 0;
    border-radius: 999px;
    background: var(--accent);
    border: 1.5px solid var(--bg-input);
    font-size: 0;
    line-height: 0;
    color: transparent;
    pointer-events: none;
    animation: badgePop 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

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

@keyframes badgePop {
    from { transform: scale(0); opacity: 0; }
    to   { transform: scale(1); opacity: 1; }
}

/* Notifications panel (2026-09 design): 400px window, New / Earlier split,
   green only where it means something (incoming, unread, mark-all-read). */
.changelog-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    width: min(400px, calc(100vw - 1.5rem));
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 16px;
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
    z-index: 1500;
    animation: dialogIn 0.12s ease-out;
    overflow: hidden;
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
}

.changelog-dropdown.hidden {
    display: none;
}

.changelog-dropdown-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0.7rem 0.95rem 0.6rem;
    border-bottom: 1px solid var(--border-card);
}

.changelog-dropdown-title {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-heading);
    letter-spacing: 0.01em;
}
.notif-count-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
}
.notif-count-pill.hidden { display: none; }
.notif-markall {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: 0;
    background: none;
    color: var(--accent-dark);
    font-family: inherit;
    font-size: 11px;
    font-weight: 700;
    padding: 4px 8px;
    border-radius: 8px;
    cursor: pointer;
}
.notif-markall:hover { background: var(--accent-soft); }
.notif-markall.hidden { display: none; }
/* The Support row's own unread pill inside the account menu. */
.auth-menu-count { margin-left: auto; flex: none; }

.changelog-entries {
    max-height: 320px;
    overflow-y: auto;
    scrollbar-width: thin;
    padding: 0.35rem 0;
}

/* ── Notifications bell — two-tab panel (TODO #69) ──────────────────── */
.notif-tab-strip {
    display: flex;
    border-bottom: 1px solid var(--border-card);
    background: var(--bg-elevated, rgba(0,0,0,0.02));
}
.notif-tab {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.5rem 0.5rem;
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.notif-tab:hover { color: var(--text-heading); }
.notif-tab.is-active {
    color: var(--text-heading);
    border-bottom-color: var(--accent);
}
.notif-tab.hidden { display: none; }
.notif-tab-count {
    background: var(--accent);
    color: var(--accent-text, #fff);
    border-radius: 999px;
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 1;
    padding: 0.15rem 0.4rem;
    min-width: 1rem;
    text-align: center;
}
.notif-tab-count.hidden { display: none; }
.notif-pane.hidden { display: none; }
/* The PANE scrolls (not the lists) so the section labels can stick. */
#notifPaneMessages {
    max-height: min(70vh, 560px);
    overflow-y: auto;
    scrollbar-width: thin;
}
.notif-sec-label {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg-card);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 8px 14px 4px;
}
.notif-sec-label.hidden { display: none; }
#notifEarlierLabel { border-top: 1px solid var(--border-card); }
#notifNewList:empty + #notifEarlierLabel { border-top: 0; }
.notif-messages-list {
    padding: 4px 6px 6px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
/* Rows: 32px direction tile, project-name headline, person-centric second
   line, hover-revealed ×. Unread rows sit in the NEW section on a soft
   green tint. */
.notif-row {
    display: flex;
    align-items: flex-start;
    gap: 0.65rem;
    padding: 0.6rem 0.65rem;
    border-radius: 12px;
    transition: background 0.12s;
    position: relative;
}
.notif-row:hover { background: var(--bg-hover); }
.notif-row.is-actionable { cursor: pointer; }
.notif-row.is-unread { background: var(--accent-soft); }
.notif-row.is-unread:hover { background: color-mix(in srgb, var(--accent) 14%, var(--bg-card)); }
.notif-row-icon {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.notif-row-icon.is-in { background: var(--accent-soft); color: var(--accent); }
.notif-row.is-unread .notif-row-icon.is-in { background: var(--bg-card); }
.notif-row-icon.is-out { background: var(--bg-input); color: var(--text-muted); }
.notif-row-body {
    flex: 1 1 auto;
    min-width: 0;
}
.notif-row-title {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.notif-row-sub {
    font-size: 12.5px;
    color: var(--text-muted);
    margin-top: 2px;
    line-height: 1.45;
    word-break: break-word;
}
.notif-person {
    color: var(--accent-dark);
    font-weight: 600;
}
.notif-row-when { white-space: nowrap; }
/* Grant / Deny pair on an open edit-access request — the one notification the
   owner answers in place. Sits under the text, inside .notif-row-body. */
.notif-row-actions {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.45rem;
}
.notif-action-btn {
    padding: 3px 10px;
    border-radius: 6px;
    border: 1px solid var(--border-card);
    background: var(--bg-card);
    color: var(--text-body);
    font-size: 0.7rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.notif-action-btn:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--text-heading);
}
.notif-action-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.notif-action-grant {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.notif-action-grant:hover:not(:disabled) {
    background: var(--accent-dark, var(--accent));
    color: #fff;
}
.notif-dismiss-x {
    flex: 0 0 auto;
    background: transparent;
    border: 0;
    color: var(--text-muted);
    font-size: 1rem;
    line-height: 1;
    padding: 0.15rem 0.4rem;
    cursor: pointer;
    border-radius: 7px;
    opacity: 0;
    transition: background 0.12s, color 0.12s, opacity 0.12s;
}
.notif-row:hover .notif-dismiss-x,
.changelog-entry:hover .notif-dismiss-x { opacity: 1; }
@media (hover: none) {
    .notif-dismiss-x { opacity: 1; }
}
.notif-dismiss-x:hover {
    color: var(--text-heading);
    background: var(--bg-hover);
}
.notif-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 2rem 1rem;
    text-align: center;
    font-size: 0.75rem;
    color: var(--text-muted);
    line-height: 1.5;
}
.notif-empty-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background: var(--bg-input);
    color: var(--text-muted);
}
.notif-empty.hidden { display: none; }

.announcement-entry .announcement-title {
    color: var(--accent);
    text-transform: none;
    letter-spacing: 0;
}
.announcement-entry .announcement-body {
    font-size: 0.72rem;
    line-height: 1.4;
    color: var(--text);
    margin-top: 0.2rem;
}
.changelog-entry .notif-dismiss-x {
    margin-left: auto;
}

.changelog-entry {
    padding: 0.5rem 0.75rem;
}

.changelog-entry + .changelog-entry {
    border-top: 1px solid var(--border-card);
}

.changelog-entry-header {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.3rem;
}

.changelog-entry-version {
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--changelog-version-color);
}

/* Major releases (N.0.0) — badge-style highlight to stand out from minor/patch entries. */
.changelog-entry-version-major {
    font-size: 0.78rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    color: var(--changelog-badge-text);
    background: var(--changelog-version-color);
    padding: 0.12rem 0.45rem;
    border-radius: 0.35rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}

.changelog-entry-date {
    font-size: 0.62rem;
    color: var(--text-faint);
    font-weight: 400;
}

.changelog-entry-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.changelog-entry-list li {
    font-size: 0.68rem;
    color: var(--text-body);
    padding-left: 0.85rem;
    position: relative;
    line-height: 1.4;
}

.changelog-entry-list li::before {
    content: '\00b7';
    position: absolute;
    left: 0.25rem;
    color: var(--changelog-version-color);
    font-weight: 700;
}

/* ("Upcoming" preview removed — product updates moved to the
   What's New modal; see .whatsnew-* styles.) */

.theme-toggle-sm {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 0.375rem;
    border: none;
    cursor: pointer;
    background: var(--toggle-bg);
    color: var(--toggle-icon);
    font-size: 0.9rem;
    transition: all 0.15s;
}

.theme-toggle-sm:hover {
    color: var(--text-heading);
}

/* ── IMPROVEMENT 6: Collapsible Panel ──────────────────────────── */
.panel-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--icon-color);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.panel-toggle:hover {
    background: var(--bg-input);
    color: var(--text-heading);
}

.panel-toggle svg {
    width: 15px;
    height: 15px;
    transition: transform 0.25s ease;
}


.panel-toggle.collapsed svg {
    transform: rotate(180deg);
}

.controls-panel.collapsed {
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    padding: 0;
    transition: max-height 0.25s ease-out, opacity 0.2s ease-out;
}

.controls-panel:not(.collapsed) {
    max-height: 100vh;
    opacity: 1;
    transition: max-height 0.25s ease-in, opacity 0.2s ease-in;
}

.section-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-heading);
    margin-bottom: 1.1rem;
}

.controls-panel {
    display: flex;
    flex-direction: column;
    gap: 0;
    flex-shrink: 0;
    /* Anchor for .browser-resizer, which hangs into the gap below the panel. */
    position: relative;
}

/* Horizontal splitter under the composer/browser row: drag DOWN for more
   object-browser rows. The row's natural height is the FLOOR (it can't be
   dragged smaller); double-click resets. Interaction model mirrors
   .side-panel-resizer. */
.browser-resizer {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -9px;
    height: 12px;
    cursor: row-resize;
    z-index: 15;
    background: transparent;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
}
.browser-resizer::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 5px;
    transform: translateX(-50%);
    width: 72px;
    height: 3px;
    background: var(--border-focus);
    opacity: 0;
    transition: opacity 0.12s;
    pointer-events: none;
    border-radius: 2px;
}
.browser-resizer:hover::before,
.browser-resizer.is-dragging::before {
    opacity: 0.85;
}
@media (max-width: 640px) {
    .browser-resizer { display: none; }
}

.drag-grip:hover {
    fill: var(--text-body) !important;
}

.flat-section {
    padding: clamp(0.5rem, 1vw, 0.75rem) 0 clamp(0.4rem, 0.8vw, 0.6rem) 0;
    border-bottom: 1px solid var(--border-card);
}

/* Composer | task-browser split: the two halves are sibling PANEL CARDS with
   a gap of page background between them (this replaced a bare 6px slab
   divider, which read as a smudge rather than a boundary). Same card
   language as the export modal's panels. */
.composer-split {
    display: flex;
    /* The browser keeps its place beside the composer down to its minimum
       width (a laptop window snapped to half the screen), then drops under. */
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0.5rem 1rem;
    /* The two cards sit 6px in from the window edges (Fotis, 2026-10-12: flush
       cards "very close to the margins of the screen"). The line beneath is the
       section border, so it still runs edge to edge. */
    padding-left: 6px;
    padding-right: 6px;
}
.composer-pane {
    /* Beside the browser, the 999 grow factor below hands the browser all the
       spare width; alone on its line, the composer fills the row. */
    flex: 1 0 auto;
    min-width: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
    padding: 0.6rem 0.95rem 0.65rem;
}
.browser-pane {
    flex: 999 1 0;
    min-width: min(18rem, 100%);
    /* The inner is pinned (below), so the pane has no content height: the
       object browser frame (styles further down, "OBJECT BROWSER") sets the
       minimum, header plus three rows. */
    position: relative;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
}

/* Pinned to the pane's box: the browser pane contributes NO height of its
   own to the composer row beyond its minimum; app.js sets the pane a
   min-height of whole rows (the rows that cover the composer beside it, or
   the count the row's drag handle chose), and the grid inside fills it. */
.browser-pane-inner {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* Fullscreen toggle. YouTube-style translucent floating button.
   Uses native Fullscreen API on .gantt-chart-container; icon swaps via
   .is-fullscreen class set by the fullscreenchange listener.

   Anchored to the viewport (position: fixed) instead of the
   chart container, so it stays at the same screen position regardless
   of chart size or vertical scroll. Bottom clears the .app-footer (~31px
   tall) plus a small visual gap. */
.chart-fullscreen-btn {
    position: fixed;
    bottom: 5rem;
    right: 0.85rem;
    z-index: 20;
    width: 2.25rem;
    height: 2.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--border-card);
    border-radius: 9999px;
    background: var(--bg-card);
    color: var(--text-muted);
    cursor: pointer;
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
    transition: background 0.12s, color 0.12s, transform 0.12s, border-color 0.12s;
}

.chart-fullscreen-btn:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
    border-color: var(--border-focus);
    transform: scale(1.06);
}

.chart-fullscreen-icon {
    width: 1.1rem;
    height: 1.1rem;
}

/* Default state: show "enter" icon, hide "exit" icon */
.chart-fullscreen-icon-exit { display: none; }

/* Fullscreen-active state: swap icons */
.chart-fullscreen-btn.is-fullscreen .chart-fullscreen-icon-enter { display: none; }
.chart-fullscreen-btn.is-fullscreen .chart-fullscreen-icon-exit  { display: inline-block; }

/* When the container is the native fullscreen element, paint a solid
   background under the SVG so the chart fills the viewport cleanly. */
.gantt-chart-container:fullscreen {
    background: var(--bg-card);
    padding: 0.5rem;
    /* Explicit so the UA fullscreen stylesheet can't silently override.
       Required for vertical scrolling when the chart is taller than the
       viewport (scroll fix for many-task charts in fullscreen). */
    overflow-y: auto;
    overflow-x: hidden;
}

/* Let the SVG keep its JS-set pixel height attribute (chartHeight + margins).
   Forcing height: 100% here clamped the SVG to the viewport and clipped
   off-screen task rows so they couldn't be scrolled to (fixed). */
.gantt-chart-container:fullscreen #ganttChart {
    width: 100%;
}

/* Fullscreen button: frosted dark pill so it stays readable on any theme's
   canvas. Only kicks in while the container is the fullscreen element —
   normal-mode appearance is unchanged. */
.gantt-chart-container:fullscreen .chart-fullscreen-btn {
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    border-color: rgba(255, 255, 255, 0.18);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    position: fixed;
}

.gantt-chart-container:fullscreen .chart-fullscreen-btn:hover {
    background: rgba(0, 0, 0, 0.78);
    color: #fff;
    border-color: rgba(255, 255, 255, 0.32);
    transform: scale(1.08);
}

.chart-section {
    /* Positioned so #versionViewBar can pin to the bottom of the chart area.
       It cannot anchor to .gantt-chart-container: that scrolls. */
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--bg-card);
    border-top: none;
    padding: clamp(0.5rem, 1vw, 0.75rem) 0 0;
    transition: background 0.2s, border-color 0.2s;
}

.chart-section .gantt-chart-container {
    /* position: relative so the floating fullscreen button anchors here */
    position: relative;
    flex: 1;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
    scrollbar-width: thin;
}

/* ── Site footer — always-visible legal strip. Flex child of the
   100vh body layout, so flex-shrink:0 keeps it pinned at the bottom without
   scrolling and only sips a sliver of height from the chart. ── */
.app-footer {
    flex-shrink: 0;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 0.4rem 1.5rem;
    background: var(--bg-card);
    border-top: 1px solid var(--border-card);
    font-size: 0.72rem;
    color: var(--text-muted);
    transition: background 0.2s, border-color 0.2s;
}
.app-footer-links {
    grid-column: 2;
    justify-self: center;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.15rem 0.55rem;
}
.app-footer-meta {
    grid-column: 3;
    justify-self: end;
}
.app-footer-links a,
.app-footer-cookie-btn {
    color: var(--text-muted);
    text-decoration: none;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    cursor: pointer;
}
.app-footer-links a:hover,
.app-footer-cookie-btn:hover {
    color: var(--text-heading);
    text-decoration: underline;
}
.app-footer-sep { color: var(--border-input, var(--border-card)); opacity: 0.7; }
.app-footer-meta { white-space: nowrap; opacity: 0.85; }
:fullscreen .app-footer { display: none; }
@media (max-width: 640px) {
    .app-footer { grid-template-columns: 1fr; justify-items: center; padding: 0.35rem 0.75rem; }
    .app-footer-meta { display: none; }
    .app-footer-links { grid-column: 1; gap: 0.1rem 0.4rem; font-size: 0.68rem; }
}

/* Drag-resize handle for the left side panel. Sits at the
   chart-x=0 boundary; positionSidePanelResizer() sets `left` after every
   redraw. 8px hit area, invisible until hover, then a 2px theme accent
   line. col-resize cursor. Double-click resets to auto-fit width. */
.side-panel-resizer {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 8px;
    transform: translateX(-4px);
    cursor: col-resize;
    z-index: 15;
    background: transparent;
    transition: background 0.12s;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
}

.side-panel-resizer::before {
    content: '';
    position: absolute;
    top: 0;
    left: 3px;
    width: 2px;
    height: 100%;
    background: var(--border-focus);
    opacity: 0;
    transition: opacity 0.12s;
    pointer-events: none;
    border-radius: 1px;
}

.side-panel-resizer:hover::before,
.side-panel-resizer.is-dragging::before {
    opacity: 0.85;
}

.side-panel-resizer.is-dragging {
    background: transparent;
}

/* While the user is dragging the resizer, lock the cursor across the page
   so it doesn't flicker between col-resize and the default arrow as the
   pointer crosses the handle boundary. */
body.side-panel-resizing,
body.side-panel-resizing * {
    cursor: col-resize !important;
    user-select: none !important;
}

/* ── Cards ─────────────────────────────────────────────────────── */
.card {
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.75rem;
    padding: clamp(0.65rem, 1.5vw, 1rem) clamp(0.75rem, 2vw, 1.25rem);
    transition: background 0.2s, border-color 0.2s;
}

/* ── Inputs ────────────────────────────────────────────────────── */
.label {
    display: block;
    font-size: 0.7rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    margin-bottom: 0.25rem;
}

.input {
    width: 100%;
    height: 2.25rem;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    border-radius: 0.5rem;
    padding: 0.45rem 0.65rem;
    font-size: 0.85rem;
    color: var(--text-input);
    outline: none;
    transition: border-color 0.15s, background 0.2s, color 0.2s;
}

.input:focus {
    border-color: var(--border-focus);
}

.input::placeholder {
    color: var(--text-placeholder);
}

/* Date trigger input */
.date-trigger {
    cursor: pointer;
}

/* ── Custom Date Picker ────────────────────────────────────────── */
.date-picker {
    position: fixed;
    z-index: 3000;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 1rem;
    padding: 0.75rem;
    width: 260px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
    animation: dialogIn 0.12s ease-out;
    user-select: none;
}

.date-picker.hidden { display: none; }

.dp-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.6rem;
}

.dp-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-heading);
    cursor: pointer;
    padding: 0.2rem 0.5rem;
    border-radius: 0.375rem;
    transition: background 0.1s;
}

.dp-title:hover {
    background: var(--bg-input);
}

.dp-nav {
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    border: none;
    background: var(--bg-input);
    color: var(--text-body);
    font-size: 1.1rem;
    font-weight: 600;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.1s;
    line-height: 1;
}

.dp-nav:hover {
    background: var(--border-focus);
    color: white;
}

.dp-weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 0;
    margin-bottom: 0.25rem;
}

.dp-weekdays span {
    text-align: center;
    font-size: 0.6rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--text-faint);
    padding: 0.25rem 0;
}

.dp-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.dp-day {
    width: 100%;
    aspect-ratio: 1;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--text-body);
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.1s;
}

.dp-day:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}

.dp-day.today {
    border: 1.5px solid var(--border-focus);
    font-weight: 700;
}

.dp-day.selected {
    background: var(--border-focus);
    color: white;
    font-weight: 700;
}

.dp-day.other-month {
    color: var(--text-faint);
    opacity: 0.4;
}

.dp-day.other-month:hover {
    opacity: 0.7;
}

/* Month / Year cells */
.dp-cell {
    padding: 0.5rem 0.25rem;
    border-radius: 9999px;
    border: none;
    background: transparent;
    color: var(--text-body);
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.1s;
    text-align: center;
}

.dp-cell:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}

.dp-cell.today {
    border: 1.5px solid var(--border-focus);
    font-weight: 700;
}

.dp-cell.selected {
    background: var(--border-focus);
    color: white;
    font-weight: 700;
}

.dp-cell.other-month {
    color: var(--text-faint);
    opacity: 0.4;
}

.dp-cell.other-month:hover {
    opacity: 0.7;
}

.dp-footer {
    display: flex;
    justify-content: space-between;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border-card);
}

.dp-today, .dp-clear {
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    padding: 0.3rem 0.6rem;
    border-radius: 9999px;
    border: none;
    cursor: pointer;
    transition: all 0.1s;
}

.dp-today {
    background: var(--border-focus);
    color: white;
}

.dp-today:hover {
    opacity: 0.85;
}

.dp-clear {
    background: var(--bg-input);
    color: var(--text-muted);
}

.dp-clear:hover {
    color: var(--text-heading);
}

/* Color swatch trigger (replaces native color input) */
.color-swatch {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 0.5rem;
    border: 2px solid var(--border-card);
    cursor: pointer;
    transition: border-color 0.1s, transform 0.1s;
}

.color-swatch:hover {
    border-color: var(--border-focus);
    transform: scale(1.06);
}

/* ── Custom Color Picker ───────────────────────────────────────── */
.color-picker-popup {
    position: fixed;
    z-index: 3000;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 1rem;
    padding: 0.75rem;
    width: 228px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
    animation: dialogIn 0.12s ease-out;
}

.color-picker-popup.hidden { display: none; }

/* Saturation/Lightness canvas */
.cp-map-area {
    position: relative;
    width: 100%;
    height: 140px;
    border-radius: 0.5rem;
    overflow: hidden;
    margin-bottom: 0.5rem;
    cursor: crosshair;
}

.cp-canvas {
    width: 100%;
    height: 100%;
    display: block;
    border-radius: 0.5rem;
}

.cp-cursor {
    position: absolute;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid white;
    box-shadow: 0 0 0 1px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.3);
    pointer-events: none;
    transform: translate(-50%, -50%);
    top: 0;
    left: 0;
}

/* Hue slider */
.cp-hue-row {
    margin-bottom: 0.5rem;
}

.cp-hue-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 12px;
    border-radius: 9999px;
    outline: none;
    cursor: pointer;
    background: linear-gradient(to right,
        hsl(0,100%,50%), hsl(30,100%,50%), hsl(60,100%,50%), hsl(90,100%,50%),
        hsl(120,100%,50%), hsl(150,100%,50%), hsl(180,100%,50%), hsl(210,100%,50%),
        hsl(240,100%,50%), hsl(270,100%,50%), hsl(300,100%,50%), hsl(330,100%,50%), hsl(360,100%,50%)
    );
}

.cp-hue-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: white;
    border: 2px solid rgba(0,0,0,0.2);
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
    cursor: pointer;
}

.cp-hue-slider::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: white;
    border: 2px solid rgba(0,0,0,0.2);
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
    cursor: pointer;
}

/* Presets label */
.cp-presets-label {
    font-size: 0.6rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-faint);
    margin-bottom: 0.3rem;
}

.cp-swatches {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 4px;
}

.cp-swatch {
    width: 100%;
    aspect-ratio: 1;
    border-radius: 0.375rem;
    border: 2px solid transparent;
    cursor: pointer;
    transition: transform 0.1s, border-color 0.1s;
}

.cp-swatch:hover {
    transform: scale(1.15);
    border-color: var(--text-heading);
}

.cp-swatch.selected {
    border-color: var(--border-focus);
    box-shadow: 0 0 0 2px var(--border-focus);
}

.cp-hex-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cp-preview {
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    border: 2px solid var(--border-card);
    flex-shrink: 0;
}

.cp-hex-input {
    flex: 1;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    border-radius: 9999px;
    padding: 0.35rem 0.6rem;
    font-size: 0.75rem;
    font-weight: 600;
    font-family: monospace;
    color: var(--text-input);
    outline: none;
    text-transform: uppercase;
    text-align: center;
}

.cp-hex-input:focus {
    border-color: var(--border-focus);
}

/* ── Priority chips ────────────────────────────────────────────── */
.priority-chip {
    cursor: pointer;
}

.priority-chip span {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.4rem 0.75rem;
    min-width: 4.25rem;
    height: 2.25rem;
    border-radius: 9999px;
    font-size: 0.75rem;
    font-weight: 600;
    border: 1px solid transparent;
    transition: all 0.15s;
}

/* :has() trick used throughout this file for radio/checkbox-styled chips:
   the visible box is a <span> sibling of a visually-hidden native input;
   `.priority-high:has(:checked) span` recolors the label the MOMENT the
   input inside it becomes checked, with no JS class-toggling needed. */
.priority-high span {
    color: var(--chip-text);
    background: var(--chip-bg);
}
.priority-high:has(:checked) span {
    color: var(--priority-high-text);
    background: var(--priority-high-bg);
    border-color: var(--priority-high-border);
}

.priority-med span {
    color: var(--chip-text);
    background: var(--chip-bg);
}
.priority-med:has(:checked) span {
    color: var(--priority-med-text);
    background: var(--priority-med-bg);
    border-color: var(--priority-med-border);
}

.priority-low span {
    color: var(--chip-text);
    background: var(--chip-bg);
}
.priority-low:has(:checked) span {
    color: var(--priority-low-text);
    background: var(--priority-low-bg);
    border-color: var(--priority-low-border);
}


.priority-custom-chip span {
    color: var(--chip-text);
    background: var(--chip-bg);
}
.priority-custom-chip:has(:checked) span {
    color: var(--priority-custom-text);
    background: var(--priority-custom-bg);
    border-color: var(--priority-custom-border);
}

/* ── Buttons ───────────────────────────────────────────────────── */
.btn-primary {
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1rem;
    background: var(--accent, #6366f1);
    color: var(--btn-add-text, white);
    font-size: 0.8rem;
    font-weight: 500;
    border-radius: 0.5rem;
    border: none;
    cursor: pointer;
    transition: background 0.15s;
}

.btn-primary:hover:not(:disabled) {
    background: var(--accent-dark, #4f46e5);
}

.btn-primary:disabled {
    background: #4b5060;
    color: #8890a0;
    cursor: not-allowed;
    opacity: 0.6;
}

.btn-export {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 7rem;
    padding: 0.5rem 1rem;
    background: var(--bg-input);
    color: var(--text-body);
    font-size: 0.8rem;
    font-weight: 500;
    border-radius: 0.5rem;
    border: 1px solid var(--border-input);
    cursor: pointer;
    transition: all 0.15s;
}

.btn-export:hover {
    background: var(--bg-hover);
    border-color: var(--border-focus);
    color: var(--text-heading);
}

.btn-accent {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.45rem 0.85rem;
    background: var(--bg-input);
    color: var(--accent, #6366f1);
    font-size: 0.8rem;
    font-weight: 500;
    border-radius: 0.5rem;
    border: 1px solid var(--border-input);
    cursor: pointer;
    transition: all 0.15s;
}

.btn-accent:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}

/* ============================================================================
   TASK CREATOR — design handoff "Task creator 1d · Composer" (2026-11-02)
   The card that creates tasks: an open title area (the colour dot, the task
   name, the notes line, the keep pill) over a 60px tool tray (Dates,
   Priority, Type, Create task), with a popover under each trigger. Every hex
   in the handoff maps to a theme token of the same value; the few the theme
   lacks are the --tc-* tokens below, at the handoff's exact values. The
   handoff has no dark mode, so dark is the theme's nearest. The handoff sets
   its type with the `font:` shorthand (line-height normal), so the card
   resets the app's 1.5 body line-height. The primary colour is the theme's
   --btn-add-bg (the handoff's #059669 in the default theme), which every
   theme family defines; --accent does not exist outside the default family,
   so it is only ever a fallback source here.
   ============================================================================ */
.composer-pane.tc-card {
    --tc-subtle: #94a3b8;          /* placeholders, the unset flag, hover borders, weekday labels */
    --tc-surface: #f8fafc;         /* the tray, the keep pill at rest, the selected list item */
    --tc-hover: #f1f5f9;           /* hover fill of tray buttons, list items and calendar arrows */
    --tc-open: #e2e8f0;            /* a tray button while its popover is open */
    --tc-rule-soft: #f1f5f9;       /* the rule above the custom priority block */
    --tc-soft: var(--accent-soft, color-mix(in srgb, var(--btn-add-bg) 10%, var(--bg-card)));
    --tc-soft-text: var(--accent-dark, var(--btn-add-bg));
    --tc-soft-border: #a7f3d0;     /* the keep pill's border while on */
    --tc-primary-hover: var(--accent-dark, color-mix(in srgb, var(--btn-add-bg) 88%, #000));
    --tc-ring: rgba(15, 23, 42, 0.08);
    --tc-card-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 4px 16px rgba(15, 23, 42, 0.05);
    --tc-pop-shadow: 0 2px 6px rgba(15, 23, 42, 0.06), 0 12px 32px rgba(15, 23, 42, 0.12);
    --tc-knob-shadow: 0 1px 2px rgba(15, 23, 42, 0.25);
    /* The card's height follows the object browser (the handoff's frame rule,
       re-delivered 2026-11-03): it stretches to the row, 188px at least, so a
       dragged-down row grows both cards, top and bottom edges matching. The
       title area (.tc-top, flex 1) absorbs the extra height; the tray stays
       60px at the bottom. */
    flex: 0 0 auto;
    width: 626px;
    max-width: 100%;
    min-height: 188px;
    box-sizing: border-box;
    padding: 0;
    border: 1px solid var(--border-card);
    border-radius: 16px;
    background: var(--bg-card);
    box-shadow: var(--tc-card-shadow);
    display: flex;
    flex-direction: column;
    line-height: normal;
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
    color: var(--text-body);
}
[data-theme$="-dark"] .composer-pane.tc-card {
    --tc-subtle: var(--text-muted);
    --tc-surface: color-mix(in srgb, var(--bg-input) 45%, var(--bg-card));
    --tc-hover: var(--bg-hover);
    --tc-open: var(--bg-input);
    --tc-rule-soft: var(--border-card);
    --tc-soft-border: var(--btn-add-bg);
    --tc-ring: rgba(255, 255, 255, 0.12);
    --tc-card-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.35);
    --tc-pop-shadow: 0 2px 6px rgba(0, 0, 0, 0.4), 0 12px 32px rgba(0, 0, 0, 0.5);
    --tc-knob-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
/* The section heading stays for screen readers only. */
.tc-card .section-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ── Top area: dot, name + notes, the keep pill ── */
.tc-top {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 22px 20px 0;
}
.tc-host { position: relative; display: flex; }
.tc-host-color { flex: none; margin-top: 3px; }
.tc-dot {
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: 0 0 0 3px var(--bg-card), 0 0 0 4px var(--border-card);
}
.tc-dot:focus-visible { outline: 2px solid var(--text-heading); outline-offset: 5px; }
.tc-fields {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.tc-card .tc-name,
.tc-card .tc-notes {
    display: block;
    width: 100%;
    min-width: 0;
    min-height: 0;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    outline: none;
    box-shadow: none;
    font-family: inherit;
    line-height: normal;
}
.tc-name { font-size: 27.4px; font-weight: 600; color: var(--text-heading); }
.tc-notes { font-size: 16.2px; font-weight: 400; color: var(--text-body); }
.tc-card .tc-name::placeholder,
.tc-card .tc-notes::placeholder { color: var(--tc-subtle); opacity: 1; }
.tc-keep {
    flex: none;
    margin-top: 2px;
    height: 30px;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 6px 0 10px;
    border: 1px solid var(--border-card);
    border-radius: 999px;
    background: var(--tc-surface);
    color: var(--text-muted);
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 500;
    line-height: normal;
    white-space: nowrap;
    cursor: pointer;
    transition: background .15s, border-color .15s, color .15s;
}
.tc-keep:hover { border-color: var(--tc-subtle); }
.tc-keep:focus-visible { outline: 2px solid var(--text-heading); outline-offset: 2px; }
.tc-keep-pin { flex: none; color: var(--tc-subtle); fill: none; }
.tc-keep[aria-checked="true"] {
    background: var(--tc-soft);
    border-color: var(--tc-soft-border);
    color: var(--tc-soft-text);
}
.tc-keep[aria-checked="true"] .tc-keep-pin { color: var(--text-heading); fill: currentColor; }
.tc-switch {
    position: relative;
    flex: none;
    width: 26px;
    height: 16px;
    border-radius: 999px;
    background: var(--border-input);
    transition: background .15s;
}
.tc-knob {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
    box-shadow: var(--tc-knob-shadow);
    transition: left .15s;
}
.tc-keep[aria-checked="true"] .tc-switch { background: var(--btn-add-bg); }
.tc-keep[aria-checked="true"] .tc-knob { left: 12px; }

/* ── The tray ── */
.tc-tray {
    flex: none;
    height: 60px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0 12px;
    background: var(--tc-surface);
    border-top: 1px solid var(--border-card);
    border-radius: 0 0 15px 15px;
}
.tc-tray-btn {
    height: 36px;
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 0 10px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 500;
    line-height: normal;
    color: var(--text-body);
    white-space: nowrap;
    cursor: pointer;
}
.tc-tray-btn:hover { background: var(--tc-hover); }
.tc-tray-btn[aria-expanded="true"] { background: var(--tc-open); }
.tc-tray-btn:focus-visible { outline: 2px solid var(--text-heading); outline-offset: 1px; }
.tc-tray-btn svg { flex: none; color: var(--text-muted); }
/* The flag is subtle until a priority is set; app.js then colours it with
   that priority's text token. */
.tc-tray-btn .tc-prio-icon { color: var(--tc-subtle); }
.tc-divider { flex: none; width: 1px; height: 18px; background: var(--border-card); }
.tc-spacer { flex: 1 1 auto; }
.tc-create {
    flex: none;
    height: 38px;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 18px 0 14px;
    border: 0;
    border-radius: 10px;
    background: var(--btn-add-bg);
    color: var(--btn-add-text);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    line-height: normal;
    white-space: nowrap;
    cursor: pointer;
    box-shadow: 0 1px 2px color-mix(in srgb, var(--btn-add-bg) 30%, transparent);
}
.tc-create:hover { background: var(--tc-primary-hover); }
.tc-create:focus-visible { outline: 2px solid var(--btn-add-bg); outline-offset: 2px; }

/* ── Popovers ── */
.tc-pop {
    position: absolute;
    top: calc(100% + 10px);
    left: 0;
    z-index: 200;
    box-sizing: border-box;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
    box-shadow: var(--tc-pop-shadow);
    color: var(--text-body);
}
.tc-pop.hidden { display: none; }
.tc-host-color .tc-pop { left: -8px; }
.tc-micro {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-muted);
}
/* Colour: ten swatches and the custom row */
.tc-pop-color { width: 230px; padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.tc-swatches { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; }
.tc-swatch {
    width: 100%;
    aspect-ratio: 1;
    padding: 0;
    border: 0;
    border-radius: 8px;
    cursor: pointer;
}
.tc-swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 4px var(--tc-swatch); }
.tc-swatch:focus-visible { outline: 2px solid var(--text-heading); outline-offset: 2px; }
.tc-custom {
    position: relative;
    height: 34px;
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 -4px -4px;
    padding: 8px 8px 0;
    border: 0;
    border-top: 1px solid var(--border-card);
    border-radius: 0;
    box-sizing: content-box;
    background: transparent;
    cursor: pointer;
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    line-height: normal;
    color: var(--text-heading);
    text-align: left;
}
.tc-custom:focus-visible { outline: 2px solid var(--text-heading); outline-offset: -2px; border-radius: 8px; }
.tc-custom-tile {
    flex: none;
    width: 22px;
    height: 22px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    border: 1.5px dashed var(--border-input);
    background: var(--bg-card);
    color: var(--text-muted);
}
.tc-custom.is-custom .tc-custom-tile {
    border: 0;
    background: var(--tc-custom);
    box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 4px var(--tc-custom);
    color: transparent;
}
.tc-custom-label { flex: 1; }
.tc-custom-hex {
    font-family: ui-monospace, monospace;
    font-size: 11.5px;
    color: var(--tc-subtle);
    text-transform: uppercase;
}
/* Lists: priority and type */
.tc-pop-list { width: 200px; padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.tc-micro-list { padding: 6px 8px 4px; }
.tc-item {
    height: 34px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 8px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    line-height: normal;
    color: var(--text-heading);
    cursor: pointer;
    text-align: left;
}
.tc-item:hover,
.tc-item:focus-visible { background: var(--tc-hover); outline: none; }
.tc-item[aria-selected="true"] { background: var(--tc-surface); }
.tc-item.hidden { display: none; }
.tc-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tc-item-hint { font-size: 11.5px; font-weight: 400; color: var(--tc-subtle); }
.tc-check { flex: none; color: var(--btn-add-bg); opacity: 0; }
.tc-item[aria-selected="true"] .tc-check { opacity: 1; }
/* The badge takes its colours from the priority palette (--priority-*), as
   the list and the chart do; the shape is the handoff's. */
.tc-pop .tc-badge {
    flex: none;
    min-width: 20px;
    height: 20px;
    padding: 0 4px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 6px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .03em;
}
.tc-glyph { flex: none; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; }
.tc-glyph-bar { width: 16px; height: 7px; border-radius: 3px; background: var(--text-heading); }
.tc-glyph-point { width: 10px; height: 10px; border-radius: 2px; background: var(--text-heading); transform: rotate(45deg); }
.tc-glyph-group { width: 16px; height: 4px; border-radius: 1px; background: var(--text-heading); }
.tc-rule { height: 1px; margin: 4px 2px; background: var(--tc-rule-soft); }
.tc-custom-block { display: flex; flex-direction: column; gap: 4px; padding: 4px 8px 6px; }
.tc-custom-title { font-size: 11px; font-weight: 500; line-height: normal; color: var(--text-muted); }
.tc-pop .tc-custom-input {
    height: 28px;
    min-height: 0;
    box-sizing: border-box;
    width: 100%;
    padding: 0 8px;
    border: 1px solid var(--border-card);
    border-radius: 7px;
    outline: none;
    background: var(--bg-card);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 400;
    line-height: normal;
    color: var(--text-heading);
}
.tc-pop .tc-custom-input:focus { border-color: var(--text-heading); box-shadow: 0 0 0 3px var(--tc-ring); }
.tc-pop .tc-custom-input::placeholder { color: var(--tc-subtle); opacity: 1; }
.tc-custom-hint { font-size: 11px; color: var(--tc-subtle); }
/* Dates: the range calendar */
.tc-pop-dates { width: 298px; padding: 12px; display: flex; flex-direction: column; gap: 8px; }
.tc-cal-head { display: flex; align-items: center; justify-content: space-between; }
.tc-cal-nav {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.tc-cal-nav:hover { background: var(--tc-hover); }
.tc-cal-nav:focus-visible { outline: 2px solid var(--text-heading); outline-offset: 1px; }
.tc-cal-title { font-size: 13px; font-weight: 600; color: var(--text-heading); }
.tc-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); row-gap: 2px; }
.tc-cal-dow {
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--tc-subtle);
}
.tc-cal-day {
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 400;
    line-height: normal;
    color: var(--text-heading);
    cursor: pointer;
}
.tc-cal-day:focus-visible { outline: 2px solid var(--text-heading); outline-offset: -2px; }
.tc-cal-day.is-today { font-weight: 600; }
.tc-cal-day.is-in { background: var(--tc-soft); color: var(--tc-soft-text); border-radius: 0; }
.tc-cal-day.is-edge { background: var(--btn-add-bg); color: var(--btn-add-text); font-weight: 600; }
.tc-cal-blank { height: 32px; }
.tc-cal-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-top: 8px;
    border-top: 1px solid var(--border-card);
}
.tc-cal-status { font-size: 11.5px; color: var(--text-muted); }
.tc-cal-clear {
    padding: 4px 6px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    font-family: inherit;
    font-size: 12px;
    font-weight: 500;
    line-height: normal;
    color: var(--text-muted);
    cursor: pointer;
}
.tc-cal-clear:hover { background: var(--tc-hover); }

/* The handoff is a 626px card. Narrower than that the card fills its row,
   the keep pill drops under the name and notes, and on a phone the tray
   keeps its icons and drops the labels. Not in the handoff, which stops at
   626. */
@media (max-width: 580px) {
    .tc-top { flex-wrap: wrap; padding-bottom: 14px; }
    .tc-fields { flex-basis: calc(100% - 36px); }
    .tc-keep { order: 3; margin-top: 0; }
}
@media (max-width: 440px) {
    .tc-tray { gap: 2px; padding: 0 8px; }
    .tc-tray-btn { padding: 0 8px; }
    .tc-tray-btn .tc-tray-label { display: none; }
}

    45%  { transform: scale(1.25) rotate(180deg); }
    75%  { transform: scale(0.96) rotate(300deg); }
    100% { transform: scale(1) rotate(360deg); }
}

.ob-empty {
    text-align: center;
    color: var(--text-faint);
    font-size: 0.75rem;
    font-weight: 400;
    padding: 1.5rem 0;
}

.task-chip {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
    padding: 0.75rem 1rem;
    border-radius: 0.65rem;
    min-width: 5.5rem;
    max-width: 8rem;
    transition: background 0.1s, box-shadow 0.15s;
}

.task-chip:hover {
    background: var(--bg-hover);
    box-shadow: 0 0 0 1px var(--border-card), 0 2px 8px rgba(255, 255, 255, 0.08);
}

[data-theme$="-dark"] .task-chip:hover {
    box-shadow: 0 0 0 1px var(--border-card), 0 2px 12px rgba(255, 255, 255, 0.06);
}

/* Delete X on hover */
.task-chip-delete {
    position: absolute;
    top: 0.35rem;
    left: 0.35rem;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: 50%;
    background: var(--delete-hover-bg);
    color: var(--delete-hover-color);
    border: 1px solid var(--delete-hover-color);
    font-size: 0.65rem;
    font-weight: 700;
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: transform 0.1s;
    z-index: 5;
    line-height: 1;
}

.task-chip:hover .task-chip-delete {
    display: flex;
}

.task-chip-delete:hover {
    transform: scale(1.2);
}

.task-chip-name {
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--text-body);
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    cursor: pointer;
    border-bottom: 1px dashed transparent;
    transition: border-color 0.1s;
}

.task-chip-name:hover {
    border-color: var(--text-muted);
}

.task-chip-name-input {
    /* Inline rename field — occupies the SAME box as .task-row-name so the row
       never changes height and the duration/priority chips don't shift. The
       "editing" affordance is an inset ring + subtle fill that add no box size
       (an inset shadow isn't clipped by the row's overflow:hidden). */
    font-family: inherit;
    font-size: 0.72rem;
    font-weight: 500;
    line-height: 1.5;
    color: var(--text-body);
    box-sizing: border-box;
    border: 0;
    padding: 0 0.25rem;
    margin: 0 -0.25rem;        /* cancel padding so text x-pos & neighbours hold */
    background: var(--bg-input);
    border-radius: 0.25rem;
    box-shadow: inset 0 0 0 1.5px var(--border-focus);
    outline: none;
    flex: 0 1 auto;
    min-width: 2rem;
    max-width: 100%;
    transition: box-shadow 0.12s ease, background 0.12s ease;
}

.task-chip-priority {
    font-size: 0.55rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.1rem 0.4rem;
    border-radius: 9999px;
    cursor: pointer;
    position: relative;
    transition: opacity 0.1s;
}

.task-chip-priority:hover {
    opacity: 0.8;
}

/* Priority popup menu */
.priority-popup {
    position: fixed;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.75rem;
    padding: 0.35rem;
    display: flex;
    gap: 0.25rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
    z-index: 1500;
    animation: popIn 0.12s ease-out;
}

.priority-popup-option {
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    padding: 0.3rem 0.6rem;
    border-radius: 9999px;
    cursor: pointer;
    border: 1px solid transparent;
    white-space: nowrap;
    transition: all 0.1s;
}

.priority-popup-option:hover {
    opacity: 0.85;
    transform: scale(1.05);
}

.priority-popup-option.active {
    outline: 2px solid var(--border-focus);
    outline-offset: 1px;
}

.priority-custom-input {
    font-size: 0.6rem;
    font-weight: 600;
    width: 4rem;
    padding: 0.25rem 0.4rem;
    border-radius: 9999px;
    border: 1px solid var(--border-focus);
    background: var(--bg-input);
    color: var(--text-input);
    outline: none;
    text-transform: uppercase;
}

@keyframes popIn {
    from { transform: scale(0.9); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
}

/* Create Task dialog — sibling of Edit Task dialog. Same DOM,
   hide the Delete button and right-align the lone primary (Create) button. */
.dialog.dialog-create-mode #dialogDelete {
    display: none;
}
.dialog.dialog-create-mode .dialog-footer {
    justify-content: flex-end;
}

/* Chart right-click context menu — opens at the cursor when the
   user right-clicks empty chart canvas. Mirrors .priority-popup styling. */
.chart-context-menu {
    position: fixed;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.6rem;
    padding: 0.3rem;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 12rem;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
    z-index: 1500;
    animation: popIn 0.12s ease-out;
    transform-origin: top left;
}

.chart-context-menu-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.5rem 0.65rem;
    border: 1px solid transparent;
    border-radius: 0.45rem;
    background: transparent;
    color: var(--text-heading);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    text-align: left;
    width: 100%;
    transition: background 0.1s, color 0.1s;
}

.chart-context-menu-item:hover,
.chart-context-menu-item:focus-visible {
    background: var(--bg-hover);
    outline: none;
}

.chart-context-menu-item.danger {
    color: var(--priority-high-text);
}

.chart-context-menu-item.danger .chart-context-menu-icon {
    color: var(--priority-high-text);
}

.chart-context-menu-item.danger:hover,
.chart-context-menu-item.danger:focus-visible {
    background: var(--priority-high-bg);
    color: var(--priority-high-text);
}

.chart-context-menu-item.danger:hover .chart-context-menu-icon,
.chart-context-menu-item.danger:focus-visible .chart-context-menu-icon {
    color: var(--priority-high-text);
}

.chart-context-menu-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    flex: 0 0 auto;
    color: var(--text-muted);
}

.chart-context-menu-item:hover .chart-context-menu-icon,
.chart-context-menu-item:focus-visible .chart-context-menu-icon {
    color: var(--text-heading);
}

.chart-context-menu-icon svg {
    width: 100%;
    height: 100%;
}

.chart-context-menu-label {
    flex: 1 1 auto;
    line-height: 1.1;
}

.task-priority-high { color: var(--priority-high-text); background: var(--priority-high-bg); }
.task-priority-medium { color: var(--priority-med-text); background: var(--priority-med-bg); }
.task-priority-low { color: var(--priority-low-text); background: var(--priority-low-bg); }
.task-priority-custom { color: var(--priority-custom-text); background: var(--priority-custom-bg); border: 1px solid var(--priority-custom-border); }

/* ── Checkbox on chips ─────────────────────────────────────────── */
.task-chip-checkbox {
    position: absolute;
    top: 0.35rem;
    right: 0.35rem;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 0.25rem;
    border: 1.5px solid var(--border-input);
    background: var(--bg-card);
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
    font-size: 0.6rem;
    color: transparent;
    transition: all 0.1s;
    z-index: 5;
}

.task-chip:hover .task-chip-checkbox,
.task-chip-checkbox.always-show {
    display: flex;
}

.task-chip-checkbox.checked {
    background: var(--border-focus);
    border-color: var(--border-focus);
    color: white;
}

.task-chip.selected {
    outline: 2px solid var(--border-focus);
    outline-offset: -1px;
}

/* Delete preview list */
.delete-preview-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
    max-height: 10rem;
    overflow-y: auto;
    padding: 0.25rem 0;
}

.delete-preview-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    min-width: 3.5rem;
}

.delete-preview-item .mini-avatar {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
}

.delete-preview-item .mini-name {
    font-size: 0.65rem;
    font-weight: 500;
    color: var(--text-body);
    max-width: 5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.delete-preview-item .mini-prio {
    font-size: 0.5rem;
    font-weight: 600;
    text-transform: uppercase;
    padding: 0.1rem 0.35rem;
    border-radius: 9999px;
}

/* ── Icon buttons (dialog close, etc.) ─────────────────────────── */
.icon-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.375rem;
    border: none;
    cursor: pointer;
    font-size: 1rem;
    transition: all 0.1s;
    flex-shrink: 0;
}

.icon-btn-edit {
    background: transparent;
    color: var(--icon-color);
}
.icon-btn-edit:hover {
    background: var(--icon-hover-bg);
    color: var(--icon-hover-color);
}

.icon-btn-delete {
    background: transparent;
    color: var(--icon-color);
}
.icon-btn-delete:hover {
    background: var(--delete-hover-bg);
    color: var(--delete-hover-color);
}

/* ── Edit input (inline) ───────────────────────────────────────── */
.edit-input {
    background: var(--bg-input);
    border: 1px solid var(--border-focus);
    border-radius: 0.375rem;
    padding: 0.15rem 0.4rem;
    font-size: 0.85rem;
    color: var(--text-input);
    outline: none;
    box-sizing: border-box;
}

/* Inline rename input for side-panel task labels.
   Blends with the SVG label so the visual transition from
   "static label" to "editable" is minimal: no border, no
   different background, same font weight / colour / right-edge
   alignment. A thin focus underline is the only edit-mode cue. */
.edit-input.edit-input-inline-label {
    background: transparent;
    border: none;
    border-bottom: 1px solid transparent;
    border-radius: 0;
    padding: 0 10px 0 0;       /* right padding = label x-anchor offset (-10) */
    /* block, not inline: an inline-level input inside the foreignObject sits
       on the parent line-box baseline, which pushes it below the row top
       (~5px at Half size — a third of the 19px row). */
    display: block;
    width: 100%;
    height: 100%;
    color: var(--text-heading);
    text-align: right;
    line-height: 1;
    font-family: inherit;
    /* font-size + font-weight come from inline style set by JS */
    transition: border-bottom-color 0.12s;
}

.edit-input.edit-input-inline-label:focus {
    border-bottom-color: var(--border-focus);
}

/* foreignObject host for the inline-rename input — clear default margins
   so the input sits flush at the row baseline. */
foreignObject.inline-rename-fo {
    overflow: visible;
}

/* ── Chart ─────────────────────────────────────────────────────── */
/* Despite the "Chart" heading two lines above, this whole cluster below
   is the chart's TOOLBAR strip (date-range fields, zoom slider, tools
   grid, project picker) -- not the chart drawing itself. The SVG chart
   (bars/axis/grid) is a separate section much further down this file;
   search for "Gantt chart canvas". */
.chart-header {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 0.25rem;
    margin-bottom: 0.4rem;
    flex-wrap: wrap;
    gap: 0.4rem;
    flex-shrink: 0;
    padding: 0 15px;
}

.viewport-controls {
    display: flex;
    align-items: center;
    justify-content: center;   /* keep the control groups (and the collapsed pills) centered at every width */
    flex-wrap: wrap;
    /* Groups are separated by this gap alone now (dividers removed below).
       1rem reads cleanly and still clears a 1920px row with ~36px to spare,
       since dropping the 3 divider items also dropped 3 flex gaps. Row-gap
       stays small for the rare wrap; .vc-compact-* is the narrow fallback. */
    column-gap: 1rem;
    row-gap: 0.2rem;
    min-width: 0;
    max-width: 100%;
}

.viewport-controls .label {
    font-size: 0.6rem;
    white-space: nowrap;
}

.viewport-field {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
    flex: 0 0 9rem;
}

.viewport-field .viewport-date {
    flex: initial;
    width: 100%;
    height: 1.85rem;
    padding: 0.2rem 0.5rem;
}

.viewport-field-label {
    font-size: 0.6rem;
    font-weight: 600;
    color: var(--text-muted);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    text-align: center;
    margin: 0;
}

.viewport-fields-block {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.viewport-fields-row {
    display: flex;
    gap: 0.4rem;
}

.viewport-span-row {
    width: 100%;
    text-align: center;
}

.viewport-span-row .viewport-span {
    display: inline-block;
    text-align: center;
}

.viewport-date {
    flex: 0 1 9rem;
    min-width: 6.5rem;
    font-size: 0.75rem;
    padding: 0.3rem 0.5rem;
}

.viewport-divider {
    /* Removed per request — the header groups are now separated by the
       .viewport-controls column-gap alone. Kept in the DOM (collab code
       references #collabDivider) but not rendered. Flip back to
       inline-block to restore the vertical rules. */
    display: none;
    width: 1px;
    align-self: stretch;
    min-height: 2.4rem;
    background: var(--text-faint);
    opacity: 0.25;
    margin: 0;
    user-select: none;
}

.viewport-apply-stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.25rem;
}

.viewport-apply-stack .viewport-apply,
.viewport-apply-stack .btn-zoom-fit {
    width: 100%;
    justify-content: center;
}

.viewport-tools-grid {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

/* ── Responsive tools overflow ──────────────────────────────
   On a normal 1080p+ screen the 7 tool tiles sit inline. When the control
   row genuinely can't hold them (narrow/scaled windows, collaborators
   present, very long date spans) JS adds .vc-compact-tools and the tiles
   collapse into this "Tools" pill + popover instead of wrapping to a 2nd
   row. Phones keep their own grid layout (JS skips coarse ≤820px). */
/* Note: var(--toolbar-pill-h, 48px) below -- and in the other collapsed
   pills that share this recipe (.range-menu-btn, .zoom-menu-btn,
   .project-menu-btn, .viewport-range-bar, .zoom-slider-inline) -- is
   never actually DEFINED anywhere in this codebase (checked: no :root, no
   inline style, no JS setProperty). Every one of them silently runs on
   the 48px fallback. Treat it as a shared height convention, but know
   that "changing the variable" really means editing every fallback. */
.tools-menu-btn {
    display: none;
    align-items: center;
    gap: 0.4rem;
    padding: 5px 12px;
    min-height: var(--toolbar-pill-h, 48px);
    border-radius: 999px;
    border: 1px solid rgba(15, 23, 42, 0.08);
    background: rgba(15, 23, 42, 0.04);
    color: var(--text-body);
    font: inherit;
    font-size: 0.74rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.12s;
}
[data-theme$="-dark"] .tools-menu-btn {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
.tools-menu-btn:hover { background: var(--bg-hover, rgba(15, 23, 42, 0.07)); }
.tools-menu-btn .tmb-ico { width: 15px; height: 15px; color: var(--accent); flex: none; }
.tools-menu-btn .tmb-caret { width: 12px; height: 12px; opacity: 0.6; transition: transform 0.15s; flex: none; }
.viewport-tools-stack.tools-open .tools-menu-btn .tmb-caret { transform: rotate(180deg); }

.viewport-controls.vc-compact-tools .tools-menu-btn { display: inline-flex; }
.viewport-controls.vc-compact-tools .viewport-tools-stack { position: relative; }
/* ── Tools popover — modern single-column toggle-row list (#2) ──────────
   7 toggles is prime (every multi-column grid orphans one — the old 3-col
   layout left "Progress" alone on row 3). The compact popover is now a
   settings-style list: [icon chip] · [label →] · [real pill switch]. Orphan-
   free, on/off reads instantly, native emerald. Scoped to .vc-compact-tools
   so the inline header row and the phone grid are untouched. */
.viewport-controls.vc-compact-tools .viewport-tools-grid {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    display: none;
    flex-direction: column;
    gap: 2px;
    width: 234px;
    padding: 6px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 14px;
    box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.45), 0 2px 8px -4px rgba(0, 0, 0, 0.22);
    animation: dialogIn 0.14s ease-out;
}
.viewport-controls.vc-compact-tools .viewport-tools-stack.tools-open .viewport-tools-grid { display: flex; }

/* Slim panel header (hidden everywhere except inside the compact popover). */
.tools-pop-header { display: none; }
.viewport-controls.vc-compact-tools .viewport-tools-grid .tools-pop-header {
    display: flex;
    align-items: center;
    padding: 3px 8px 7px;
    margin-bottom: 2px;
    border-bottom: 1px solid var(--border-card);
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
}

/* Each tile → a full-width settings row */
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile {
    flex: 0 0 auto;
    width: 100%;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    min-width: 0;
    min-height: 38px;
    padding: 6px 8px;
    border-radius: 9px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-body);
}
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile:hover {
    background: var(--bg-hover);
    border-color: transparent;
    color: var(--text-heading);
}
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile.active {
    background: var(--accent-soft);
    border-color: transparent;
}
/* Icon chip — larger; fills emerald when active via the base .tool-tile.active rule */
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile-ico {
    width: 26px;
    height: 26px;
    border-radius: 8px;
    flex: none;
}
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile-ico svg {
    width: 15px;
    height: 15px;
}
/* Label — left-aligned, fills the row, ellipsis for long localized strings */
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile-name {
    flex: 1 1 auto;
    min-width: 0;
    text-align: left;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Switch — a real right-aligned pill toggle (was a 5px sliver) */
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile-track {
    flex: none;
    width: 30px;
    height: 17px;
    border-radius: 999px;
    background: var(--toggle-bg);
}
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile-knob {
    width: 12px;
    height: 12px;
    left: 2px;
    background: var(--toggle-icon);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile.active .tool-tile-track {
    background: var(--accent);
}
.viewport-controls.vc-compact-tools .viewport-tools-grid .tool-tile.active .tool-tile-knob {
    left: calc(100% - 12px - 2px);
    background: #fff;
}
/* Extra room while compact: drop the parenthetical day-count and shrink the
   zoom slider so the popover threshold reaches down to ~1450px. */
.viewport-controls.vc-compact-tools .vp-span-days { display: none; }
.viewport-controls.vc-compact-tools .zoom-slider-inline .zoom-slider { width: 5rem; }
@media (prefers-reduced-motion: reduce) {
    .viewport-controls.vc-compact-tools .viewport-tools-grid { animation: none; }
    .tools-menu-btn .tmb-caret { transition: none; }
}
[data-a11y-motion="reduce"] .viewport-controls.vc-compact-tools .viewport-tools-grid { animation: none; }
[data-a11y-motion="reduce"] .tools-menu-btn .tmb-caret { transition: none; }

/* Stage 2: the viewport date-range bar collapses into a "Timeline" pill +
   popover when even the tools-collapsed row can't fit (very narrow/scaled
   windows). Mirrors the tools popover. The floated bar keeps its own pill. */
.range-menu-btn {
    display: none;
    align-items: center;
    gap: 0.4rem;
    padding: 5px 12px;
    min-height: var(--toolbar-pill-h, 48px);
    border-radius: 999px;
    border: 1px solid rgba(15, 23, 42, 0.08);
    background: rgba(15, 23, 42, 0.04);
    color: var(--text-body);
    font: inherit;
    font-size: 0.74rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.12s;
}
[data-theme$="-dark"] .range-menu-btn {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
.range-menu-btn:hover { background: var(--bg-hover, rgba(15, 23, 42, 0.07)); }
.range-menu-btn .rmb-ico { width: 15px; height: 15px; color: var(--accent); flex: none; }
.range-menu-btn .rmb-caret { width: 12px; height: 12px; opacity: 0.6; transition: transform 0.15s; flex: none; }
.viewport-range-stack.range-open .range-menu-btn .rmb-caret { transform: rotate(180deg); }

.viewport-controls.vc-compact-range .range-menu-btn { display: inline-flex; }
.viewport-controls.vc-compact-range .viewport-range-stack { position: relative; }
.viewport-controls.vc-compact-range .viewport-range-bar {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    display: none;
    /* Opaque popover surface (fixes #1: the floated bar was inheriting the base
       translucent rgba(...,0.04) fill, so the chart's TODAY line + gridlines
       bled through). --bg-card is theme-aware and this 3-class selector outranks
       the dark base override, so one declaration covers light + every dark +
       high-contrast. */
    background: var(--bg-card);
    border-color: var(--border-card);
    box-shadow: 0 14px 32px -10px rgba(0, 0, 0, 0.4);
    animation: dialogIn 0.14s ease-out;
    /* Very-narrow fallback: if the whole bar is wider than the window, wrap its
       fields onto a second row (centered) instead of running off-screen. The
       max-width caps it to the viewport so anchorPanel keeps it fully on-screen;
       a squarer radius reads better than a 999px pill once it's two rows tall. */
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 2px;
    width: max-content;              /* one row at its natural 608px when it fits… */
    max-width: calc(100vw - 16px);   /* …capped to the window, so it wraps only when it must */
    border-radius: 18px;
}
.viewport-controls.vc-compact-range .viewport-range-stack.range-open .viewport-range-bar { display: flex; }
@media (prefers-reduced-motion: reduce) {
    .viewport-controls.vc-compact-range .viewport-range-bar { animation: none; }
    .range-menu-btn .rmb-caret { transition: none; }
}
[data-a11y-motion="reduce"] .viewport-controls.vc-compact-range .viewport-range-bar { animation: none; }
[data-a11y-motion="reduce"] .range-menu-btn .rmb-caret { transition: none; }

/* ── Stage 3 (#4): the manual-zoom slider collapses into a magnifier + live-%
   pill + popover. Mirrors the Tools / Timeline stages; the floated slider stays
   an OPAQUE pill (var(--bg-card)) so it reads over busy chart content. */
.zoom-menu-btn {
    display: none;
    align-items: center;
    gap: 0.4rem;
    padding: 5px 12px;
    min-height: var(--toolbar-pill-h, 48px);
    border-radius: 999px;
    border: 1px solid rgba(15, 23, 42, 0.08);
    background: rgba(15, 23, 42, 0.04);
    color: var(--text-body);
    font: inherit;
    font-size: 0.74rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.12s;
}
[data-theme$="-dark"] .zoom-menu-btn {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
.zoom-menu-btn:hover { background: var(--bg-hover, rgba(15, 23, 42, 0.07)); }
.zoom-menu-btn .zmb-ico { width: 15px; height: 15px; color: var(--accent); flex: none; }
.zoom-menu-btn .zmb-pct { min-width: 2.4rem; text-align: center; font-variant-numeric: tabular-nums; }
.zoom-menu-btn .zmb-caret { width: 12px; height: 12px; opacity: 0.6; transition: transform 0.15s; flex: none; }
.viewport-zoom-stack.zoom-open .zoom-menu-btn .zmb-caret { transform: rotate(180deg); }

.viewport-controls.vc-compact-zoom .zoom-menu-btn { display: inline-flex; }
.viewport-controls.vc-compact-zoom .viewport-zoom-stack { position: relative; }
.viewport-controls.vc-compact-zoom .zoom-slider-inline {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    display: none;
    background: var(--bg-card);          /* opaque — overrides the translucent inline fill */
    border-color: var(--border-card);
    box-shadow: 0 14px 32px -10px rgba(0, 0, 0, 0.4);
    animation: dialogIn 0.14s ease-out;
}
.viewport-controls.vc-compact-zoom .viewport-zoom-stack.zoom-open .zoom-slider-inline { display: flex; }
/* comfortable slider in the popover — undo the stage-1 (.vc-compact-tools) 5rem squeeze */
.viewport-controls.vc-compact-zoom .zoom-slider-inline .zoom-slider { width: 7rem; }
@media (prefers-reduced-motion: reduce) {
    .viewport-controls.vc-compact-zoom .zoom-slider-inline { animation: none; }
    .zoom-menu-btn .zmb-caret { transition: none; }
}
[data-a11y-motion="reduce"] .viewport-controls.vc-compact-zoom .zoom-slider-inline { animation: none; }
[data-a11y-motion="reduce"] .zoom-menu-btn .zmb-caret { transition: none; }

/* ── Stage 4 (#3): the PROJECT bar collapses into a "Project" pill + popover,
   LAST of the four groups so the active chart name + Synced status stay visible
   longest. The collapsed trigger keeps the live chart name. Opaque card panel
   (it can wrap the name + 3 buttons + Synced to 2 rows). */
.project-menu-btn {
    display: none;
    align-items: center;
    gap: 0.4rem;
    padding: 5px 12px;
    min-height: var(--toolbar-pill-h, 48px);
    border-radius: 999px;
    border: 1px solid rgba(15, 23, 42, 0.08);
    background: rgba(15, 23, 42, 0.04);
    color: var(--text-body);
    font: inherit;
    font-size: 0.74rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.12s;
    max-width: 16rem;
}
[data-theme$="-dark"] .project-menu-btn {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
.project-menu-btn:hover { background: var(--bg-hover, rgba(15, 23, 42, 0.07)); }
.project-menu-btn .pmb-ico { width: 15px; height: 15px; color: var(--accent); flex: none; }
.project-menu-btn .pmb-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project-menu-btn .pmb-caret { width: 12px; height: 12px; opacity: 0.6; transition: transform 0.15s; flex: none; }
.viewport-project-stack.project-open .project-menu-btn .pmb-caret { transform: rotate(180deg); }

.viewport-controls.vc-compact-project .project-menu-btn { display: inline-flex; }
.viewport-controls.vc-compact-project .viewport-project-stack { position: relative; }
.viewport-controls.vc-compact-project .project-picker-bar {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;                 /* leftmost group → anchor left (anchorPanel confirms via r.left<320) */
    z-index: 60;
    display: none;
    /* SINGLE COLUMN — one control per row, top to bottom, same list shape as the
       Tools popover above. A wrapping ROW dealt the six items into two ragged
       columns (name beside My Charts, Synced beside Share), which read as a grid
       instead of a menu. Width hugs the longest row (max-content) instead of
       being fixed: the rows are short — "Synced", "Share" — so any fixed width
       wide enough for the worst case left most of the panel empty. min/max keep
       it from becoming a sliver or a slab. */
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 2px;
    width: max-content;
    min-width: 132px;
    max-width: min(92vw, 260px);
    padding: 8px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 14px;
    box-shadow: 0 14px 32px -10px rgba(0, 0, 0, 0.4);
    animation: dialogIn 0.14s ease-out;
}
.viewport-controls.vc-compact-project .viewport-project-stack.project-open .project-picker-bar { display: flex; }
/* The chart name and My Charts live in their own nested flex row, so the column
   above only stacks the pair as a unit — stack their insides too. */
.viewport-controls.vc-compact-project .project-picker-bar .my-charts-bar {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
}
/* The "(was Loaded Projects)" breadcrumb is absolutely positioned and nowrap, so
   under a panel this narrow it hangs off both edges. Hidden here exactly as the
   phone layout already hides it — the My Charts row it points at is one line
   below the chart name anyway, and the inline toolbar still shows it. */
/* Sync badge: match the ghost buttons' padding so every row lines up on the
   same left edge. */
.viewport-controls.vc-compact-project .project-picker-bar .project-cloud-status,
.viewport-controls.vc-compact-project .project-picker-bar .project-cloud-status[data-state] {
    margin-left: 0;
    padding: 0.34rem 0.7rem;
    justify-content: flex-start;
}
/* Share's inline divider and its nudge only make sense shoulder-to-shoulder
   on one row — drop both in the stacked panel. */
.viewport-controls.vc-compact-project .project-picker-bar .collaborate-btn {
    margin-left: 0;
}
.viewport-controls.vc-compact-project .project-picker-bar .collaborate-btn::before {
    display: none;
}
/* The badge's label is normally capped at 9.5rem and centred so a hover swap can
   never widen the inline pill and shove the rest of the toolbar sideways. Nothing
   sits beside it in the stacked panel, so let it use the full row and read
   left-aligned like every other line. */
.viewport-controls.vc-compact-project .project-picker-bar .cloud-status-label,
.viewport-controls.vc-compact-project .project-picker-bar .cloud-status-hover-label {
    max-width: none;
    text-align: left;
}
@media (prefers-reduced-motion: reduce) {
    .viewport-controls.vc-compact-project .project-picker-bar { animation: none; }
    .project-menu-btn .pmb-caret { transition: none; }
}
[data-a11y-motion="reduce"] .viewport-controls.vc-compact-project .project-picker-bar { animation: none; }
[data-a11y-motion="reduce"] .project-menu-btn .pmb-caret { transition: none; }

.viewport-dot {
    color: var(--text-faint);
    font-size: 1.2rem;
    line-height: 1;
}


.viewport-span {
    font-size: 0.7rem;
    color: var(--text-muted);
    white-space: nowrap;
    font-weight: 500;
    min-width: 0;
    flex: 0 1 auto;
    text-align: center;
    /* Pill matching the .zoom-slider-inline "Adjust Manually" control EXACTLY:
       flat translucent slate fill, rounded — NO drop shadow, NO border.
       Slim vertical padding keeps it ~19px tall. */
    padding: 0.07rem 1rem;
    border-radius: 999px;
    background: rgba(100, 116, 139, 0.10);
}
/* No empty pill before the span is populated. */
.viewport-span:empty {
    padding: 0;
    background: none;
}

/* ── Minimal inline viewport-range bar (cluster redesign, chosen layout #4) ──
   One rounded pill: calendar · Start → End · duration · Apply · Fit-icon. */
.viewport-range-bar {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 6px 5px 13px;
    /* Shared toolbar-pill height (project bar + zoom bar + this one all match).
       min-height (not height) so a 2-line folded duration can still grow. */
    min-height: var(--toolbar-pill-h, 48px);
    border-radius: 999px;
    background: rgba(15, 23, 42, 0.04);
    border: 1px solid rgba(15, 23, 42, 0.10);
}
[data-theme$="-dark"] .viewport-range-bar {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
.viewport-range-bar .vrb-cal { width: 15px; height: 15px; color: var(--accent); flex: none; }
.viewport-range-bar .vrb-arrow { color: var(--text-faint); font-size: 13px; flex: none; }
/* readonly date triggers, restyled as inline text (override .input/.viewport-date) */
.viewport-range-bar .vrb-date {
    width: 7.2rem;
    min-width: 0;
    flex: none;
    background: transparent;
    border: 0;
    padding: 4px 6px;
    border-radius: 7px;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--text-heading);
    text-align: center;
    cursor: pointer;
    box-shadow: none;
}
.viewport-range-bar .vrb-date:hover,
.viewport-range-bar .vrb-date:focus { outline: none; background: var(--bg-hover); }
/* duration becomes a quiet sibling with a divider (overrides the standalone pill).
   FIXED-WIDTH slot: the text re-renders on every zoom step, and since the
   chart-header is centre-justified, a growing/shrinking span used to shift the
   whole toolbar (the zoom −/+ drifted out from under the cursor). Overlong
   strings fold onto a second line instead of widening the pill. */
.viewport-range-bar .viewport-span {
    background: none;
    border: 0;
    border-left: 1px solid var(--border-card);
    border-radius: 0;
    box-shadow: none;
    padding: 0 0 0 10px;
    margin-left: 3px;
    font-size: 0.68rem;
    color: var(--text-muted);
    width: 11.5rem;
    flex: none;
    white-space: normal;
    line-height: 1.2;
    text-align: center;
}
.viewport-range-bar .viewport-span:empty { border-left: 0; padding: 0; margin: 0; width: auto; }

.viewport-apply {
    flex: none;
    margin-left: 4px;
    font-size: 0.74rem;
    font-weight: 700;
    padding: 0.45rem 1.1rem;
    border-radius: 999px;
    background: #10b981;
    color: #04241a;
    border: 0;
    transition: background 0.15s ease, transform 0.12s ease;
}
/* (0,4,0) beats .btn-primary:hover (0,3,0) so the emerald stays on hover */
.viewport-apply:hover:not(:disabled):not(.inactive) { background: #34d399; }
.viewport-apply:active:not(.inactive) { transform: translateY(1px); }

.viewport-apply.inactive {
    opacity: 0.35;
    pointer-events: none;
}

.viewport-apply.active {
    animation: applyPulse 0.3s ease-out;
}

@keyframes applyPulse {
    0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.45); }
    100% { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
}

/* ── Zoom Slider (pinned to chart bottom, pill box) ──────────── */
/* Stacked label + control wrapper — used for the zoom slider + tools
   groups so each carries a small "Adjust Manually" / "Tools" caption
   above it, mirroring the VIEWPORT START/END labels above the date
   inputs. */
.viewport-stack {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
}

/* Same inline-pill design system as .project-picker-bar / .viewport-range-bar:
   bordered translucent capsule · accent lead icon · ghost internals · quiet
   divider-separated readout. The "Adjust Manually" caption is dropped for the
   minimal look (mirrors .project-bar-label). */
.zoom-slider-inline {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 10px 5px 13px;
    min-height: var(--toolbar-pill-h, 48px);
    background: rgba(15, 23, 42, 0.04);
    border: 1px solid rgba(15, 23, 42, 0.10);
    border-radius: 999px;
}

[data-theme$="-dark"] .zoom-slider-inline {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}

.zoom-slider-inline .zsi-ico {
    width: 15px;
    height: 15px;
    color: var(--accent);
    flex: none;
}

.viewport-field-label[for="zoomSlider"] { display: none; }

.zoom-slider-inline .zoom-slider {
    width: 7rem;
}

/* Second, older presentation of the same control -- a pill pinned to the
   bottom-center of the chart, instead of inline in the toolbar above
   (.zoom-slider-inline, the current design). Not present anywhere in
   index.html's markup; app.js's updateZoomSliderMargin() looks it up
   defensively (`if (!row) return`) and no-ops when absent. Looks like
   inactive/legacy CSS kept alongside the live inline version -- verify in
   the rendered DOM before spending time on either. */
.zoom-slider-row {
    position: absolute;
    bottom: 0.4rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
}

.zoom-slider-box {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 1rem;
    border: 1px solid var(--border-card);
    border-radius: 9999px;
    background: var(--bg-card);
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
}

.zoom-slider-row .zoom-slider {
    width: 8rem;
}

/* Percent readout — quiet divider sibling, same recipe as the range bar's
   duration span ("11 months, 30 days…"). */
.zoom-pct {
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
    user-select: none;
    min-width: 2.4rem;
    text-align: center;
    border-left: 1px solid var(--border-card);
    padding-left: 10px;
    margin-left: 3px;
}

/* − / + steppers — borderless ghost rounds, like the project bar's items. */
.zoom-step-btn {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--text-body);
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    flex: none;
    transition: background 0.12s, color 0.12s;
}

.zoom-step-btn:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}

.zoom-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 4px;
    background: var(--border-input);
    border-radius: 3px;
    outline: none;
    margin: 0;
    cursor: pointer;
}

.zoom-slider::-webkit-slider-runnable-track {
    height: 4px;
    background: var(--border-input);
    border-radius: 3px;
    border: none;
}

.zoom-slider::-moz-range-track {
    height: 4px;
    background: var(--border-input);
    border-radius: 3px;
    border: none;
}

.zoom-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: var(--border-focus);
    border: 2px solid var(--bg-card);
    box-shadow: 0 0 0 1px var(--border-input);
    cursor: grab;
    margin-top: -4.5px;
    transition: transform 0.1s, box-shadow 0.1s;
}

.zoom-slider::-webkit-slider-thumb:hover {
    transform: scale(1.3);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 20%, transparent);
}

.zoom-slider::-webkit-slider-thumb:active {
    cursor: grabbing;
    transform: scale(1.15);
}

.zoom-slider::-moz-range-thumb {
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: var(--border-focus);
    border: 2px solid var(--bg-card);
    box-shadow: 0 0 0 1px var(--border-input);
    cursor: grab;
    transition: transform 0.1s, box-shadow 0.1s;
}

.zoom-slider::-moz-range-thumb:hover {
    transform: scale(1.3);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 20%, transparent);
}

.zoom-slider::-moz-range-thumb:active {
    cursor: grabbing;
    transform: scale(1.15);
}

/* ── Gantt chart canvas ─────────────────────────────────────────────
   The actual SVG chart: container, grid lines, axis text, task bars,
   labels, selection rubber-band. Distinct from "Chart toolbar controls"
   far above, which only styles the date-range/zoom/tools controls that
   sit ABOVE this container -- not the drawing itself. ── */
.gantt-chart-container {
    position: relative;
    width: 100%;
    min-height: 350px;
    display: flex;
    flex-direction: column;
}

/* ── Plan arrival banner (6.10.4) ──────────────────────────────────────
   A card floating at the top of the chart when a chart arrives through a
   /#plan= link (a chart an AI assistant made with the render command). It
   overlays the chart rather than sitting in the flow, so the chart's
   measured height does not change when it appears or goes. z-index 40 sits
   above the cursor layer (4) and below every dialog overlay. */
.plan-arrival {
    position: absolute;
    top: 14px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 40;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 8px 12px;
    width: max-content;
    max-width: min(92%, 760px);
    padding: 10px 10px 10px 14px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--bg-input);
    color: var(--text-body);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.35;
}
.plan-arrival.hidden { display: none; }
.plan-arrival-icon { flex: none; color: var(--accent); }
.plan-arrival-text { flex: 1 1 240px; min-width: 0; text-wrap: pretty; }
.plan-arrival-text.hidden { display: none; }
.plan-arrival-btn {
    flex: none;
    padding: 7px 14px;
    border: 1px solid var(--border-input);
    border-radius: 999px;
    background: var(--bg-hover);
    color: var(--text-heading);
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
}
.plan-arrival-btn:hover { border-color: var(--border-focus); }
.plan-arrival-btn.hidden { display: none; }
.plan-arrival-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.plan-arrival-primary:hover { filter: brightness(1.08); }
.plan-arrival-close {
    flex: none;
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}
.plan-arrival-close:hover { background: var(--bg-hover); color: var(--text-heading); }
@media (max-width: 640px) {
    .plan-arrival { top: 8px; max-width: 96%; padding: 10px 10px 10px 12px; }
}

.ctrl-zoom-overlay {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 0.6rem 1.2rem;
    background: var(--bg-card);
    color: var(--text-heading);
    font-size: 0.85rem;
    font-weight: 600;
    border-radius: 0.5rem;
    border: 1px solid var(--border-card);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
    z-index: 2000;
    pointer-events: none;
    opacity: 1;
    transition: opacity 0.2s ease;
}

.ctrl-zoom-overlay.hidden {
    opacity: 0;
    pointer-events: none;
}

.empty-state {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 4rem 0;
}

.empty-state svg { color: var(--text-faint); }
.empty-state p { color: var(--text-faint); }

.grid line {
    stroke: var(--grid-stroke);
    shape-rendering: crispEdges;
}

.grid-vertical line {
    stroke-opacity: 0.5;
}

.grid-horizontal line {
    stroke-opacity: 0.8;
}

.task-bar {
    cursor: move;
    transition: opacity 0.15s, filter 0.15s;
    filter: brightness(1);
}

.task-bar:hover {
    filter: brightness(1.15);
}

.resize-handle {
    transition: opacity 0.1s;
    opacity: 0;
}

.task-group:hover .resize-handle {
    opacity: 1;
    fill: rgba(255, 255, 255, 0.2);
}

.task-label {
    fill: var(--task-label-fill);
    font-size: 0.75rem;
    font-weight: 500;
    pointer-events: none;
}

.axis text {
    font-size: 0.75rem;
    fill: var(--axis-text);
}

.y.axis text:hover {
    fill: var(--text-heading);
}

.axis path,
.axis line {
    stroke: var(--axis-stroke);
}

/* Weekday initial stacked above the day number at day-level zoom.
   More specific than `.axis text` so it keeps its smaller size + muted fill. */
.axis text.x-axis-dow {
    fill: var(--text-muted);
    font-size: 0.5rem;
    font-weight: 600;
}

/* Y-axis / X-axis opaque mask rects (behind task labels + date labels).
   Follow the theme via --chart-bg. CSS wins over the `fill` SVG attribute,
   so this auto-updates on theme switch without a chart re-render. */
.y-axis-bg,
.x-axis-bg {
    fill: var(--chart-bg);
}

/* ── Shift+drag marquee (multi-select rubber-band) ─────────────── */
.selection-rubber-band {
    fill: var(--border-focus);
    fill-opacity: 0.12;
    stroke: var(--border-focus);
    stroke-width: 1;
    stroke-dasharray: 4 3;
    pointer-events: none;
}

/* Selected task bars, groups, milestones, and off-bar labels.
   Uses the focus color so the selection adapts to every theme. */
.task-bar.selected,
.group-bar.selected {
    stroke: var(--border-focus);
    stroke-width: 3.5;
    stroke-dasharray: none;
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--border-focus) 70%, transparent));
}
.milestone-diamond.selected {
    stroke: var(--border-focus);
    stroke-width: 3;
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--border-focus) 70%, transparent));
}
.bar-label.selected {
    fill: var(--border-focus);
    font-weight: 700;
}

/* ── Toast ─────────────────────────────────────────────────────── */
.message-box {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);   /* dead-centre of the screen */
    display: none;                 /* toggled to flex by showMessage() */
    align-items: center;
    gap: 0.6rem;
    max-width: min(92vw, 440px);
    padding: 0.9rem 1.5rem;
    border-radius: 999px;
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1.3;
    z-index: 1000;
    pointer-events: none;          /* purely informational — never block clicks beneath */
    box-shadow: 0 14px 44px rgba(0, 0, 0, 0.35);
    animation: toastCenterPop 0.24s ease-out;
}

.message-box .toast-icon {
    width: 1.2rem;
    height: 1.2rem;
    flex-shrink: 0;
}

.message-box.toast-success {
    background: var(--toast-success-bg);
    color: var(--toast-success-text);
    border: 1px solid var(--toast-success-border);
}

/* Errors get the loudest treatment: solid saturated red, white text, a red
   glow, and a pop-then-shake entry so it's caught even in peripheral vision
   when the eye is on the top toolbar. */
.message-box.toast-error {
    background: #dc2626;
    color: #ffffff;
    border: 1px solid #f87171;
    box-shadow: 0 10px 34px rgba(220, 38, 38, 0.5), 0 3px 10px rgba(0, 0, 0, 0.25);
    animation: toastError 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

@keyframes slideIn {
    from { transform: translateX(1rem); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

/* Centre-screen toast: a gentle scale-up + fade. Every frame keeps
   translate(-50%,-50%) so it stays dead-centre. */
@keyframes toastCenterPop {
    from { transform: translate(-50%, -50%) scale(0.92); opacity: 0; }
    to   { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}

/* Error toast pop + shake. Every frame keeps translate(-50%,-50%) so it stays
   centred while it nudges left/right. */
@keyframes toastError {
    0%   { transform: translate(-50%, -50%) scale(0.9); opacity: 0; }
    45%  { transform: translate(-50%, -50%) scale(1.05); opacity: 1; }
    60%  { transform: translate(calc(-50% - 7px), -50%) scale(1); }
    73%  { transform: translate(calc(-50% + 7px), -50%) scale(1); }
    86%  { transform: translate(calc(-50% - 4px), -50%) scale(1); }
    100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}

/* Respect the OS setting and the a11y panel's reduce-motion switch. */
@media (prefers-reduced-motion: reduce) {
    .message-box, .message-box.toast-error { animation: none; }
}
[data-a11y-motion="reduce"] .message-box,
[data-a11y-motion="reduce"] .message-box.toast-error { animation: none; }

/* ── Task Edit Dialog ──────────────────────────────────────────── */
.dialog-overlay {
    position: fixed;
    inset: 0;
    background: var(--dialog-overlay);
    backdrop-filter: blur(4px);
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: fadeIn 0.15s ease-out;
}

.dialog-overlay.hidden {
    display: none;
}

.dialog {
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.75rem;
    width: 100%;
    max-width: min(520px, calc(100vw - 2rem));
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
    animation: dialogIn 0.15s ease-out;
}

.dialog-header {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem 1.15rem;
    background: var(--bg-input);
    border-bottom: 1px solid var(--border-card);
    border-top-left-radius: 0.75rem;
    border-top-right-radius: 0.75rem;
    --hx: -200px;
    --hy: 50%;
}
/* Cursor-tracking indigo "glare", mirroring .app-header-compact. JS toggles
   `.header-spotlight` on mouseenter/leave and writes --hx/--hy on mousemove
   via a global delegation handler in app.js. */
.dialog-header::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.3s ease;
    background: radial-gradient(
        circle 200px at var(--hx) var(--hy),
        rgba(5, 150, 105, 0.18) 0%,
        rgba(5, 150, 105, 0.06) 50%,
        transparent 100%
    );
}
.dialog-header.header-spotlight::before { opacity: 1; }
[data-theme$="-dark"] .dialog-header::before {
    background: radial-gradient(
        circle 200px at var(--hx) var(--hy),
        rgba(52, 211, 153, 0.25) 0%,
        rgba(52, 211, 153, 0.08) 50%,
        transparent 100%
    );
}
.dialog-header > * { position: relative; z-index: 1; }

/* Left-aligned title group — lets a leading control (e.g. the Templates
   modal's "← All templates" back button) sit beside the title while the
   close button stays pinned right via the header's space-between. */
.dialog-header-lead {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
}

.dialog-header h3,
.dialog-header .dialog-title {
    color: var(--text-heading);
    margin: 0;
}

.dialog-body {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: 1.15rem;
}

.dialog-row {
    display: flex;
    gap: 0.75rem;
}

.dialog-notes {
    resize: vertical;
    min-height: 4rem;
    font-family: inherit;
}

.dialog-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem 1.15rem;
    border-top: 1px solid var(--border-card);
}

.btn-dialog-delete {
    padding: 0.4rem 0.85rem;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.75rem;
    font-weight: 500;
    border-radius: 0.375rem;
    border: 1px solid var(--dialog-delete-border);
    cursor: pointer;
    transition: all 0.15s;
}

.btn-dialog-delete:hover {
    background: var(--dialog-delete-hover-bg);
    color: var(--dialog-delete-hover-text);
    border-color: var(--dialog-delete-hover-border);
}

/* ── App experience card (collab/app-survey.js) ─────────────────────
   The one-time "how is it going?" ask. It is a normal dialog — overlay,
   header, body, footer, the app's own buttons — so it reads as the product
   asking rather than as a widget, and it themes itself. Only the answer rows
   and the follow-up chips need styles of their own. */
.asv-dialog { max-width: min(440px, calc(100vw - 2rem)); }
.asv-sub { margin: -0.25rem 0 0; font-size: 0.78rem; color: var(--text-muted); }
.asv-rates { display: grid; gap: 0.4rem; }
.asv-rate {
    text-align: left;
    padding: 0.55rem 0.8rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border-input);
    background: var(--bg-input);
    color: var(--text-body);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s;
}
.asv-rate:hover { border-color: var(--btn-add-bg); background: var(--bg-hover); }
.asv-rate[aria-checked="true"] { border-color: var(--btn-add-bg); background: var(--accent-soft); color: var(--text-heading); }
.asv-rate:focus-visible, .asv-chip:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 1px; }
.asv-q { margin: 0 0 0.4rem; font-size: 0.78rem; font-weight: 600; color: var(--text-heading); }
.asv-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.asv-chip {
    padding: 0.3rem 0.65rem;
    border-radius: 999px;
    border: 1px solid var(--border-input);
    background: var(--bg-input);
    color: var(--text-body);
    font: inherit;
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.15s;
}
.asv-chip:hover { border-color: var(--btn-add-bg); }
.asv-chip[aria-pressed="true"] { border-color: var(--btn-add-bg); background: var(--accent-soft); color: var(--text-heading); font-weight: 600; }
.asv-notelink {
    align-self: flex-start;
    padding: 0;
    border: 0;
    background: none;
    color: var(--text-muted);
    font: inherit;
    font-size: 0.75rem;
    text-decoration: underline;
    cursor: pointer;
}
.asv-notelink:hover { color: var(--text-body); }
.asv-dialog .hidden { display: none; }
/* No phone override on purpose: below 640px the app's own rule
   (.dialog { margin: 1rem; max-width: none; }) already turns every dialog into
   a full-width inset card, and this one should look like the rest. */

/* ── GDPR self-service buttons + dialogs ─────────────────────────── */
.btn-secondary {
    padding: 0.4rem 0.85rem;
    background: transparent;
    color: var(--text-body);
    font-size: 0.75rem;
    font-weight: 500;
    border-radius: 0.375rem;
    border: 1px solid var(--border-card);
    cursor: pointer;
    transition: all 0.15s;
}
.btn-secondary:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
    border-color: var(--border-input);
}
.btn-secondary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-danger {
    padding: 0.4rem 0.85rem;
    background: #b91c1c;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    border-radius: 0.375rem;
    border: 1px solid #b91c1c;
    cursor: pointer;
    transition: all 0.15s;
}
.btn-danger:hover { background: #991b1b; border-color: #991b1b; }
.btn-danger:disabled {
    background: transparent;
    color: #fca5a5;
    border-color: rgba(185, 28, 28, 0.35);
    cursor: not-allowed;
}

.profile-privacy-status { margin-top: 0.7rem; padding: 0.5rem 0.7rem; font-size: 0.72rem; border-radius: 0.3rem; background: var(--accent-soft); color: var(--accent-dark); }
.profile-privacy-status.is-error { background: rgba(185, 28, 28, 0.08); color: #b91c1c; }

.account-delete-warning { font-size: 0.78rem; color: var(--text-body); line-height: 1.55; margin: 0 0 0.9rem; }
.account-delete-warning strong { color: var(--text-heading); }
.account-delete-confirm-prompt { font-size: 0.78rem; margin: 0 0 0.4rem; }
.account-delete-confirm-prompt strong,
.account-delete-name { color: #b91c1c; font-family: 'IBM Plex Mono', monospace; font-weight: 700; }
.account-delete-input { width: 100%; margin-top: 0.3rem; }
/* Post-deletion acknowledgement state (shown after delete_my_account succeeds). */
.account-delete-done { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 0.7rem; padding: 0.4rem 0 0.2rem; }
.account-delete-done svg { color: #16a34a; flex-shrink: 0; }
.account-delete-done p { margin: 0; font-size: 0.9rem; color: var(--text-body); line-height: 1.5; }

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes dialogIn {
    from { transform: scale(0.95); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
}

/* ── Export Image Dialog ────────────────────────────────────────────
   Redesigned with the tabbed export modal (see .export-tabbed-dialog):
   the settings column is a raised panel card, every radio row is a
   connected segmented control, the Extras checkboxes sit in a quiet
   two-column grid, and the accent colour is reserved for the format
   choice, checked boxes and the primary footer action. All colours run
   through the theme variables, so every palette (light, dark and the
   high-contrast pair) keeps working. */
.export-dialog {
    max-width: min(1280px, calc(100vw - 2rem));
}

/* Inside the export modal the controls are rectangular (8px), not pills —
   declared before the filename row below so its corner squaring wins. */
.export-tabbed-dialog .input {
    border-radius: 8px;
    padding-left: 0.7rem;
    padding-right: 0.7rem;
}

.export-dialog-body {
    padding: 1rem 1.15rem;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.export-dialog-grid {
    display: grid;
    grid-template-columns: minmax(0, 460px) minmax(0, 1fr);
    /* One row, forced to the full height of the dialog body. Left as auto the row
       measured the taller column's content, so the preview ended wherever its own
       content ended and left dead space below the notes. */
    grid-template-rows: minmax(0, 1fr);
    align-items: stretch;
    gap: 1.35rem;
    flex: 1;
    min-height: 0;
}

.export-dialog-controls {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    min-width: 0;
    /* Only scrolls when the options genuinely outgrow the window. */
    min-height: 0;
    overflow-y: auto;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
    padding: 1rem 1.05rem;
}

/* Hairline dividers between option groups; the poster inset card carries
   its own border instead. */
.export-dialog-controls > div + div:not(.export-optgroup) {
    border-top: 1px solid color-mix(in srgb, var(--border-card) 55%, transparent);
    padding-top: 0.9rem;
}

/* Section headers inside the export panels: small, tracked uppercase. */
.export-dialog-body .label,
.excel-dialog-body .label {
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.07em;
    color: var(--text-muted);
}

.export-format-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.45rem;
    margin-top: 0.35rem;
}

.export-format-card {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 0.55rem;
    padding: 0.55rem 0.65rem;
    background: var(--bg-input);
    border: 1px solid var(--border-card);
    border-radius: 10px;
    color: var(--text-body);
    cursor: pointer;
    transition: border-color 0.12s ease, background 0.12s ease;
    text-align: left;
}

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

.export-format-card[aria-checked="true"] {
    border-color: var(--title-accent);
    background: color-mix(in srgb, var(--title-accent) 8%, var(--bg-input));
}

.export-format-icon {
    width: 22px;
    height: 22px;
    color: var(--text-muted);
    flex-shrink: 0;
    transition: color 0.12s ease;
}

.export-format-card[aria-checked="true"] .export-format-icon {
    color: var(--title-accent);
}

.export-format-text {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    min-width: 0;
    line-height: 1.15;
}

.export-format-name {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-heading);
    letter-spacing: 0.02em;
}

.export-format-hint {
    font-size: 0.62rem;
    color: var(--text-faint);
    font-weight: 500;
}

.export-filename-row {
    display: flex;
    align-items: stretch;
    gap: 0;
    margin-top: 0.3rem;
}

.export-filename-row .input {
    border-radius: 8px 0 0 8px;
    border-right: none;
    flex: 1;
    min-width: 0;
}

.export-filename-ext {
    display: inline-flex;
    align-items: center;
    padding: 0 0.8rem 0 0.6rem;
    background: var(--bg-hover);
    border: 1px solid var(--border-card);
    border-radius: 0 8px 8px 0;
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 500;
    font-family: ui-monospace, monospace;
    white-space: nowrap;
}

.export-scale-group {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

/* ── Segmented controls ─────────────────────────────────────────────
   Every mutually-exclusive option row (orientation, margins, quality,
   timeline range, background, poster sheets/columns/rows/overlap,
   resolution, scale, and the spreadsheet chip rows) shares one connected
   track: a recessed rail with a raised tile on the active segment. The
   markup and [aria-checked] wiring are untouched — this is pure skin. */
.export-bg-row,
.export-resolution-row,
.export-scale-row,
.excel-chip-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    margin-top: 0.35rem;
    padding: 3px;
    background: var(--bg-input);
    border: 1px solid color-mix(in srgb, var(--border-card) 80%, transparent);
    border-radius: 9px;
    width: 100%;
}

.export-scale-chip,
.export-bg-chip,
.export-res-chip,
.excel-chip {
    flex: 1 1 auto;
    min-width: max-content;
    padding: 0.32rem 0.55rem;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 500;
    line-height: 1.25;
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
}

.export-scale-chip:hover:not([aria-checked="true"]),
.export-bg-chip:hover:not([aria-checked="true"]),
.export-res-chip:hover:not([aria-checked="true"]),
.excel-chip:hover:not([aria-checked="true"]) {
    color: var(--text-heading);
    background: color-mix(in srgb, var(--text-body) 7%, transparent);
}

.export-scale-chip[aria-checked="true"],
.export-bg-chip[aria-checked="true"],
.export-res-chip[aria-checked="true"],
.excel-chip[aria-checked="true"] {
    background: var(--bg-card);
    color: var(--text-heading);
    font-weight: 600;
    box-shadow: inset 0 0 0 1px var(--border-input), 0 1px 2px rgba(0, 0, 0, 0.16);
}

.export-scale-chip[disabled] {
    opacity: 0.35;
    cursor: not-allowed;
}

.export-dims-line {
    font-size: 0.68rem;
    color: var(--text-faint);
    font-family: ui-monospace, monospace;
    min-height: 1em;
}

/* Second and later control groups inside ONE export-dialog block (the poster
   options stack scale + overlap + quality + toggles under a single heading, so
   they need the gap the parent flex column would otherwise only give siblings).
   Margin only — it is applied to both .label spans and .export-extras-row, whose
   own display must survive. */
.export-subgroup {
    margin-top: 0.7rem;
}

/* The poster-only options render as one inset sub-card, so the whole block
   reads as a unit that belongs to the "Poster to tape together" choice. */
.export-optgroup {
    padding: 0.75rem 0.85rem;
    background: color-mix(in srgb, var(--bg-input) 45%, transparent);
    border: 1px solid color-mix(in srgb, var(--border-card) 70%, transparent);
    border-radius: 10px;
    transition: opacity 0.15s ease;
}

/* Option groups that only apply to one layout choice (the poster controls) dim
   instead of disappearing. Hiding them made the panel look like it had lost
   options every time the layout was set back to a single page. */
.export-optgroup-off {
    opacity: 0.38;
    pointer-events: none;
}

/* Print tab: the quiet "step two" line under the preview, telling the user the
   browser's own print dialog is what comes after the Print button. */
.export-print-step2 {
    margin-top: 0.35rem;
    font-style: italic;
    opacity: 0.85;
}

/* Custom Color row — sits directly under the Background segmented control:
   quiet checkbox + label on the left, colour swatch on the right. The check
   square (not a glowing pill) is what signals the active state. */
.export-bg-custom-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    margin-top: 0.45rem;
    padding: 0.15rem 0.1rem;
}
.export-bg-custom-title {
    font-size: 0.74rem;
    font-weight: 500;
    color: var(--text-body);
}
.export-bg-custom-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.15rem;
}
.export-bg-custom-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    padding: 0;
    background: var(--bg-input);
    border: 1.5px solid var(--border-input);
    border-radius: 5px;
    color: transparent;
    cursor: pointer;
    transition: all 0.12s ease;
}
.export-bg-custom-check:hover { border-color: var(--title-accent); }
.export-bg-custom-check[aria-pressed="true"] {
    background: var(--title-accent);
    border-color: var(--title-accent);
    color: #fff;
}
.export-bg-custom-swatch-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
    cursor: pointer;
}
.export-bg-custom-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    padding: 0;
    border: none;
    background: none;
}
.export-bg-custom-swatch {
    display: inline-block;
    width: 28px;
    height: 18px;
    border: 1px solid var(--border-input);
    border-radius: 5px;
    background: #ffffff;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}
.export-bg-custom-swatch-wrap:hover .export-bg-custom-swatch {
    border-color: var(--title-accent);
}

/* Extras — a structured two-column grid of quiet checkbox rows (the pill
   soup is gone). Checked state lives in the accent-filled check square. */
.export-extras-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.1rem 0.55rem;
    margin-top: 0.35rem;
}
.export-extra-pill {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.3rem 0.4rem;
    background: transparent;
    border: none;
    border-radius: 7px;
    color: var(--text-body);
    font-size: 0.74rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.12s ease;
    user-select: none;
    min-width: 0;
}
.export-extra-pill:hover {
    background: color-mix(in srgb, var(--text-body) 6%, transparent);
}
.export-extra-pill input[type="checkbox"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}
.export-extra-pill-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    background: var(--bg-input);
    border: 1.5px solid var(--border-input);
    border-radius: 5px;
    color: transparent;
    transition: all 0.12s ease;
    flex-shrink: 0;
}
.export-extra-pill input[type="checkbox"]:checked ~ .export-extra-pill-check {
    background: var(--title-accent);
    border-color: var(--title-accent);
    color: #fff;
}
.export-extra-pill-label {
    color: var(--text-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.export-extra-pill:has(input[type="checkbox"]:checked) .export-extra-pill-label {
    color: var(--text-heading);
}

.export-bg-note {
    margin-top: 0.3rem;
    font-size: 0.65rem;
    color: var(--text-faint);
    font-style: italic;
}

.export-bg-note.hidden { display: none; }

.export-quality-group { display: flex; flex-direction: column; gap: 0.25rem; }
.export-quality-group.hidden { display: none; }

.export-quality-group label {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.export-preview-pane {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-width: 0;
    align-self: stretch;
}

/* Positioning context for the floating zoom toolbar: the shell wraps the
   scrollable preview box, so the pill overlays the canvas but never scrolls
   with it (an absolutely-positioned child INSIDE a scroll container would). */
.export-preview-shell {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.export-preview-box {
    position: relative;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem;
    /* Neutral base — NOT --bg-hover: the checkerboard signals transparency,
       so it must stay achromatic when the hover wash is brand-tinted. */
    background-color: var(--bg-body);
    background-image:
        linear-gradient(45deg, rgba(128,128,128,0.10) 25%, transparent 25%),
        linear-gradient(-45deg, rgba(128,128,128,0.10) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, rgba(128,128,128,0.10) 75%),
        linear-gradient(-45deg, transparent 75%, rgba(128,128,128,0.10) 75%);
    background-size: 14px 14px;
    background-position: 0 0, 0 7px, 7px -7px, -7px 0;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    min-height: 200px;
    overflow: auto;
}
/* Zoomed in, the preview is dragged around rather than wheel-scrolled (the wheel
   zooms). The canvas itself ignores the drag so the cursor never turns into a
   text/image cursor mid-gesture. */
.export-preview-box.is-zoomed { cursor: grab; }
/* Once the canvas overflows, centring it with justify-content/align-items puts part
   of it BEFORE the scroll origin, where no amount of dragging can reach it — the
   left and right edges were simply unreachable. Anchor to the start when zoomed and
   let the canvas's own `margin: auto` keep whichever axis still fits centred. */
.export-preview-box.is-zoomed { justify-content: flex-start; align-items: flex-start; }
.export-preview-box.is-panning { cursor: grabbing; }
.export-preview-box.is-zoomed > canvas { pointer-events: none; user-select: none; }

/* A flex item centred with justify-content cannot be scrolled back to its start
   edge once it overflows; auto margins centre it AND release when space runs out. */
.export-preview-box > canvas { margin: auto; flex: 0 0 auto; }

/* Zoom controls — a floating, softly blurred pill hovering over the bottom
   of the preview canvas (anchored to .export-preview-shell, see above). */
.export-zoom-row {
    position: absolute;
    left: 50%;
    bottom: 0.85rem;
    transform: translateX(-50%);
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 0.45rem;
    width: min(360px, calc(100% - 2.5rem));
    margin: 0;
    padding: 0.38rem 0.7rem;
    background: color-mix(in srgb, var(--bg-card) 82%, transparent);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    border: 1px solid var(--border-card);
    border-radius: 999px;
    box-shadow: 0 10px 28px -8px rgba(0, 0, 0, 0.35);
}
.export-zoom-row input[type="range"] { flex: 1; min-width: 0; }
.export-zoom-pct {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-muted);
    min-width: 3.1rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.export-zoom-btn {
    flex: none;
    width: 1.5rem;
    height: 1.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 999px;
    background: transparent;
    color: var(--text-body);
    font-size: 0.8rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.export-zoom-btn:hover {
    background: color-mix(in srgb, var(--text-body) 9%, transparent);
    color: var(--text-heading);
}

/* Every preview canvas floats as a "page" on the checkerboard canvas. */
.export-preview-box > canvas {
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
    border-radius: 2px;
}

/* #exportPreviewCanvas is sized explicitly by its renderPreview (fit × zoom),
   exactly like the paged previews — max-width/height caps would fight the
   zoomed size, so it carries no constraints of its own. */

.export-preview-loading {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    background: color-mix(in srgb, var(--bg-card) 84%, transparent);
    color: var(--text-muted);
    font-size: 0.75rem;
    font-weight: 500;
    border-radius: inherit;
    backdrop-filter: blur(2px);
}

.export-preview-loading.hidden { display: none; }

/* Mobile: stack controls over preview */
@media (max-width: 639px) {
    .export-dialog {
        max-width: calc(100vw - 1rem);
    }
    .export-dialog-grid {
        grid-template-columns: 1fr;
        gap: 0.85rem;
    }
    .export-dialog-controls {
        padding: 0.8rem 0.75rem;
    }
    .export-preview-box {
        min-height: 160px;
    }
}

/* ── Responsive ────────────────────────────────────────────────── */

/* Five breakpoints follow, each with its own intro comment: mobile
   (max-width 639px, stacks the header/composer/toolbar vertically),
   tablet (640-1099px, tighter composer columns, keeps the single-row
   toolbar), tablet portrait (min-width 640px, portrait, max-aspect-ratio
   3/4 -- stacks the top panel), ultrawide (min-aspect-ratio 21/9 AND
   min-width 2400px -- caps content width so controls don't float apart),
   and short/720p viewports (max-height 700px -- trims vertical padding).
   A SEPARATE, later block handles real touch phones via
   `@media (pointer: coarse)`; search "real mobile layout" further down. */
/* Mobile: stack vertically, icon-only toolbar, WCAG-compliant touch targets */
@media (max-width: 639px) {
    .top-panel {
        flex-direction: column;
        --panel-x: 0px;
        padding: 0 var(--panel-x);
    }

    .top-panel-right {
        flex-direction: row;
        flex-wrap: wrap;
        padding: 0 0 0.5rem 0;
    }

    /* Header: the six groups wrap onto as many rows as they need; the clock
       and the divider go, the ladder in app.js has already folded the labels. */
    .app-header-compact {
        height: auto;
        flex-wrap: wrap;
        row-gap: 6px;
        gap: 8px;
        padding: 8px 10px;
    }
    .app-title-compact { font-size: 1.05rem; }
    .header-clock, .hdr-divider { display: none; }

    /* Stack Task Properties above Object Browser — mirrors tablet block */
    .flat-section {
        flex-direction: column !important;
        gap: 0.5rem !important;
    }
    .flat-section > div:first-child {
        flex: 1 1 auto !important;
        min-width: 0 !important;
        padding-right: 0 !important;
        width: 100%;
    }
    .flat-section > div[style*="width: 6px"] {
        display: none;
    }
    #taskSection {
        padding-left: 0 !important;
        width: 100%;
    }

    /* Compact priority chips at mobile — fit next to color swatch */
    .priority-chip span {
        width: auto;
        min-width: 2.75rem;
        height: 1.75rem;
        padding: 0.2rem 0.5rem;
        font-size: 0.7rem;
    }
    .type-chip {
        padding: 0.25rem 0.5rem;
        font-size: 0.7rem;
    }

    .dialog {
        margin: 1rem;
        max-width: none;
    }

    /* WCAG 2.2: minimum 44×44px touch targets on mobile — keeps icon buttons large */
    .icon-btn {
        width: 2.75rem;
        height: 2.75rem;
        font-size: 1.1rem;
    }

    /* Chart controls stay centered (matches base); collapse ladder avoids wrap */
    .chart-header {
        padding: 0 0.5rem;
        justify-content: center;
        row-gap: 0.35rem;
    }
    .zoom-slider-inline {
        min-width: 100px;
    }
}

/* Tablet / narrow-desktop layout.
   Fires only at narrow widths (640-1099px). Above 1099px, the base capped
   single-row layout applies regardless of aspect ratio — narrow-aspect tall
   windows keep horizontal toolbar instead of stacking. */
@media (min-width: 640px) and (max-width: 1099px) {
    .top-panel {
        --panel-x: 0px;
        padding: 0.25rem var(--panel-x);
    }

    /* Header: one row at tablet width, tighter padding (the density ladder in
       app.js folds the labels before anything could overflow). */
    .app-header-compact { padding: 0 12px; gap: 10px; }
    .header-clock { display: none; }

    /* Compact priority + type chips to fit alongside color swatch on one row */
    .priority-chip span {
        min-width: 2.75rem;
        height: 1.75rem;
        padding: 0.2rem 0.5rem;
        font-size: 0.7rem;
    }
    .type-chip {
        padding: 0.25rem 0.5rem;
        font-size: 0.7rem;
    }

    /* Chart viewport controls stay centered (matches base .chart-header) — the
       collapse ladder prevents a 2-row wrap, so centering reads cleanly. */
    .chart-header {
        justify-content: center;
        row-gap: 0.4rem;
        margin-top: 0.25rem;
        margin-bottom: 0.5rem;
    }

    /* Below 1100px, compress the chart-properties row back to the tight
       spacing so it can still fit on one line on smaller windows. */
    .viewport-controls {
        gap: 0.4rem;
    }
    .viewport-divider {
        margin: 0 0.25rem;
    }
}

/* Tablet portrait orientation — stack top panel vertically (phones handled by mobile block below) */
@media (min-width: 640px) and (orientation: portrait) and (max-aspect-ratio: 3/4) {
    .top-panel {
        flex-direction: column;
        flex-wrap: nowrap;
    }
    .top-panel-left,
    .top-panel-right {
        flex: 1 1 auto;
        width: 100%;
        align-items: stretch;
    }
    .top-panel-right {
        padding: 0.25rem 0 0.5rem 0;
    }
    .chart-section {
        flex: 1 1 auto;
    }
}

/* Ultrawide ONLY (21:9 and wider, ≥2400px wide) — constrain top-panel so controls
   don't float. The min-width gate prevents this from firing on laptop browsers
   whose viewport accidentally becomes >2:1 due to taskbar/chrome reducing height. */
@media (min-aspect-ratio: 21/9) and (min-width: 2400px) {
    .top-panel {
        max-width: min(1800px, 95vw);
        margin-left: auto;
        margin-right: auto;
    }
}

/* Large screens and ultrawide: no extra padding — content fills width */

/* Short viewports: compact controls (covers 720p and half-docked windows) */
@media (max-height: 700px) {
    .app-header {
        padding-top: 0.5rem;
        padding-bottom: 0.25rem;
    }

    .top-panel {
        padding-top: 0.4rem;
        padding-bottom: 0.25rem;
    }

    .controls-panel {
        gap: 0.35rem;
        padding-bottom: 0.35rem;
    }

    .app-header h1 {
        font-size: 1.35rem;
    }

    .app-header p {
        display: none;
    }
}

/* ── IMPROVEMENT 3: Today Marker ───────────────────────────────── */
.today-marker {
    stroke: #ef4444;
    stroke-width: 1.5;
    stroke-dasharray: 6 3;
    pointer-events: none;
}

.today-marker-label {
    fill: #ef4444;
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    pointer-events: none;
}

/* ── Critical path overlay (Tools ▸ Critical) ────────────────── */
/* Ring drawn 3px outside each driving bar — the background gap keeps it
   readable on any bar colour, red bars included. Driving arrows are stroked
   by the renderer with the same --critical, resolved at draw time so image
   exports inline a literal colour. Light red-600; every dark theme red-400. */
[data-theme$="-dark"] {
    --critical: #f87171;
}
.critical-ring {
    fill: none;
    stroke: var(--critical, #dc2626);
    stroke-width: 2.25;
    pointer-events: none;
}

/* ── #122 Hover guide line ─────────────────────────────────────── */
/* Vertical line revealed after a dwell, marking the bar edge nearest the
   pointer. Neon green + glow so it reads instantly against bars and grid;
   element opacity is animated 0→1 for the fade-in. */
.hover-guide {
    stroke: #39ff14;
    stroke-width: 2;
    stroke-dasharray: 5 3;
    stroke-opacity: 0.95;
    filter: drop-shadow(0 0 3px rgba(57, 255, 20, 0.85));
    pointer-events: none;
}

/* ── IMPROVEMENT 8: Row Hover Highlight ────────────────────────── */
.row-hover-rect {
    transition: opacity 0.1s;
    pointer-events: all;
    cursor: default;
}

/* ── Chart Pan ────────────────────────────────────────────────── */
#ganttChart {
    cursor: grab;
    flex-shrink: 0;
}
#ganttChart:active {
    cursor: grabbing;
}
#ganttChart.panning .row-hover-rect {
    pointer-events: none;
}
#ganttChart.panning * {
    cursor: grabbing !important;
}
.connect-mode #ganttChart {
    cursor: crosshair;
}

/* ── IMPROVEMENT 1 (continued): Zoom to Fit Button ─────────────── */
/* Fit-to-Screen — icon-only ghost circle inside the range bar (label via tooltip/aria). */
.btn-zoom-fit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 30px;
    height: 30px;
    padding: 0;
    background: transparent;
    color: var(--text-muted);
    border-radius: 999px;
    border: 1px solid var(--border-card);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
    white-space: nowrap;
}
.btn-zoom-fit svg { width: 15px; height: 15px; }
.btn-zoom-fit:hover {
    background: var(--bg-hover);
    border-color: var(--accent);
    color: var(--text-heading);
}

/* Lock tool active — drags are blocked in JS; switch the grab cursors to default
   so it's visually clear the chart is view-only. (!important beats the inline cursor.) */
body.chart-locked .task-bar,
body.chart-locked .grip-hitarea,
body.chart-locked .resize-handle,
body.chart-locked .event-bar,
body.chart-locked .resize-left-evt,
body.chart-locked .resize-right-evt { cursor: default !important; }

/* The "no" cursor is reserved for the EVENTS ROW, and only there (Fotis,
   2026-08-05). It earns it because the row's whole affordance is "click empty
   space to add an event" — with that gone there is nothing else the row does, so
   the pointer would be a lie.
   It is deliberately NOT used on the Lock tool, the Link tool or a task name:
   Lock still opens the "ask for edit access" dialog, Link is a toolbar control
   like any other, and a frozen name simply isn't a button. Those three stay on
   their normal cursor and are gated in JS only. */
body.chart-locked .events-row-bg { cursor: not-allowed !important; }

/* Task name frozen by the lock (edit dialog). Reads as inert, not broken — the
   dimming carries that on its own, no cursor change. */
#dlgName.is-locked { opacity: 0.7; }

.btn-zoom-fit:disabled {
    opacity: 0.35;
    pointer-events: none;
}

/* ── NEW: Type selector chips ──────────────────────────────── */
.type-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem;
    height: 2.25rem;
    font-size: 0.65rem;
    font-weight: 600;
    border-radius: 6px;
    border: 1.5px solid var(--border-input);
    background: var(--bg-input);
    color: var(--text-muted);
    cursor: pointer;
    transition: all 0.15s;
}
.type-chip:has(input:checked) {
    border-color: var(--border-focus);
    background: var(--bg-hover);
    color: var(--text-heading);
}
.type-chip-icon {
    width: 0.85rem;
    height: 0.6rem;
    flex-shrink: 0;
}

/* ── NEW: Progress bar overlay on chart bars ───────────────── */
.progress-fill {
    pointer-events: none;
    opacity: 0.35;
}

/* Completion percentage drawn on a task/group bar when the Progress tool is
   on. Colour + halo are set inline per bar (contrast-on-bar for the inside
   variant, muted+halo for the narrow-bar outside variant); this rule only
   carries shared traits. */
.bar-progress-label,
.bar-progress-label-outside {
    pointer-events: none;
    letter-spacing: 0.02em;
}

/* ── NEW: Milestone diamond in chart ───────────────────────── */
.milestone-diamond {
    cursor: pointer;
    stroke: var(--border-card);
    stroke-width: 1.5;
    stroke-linejoin: round;
}

/* ── NEW: Group summary bar ────────────────────────────────── */
.group-bar {
    cursor: pointer;
}
.group-bracket {
    pointer-events: none;
}

/* ── NEW: Dependency arrows ────────────────────────────────── */
.dependency-arrows path {
    stroke-width: 1.5;
    fill: none;
}

/* ── NEW: Connect mode ─────────────────────────────────────── */
.snap-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border-input);
    cursor: pointer;
    background: var(--bg-input);
    color: var(--text-muted);
    transition: all 0.25s ease;
    padding: 0;
}

.snap-toggle:hover {
    border-color: var(--border-focus);
    color: var(--text-heading);
}

.snap-toggle.active {
    background: var(--accent, #6366f1);
    color: var(--btn-add-text, white);
    border-color: var(--accent, #6366f1);
    transform: scale(1.05);
}

.snap-toggle.active:hover {
    background: var(--accent-dark, #4f46e5);
    border-color: var(--accent-dark, #4f46e5);
}

.tool-label-btn {
    font-family: 'IBM Plex Sans', sans-serif;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.03em;
}

#todayToggle {
    font-family: 'IBM Plex Sans', sans-serif;
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.03em;
}

.snap-guide-line {
    stroke: #06b6d4;
    stroke-width: 1.5;
    stroke-dasharray: 4 3;
    pointer-events: none;
    opacity: 0.9;
}

/* ── Drag Tooltip ─────────────────────────────────────────────── */
.drag-tooltip {
    position: fixed;
    display: none;
    transform: translateX(-50%);
    padding: 0.32rem 0.75rem;
    border-radius: 12px;
    font-family: 'IBM Plex Sans', sans-serif;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
    text-align: center;
    line-height: 1.25;
    pointer-events: none;
    z-index: 10000;
    /* Neutral-dark surface so the green/red delta tokens are legible (a green pill
       would hide the "added days" green). Works over light and dark charts. */
    background: #1f2530;
    color: #f1f5f9;
    border: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
}
/* Second line of the drag tooltip: day delta + row (e.g. "+7 days · Row 3 → 5"). */
.drag-tooltip .dt-sub {
    font-size: 0.64rem;
    font-weight: 600;
    margin-top: 1px;
}
.drag-tooltip .dt-sep { color: #f1f5f9; opacity: 0.4; margin: 0 5px; }
.drag-tooltip .dt-up { color: #4ade80; }    /* later dates / row increase */
.drag-tooltip .dt-down { color: #f87171; }  /* earlier dates / row decrease */
.drag-tooltip .dt-flat { color: #cbd5e1; }  /* no change */

.snap-toggle svg {
    width: 0.85rem;
    height: 0.85rem;
}

/* btn-connect styles removed — Link button now uses .snap-toggle */
.gantt-chart-container.connect-mode {
    cursor: crosshair;
}
/* Armed a typed link from a bar handle — same crosshair affordance until the
   user clicks the target task (or presses Escape to cancel). */
.gantt-chart-container.linking-active {
    cursor: crosshair;
}

/* ── Tool tiles — compact tactile switches (Link · Snap · Today · Events) ──
   icon square + label + on/off track. Replaces the old .snap-toggle
   abbreviation buttons (L / TOD / EVT). State driven by .active (toggled in JS). */
.tool-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.13rem;
    min-width: 2.85rem;
    padding: 0.28rem 0.35rem 0.24rem;
    border-radius: 11px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    color: var(--text-muted);
    cursor: pointer;
    transition: all 0.22s cubic-bezier(.22, 1, .36, 1);
}
.tool-tile:hover {
    border-color: var(--border-focus);
    color: var(--text-heading);
}
.tool-tile:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.tool-tile-ico {
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 7px;
    display: grid;
    place-items: center;
    background: var(--bg-input);
    color: var(--text-muted);
    transition: all 0.22s ease;
}
.tool-tile-ico svg {
    width: 0.85rem;
    height: 0.85rem;
    fill: none;
    stroke: currentColor;
}
.tool-tile-name {
    font-size: 0.55rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.01em;
    color: var(--text-muted);
    white-space: nowrap;
    transition: color 0.22s ease;
}
.tool-tile-track {
    width: 1.35rem;
    height: 0.34rem;
    border-radius: 999px;
    background: var(--border);
    position: relative;
    transition: background 0.22s ease;
}
.tool-tile-knob {
    position: absolute;
    top: 50%;
    left: 1.5px;
    transform: translateY(-50%);
    width: 0.26rem;
    height: 0.26rem;
    border-radius: 50%;
    background: var(--bg-body);
    box-shadow: 0 1px 1.5px rgba(0, 0, 0, 0.35);
    transition: left 0.22s cubic-bezier(.22, 1, .36, 1), background 0.22s ease;
}
.tool-tile:hover .tool-tile-name {
    color: var(--text-heading);
}
.tool-tile.active {
    border-color: var(--accent);
    background: var(--accent-soft);
}
.tool-tile.active .tool-tile-ico {
    background: var(--accent);
    color: var(--btn-add-text, #fff);
}
.tool-tile.active .tool-tile-name {
    color: var(--text-heading);
}
.tool-tile.active .tool-tile-track {
    background: var(--accent);
}
.tool-tile.active .tool-tile-knob {
    left: calc(100% - 0.26rem - 1.5px);
    background: #fff;
}

/* ── NEW: Dialog progress slider ───────────────────────────── */
/* pill container for the dialog progress slider, matching the
   shadow language of the zoom-slider-box on the chart. Uses --bg-input so
   the pill stands clear of the dialog's own --bg-card body in both themes. */
/* #141: progress and export-quality are numeric steppers now, not sliders. */
.dlg-num { max-width: 110px; }
.dlg-progress-box {
    display: flex;
    align-items: center;
    margin-top: 0.35rem;
    padding: 0.45rem 0.85rem;
    border: 1px solid var(--border-input);
    border-radius: 9999px;
    background: var(--bg-input);
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
}

.dlg-progress-slider {
    width: 100%;
    height: 6px;
    -webkit-appearance: none;
    appearance: none;
    background: var(--border-input);
    border-radius: 3px;
    outline: none;
    margin-top: 0;
}
.dlg-progress-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--border-focus);
    cursor: pointer;
}
.dlg-progress-slider::-moz-range-thumb {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--border-focus);
    cursor: pointer;
    border: none;
}

/* ── NEW: Dialog select (parent dropdown) ──────────────────── */
.dlg-select {
    font-size: 0.8rem;
    padding: 0.35rem 0.5rem;
}

/* ── NEW: Dialog dependency list ───────────────────────────── */
.dlg-deps-list {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    max-height: 100px;
    overflow-y: auto;
    font-size: 0.75rem;
}
.dlg-dep-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.4rem;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    border-radius: 6px;
}
.dlg-dep-row select {
    font-size: 0.7rem;
    padding: 0.1rem 0.25rem;
    border-radius: 4px;
    border: 1px solid var(--border-input);
    background: var(--bg-card);
    color: var(--text-body);
}
.dlg-dep-name {
    flex: 1;
    color: var(--text-heading);
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.dlg-dep-remove {
    color: var(--text-faint);
    cursor: pointer;
    font-size: 0.9rem;
    line-height: 1;
    padding: 0 0.2rem;
    border: none;
    background: none;
}
.dlg-dep-remove:hover { color: #ef4444; }
.dlg-dep-empty {
    color: var(--text-faint);
    font-size: 0.7rem;
    font-style: italic;
}

/* #46 follow-up — editable dependency rows in the Edit Task dialog:
   [source name] [FS SS FF SF chips] [− offset +] [×]. Reuses the popover's
   .dep-lag-btn / .dep-lag-val stepper pieces at a compact size. */
.dlg-deps-list { max-height: 132px; }
.dlg-dep-types {
    display: inline-flex;
    gap: 2px;
    flex: 0 0 auto;
}
.dlg-dep-type-chip {
    border: none;
    background: var(--bg-body);
    color: var(--text-muted);
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    padding: 2px 5px;
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}
.dlg-dep-type-chip:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}
.dlg-dep-type-chip.active {
    background: var(--accent, #059669);
    color: #fff;
}
.dlg-dep-lag {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    flex: 0 0 auto;
}
.dlg-dep-lag .dep-lag-btn {
    width: 1.2rem;
    height: 1.2rem;
    font-size: 0.78rem;
}
.dlg-dep-lag .dep-lag-val {
    min-width: 2.1rem;
    font-size: 0.68rem;
}

/* ── Ko-fi support button ─────────────────────────────────── */
.kofi-btn {
    display: inline-flex;
    align-items: center;
    margin-left: 0.5rem;
    transition: transform 0.15s ease, filter 0.15s ease;
    text-decoration: none;
    line-height: 0;
}
.kofi-btn:hover {
    transform: translateY(-1px);
    filter: brightness(1.05);
}
.kofi-btn img {
    display: block;
    height: 36px;
    max-height: 36px;
    width: auto;
    max-width: 200px;
    border-radius: 6px;
}

/* ── Interactive linking ──────────────────────────────────── */
.link-handle {
    stroke: #0f172a;
    stroke-width: 1.5px;
    opacity: 0.85;
    transition: opacity 0.15s, r 0.15s;
}
.link-handle-right {
    fill: #39FF14;      /* neon green — task END, connection source */
    cursor: crosshair;
}
.link-handle-left {
    fill: #00BFFF;      /* bright blue — task START, connection target */
    cursor: default;
}
.link-handle:hover {
    opacity: 1;
    r: 7;
}
.link-rubber-band {
    stroke: #39FF14;
    stroke-width: 2px;
    stroke-linecap: round;
    pointer-events: none;
}

.dep-arrow-group.hover .dep-arrow {
    stroke-width: 2.5 !important;
    stroke: #38bdf8 !important;   /* light-blue = edit / neutral hover */
}
.dep-arrow-group.delete-hover .dep-arrow {
    stroke: #ef4444 !important;   /* red only while hovering the delete × */
}
.dep-delete-badge {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
    cursor: pointer;
}
.dep-delete-badge circle {
    fill: #ef4444;
    stroke: #fff;
    stroke-width: 1.5;
}
.dep-delete-badge text {
    fill: #fff;
    font-size: 12px;
    font-weight: 700;
    pointer-events: none;
    user-select: none;
}
.dep-arrow-group.hover .dep-delete-badge {
    opacity: 1;
    pointer-events: all;
}
/* Edit (pencil) + delete (×) badges revealed on hover. */
.dep-edit-badge {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
    cursor: pointer;
}
.dep-edit-badge circle {
    fill: #38bdf8;
    stroke: #fff;
    stroke-width: 1.5;
}
.dep-edit-icon path {
    fill: none;
    stroke: #fff;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.dep-arrow-group.hover .dep-edit-badge {
    opacity: 1;
    pointer-events: all;
}
.dep-delete-badge { cursor: pointer; }
/* Clicking anywhere on the line opens the link editor (all pointers). */
.dep-arrow-hit { cursor: pointer; }

/* Shared rounded box behind the pencil + × hover badges. */
.dep-badge-pill {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
    fill: var(--bg-card);
    stroke: var(--border-card);
    stroke-width: 1;
}
.dep-arrow-group.hover .dep-badge-pill { opacity: 1; }

/* Dependency-type popover — retype a link among FS/SS/FF/SF. */
.dep-type-popover {
    position: fixed;
    z-index: 1200;
    min-width: 188px;
    padding: 6px;
    border-radius: 10px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
    animation: presencePopIn 0.14s ease;
}
.dep-type-popover-title {
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    padding: 4px 6px 6px;
}
.dep-type-row {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 6px 7px;
    border: none;
    background: none;
    border-radius: 7px;
    cursor: pointer;
    font-size: 0.8rem;
    color: var(--text);
    text-align: left;
}
.dep-type-row:hover { background: var(--bg-hover); }
.dep-type-row.active { background: var(--bg-hover); }
.dep-type-code {
    flex: 0 0 auto;
    min-width: 26px;
    text-align: center;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    padding: 2px 5px;
    border-radius: 5px;
    background: var(--bg-body);
    color: var(--text-muted);
}
.dep-type-row.active .dep-type-code {
    background: var(--accent);
    color: #fff;
}
.dep-type-label { flex: 1 1 auto; white-space: nowrap; }
.dep-type-row.active .dep-type-label { font-weight: 600; }
/* Create-from-edge popover: lead with the app's emerald accent. (The edit
   popover keeps its neutral look — only this `--create` variant is tinted.) */
.dep-type-popover-create .dep-type-popover-title { color: var(--accent); }
.dep-type-popover-create .dep-type-code {
    background: var(--accent);
    color: #fff;
}
.dep-type-popover-create .dep-type-row:hover { background: var(--accent-soft); }
.dep-type-popover-create .dep-type-row:hover .dep-type-label { color: var(--accent-dark); }
.dep-type-delete {
    margin-top: 4px;
    border-top: 1px solid var(--border-card);
    border-radius: 0 0 7px 7px;
    color: #ef4444;
}
.dep-type-delete:hover { background: rgba(239, 68, 68, 0.1); }

/* ── #46 lag/lead offset stepper (inside the link popover) ────── */
.dep-lag-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 7px 2px;
    margin-top: 4px;
    border-top: 1px solid var(--border-card);
}
.dep-lag-label {
    flex: 1 1 auto;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
}
.dep-lag-btn {
    flex: 0 0 auto;
    width: 1.5rem;
    height: 1.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-input);
    background: var(--bg-input);
    color: var(--text-heading);
    border-radius: 999px;
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s;
}
.dep-lag-btn:hover {
    border-color: var(--accent, #059669);
    background: var(--accent-soft, rgba(5, 150, 105, 0.08));
}
.dep-lag-val {
    flex: 0 0 auto;
    min-width: 2.6rem;
    text-align: center;
    font-size: 0.78rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}
.dep-lag-val.nonzero { color: var(--accent, #059669); }
.dep-lag-hint {
    padding: 1px 7px 5px;
    font-size: 0.62rem;
    color: var(--text-faint);
    text-align: right;
}

/* Offset chip riding the dependency arrow ("+2d" / "−1d"). Pure annotation —
   never intercepts the pointer (the arrow underneath opens the editor). */
.dep-lag-chip { pointer-events: none; }
.dep-lag-chip rect {
    fill: var(--bg-card);
    stroke: var(--border-card);
    stroke-width: 1;
}
.dep-lag-chip text {
    font-size: 10px;
    font-weight: 700;
    fill: var(--text-muted);
}
/* Chips live in their own top-most layer (nothing may paint over them);
   hovering their arrow highlights them via the .hover class set in JS. */
.dep-lag-chip-layer { pointer-events: none; }
.dep-lag-chip.hover rect { stroke: var(--accent, #059669); }
.dep-lag-chip.hover text { fill: var(--accent, #059669); }

/* ── NEW: Connect mode toast ───────────────────────────────── */
.connect-toast {
    position: fixed;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    background: #1e293b;
    color: white;
    padding: 0.5rem 1rem;
    border-radius: 8px;
    font-size: 0.8rem;
    font-weight: 500;
    z-index: 9999;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    pointer-events: none;
}

/* ── SEO Footer ───────────────────────────────────────────────── */
.seo-footer {
    position: relative;
    flex-shrink: 0;
    background: var(--bg-body);
    border-top: 1px solid var(--border-card);
    color: var(--text-body);
    font-size: 0.6rem;
    line-height: 1.4;
}
.seo-footer-toggle-bar {
    display: flex;
    justify-content: center;
    padding: 0.25rem 0;
    cursor: pointer;
    border-bottom: 1px solid var(--border-card);
}
.seo-footer-toggle-bar:hover {
    background: var(--bg-hover);
}
.footer-toggle-btn {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    background: var(--border-focus);
    border: none;
    color: #ffffff;
    cursor: pointer;
    padding: 0.25rem 0.85rem;
    border-radius: 0.5rem;
    font-family: inherit;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    transition: all 0.25s ease;
    box-shadow: 0 1px 3px rgba(99, 102, 241, 0.3);
}
.footer-toggle-btn:hover {
    background: #4f46e5;
    box-shadow: 0 2px 8px rgba(99, 102, 241, 0.45);
    transform: scale(1.05);
}
.footer-toggle-btn.collapsed {
    background: #f59e0b;
    box-shadow: 0 1px 3px rgba(245, 158, 11, 0.3);
}
.footer-toggle-btn.collapsed:hover {
    background: #d97706;
    box-shadow: 0 2px 8px rgba(245, 158, 11, 0.45);
}
.footer-toggle-icon {
    width: 12px;
    height: 12px;
    transition: transform 0.2s;
}
.footer-toggle-icon.collapsed {
    transform: rotate(180deg);
}
.seo-footer-inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 2rem 0.5rem;
    overflow: auto;
    transition: max-height 0.3s ease, opacity 0.2s ease;
    max-height: 80vh;
    opacity: 1;
}
.seo-footer-inner.collapsed {
    max-height: 0;
    opacity: 0;
    padding-bottom: 0;
    overflow: hidden;
}
.seo-footer-section {
    margin-top: 1rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border-card);
}
.seo-comparison-wrap {
    overflow-x: auto;
}
.seo-comparison {
    border-collapse: collapse;
    width: 100%;
    margin: 0.4rem 0 0.3rem;
    font-size: 0.6rem;
    color: var(--text-body);
}
.seo-comparison th,
.seo-comparison td {
    border: 1px solid var(--border-card);
    padding: 0.25rem 0.4rem;
    text-align: left;
}
.seo-comparison th {
    background: var(--bg-input);
    color: var(--text-heading);
    font-weight: 600;
    font-size: 0.55rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.seo-comparison td.yes {
    color: #16a34a;
    font-weight: 600;
}
.seo-footer-fineprint {
    font-size: 0.55rem;
    color: var(--text-faint);
    margin: 0.2rem 0 0;
}
.seo-faq {
    margin: 0.3rem 0 0;
    font-size: 0.6rem;
    color: var(--text-body);
}
.seo-faq dt {
    font-weight: 600;
    color: var(--text-heading);
    margin: 0.45rem 0 0.1rem;
}
.seo-faq dd {
    margin: 0 0 0.2rem;
    color: var(--text-muted);
}

/* ── About Button (top bar) ──────────────────────────────────── */
.btn-about {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.6rem;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.7rem;
    font-weight: 500;
    border-radius: 0.375rem;
    border: 1px solid var(--border-input);
    cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
}
.btn-about:hover {
    background: var(--bg-input);
    border-color: var(--border-focus);
    color: var(--text-heading);
}

/* ── About Dialog ────────────────────────────────────────────── */
.about-dialog {
    max-width: min(780px, calc(100vw - 2rem));
}
.about-body {
    gap: 1.1rem;
    max-height: calc(100dvh - 6rem);
    overflow-y: auto;
}
.about-section {
    border-top: 1px solid var(--border-card);
    padding-top: 0.8rem;
}
.about-section:first-child {
    border-top: none;
    padding-top: 0;
}
.about-section h2 {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-heading);
    margin: 0 0 0.4rem;
}
.about-section p {
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--text-body);
    margin: 0 0 0.5rem;
}
.about-section .seo-comparison {
    font-size: 0.7rem;
}
.about-section .seo-faq {
    font-size: 0.75rem;
}
.about-section .seo-faq dt {
    margin-top: 0.7rem;
    font-size: 0.78rem;
}
.about-section .seo-faq dd {
    line-height: 1.5;
}
.about-footer-meta {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.6rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border-card);
    font-size: 0.6rem;
    color: var(--text-faint);
}
.about-version {
    opacity: 0.6;
    letter-spacing: 0.05em;
    font-weight: 500;
}
.seo-footer.footer-collapsed .seo-footer-toggle-bar {
    border-bottom: none;
}
.seo-footer-columns {
    display: flex;
    gap: 3rem;
}
.seo-footer-col {
    flex: 1;
}
.seo-footer h2 {
    font-size: 0.55rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-faint);
    margin: 0 0 0.2rem;
}
.seo-footer p {
    margin: 0 0 0.15rem;
    color: var(--text-muted);
}
.seo-footer code {
    background: var(--bg-input);
    padding: 0.05rem 0.25rem;
    border-radius: 3px;
    font-size: 0.55rem;
}
.seo-footer-bottom {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 0.4rem;
    padding-top: 0.35rem;
    border-top: 1px solid var(--border-card);
    font-size: 0.55rem;
    color: var(--text-faint);
}
.seo-footer-version {
    opacity: 0.5;
    letter-spacing: 0.05em;
    font-weight: 500;
}
.seo-footer-langs {
    opacity: 0.7;
}

/* ── Share Button & Dialog ──────────────────────────────────────── */
.share-wrapper {
    position: relative;
}
.share-header {
    position: relative;
}

/* The share dialog — "message-first" (2026-09 design system): an editable
   ready-to-send message in a chat-bubble textarea, a green Copy message
   primary + outlined Copy link, then the channel tiles. Green appears only
   on the primary action and the heart tile. */
.share-dialog {
    max-width: min(548px, calc(100vw - 2rem));
    padding: 0;
    border-radius: 18px;
    overflow: hidden;
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
}
.share-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px 12px 18px;
    border-bottom: 1px solid var(--border-card);
}
.share-head-tile {
    width: 30px;
    height: 30px;
    border-radius: 10px;
    background: var(--accent-soft);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}
.share-head-text { flex: 1 1 auto; min-width: 0; }
.share-head-title { font-size: 14.5px; font-weight: 700; color: var(--text-heading); margin: 0; }
.share-head-sub { font-size: 10.5px; color: var(--text-muted); }
.share-head-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    border: 0;
    background: transparent;
    color: var(--icon-color, var(--text-muted));
    cursor: pointer;
    flex: none;
}
.share-head-x:hover { color: var(--text-heading); background: var(--bg-input); }
.share-body { padding: 16px 18px 18px; }
/* The editable ready-to-send message, styled as a chat bubble. */
.share-msg {
    width: 100%;
    display: block;
    border: 1px solid var(--border-input);
    border-radius: 16px 16px 16px 4px;
    background: var(--bg-input);
    color: var(--text-input);
    font-family: inherit;
    font-size: 13.5px;
    line-height: 1.5;
    padding: 11px 13px;
    resize: none;
    outline: none;
    min-height: 84px;
}
.share-msg:focus { border-color: var(--border-focus); }
.share-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.share-btn-primary,
.share-btn-ghost {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border-radius: 10px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.share-btn-primary {
    border: 0;
    background: var(--btn-add-bg);
    color: var(--btn-add-text);
}
.share-btn-primary:hover { background: var(--accent-dark); }
.share-btn-ghost {
    border: 1px solid var(--border-input);
    background: var(--bg-card);
    color: var(--text-body);
}
.share-btn-ghost:hover { background: var(--bg-input); }
.share-divider {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 16px 0 12px;
}
.share-divider i { flex: 1; height: 1px; background: var(--border-card); }
.share-divider span {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.share-platforms {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 14px;
}
.share-platform {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    text-decoration: none;
    color: var(--text-body);
    min-width: 52px;
}
.share-platform-tile {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 12px;
    background: var(--bg-input);
    border: 1px solid var(--border-card);
    color: var(--text-body);
    transition: border-color 0.15s, background 0.15s;
}
.share-platform:hover .share-platform-tile { border-color: var(--border-input); background: var(--bg-hover); }
.share-platform-label { font-size: 10.5px; color: var(--text-muted); }
/* Native share sheet available (touch devices): one Share button replaces
   the channel row. */
.share-dialog.share-native-mode .share-divider,
.share-dialog.share-native-mode .share-platforms { display: none; }
.share-dialog.share-native-mode #shareNativeBtn { display: inline-flex; }

/* ── Event Bars ───────────────────────────────────────────────── */
.event-bar {
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.15));
    transition: filter 0.1s;
}
.event-bar:hover {
    filter: drop-shadow(0 2px 4px rgba(0,0,0,0.25));
}
.event-shadow {
    pointer-events: none;
}
.events-label-text {
    text-decoration: none !important;
    pointer-events: none;
}

.events-row-bg {
    transition: fill 0.15s;
}
.events-row-bg:hover {
    fill: var(--bg-hover, rgba(99, 102, 241, 0.08)) !important;
}

/* ============================================================================
   EXPORT-AS DROPDOWN — Image / Excel chooser
   ============================================================================ */
.export-wrapper {
    position: relative;
    display: inline-flex;
}
/* Small bordered buttons of the project bar (My charts, New project, Collaborate,
   Report chart). They used to share this base with the header; the header now
   has its own .hdr-* rules, so this base serves the project bar alone. */
.btn-export-sm {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.6rem;
    background: var(--bg-input);
    color: var(--text-body);
    font-size: 0.7rem;
    font-weight: 500;
    border-radius: 0.375rem;
    border: 1px solid var(--border-input);
    cursor: pointer;
    text-decoration: none;
    transition: all 0.15s;
    white-space: nowrap;
}
.btn-export-sm:hover {
    background: var(--bg-hover);
    border-color: var(--border-focus);
    color: var(--text-heading);
    text-decoration: none;
}

.export-wrapper .btn-export-sm {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.export-caret {
    margin-left: 0.1rem;
    opacity: 0.7;
    transition: transform 0.15s ease;
}
.export-wrapper .btn-export-sm[aria-expanded="true"] .export-caret {
    transform: rotate(180deg);
}
.export-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.5rem;
    padding: 0.25rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
    z-index: 1500;
    animation: dialogIn 0.12s ease-out;
    min-width: 12rem;
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.export-menu.hidden { display: none; }
.export-menu-option {
    display: grid;
    grid-template-columns: 18px 1fr auto;
    align-items: center;
    gap: 0.5rem;
    padding: 0.45rem 0.55rem;
    border: none;
    background: transparent;
    color: var(--text-body);
    cursor: pointer;
    border-radius: 0.35rem;
    text-align: left;
    transition: background 0.1s;
}
.export-menu-option:hover,
.export-menu-option:focus-visible {
    background: var(--bg-hover);
    outline: none;
}
.export-menu-option svg {
    width: 16px;
    height: 16px;
    color: var(--text-muted);
}
.export-menu-option:hover svg,
.export-menu-option:focus-visible svg {
    color: var(--border-focus);
}
.export-menu-label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-heading);
    white-space: nowrap;
}
.export-menu-hint {
    font-size: 0.68rem;
    color: var(--text-muted);
    font-weight: 400;
    white-space: nowrap;
    padding-left: 0.75rem;
}

/* ============================================================================
   EXCEL EXPORT DIALOG
   ============================================================================ */
.excel-dialog {
    max-width: 580px;
    width: 100%;
}
.excel-dialog-body {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
/* ── MCP Connection modal (About/Templates-style shell, narrower) ── */
.mcp-modal { max-width: 560px; width: min(560px, calc(100vw - 2rem)); }
.mcp-modal-body { padding: 1.25rem 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 1.05rem; }

/* "See it in action" animated visuals — mirrors the /mcp page cards. Decorative
   (aria-hidden in markup); reuses the mcpStepIn entrance + looping mf-* anims. */
.mcp-feats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 2px 0; }
.mcp-feat { border: 1px solid var(--border-card); border-radius: 12px; overflow: hidden; background: var(--bg-input); animation: mcpStepIn 0.34s ease backwards; }
.mcp-feat:nth-child(2) { animation-delay: 0.08s; }
.mcp-feat:nth-child(3) { animation-delay: 0.16s; }
.mcp-feat-visual { position: relative; height: 88px; overflow: hidden;
    background:
        linear-gradient(to right, var(--border-card) 1px, transparent 1px) 0 0 / 26px 100%,
        linear-gradient(to bottom, var(--border-card) 1px, transparent 1px) 0 0 / 100% 20px,
        var(--bg-card); }
.mf-mcp { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 8px; height: 100%; padding: 0 12px; }
.mf-bubble { display: inline-flex; gap: 4px; align-items: center; padding: 6px 8px; border-radius: 10px 10px 10px 3px; background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent); }
.mf-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--accent-dark); animation: mfDots 1.4s ease-in-out infinite; }
.mf-dot:nth-child(2) { animation-delay: 0.18s; } .mf-dot:nth-child(3) { animation-delay: 0.36s; }
@keyframes mfDots { 0%, 60%, 100% { opacity: 0.35; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-2px); } }
.mf-bar { height: 10px; border-radius: 999px; background: var(--accent); display: inline-flex; align-items: center; justify-content: flex-end; padding-right: 3px; overflow: hidden; animation: mfGrow 3.6s ease-in-out infinite; }
.mf-check { width: 7px; height: 7px; flex: 0 0 auto; }
@keyframes mfGrow { 0%, 25% { width: 0; opacity: 0; } 35% { opacity: 1; } 60%, 88% { width: 80px; opacity: 1; } 100% { width: 80px; opacity: 0; } }
.mf-bars { position: absolute; inset: 0; }
.mf-tl { position: absolute; height: 8px; border-radius: 999px; transform-origin: left center; animation: mfDraw 3.4s ease-in-out infinite; }
.mf-tl-1 { left: 12%; top: 20px; width: 42%; background: var(--accent); opacity: 0.85; }
.mf-tl-2 { left: 26%; top: 42px; width: 50%; background: color-mix(in srgb, var(--accent) 55%, var(--border-card)); animation-delay: 0.5s; }
.mf-tl-3 { left: 40%; top: 64px; width: 32%; background: color-mix(in srgb, var(--accent) 35%, var(--border-card)); animation-delay: 1s; }
@keyframes mfDraw { 0%, 8% { transform: scaleX(0); opacity: 0; } 22% { opacity: 1; } 78% { transform: scaleX(1); opacity: 1; } 96%, 100% { transform: scaleX(1); opacity: 0; } }
.mf-sync { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.mf-sync-core { width: 26px; height: 26px; color: var(--accent-dark); position: relative; z-index: 1; }
.mf-ring { position: absolute; width: 36px; height: 36px; border-radius: 50%; border: 2px solid var(--accent); opacity: 0; animation: mfPulse 2.6s ease-out infinite; }
.mf-ring:nth-child(2) { animation-delay: 1.3s; }
@keyframes mfPulse { 0% { transform: scale(0.5); opacity: 0.7; } 70% { transform: scale(1.3); opacity: 0; } 100% { transform: scale(1.3); opacity: 0; } }
@media (max-width: 560px) { .mcp-feats { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) { .mf-dot, .mf-bar, .mf-tl, .mf-ring { animation: none !important; } .mf-bar { width: 80px; } .mf-tl { transform: none; opacity: 1; } }
.mcp-intro { margin: 0; font-size: 0.85rem; line-height: 1.55; color: var(--text-body); }
.mcp-account { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; padding: 0.55rem 0.95rem; background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent); border-radius: 999px; font-size: 0.8rem; align-self: flex-start; max-width: 100%; }
.mcp-account-ico { width: 13px; height: 13px; color: var(--accent-dark); flex: 0 0 auto; }
.mcp-account-label { color: var(--text-muted); }
.mcp-account-email { font-weight: 600; color: var(--accent-dark); overflow-wrap: anywhere; }

/* client picker: choose Claude Code vs Claude Desktop first,
   then only that path's steps render (animated in). */
.mcp-picker { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 6px; }
.mcp-picker-tile {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px; text-align: left;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--card-bg);
    cursor: pointer;
    transition: border-color 0.18s ease, background 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}
.mcp-picker-tile:hover { border-color: var(--accent-dark); transform: translateY(-1px); }
.mcp-picker-tile.active {
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: inset 0 0 0 1px var(--accent);
}
.mcp-picker-ico {
    width: 34px; height: 34px; flex: 0 0 auto;
    border-radius: 10px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-dark);
    transition: background 0.18s ease, color 0.18s ease, transform 0.2s ease;
}
.mcp-picker-tile.active .mcp-picker-ico { background: var(--accent); color: #fff; transform: scale(1.07); }
.mcp-picker-ico svg { width: 16px; height: 16px; }
.mcp-picker-texts { min-width: 0; }
.mcp-picker-name { display: block; font-size: 0.8rem; font-weight: 700; color: var(--text-heading); white-space: nowrap; }
.mcp-picker-note { display: block; font-size: 0.68rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Numbered steps with a connector line; cascade in on pane switch. */
.mcp-pane { display: flex; flex-direction: column; gap: 0.9rem; }
.mcp-pane .mcp-step { animation: mcpStepIn 0.3s ease backwards; }
.mcp-pane .mcp-step:nth-child(2) { animation-delay: 0.08s; }
.mcp-pane .mcp-legacy { animation: mcpStepIn 0.3s ease backwards; animation-delay: 0.16s; }
@keyframes mcpStepIn {
    from { opacity: 0; transform: translateY(7px); }
    to   { opacity: 1; transform: none; }
}
.mcp-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1rem; }
.mcp-step { position: relative; padding-left: 36px; }
.mcp-step-num {
    position: absolute; left: 0; top: -1px;
    width: 24px; height: 24px;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-dark);
    font-size: 0.72rem; font-weight: 800;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}
.mcp-step:not(:last-child)::before {
    content: '';
    position: absolute; left: 11.5px; top: 27px; bottom: -16px;
    width: 1.5px;
    background: var(--border-card);
}
.mcp-steps li { color: var(--text-body); }
.mcp-step-title { font-weight: 600; font-size: 0.82rem; color: var(--text-heading); }
.mcp-step-hint { margin: 0.3rem 0 0; font-size: 0.78rem; line-height: 1.5; color: var(--text-muted); }
.mcp-keynote { margin: 0; font-size: 0.74rem; line-height: 1.5; color: var(--text-muted); }

/* Legacy (manual config) accordion — collapsed by default. */
.mcp-legacy { border: 1px dashed var(--border-card); border-radius: 12px; padding: 0.3rem 0.6rem; }
.mcp-legacy-toggle {
    display: flex; align-items: center; gap: 0.45rem;
    width: 100%; padding: 0.3rem 0.15rem;
    background: none; border: 0; cursor: pointer;
    font-size: 0.74rem; font-weight: 600; text-align: left;
    color: var(--text-muted);
    transition: color 0.15s ease;
}
.mcp-legacy-toggle:hover { color: var(--text-heading); }
.mcp-legacy-chevron { width: 12px; height: 12px; flex: 0 0 auto; transition: transform 0.22s ease; }
.mcp-legacy.open .mcp-legacy-chevron { transform: rotate(90deg); }
.mcp-legacy-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.28s ease; }
.mcp-legacy.open .mcp-legacy-body { grid-template-rows: 1fr; }
.mcp-legacy-inner { overflow: hidden; min-height: 0; display: flex; flex-direction: column; gap: 0.45rem; padding: 0 0.15rem; }
.mcp-legacy.open .mcp-legacy-inner { padding-bottom: 0.5rem; }

@media (prefers-reduced-motion: reduce) {
    .mcp-pane .mcp-step, .mcp-pane .mcp-legacy { animation: none; }
    .mcp-legacy-body, .mcp-legacy-chevron, .mcp-picker-tile, .mcp-picker-ico { transition: none; }
}
.mcp-code-row { display: flex; gap: 0.5rem; align-items: stretch; margin-top: 0.5rem; }
.mcp-code { flex: 1; min-width: 0; font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 0.72rem; line-height: 1.5; background: var(--bg-input); border: 1px solid var(--border-input); border-radius: 12px; padding: 0.6rem 0.75rem; color: var(--text-input); white-space: pre-wrap; overflow-wrap: anywhere; }
.mcp-copy { flex: 0 0 auto; border-radius: 999px; align-self: flex-start; }
.mcp-endpoint-line { margin: 0; font-size: 0.76rem; color: var(--text-muted); overflow-wrap: anywhere; }
.mcp-guide-link { color: var(--title-accent); font-weight: 600; text-decoration: none; }
.mcp-guide-link:hover { text-decoration: underline; }
.mcp-code-inline { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.74rem; background: var(--bg-input); border-radius: 6px; padding: 0.1rem 0.4rem; color: var(--text-body); }

/* ──────────────────────────────────────────────────────────────────
   What's New: the card deck (2026-10-04 redesign, design_handoff_whats_new
   option 6a light / 6b dark). One 1180 × 640 window on the dialog shell:
   copy on the left, a three-card deck on the right that flips to the next
   feature, one dot per feature underneath (diamond = new in the current
   release, circle = earlier). Tokens are the app's own, so the dark theme
   swaps them like everywhere else. Markup: index.html #whatsNewOverlay.
   Data + behaviour: app.js WHATS_NEW_* / wnDeck*.
   ────────────────────────────────────────────────────────────────── */
.whatsnew-modal {
    width: min(1180px, calc(100vw - 2rem));
    height: min(640px, calc(100dvh - 2rem));
    max-width: none;
    max-height: none;
    padding: 0;
    border-radius: 18px;
    overflow: hidden;
    display: grid;
    grid-template-columns: 380px minmax(0, 1fr);
    color: var(--text-body);
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
    outline: none;
}
[data-theme$="-dark"] .whatsnew-modal { box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6); }

/* Left column: header row, the top card's copy centred in the free space,
   the controls at the foot. */
.whatsnew-side {
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: 30px 34px;
    border-right: 1px solid var(--border-card);
}
.whatsnew-side-head { display: flex; align-items: center; gap: 10px; }
.whatsnew-title { font-size: 18px; font-weight: 700; color: var(--text-heading); }
.whatsnew-version-chip {
    padding: 3px 11px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px; font-weight: 700;
}
/* "A newer version is available" notice: shown only once a new service
   worker has taken over the tab (app.js reflectWhatsNewUpdate). */
.whatsnew-update {
    display: flex; align-items: center; gap: 10px;
    margin-top: 16px;
    padding: 10px 10px 10px 14px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--bg-input);
    color: var(--text-body);
    font-size: 13px; font-weight: 500; line-height: 1.35;
    text-wrap: pretty;
}
.whatsnew-update > svg { flex: none; color: var(--accent); }
.whatsnew-update > span { flex: 1 1 auto; min-width: 0; }
.whatsnew-update-btn {
    flex: none;
    padding: 8px 14px;
    border-radius: 999px;
    font-size: 12.5px; font-weight: 700;
    white-space: nowrap;
}
.whatsnew-update.hidden { display: none; }
.whatsnew-copy { margin: auto 0; padding: 18px 0; min-height: 0; }
.whatsnew-eyebrow {
    font-size: 10.5px; font-weight: 700;
    letter-spacing: 0.09em; text-transform: uppercase;
    color: var(--accent);
}
.whatsnew-feature-title {
    margin-top: 8px;
    font-size: 26px; font-weight: 700; line-height: 1.25;
    color: var(--text-heading);
    text-wrap: pretty;
}
.whatsnew-feature-desc {
    margin: 10px 0 0;
    font-size: 15px; line-height: 1.65;
    color: var(--text-muted);
    text-wrap: pretty;
}
/* Admin announcements, when any are live; the slot collapses while empty. */
.whatsnew-announcements { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; max-height: 140px; overflow-y: auto; }
.whatsnew-announcements:empty { display: none; }
.whatsnew-side-foot { display: flex; align-items: center; gap: 10px; }
.whatsnew-nav-btn {
    width: 40px; height: 40px; padding: 0;
    border-radius: 11px;
    border: 1px solid var(--border-input);
    background: transparent;
    color: var(--text-body);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease;
}
.whatsnew-nav-btn:hover { background: var(--bg-input); }
.whatsnew-nav-btn svg { width: 16px; height: 16px; }
.whatsnew-counter {
    margin-left: 4px;
    font-size: 13px; font-weight: 600;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.whatsnew-gotit {
    margin-left: auto;
    padding: 12px 26px;
    border-radius: 12px;
    font-size: 14.5px; font-weight: 700;
}

/* Right column: the stage with the deck, the close button, the dot row. */
.whatsnew-stage {
    position: relative;
    display: flex; flex-direction: column;
    min-height: 0;
    background: var(--bg-body);
    overflow: hidden;
}
.whatsnew-close {
    position: absolute; top: 14px; right: 14px; z-index: 10;
    width: 34px; height: 34px; padding: 0;
    border: 0; border-radius: 10px;
    background: transparent;
    color: var(--icon-color);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease;
}
.whatsnew-close:hover { background: var(--bg-input); }
.whatsnew-close svg { width: 16px; height: 16px; }
.whatsnew-deck { flex: 1; position: relative; min-height: 0; perspective: 1600px; }
/* Card k (0 = top) sits at translateY(calc(-50% + k·-18px)) scale(1 − k·0.05),
   opacity 1 − k·0.25; app.js writes those inline. The flip rotates the top
   card 180° around Y; backface-visibility hides it past 90°, revealing the
   card behind, which moves up a slot with the same easing. */
.whatsnew-card {
    position: absolute; left: 60px; right: 60px; top: 50%;
    transform-style: preserve-3d;
    backface-visibility: hidden;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 14px;
    box-shadow: 0 18px 44px rgba(2, 6, 23, 0.18);
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.55s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}
[data-theme$="-dark"] .whatsnew-card { box-shadow: 0 18px 44px rgba(0, 0, 0, 0.6); }
.whatsnew-card-shot { aspect-ratio: 1000 / 400; background: var(--bg-input); }
.whatsnew-card-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.whatsnew-card-shot .whatsnew-shot-dark { display: none; }
[data-theme$="-dark"] .whatsnew-card-shot .whatsnew-shot-light { display: none; }
[data-theme$="-dark"] .whatsnew-card-shot .whatsnew-shot-dark { display: block; }
.whatsnew-card-foot {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 16px;
    border-top: 1px solid var(--border-card);
    font-size: 13.5px; font-weight: 600;
    color: var(--text-heading);
}
.whatsnew-card-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.whatsnew-card-area { color: var(--text-muted); font-weight: 500; white-space: nowrap; }
.whatsnew-vpill {
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
    background: var(--bg-input);
    color: var(--text-muted);
    border: 1px solid var(--border-input);
    white-space: nowrap;
}
.whatsnew-vpill.is-new { background: var(--accent); color: var(--btn-add-text, #fff); border-color: var(--accent); }

/* Dot row: one dot per feature, the legend pinned to the left. */
.whatsnew-dots-row {
    position: relative;
    display: flex; align-items: center; justify-content: center; gap: 7px;
    padding: 0 0 22px;
    flex-shrink: 0;
}
.whatsnew-legend {
    position: absolute; left: 24px; top: 0; height: 8px;
    display: flex; align-items: center; gap: 14px;
    font-size: 11.5px; font-weight: 600;
    color: var(--text-muted);
    pointer-events: none;
}
.whatsnew-legend-item { display: flex; align-items: center; gap: 6px; }
.whatsnew-legend-diamond { width: 7px; height: 7px; background: var(--accent); border-radius: 2px; transform: rotate(45deg); }
.whatsnew-legend-dot { width: 7px; height: 7px; background: var(--border-input); border-radius: 999px; }
.whatsnew-dots { display: flex; align-items: center; gap: 7px; }
.whatsnew-dot {
    width: 8px; height: 8px; padding: 0;
    border: 0; border-radius: 999px;
    background: var(--border-input);
    cursor: pointer;
    transition: width 0.3s ease, background 0.3s ease, transform 0.3s ease;
}
.whatsnew-dot.is-new { border-radius: 2px; background: var(--accent); transform: rotate(45deg); }
.whatsnew-dot.is-active { width: 24px; transform: none; background: var(--text-muted); }
.whatsnew-dot.is-new.is-active { background: var(--accent); }
.whatsnew-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Reduced motion (the OS setting or the a11y panel's switch): no flip, a short
   cross-fade instead; app.js also leaves the auto-advance off. */
@media (prefers-reduced-motion: reduce) {
    .whatsnew-card { transition: transform 0.25s ease, opacity 0.25s ease; }
    .whatsnew-dot { transition: none; }
}
[data-a11y-motion="reduce"] .whatsnew-card { transition: transform 0.25s ease, opacity 0.25s ease; }
[data-a11y-motion="reduce"] .whatsnew-dot { transition: none; }

/* Narrow screens: the two columns stack and the deck keeps its proportions. */
@media (max-width: 1000px) {
    .whatsnew-modal { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }
    .whatsnew-side { padding: 20px 22px; border-right: 0; border-bottom: 1px solid var(--border-card); }
    .whatsnew-copy { margin: 0; padding: 12px 0; }
    .whatsnew-feature-title { font-size: 20px; }
    .whatsnew-feature-desc { font-size: 13.5px; line-height: 1.5; }
    .whatsnew-card { left: 24px; right: 24px; }
    .whatsnew-legend { display: none; }
}
@media (max-width: 760px) {
    .whatsnew-modal { height: calc(100dvh - 2rem); }
    .whatsnew-side-foot { flex-wrap: wrap; }
}



/* ── Spreadsheet/project-file export dialogs — shared content ──────
   ".excel-section" / ".excel-format-card" / ".excel-cols-*" style the
   INSIDE of all four ".excel-dialog" shells (Export as Spreadsheet, plus
   the MSP and TJP project-file exporters -- see index.html #excelDialog /
   #ganttExportDialog / #mspExportDialog / #tjpExportDialog). Grouped here
   by feature era, not by dialog, so it sits far from the ".excel-dialog"
   shell comment earlier in this file. */
.excel-section {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* Two-panel layout filling the whole tab: settings on the left, the data
   selection (columns / sections / fields) with the live counter pill on
   the right. Panels are cards on the modal's deep backdrop, matching the
   settings column of the Image/Document/Print tabs. */
.excel-layout {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
    align-items: stretch;
    gap: 1.35rem;
    flex: 1;
    min-height: 0;
    width: 100%;
    max-width: 1520px;
    margin: 0 auto;
}
.excel-panel {
    display: flex;
    flex-direction: column;
    gap: 0.95rem;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
    padding: 1.05rem 1.15rem;
}
/* Hairline dividers between a panel's option groups; the counter pill
   anchors itself to the panel bottom instead. */
.excel-panel > * + *:not(.excel-summary) {
    border-top: 1px solid color-mix(in srgb, var(--border-card) 55%, transparent);
    padding-top: 0.9rem;
}
/* The group that owns the panel's leftover height (e.g. the columns grid). */
.excel-section-fill {
    flex: 1 1 auto;
    min-height: 0;
}
/* The XLSX choice dims this group (syncExcelUI sets opacity inline);
   ease the change so it reads as disable, not flicker. */
#excelDelimiterGroup {
    transition: opacity 0.15s ease;
}

.excel-format-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.5rem;
    margin-top: 0.35rem;
}
.excel-format-card {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border-card);
    background: var(--bg-input);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color 0.12s ease, background 0.12s ease;
    text-align: left;
}
.excel-format-card:hover {
    border-color: var(--border-input);
}
.excel-format-card[aria-checked="true"] {
    background: color-mix(in srgb, var(--title-accent) 8%, var(--bg-input));
    border-color: var(--title-accent);
}
.excel-format-icon {
    width: 22px;
    height: 22px;
    color: var(--text-muted);
    flex-shrink: 0;
    transition: color 0.12s ease;
}
.excel-format-card[aria-checked="true"] .excel-format-icon {
    color: var(--title-accent);
}
.excel-format-text {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    min-width: 0;
    line-height: 1.2;
}
.excel-format-name {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-heading);
    letter-spacing: 0.02em;
}
.excel-format-hint {
    font-size: 0.68rem;
    color: var(--text-muted);
}

.excel-grid-2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.85rem;
}
.excel-grid-2 > div {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 0;
}
/* .excel-chip-row / .excel-chip: styled with the shared segmented-control
   block next to .export-bg-row (see the Export dialog section above). */
.excel-chip-row {
    margin-top: 0;
}

.excel-cols-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.excel-cols-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.excel-link-btn {
    background: transparent;
    border: 0;
    padding: 2px 4px;
    font-size: 0.68rem;
    color: var(--title-accent);
    font-weight: 500;
    cursor: pointer;
    border-radius: 4px;
}
.excel-link-btn:hover {
    background: var(--bg-hover);
}
/* Open grid — the panel card is the container now, so the extra inset box
   is gone; each row carries its own hover highlight. */
.excel-cols-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.15rem 0.6rem;
    margin-top: 0.2rem;
}
.excel-col-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.74rem;
    color: var(--text-body);
    cursor: pointer;
    padding: 0.26rem 0.35rem;
    border-radius: 6px;
    user-select: none;
    transition: background 0.12s ease;
}
.excel-col-toggle:hover {
    background: color-mix(in srgb, var(--text-body) 6%, transparent);
}
.excel-col-toggle input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 15px;
    height: 15px;
    margin: 0;
    flex: none;
    background: var(--bg-input);
    border: 1.5px solid var(--border-input);
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.excel-col-toggle input[type="checkbox"]:checked {
    background-color: var(--title-accent);
    border-color: var(--title-accent);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
    background-size: 10px 10px;
    background-position: center;
    background-repeat: no-repeat;
}

/* Live counter ("Exporting N task(s), M column(s).") — a status pill pinned
   to the bottom of the data panel, right above the footer's Export button. */
.excel-summary {
    margin-top: auto;
    align-self: flex-end;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.38rem 0.85rem;
    background: color-mix(in srgb, var(--bg-input) 60%, transparent);
    border: 1px solid var(--border-card);
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--text-body);
    max-width: 100%;
}
.excel-summary::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 999px;
    background: var(--title-accent);
    flex: none;
}

@media (max-width: 760px) {
    .excel-layout {
        display: flex;
        flex-direction: column;
        overflow-y: auto;
    }
    .excel-panel {
        flex: none;
        overflow-y: visible;
    }
}
@media (max-width: 580px) {
    .excel-grid-2 { grid-template-columns: 1fr; }
    .excel-cols-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Import dialog */
.import-dialog {
    max-width: 720px;
    width: 100%;
}
.import-dialog-body {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
}
.import-section {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.import-structure-hint {
    border: 1px solid var(--accent, #6366f1);
    background: var(--accent-soft, #eef2ff);
    border-radius: 6px;
    padding: 10px 12px;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-body);
}
.import-structure-hint.hidden { display: none; }
.import-structure-hint strong { color: var(--accent-dark, #4f46e5); margin-right: 4px; }
.import-structure-hint span { margin-right: 4px; }
.import-gsheet-section.hidden,
#importGsheetSection.hidden { display: none; }
.import-gsheet-hint {
    margin: 0 0 0.4rem;
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.45;
}
.import-gsheet-row {
    display: flex;
    gap: 8px;
}
.import-gsheet-row .input {
    flex: 1;
    min-width: 0;
}
.import-drop-zone {
    border: 2px dashed var(--border-input);
    background: var(--bg-input);
    border-radius: 0.6rem;
    padding: 1.2rem 1rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    color: var(--text-muted);
    transition: border-color 0.15s ease, background 0.15s ease;
    cursor: pointer;
}
.import-drop-zone:hover,
.import-drop-zone.drag-over {
    border-color: var(--border-focus);
    background: var(--bg-hover);
}
.import-drop-icon svg {
    width: 1.6rem;
    height: 1.6rem;
    opacity: 0.6;
}
.import-drop-text {
    font-size: 0.8rem;
    display: flex;
    gap: 0.3rem;
    align-items: baseline;
}
.import-file-name {
    font-size: 0.75rem;
    color: var(--text-heading);
    font-weight: 600;
    word-break: break-all;
}
.import-validation {
    padding: 0.55rem 0.75rem;
    border-radius: 0.4rem;
    font-size: 0.72rem;
    line-height: 1.4;
}
.import-validation.ok {
    background: rgba(34, 197, 94, 0.1);
    color: #15803d;
    border: 1px solid rgba(34, 197, 94, 0.3);
}
.import-validation.warn {
    background: rgba(234, 179, 8, 0.1);
    color: #a16207;
    border: 1px solid rgba(234, 179, 8, 0.3);
}
.import-validation.error {
    background: rgba(239, 68, 68, 0.1);
    color: #b91c1c;
    border: 1px solid rgba(239, 68, 68, 0.3);
}
.import-validation ul {
    margin: 0.25rem 0 0 1.1rem;
    padding: 0;
}
.import-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 1.2rem;
    padding: 0.5rem 0.7rem;
    background: var(--bg-hover);
    border-radius: 0.4rem;
    font-size: 0.72rem;
    color: var(--text-muted);
}
.import-stats strong {
    color: var(--text-heading);
    font-weight: 600;
}
.import-preview-body {
    overflow: auto;
    max-height: 240px;
    border: 1px solid var(--border-input);
    border-radius: 0.4rem;
    background: var(--bg-input);
}
.import-preview-body table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.7rem;
}
.import-preview-body th,
.import-preview-body td {
    padding: 0.32rem 0.55rem;
    border-bottom: 1px solid var(--border-input);
    text-align: left;
    vertical-align: top;
    white-space: nowrap;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
}
.import-preview-body th {
    background: var(--bg-hover);
    color: var(--text-heading);
    font-weight: 600;
    position: sticky;
    top: 0;
}
.import-preview-body th .mapping {
    display: block;
    font-weight: 400;
    font-size: 0.62rem;
    color: var(--text-faint);
    margin-top: 0.1rem;
}
.import-preview-body tr.row-error td {
    background: rgba(239, 68, 68, 0.08);
}

/* ── Project picker bar ───────────────────────────────────────── */
/* Inline child of .viewport-controls — flows naturally next to the
   zoom slider, separated by a .viewport-divider. */
/* ── Project bar — same inline-pill design system as .viewport-range-bar ── */
.project-picker-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    position: relative;
    padding: 5px 8px 5px 6px;
    min-height: var(--toolbar-pill-h, 48px);
    border-radius: 999px;
    background: rgba(15, 23, 42, 0.04);
    border: 1px solid rgba(15, 23, 42, 0.10);
}
[data-theme$="-dark"] .project-picker-bar {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
/* Drop the floating "PROJECT" caption for the minimal look (matches the viewport bar). */
.project-bar-label { display: none; }
.my-charts-bar { gap: 6px; }

/* Buttons inside the bar → borderless ghost items (no nested pills). Scoped to the
   project bar so the shared .btn-export-sm used in the header stays as-is. */
.project-picker-bar .btn-export-sm {
    background: transparent;
    border: 1px solid transparent;
    border-radius: 999px;
    color: var(--text-body);
    padding: 0.34rem 0.7rem;
    font-weight: 500;
}
.project-picker-bar .btn-export-sm:hover {
    background: var(--bg-hover);
    border-color: transparent;
    color: var(--text-heading);
}

/* Report-this-chart (#131): a project action, so it sits in the project bar with
   My Charts / New Project / Share and inherits their ghost pill styling. Only
   read-only viewers (anon public / shared-viewer) ever see it. Must come AFTER
   the .btn-export-sm:hover rule above to win the red hover, since both selectors
   carry the same specificity. */
.report-chart-btn { display: none; }
body.shared-readonly .report-chart-btn { display: inline-flex; }
.project-picker-bar .report-chart-btn:hover {
    color: var(--danger, #dc2626);
    background: color-mix(in srgb, var(--danger, #dc2626) 8%, transparent);
    border-color: transparent;
}
/* Active project name stays the emphasised anchor pill. */
.project-picker-bar .active-project-name {
    background: rgba(100, 116, 139, 0.12);
    font-size: 0.8rem;
    padding: 0.34rem 0.8rem;
}
.project-picker-bar .active-project-name:hover { background: rgba(100, 116, 139, 0.18); }

/* Cloud sync status → quiet inline indicator, LAST in the row (after Share).
   The [data-state] selector raises specificity above the per-state rules so we
   drop their fills/borders but keep their semantic text colour (green = synced,
   red = not synced). */
.project-picker-bar .project-cloud-status,
.project-picker-bar .project-cloud-status[data-state] {
    background: none;
    border: 0;
    border-radius: 0;
    padding: 0 0.35rem 0 0.25rem;
    font-weight: 500;
}

/* Share leads the trailing share/sync pair now, so it carries the divider that
   separates the pair from the project actions. Drawn as a detached hairline
   (not a border) so the button keeps its own pill-shaped hover wash. */
.project-picker-bar .collaborate-btn {
    position: relative;
    margin-left: 8px;
}
.project-picker-bar .collaborate-btn::before {
    content: '';
    position: absolute;
    left: -7px;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
    height: 65%;
    background: var(--border-card);
}

/* ── My Charts bar (replaces the Loaded Projects picker by default) ────── */
.my-charts-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}
/* Two-line stack: the chart name pill on top, a subtle "Shared by …"
   subtitle below when the active chart is owned by someone else. The
   container is align-items:flex-start so the name pill keeps its own
   rounded shape and the subtitle sits flush-left underneath. */
.active-project-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    min-width: 0;
}
.active-project-shared-by {
    font-size: 0.66rem;
    line-height: 1.1;
    color: var(--text-muted);
    padding: 0 0.5rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 14rem;
    opacity: 0.85;
    user-select: none;
}
.active-project-shared-by.hidden {
    display: none;
}
.active-project-name {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--text-heading, #1e293b);
    max-width: 14rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 0.25rem 0.7rem;
    border-radius: 999px;
    background: rgba(100, 116, 139, 0.10);
    outline: none;
    cursor: pointer;
    transition: background 0.12s;
    user-select: none;
}
/* Hide the pill entirely when there's no active project — otherwise it
   renders as a stray empty pill blob. */
.active-project-name:empty {
    display: none;
}
.active-project-name:hover {
    background: rgba(100, 116, 139, 0.18);
}
.active-project-name[contenteditable="true"] {
    background: var(--input-bg, rgba(100, 116, 139, 0.22));
    cursor: text;
    user-select: text;
    /* No ellipsis while editing — it paints over the caret while the content
       scrolls, clipping the text mid-word as you type. (JS pins style.width
       numerically and re-measures per keystroke, so the pill GLIDES to fit
       the text live via the width transition below.) */
    text-overflow: clip;
    transition: background 0.12s, width 0.14s cubic-bezier(0.25, 0.9, 0.3, 1);
    /* STRICTLY single line while editing (the base nowrap stays in force).
       The text gets wider the instant a key lands but the width transition
       above takes 140ms to catch up — with wrapping enabled here, every
       keystroke folded the tail onto a second line for the glide duration
       (the "changes line while typing" bounce). Single line + the base
       overflow:hidden lets the browser's native caret-follow scroll cover
       that gap invisibly; a name longer than the JS width cap scrolls
       horizontally under the caret exactly like a plain <input>. No
       horizontal max-width cap — the inline style.width from
       syncActiveNameEditWidth is the single source of truth, clamped so the
       header's density ladder can never be tripped by an edit. */
    max-width: none;
}
@media (prefers-reduced-motion: reduce) {
    .active-project-name[contenteditable="true"] { transition: background 0.12s; }
}
[data-a11y-motion="reduce"] .active-project-name[contenteditable="true"] { transition: background 0.12s; }
/* While editing, keep the field visible + focusable even when emptied. The
   base `.active-project-name:empty { display:none }` rule above would hide it
   the instant you backspace the last character — a display:none element can't
   hold focus, so the caret drops and you get kicked out of the field. Override
   it during edit so the cursor stays put until you actually click away. */
.active-project-name[contenteditable="true"]:empty {
    display: inline-block;
    min-width: 2.5rem;
}
.my-charts-btn {
    /* Inherits .btn-export-sm styles; tweak only what's needed. */
}
/* ── Migration banner (pillow — per #85) ─────────────────────────── */
.migration-dialog {
    max-width: 520px;
}
.migration-body {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
}
.migration-intro {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--text);
}
.migration-project-list {
    max-height: 200px;
    overflow-y: auto;
    border: 1px solid var(--border-input);
    border-radius: 7px;
    padding: 0.5rem 0.75rem;
    background: var(--bg-elevated, rgba(0,0,0,0.02));
    font-size: 0.8rem;
}
.migration-project-list-item {
    padding: 0.25rem 0;
    color: var(--text);
    white-space: normal;
    overflow-wrap: anywhere;
    overflow: hidden;
    text-overflow: ellipsis;
}
.migration-help {
    margin: 0;
    font-size: 0.78rem;
    color: var(--text-muted);
}
.migration-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
}
.migration-primary-actions {
    display: flex;
    gap: 0.5rem;
}
.btn-text-quiet {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 0.78rem;
    cursor: pointer;
    padding: 0.4rem 0.5rem;
    border-radius: 5px;
    transition: color 0.12s, background 0.12s;
}
.btn-text-quiet:hover {
    color: var(--accent-warning, #c2410c);
    background: var(--bg-elevated, rgba(0,0,0,0.04));
}

/* ── Legacy dropdown project-switcher ───────────────────────────────
   Dead in the shipped UI, but still maintained by JS: index.html wraps
   this whole widget in `<div class="project-picker" data-legacy-ui
   hidden>`, so it and everything under it (.project-picker-item,
   -item-name, -item-cloud, -item-delete, -divider, -footer-action) never
   becomes visible to a user. app.js's renderProjectPickerMenu() still
   populates the hidden #projectPickerMenu on every project change, and
   `.project-picker[data-legacy-ui]` is queried elsewhere in app.js --
   likely a kept-for-rollback escape hatch. The LIVE project switcher is
   the "My Charts" bar/gallery (.project-picker-bar / .my-charts-*)
   documented above. Confirm before deleting either half. */
.project-picker {
    position: relative;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.12rem;
}
.project-picker-label {
    font-size: 0.6rem;
    font-weight: 600;
    color: var(--text-muted);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    margin: 0 0 0 0.85rem;
    line-height: 1;
    user-select: none;
}
.project-picker-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.32rem 0.7rem 0.32rem 0.85rem;
    background: var(--surface, var(--bg-elevated, #fff));
    color: var(--text, var(--text-heading, #1e293b));
    border: 1px solid var(--border-input);
    border-radius: 7px;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    min-width: 9rem;
    max-width: 18rem;
    transition: border-color 0.15s, background 0.15s;
}
.project-picker-trigger:hover {
    border-color: var(--border-focus, var(--text-heading));
}
.project-picker-trigger[aria-expanded="true"] {
    border-color: var(--accent, #6366f1);
}
.project-picker-trigger[aria-expanded="true"]:hover::after,
.project-picker-trigger[aria-expanded="true"]:hover::before {
    display: none;
}
.project-picker-name {
    flex: 1;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    outline: none;
    min-width: 0;
}
.project-picker-name[contenteditable="true"] {
    background: var(--input-bg, rgba(0,0,0,0.04));
    padding: 0 4px;
    margin: 0 -4px;
    border-radius: 4px;
    overflow: visible;
    /* A capped width (see syncActiveNameEditWidth) wraps a long name onto as
       many rows as it needs — the popover grows downward, never sideways. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    max-width: none;
    height: auto;
    cursor: text;
    text-overflow: clip;
}
.project-picker-chevron {
    flex-shrink: 0;
    opacity: 0.65;
    transition: transform 0.15s;
}
.project-picker-trigger[aria-expanded="true"] .project-picker-chevron {
    transform: rotate(180deg);
    opacity: 0.9;
}
.project-picker-menu {
    position: absolute;
    top: calc(100% + 5px);
    left: 0;
    min-width: 14rem;
    max-width: 22rem;
    max-height: 320px;
    overflow-y: auto;
    background: var(--surface, var(--bg-elevated, #fff));
    border: 1px solid var(--border-input);
    border-radius: 8px;
    box-shadow: 0 8px 22px rgba(0,0,0,0.14);
    z-index: 60;
    padding: 4px;
}
.project-picker-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 9px;
    cursor: pointer;
    font-size: 0.78rem;
    border-radius: 5px;
    color: var(--text, var(--text-heading));
}
.project-picker-item:hover {
    background: var(--surface-hover, rgba(0,0,0,0.05));
}
.project-picker-item.active {
    background: rgba(99, 102, 241, 0.12);
    color: var(--accent, #6366f1);
    font-weight: 600;
}
.project-picker-item-name {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.project-picker-item-delete {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    background: transparent;
    border: none;
    color: var(--text-muted, #64748b);
    cursor: pointer;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.1s, background 0.1s, color 0.1s;
    padding: 0;
    font-size: 1rem;
    line-height: 1;
}
.project-picker-item:hover .project-picker-item-delete {
    opacity: 0.85;
}
.project-picker-item-delete:hover {
    background: rgba(239, 68, 68, 0.15);
    color: #ef4444;
    opacity: 1;
}
.project-picker-item.active .project-picker-item-delete {
    display: none;
}

/* Divider + footer action at the bottom of the project picker menu —
   shown to signed-in account holders so the empty-list state still has
   a clear "load one of your cloud charts" entry point. */
.project-picker-divider {
    height: 1px;
    margin: 4px 2px;
    background: var(--border-input, rgba(0,0,0,0.08));
}
.project-picker-footer-action {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 9px;
    cursor: pointer;
    font-size: 0.78rem;
    border-radius: 5px;
    background: transparent;
    border: none;
    color: var(--accent, #6366f1);
    font-weight: 500;
    text-align: left;
    transition: background 0.1s;
}
.project-picker-footer-action:hover {
    background: rgba(99, 102, 241, 0.1);
}
.project-picker-footer-action svg {
    flex-shrink: 0;
    color: var(--accent, #6366f1);
}

/* Cloud-sync per-project toggle (Phase A2.2 — flag-gated, only rendered
   when collab is enabled and a Supabase session is active). */
.project-picker-item-cloud {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    background: transparent;
    border: none;
    cursor: pointer;
    border-radius: 4px;
    padding: 0;
    flex-shrink: 0;
    transition: background 0.12s, color 0.12s, opacity 0.12s;
}
.project-picker-item-cloud.cloud-mode-off {
    color: var(--text-muted, #94a3b8);
    opacity: 0.55;
}
.project-picker-item-cloud.cloud-mode-off:hover {
    opacity: 1;
    background: rgba(99, 102, 241, 0.1);
    color: var(--accent, #6366f1);
}
.project-picker-item-cloud.cloud-mode-synced {
    color: var(--accent, #6366f1);
    opacity: 1;
}
.project-picker-item-cloud.cloud-mode-synced:hover {
    background: rgba(99, 102, 241, 0.12);
}
.project-picker-item-cloud.cloud-mode-saving {
    color: var(--accent, #6366f1);
    opacity: 1;
    animation: cloud-pulse 1.1s ease-in-out infinite;
    pointer-events: none;
}
.project-picker-item-cloud.cloud-mode-error {
    color: #ef4444;
    opacity: 1;
}
.project-picker-item-cloud.cloud-mode-error:hover {
    background: rgba(239, 68, 68, 0.12);
}
@keyframes cloud-pulse {
    0%, 100% { opacity: 0.45; }
    50%      { opacity: 1; }
}

/* Toolbar status badge — reflects the active project's sync state */
.project-cloud-status {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-left: 0.5rem;
    padding: 0.18rem 0.55rem 0.18rem 0.45rem;
    font-size: 0.72rem;
    font-weight: 500;
    line-height: 1;
    border-radius: 999px;
    background: rgba(99, 102, 241, 0.1);
    color: var(--accent, #6366f1);
    white-space: nowrap;
    transition: opacity 0.2s, background 0.2s, color 0.2s;
}
.project-cloud-status[data-state="saving"] {
    background: rgba(99, 102, 241, 0.14);
    color: var(--accent, #6366f1);
    animation: cloud-pulse 1.1s ease-in-out infinite;
    cursor: default;
}
.project-cloud-status[data-state="saved"] {
    background: rgba(34, 197, 94, 0.14);
    color: #16a34a;
    cursor: default;
}
.project-cloud-status[data-state="error"] {
    background: rgba(239, 68, 68, 0.14);
    color: #ef4444;
    cursor: help;
}
.project-cloud-status[data-state="idle"] {
    background: rgba(34, 197, 94, 0.12);
    color: #16a34a;
    cursor: default;
}
.project-cloud-status[data-state="off"] {
    background: rgba(239, 68, 68, 0.1);
    color: #ef4444;
    border: 1px solid rgba(239, 68, 68, 0.3);
    cursor: pointer;
    font-weight: 600;
    padding: 0.25rem 0.7rem;
}
.project-cloud-status[data-state="off"]:hover {
    background: rgba(239, 68, 68, 0.18);
    border-color: rgba(239, 68, 68, 0.5);
}
.project-cloud-status[data-state="off"]:focus-visible {
    outline: 2px solid rgba(239, 68, 68, 0.6);
    outline-offset: 2px;
}
.project-cloud-status[data-state="off"]:active {
    transform: translateY(1px);
}

/* Hover-swap labels — used only by the "off" (Not synced) state to reveal
   "Start syncing" / "Sign in to sync". The .has-hover-swap class is added
   by JS only when a swap label is set; synced states no longer set one
   (no Stop-syncing affordance), so they stay as pure indicators.
   Both labels are ALWAYS laid out, stacked in one grid cell, and toggled
   with visibility — the badge is sized by the wider of the two up front,
   so hovering can never widen it and shift the whole project pill.
   Overlong (localized) labels fold onto a second line instead of
   stretching the badge. */
.project-cloud-status .cloud-status-label,
.project-cloud-status .cloud-status-hover-label {
    max-width: 9.5rem;
    white-space: normal;
    line-height: 1.15;
}
.project-cloud-status .cloud-status-hover-label { display: none; }
/* ONE column since the icon left this element (it lives in
   #versionHistoryBtn now). The old two-column grid put the icon in column 1 and
   both labels in column 2; with no icon, column 1 stayed as an empty gap the
   width of the column-gap. */
.project-cloud-status.has-hover-swap {
    cursor: pointer;
    display: inline-grid;
    grid-template-columns: auto;
    align-items: center;
}
.project-cloud-status.has-hover-swap .cloud-status-label,
.project-cloud-status.has-hover-swap .cloud-status-hover-label {
    grid-area: 1 / 1;
    display: block;
    text-align: center;
}
.project-cloud-status.has-hover-swap .cloud-status-hover-label { visibility: hidden; }
.project-cloud-status.has-hover-swap:hover .cloud-status-label,
.project-cloud-status.has-hover-swap:focus-visible .cloud-status-label { visibility: hidden; }
.project-cloud-status.has-hover-swap:hover .cloud-status-hover-label,
.project-cloud-status.has-hover-swap:focus-visible .cloud-status-hover-label { visibility: visible; }

/* Cloud UI is gated by body.collab-enabled — extra defense if the class
   never lands (e.g., flag toggled but bootstrap blocked by network). */
body:not(.collab-enabled) .project-cloud-status,
body:not(.collab-enabled) .project-picker-item-cloud {
    display: none !important;
}
body.shared-readonly .project-cloud-status {
    display: none !important;
}

/* My charts dialog — S211 gallery refactor: wide modal, card grid with
   full-width thumbnails, whole-card open, thumb-corner delete. The point
   is to present the user's charts as a portfolio, not a utility list. */
/* ── My Charts window (2026-09 workspace redesign) ────────────────────────
   Softcard: header (icon tile + title + "{n} charts · synced" subtitle), a
   toolbar with search / sort / New chart, and a 3-column card grid that
   scrolls internally while header + toolbar stay put. Empty projects render
   as dashed "ghost" cards. Every color reads a theme token, so all palettes
   (light + dark) just work. */
.dialog.my-charts-modal {
    max-width: min(760px, calc(100vw - 2rem));
    width: 100%;
    border-radius: 18px;   /* outranks the shared .dialog 14px harmonizer below */
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
}
[data-theme$="-dark"] .my-charts-modal { box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6); }

/* Header — neutral icon tile, title + count subtitle, close. */
.my-charts-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px 12px 18px;
    border-bottom: 1px solid var(--border-card);
}
.my-charts-header-icon {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    background: var(--bg-input);
    color: var(--text-heading);
    display: flex;
    align-items: center;
    justify-content: center;
}
.my-charts-header-text { flex: 1; min-width: 0; }
.my-charts-title {
    font-size: 14.5px;
    font-weight: 700;
    color: var(--text-heading);
    line-height: 1.25;
}
.my-charts-sub {
    font-size: 10.5px;
    color: var(--text-muted);
    min-height: 14px;   /* reserves the line while the count loads */
}
.my-charts-close {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 9px;
    background: transparent;
    color: var(--icon-color);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.my-charts-close:hover { background: var(--bg-input); color: var(--text-heading); }

/* Toolbar — search (flex-1), sort popover trigger, New chart. */
.my-charts-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 14px 18px 0;
}
.my-charts-search {
    flex: 1 1 180px;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 9px;
    height: 38px;
    padding: 0 14px;
    border: 1px solid var(--border-input);
    border-radius: 12px;
    background: var(--bg-input);
    color: var(--icon-color);
}
.my-charts-search:focus-within { border-color: var(--border-focus); }
.my-charts-search svg { flex: 0 0 auto; }
.my-charts-search input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: transparent;
    font: inherit;
    font-size: 13px;
    color: var(--text-input);
    outline: none;
    padding: 0;
}
.my-charts-search input::placeholder { color: var(--text-placeholder); }
.my-charts-sort-btn {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 7px;
    height: 38px;
    padding: 0 14px;
    border: 1px solid var(--border-input);
    border-radius: 12px;
    background: var(--bg-card);
    color: var(--text-body);
    font: inherit;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.12s ease;
}
.my-charts-sort-btn svg { color: var(--icon-color); }
.my-charts-sort-btn:hover,
.my-charts-sort-btn[aria-expanded="true"] { background: var(--bg-input); }
.my-charts-new-btn {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 7px;
    height: 38px;
    padding: 0 16px;
    border: 0;
    border-radius: 12px;
    background: var(--btn-add-bg, var(--accent));
    color: var(--btn-add-text, #ffffff);
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: background 0.12s ease, transform 0.1s ease;
}
.my-charts-new-btn:hover { background: var(--accent-dark); }
.my-charts-new-btn:active { transform: translateY(1px); }

/* ── Body opens at a FIXED size ───────────────────────────────────────────
   The rows arrive from Supabase a few hundred ms after the dialog appears;
   a min-height FLOOR reserves the grid's space so the dialog doesn't jump,
   and the loading / empty states centre inside it. A floor and NOT a flex
   height — a definite-height flex parent once turned the grid's height
   definite and re-resolved its auto rows (cards squashed to the thumb
   height, meta clipped away). `:not(.hidden)` keeps the JS's .hidden
   toggling working — a bare `display: flex` would outrank Tailwind's
   single-class .hidden. */
.my-charts-body { position: relative; }
.my-charts-body { min-height: min(56vh, 580px); }
.my-charts-status:not(.hidden),
.my-charts-empty:not(.hidden) {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    min-height: min(56vh, 580px);
}
/* Rows fade in once they land, rather than appearing all at once. JS adds
   .is-ready on the frame after .hidden comes off, so the transition runs. */
.my-charts-list { opacity: 0; transition: opacity 0.22s ease-out; }
.my-charts-list.is-ready { opacity: 1; }
/* While the list is fetched the status element holds the cascade loader
   (buildCascadeLoader in app.js, styled under "CASCADE LOADER" at the end of
   this file); the error message that reuses the element is plain text. */
@media (prefers-reduced-motion: reduce) {
    .my-charts-list { transition: none; }
    .my-charts-pop { animation: none; }
}
[data-a11y-motion="reduce"] .my-charts-list { transition: none; }
[data-a11y-motion="reduce"] .my-charts-pop { animation: none; }
.my-charts-status,
.my-charts-empty {
    padding: 1.2rem 0.5rem;
    text-align: center;
    color: var(--text-muted, #64748b);
    font-size: 0.8rem;
    line-height: 1.5;
}
.my-charts-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 2.4rem 1rem 2.6rem;
}
.my-charts-empty-icon {
    width: 42px;
    height: 42px;
    color: var(--text-faint, #94a3b8);
    margin-bottom: 4px;
}
.my-charts-empty-title {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--text-heading);
}
.my-charts-empty-body {
    font-size: 0.78rem;
    max-width: 400px;
    line-height: 1.55;
}
/* "No charts match" state for a search that eats everything. */
.my-charts-nomatch {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    min-height: min(40vh, 360px);
    padding: 2rem 1rem;
    text-align: center;
    color: var(--text-muted);
}
.my-charts-nomatch.hidden { display: none; }
.my-charts-nomatch-text { font-size: 0.85rem; }
.my-charts-nomatch-clear {
    border: 0;
    background: transparent;
    padding: 4px 6px;
    color: var(--accent);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.my-charts-nomatch-clear:hover { color: var(--accent-dark); }
.my-charts-list {
    list-style: none;
    margin: 0;
    padding: 16px 18px 20px;
    max-height: min(56vh, 580px);
    overflow-y: auto;
    overflow-x: hidden;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
    align-content: start;
}
@media (max-width: 720px) {
    .my-charts-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
    .my-charts-list { grid-template-columns: minmax(0, 1fr); }
}
.my-charts-item {
    position: relative;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-card);
    border-radius: 14px;
    background: var(--bg-card);
    overflow: hidden;
    min-width: 0;
    cursor: pointer;
    box-shadow: 0 2px 10px rgba(2, 6, 23, 0.06);
    transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
    animation: my-charts-card-in 0.3s ease both;
}
[data-theme$="-dark"] .my-charts-item { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4); }
.my-charts-item:nth-child(2)  { animation-delay: 0.03s; }
.my-charts-item:nth-child(3)  { animation-delay: 0.06s; }
.my-charts-item:nth-child(4)  { animation-delay: 0.09s; }
.my-charts-item:nth-child(5)  { animation-delay: 0.12s; }
.my-charts-item:nth-child(6)  { animation-delay: 0.15s; }
.my-charts-item:nth-child(n+7) { animation-delay: 0.18s; }
@keyframes my-charts-card-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}
.my-charts-item:hover,
.my-charts-item:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(2, 6, 23, 0.12);
}
[data-theme$="-dark"] .my-charts-item:hover,
[data-theme$="-dark"] .my-charts-item:focus-visible {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
}
.my-charts-item:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.my-charts-item.is-opening {
    opacity: 0.55;
    pointer-events: none;
}
/* Ghost cards — projects with no tasks yet. Dashed, flat, quieter, so real
   work stands out; still fully clickable. */
.my-charts-item--ghost {
    border-style: dashed;
    border-color: var(--border-input);
    box-shadow: none;
}
.my-charts-item--ghost:hover,
.my-charts-item--ghost:focus-visible {
    transform: none;
    box-shadow: none;
    background: var(--bg-hover);
}
@media (prefers-reduced-motion: reduce) {
    .my-charts-item { animation: none; transform: none; }
}
.my-charts-thumb-wrap { position: relative; }
.my-charts-thumb {
    height: 104px;
    padding: 12px;
    background: var(--bg-input);
    background: color-mix(in srgb, var(--bg-card) 60%, var(--bg-input));
    border-bottom: 1px solid var(--border-card);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.my-charts-thumb-bar {
    flex: 0 0 auto;
    height: 7px;
    border-radius: 4px;
}
/* Dark themes: lift the real task colors a step so thumbnails stay
   legible on the near-black thumb surface. */
[data-theme$="-dark"] .my-charts-thumb-bar { filter: brightness(1.25) saturate(1.05); }
.my-charts-thumb.is-empty,
.my-charts-item--ghost .my-charts-thumb {
    background: transparent;
    align-items: center;
    justify-content: center;
}
.my-charts-thumb-empty-label {
    font-size: 11px;
    color: var(--text-muted);
    text-align: center;
    padding: 0 6px;
}
.my-charts-thumb.is-loading {
    background-image: linear-gradient(
        100deg,
        rgba(0,0,0,0) 30%,
        rgba(0,0,0,0.05) 50%,
        rgba(0,0,0,0) 70%
    );
    background-size: 200% 100%;
    animation: my-charts-thumb-shimmer 1.2s linear infinite;
}
.my-charts-thumb.is-error::after {
    content: "·";
    font-size: 18px;
    opacity: 0.5;
}
@keyframes my-charts-thumb-shimmer {
    0% { background-position: 100% 0; }
    100% { background-position: -100% 0; }
}
/* Task count on the thumbnail's bottom-right corner. */
.my-charts-thumb-count {
    position: absolute;
    right: 10px;
    bottom: 7px;
    font-size: 9px;
    color: var(--text-muted);
    pointer-events: none;
}
/* Hover actions (share + ⋯) on the thumbnail's top-right corner: fade in
   on card hover / focus on desktop, always visible on touch. */
.my-charts-item-actions {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    gap: 5px;
    opacity: 0;
    transition: opacity 0.15s ease;
}
.my-charts-item:hover .my-charts-item-actions,
.my-charts-item:focus-within .my-charts-item-actions { opacity: 1; }
@media (hover: none) { .my-charts-item-actions { opacity: 1; } }
.my-charts-item-action {
    width: 26px;
    height: 26px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-card);
    border-radius: 8px;
    background: var(--bg-card);
    color: var(--text-muted);
    box-shadow: 0 2px 10px rgba(2, 6, 23, 0.06);
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
[data-theme$="-dark"] .my-charts-item-action { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4); }
.my-charts-item-action:hover { background: var(--bg-input); color: var(--text-heading); }
.my-charts-item-action:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.my-charts-item-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 11px 13px 12px;
}
.my-charts-item-name {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Inline rename: mirrors the name's box so nothing shifts. */
.my-charts-rename-input {
    width: 100%;
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-input);
    background: var(--bg-input);
    border: 1px solid var(--border-focus);
    border-radius: 6px;
    padding: 0 5px;
    margin: -1px -6px;
    outline: none;
}
.my-charts-item-meta {
    font-size: 11.5px;
    color: var(--text-muted);
}
.my-charts-item-meta-line {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    line-height: 1.35;
    max-width: 100%;
}
.my-charts-item-empty-tag { white-space: nowrap; }
.my-charts-item-shared {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    color: var(--accent);
}
/* Sort + card-action popovers. Fixed-positioned (the grid scrolls under
   them, so the JS closes them on scroll). */
.my-charts-pop {
    position: fixed;
    z-index: 2100;
    min-width: 160px;
    padding: 5px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--bg-card);
    box-shadow: 0 8px 24px rgba(2, 6, 23, 0.14);
    display: flex;
    flex-direction: column;
    gap: 2px;
    animation: dialogIn 0.12s ease-out;
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
}
[data-theme$="-dark"] .my-charts-pop { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55); }
.my-charts-pop.hidden { display: none; }
.my-charts-pop-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-body);
    font: inherit;
    font-size: 12.5px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background 0.1s ease;
}
.my-charts-pop-item:hover { background: var(--bg-input); }
.my-charts-pop-item svg { color: var(--icon-color); flex: 0 0 auto; }
.my-charts-pop-item.is-active {
    background: var(--accent-soft);
    color: var(--accent-dark);
    font-weight: 600;
}
.my-charts-pop-item.is-danger { color: var(--delete-hover-color, #dc2626); }
.my-charts-pop-item.is-danger svg { color: currentColor; }
.my-charts-pop-item.is-danger:hover { background: var(--delete-hover-bg, #fee2e2); }
/* Bulk-select checkbox (owner cards) — top-left, a small round tick. */
.my-charts-item-select {
    position: absolute;
    top: 8px;
    left: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border-radius: 999px;
    background: rgba(17, 17, 17, 0.55);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.4);
    backdrop-filter: blur(4px);
    cursor: pointer;
    opacity: 0;
    transition: background 0.12s, border-color 0.12s, opacity 0.15s;
}
/* The tick springs in rather than popping (the JS sets 16px on the svg;
   the smaller round box needs 12px). */
.my-charts-item-select svg {
    width: 12px;
    height: 12px;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 0.15s, transform 0.18s cubic-bezier(0.2, 0.7, 0.3, 1.4);
}
.my-charts-item:hover .my-charts-item-select,
.my-charts-list.has-selection .my-charts-item-select,
.my-charts-item-select:focus-visible { opacity: 1; }
@media (hover: none) { .my-charts-item-select { opacity: 1; } }
.my-charts-item.selected .my-charts-item-select {
    opacity: 1;
    background: var(--accent, #059669);
    border-color: var(--accent, #059669);
}
.my-charts-item.selected .my-charts-item-select svg { opacity: 1; transform: scale(1); }
/* Selection ring: quiet grey, drawn INSIDE the card edge (negative offset) so
   the list's scroll container can never clip it on the top row. */
.my-charts-item.selected { outline: 2px solid var(--text-muted, #64748b); outline-offset: -2px; }
.my-charts-item-select:focus-visible { outline: 2px solid var(--accent, #059669); outline-offset: 2px; }

/* Bulk action bar — appears above the grid when ≥1 chart is selected.
   It keeps its display and animates height + fade + a small rise instead:
   the old display:none toggle popped it in within one frame AND shoved the
   whole card grid down in the same frame, which read as a violent jump.
   visibility flips at the animation edges so the collapsed bar's buttons
   can't be tabbed into or read by a screen reader. */
.my-charts-bulkbar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 12px 18px 0;
    padding: 10px 14px;
    border: 1px solid var(--border-card, #e2e8f0);
    border-radius: 12px;
    background: var(--bg-card, #fff);
    max-height: 64px;
    overflow: hidden;
    visibility: visible;
    transform: translateY(0);
    transition:
        max-height 0.28s cubic-bezier(0.2, 0.7, 0.3, 1),
        opacity 0.22s ease,
        transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1),
        margin-top 0.28s cubic-bezier(0.2, 0.7, 0.3, 1),
        padding 0.28s cubic-bezier(0.2, 0.7, 0.3, 1),
        border-width 0.28s ease,
        visibility 0s linear 0s;
}
.my-charts-bulkbar.hidden {
    display: flex;
    max-height: 0;
    opacity: 0;
    transform: translateY(-6px);
    margin-top: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-width: 0;
    pointer-events: none;
    visibility: hidden;
    transition:
        max-height 0.24s cubic-bezier(0.4, 0, 0.7, 1),
        opacity 0.18s ease,
        transform 0.24s cubic-bezier(0.4, 0, 0.7, 1),
        margin-top 0.24s cubic-bezier(0.4, 0, 0.7, 1),
        padding 0.24s cubic-bezier(0.4, 0, 0.7, 1),
        border-width 0.24s ease,
        visibility 0s linear 0.24s;
}
@media (prefers-reduced-motion: reduce) {
    .my-charts-bulkbar,
    .my-charts-bulkbar.hidden { transition: none; }
    .my-charts-item-select svg { transition: none; }
}
[data-a11y-motion="reduce"] .my-charts-bulkbar,
[data-a11y-motion="reduce"] .my-charts-bulkbar.hidden { transition: none; }
[data-a11y-motion="reduce"] .my-charts-item-select svg { transition: none; }
.my-charts-bulkbar-count { font-size: 0.85rem; font-weight: 600; color: var(--text-heading); }
.my-charts-bulkbar-actions { margin-left: auto; display: flex; gap: 8px; }
.my-charts-bulkbar-clear {
    background: transparent;
    border: 1px solid var(--border-card, #e2e8f0);
    color: var(--text-body);
    border-radius: 8px;
    padding: 6px 14px;
    font-size: 0.82rem;
    cursor: pointer;
}
.my-charts-bulkbar-clear:hover { border-color: var(--text-muted, #64748b); }
.my-charts-bulkbar-delete { padding: 6px 14px; font-size: 0.82rem; }

/* Delete-chart confirm dialog */
.delete-chart-dialog { max-width: 460px; }
.delete-chart-body {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}
.delete-chart-name {
    margin: 0;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--text-heading);
}
.delete-chart-warning {
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text);
    padding: 0.65rem 0.8rem;
    background: rgba(239, 68, 68, 0.08);
    border: 1px solid rgba(239, 68, 68, 0.25);
    border-radius: 10px;
}
.delete-chart-warning strong { color: #ef4444; }
.delete-chart-help {
    margin: 0;
    font-size: 0.76rem;
    color: var(--text-muted);
}
.delete-chart-footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

/* ─────────────────────────────────────────────────────────────
   Export progress + donation prompt
   Modal opens for every export. Donation pitch auto-suppresses
   after 5 closes without donating, or after a single Donate click.
   ───────────────────────────────────────────────────────────── */
.export-progress-dialog { max-width: 420px; width: calc(100vw - 2rem); }
.export-progress-dialog .dialog-header { border-bottom: 1px solid var(--border-card); }
.export-progress-title { margin: 0; font-size: 0.95rem; }
.export-progress-body {
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
    padding: 1.25rem 1.15rem;
}
.export-progress-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.7rem;
    padding: 0.5rem 0 0.3rem;
}
.export-progress-icon {
    color: var(--accent);
    flex-shrink: 0;
}
/* The cascade loader (styled at the end of the file) sits in the old spinner's
   slot and keeps its class, so the modal's show / hide logic is unchanged. */
.export-progress-spinner.hidden { display: none; }
.export-progress-state .export-progress-spinner { margin: 4px 0 2px; }
.export-progress-check { color: #10b981; }
.export-progress-error-icon { color: #ef4444; }

/* Sectioned progress ring (multi-sheet exports) — one arc per sheet.
   Completed segments light up in accent with a soft glow; the in-flight
   segment pulses; the rest stay a faint track. Center shows "current/total". */
.export-progress-steps {
    display: flex;
    align-items: center;
    justify-content: center;
}
.export-progress-steps.hidden { display: none; }
.export-progress-steps svg {
    width: 76px;
    height: 76px;
    overflow: visible;
}
.eps-seg {
    fill: none;
    stroke: color-mix(in srgb, var(--border-input) 70%, transparent);
    stroke-width: 5.5;
    transition: stroke 0.3s ease, filter 0.3s ease;
}
.eps-seg.done {
    stroke: var(--accent, #059669);
    filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent, #059669) 70%, transparent));
}
.eps-seg.active {
    stroke: color-mix(in srgb, var(--accent, #059669) 60%, transparent);
    animation: eps-pulse 0.9s ease-in-out infinite;
}
@keyframes eps-pulse {
    0%, 100% { opacity: 0.4; }
    50% { opacity: 1; }
}
.eps-count {
    fill: var(--text-heading, #1e293b);
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
    .eps-seg.active { animation: none; opacity: 1; }
    .eps-seg.done { filter: none; }
}
[data-a11y-motion="reduce"] .eps-seg.active { animation: none; opacity: 1; }
[data-a11y-motion="reduce"] .eps-seg.done { filter: none; }
.export-progress-status {
    margin: 0;
    font-size: 0.82rem;
    color: var(--text-muted);
    text-align: center;
    line-height: 1.4;
}
.export-progress-pitch {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.85rem 0.95rem;
    background: var(--accent-soft);
    border: 1px solid var(--border-card);
    border-radius: 10px;
}
.export-progress-pitch-heading {
    margin: 0;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-heading);
}
.export-progress-pitch-body {
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--text-body);
}
.export-progress-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}
.btn-progress-donate {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.42rem 0.95rem;
    background: var(--accent);
    color: #fff;
    font-size: 0.78rem;
    font-weight: 600;
    border-radius: 0.5rem;
    border: 1px solid var(--accent);
    cursor: pointer;
    transition: all 0.15s;
}
.btn-progress-donate:hover {
    background: var(--accent-dark, var(--accent));
    border-color: var(--accent-dark, var(--accent));
    transform: translateY(-1px);
}
.btn-progress-donate svg { flex-shrink: 0; }

/* Collaborate button — sits next to the cloud status badge, only
   visible when the active project is synced to the cloud. */
.collaborate-btn {
    flex-shrink: 0;
}
.collaborate-btn svg {
    margin-right: 0.05rem;
}
body:not(.collab-enabled) .collaborate-btn {
    display: none !important;
}


/* ─────────────────────────────────────────────────────────────
   Custom role dropdown — replaces native <select>
   Used in Share dialog (invite row, collaborator rows, General Access).
   ───────────────────────────────────────────────────────────── */
.role-dd {
    position: relative;
    display: inline-block;
}
.role-dd-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 10px;
    background: var(--bg-input, var(--surface, #fff));
    border: 1px solid var(--border-input, #cbd5e1);
    border-radius: 6px;
    color: var(--text-input, var(--text-body, #1e293b));
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
    min-width: 92px;
    transition: border-color 0.14s, background 0.14s, box-shadow 0.14s;
}
.role-dd-trigger:hover {
    border-color: var(--border-focus, var(--accent, #6366f1));
    background: var(--bg-card, var(--surface, #fff));
}
.role-dd-trigger:focus-visible {
    outline: none;
    border-color: var(--border-focus, var(--accent, #6366f1));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus, #6366f1) 22%, transparent);
}
.role-dd.is-open .role-dd-trigger {
    border-color: var(--border-focus, var(--accent, #6366f1));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus, #6366f1) 22%, transparent);
}
.role-dd.is-disabled .role-dd-trigger,
.role-dd-trigger:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.role-dd-label {
    flex: 1;
    text-align: left;
    white-space: nowrap;
}
.role-dd-chevron {
    flex-shrink: 0;
    color: var(--text-muted, #64748b);
    transition: transform 0.18s ease;
}
.role-dd.is-open .role-dd-chevron {
    transform: rotate(180deg);
    color: var(--border-focus, var(--accent, #6366f1));
}

.role-dd-compact .role-dd-trigger {
    padding: 5px 8px;
    font-size: 0.74rem;
    min-width: 84px;
    background: transparent;
    border-color: transparent;
}
.role-dd-compact .role-dd-trigger:hover {
    background: var(--bg-hover, rgba(0,0,0,0.04));
    border-color: var(--border-input, rgba(0,0,0,0.12));
}
.role-dd-compact.is-open .role-dd-trigger {
    background: var(--bg-card, var(--surface, #fff));
}

.role-dd-popover {
    /* Base position is a fallback — role-dropdown.js re-anchors the open
       popover position:fixed from the trigger rect (escapes the scrollable
       people list's clipping). z-index must clear the dialog overlay (2000). */
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 2100;
    min-width: 180px;
    background: var(--surface, var(--bg-card, #fff));
    border: 1px solid var(--border-card, var(--border-input, #e2e8f0));
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    animation: shareCollabDdOpen 0.14s ease;
}
@keyframes shareCollabDdOpen {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
}
.role-dd-popover.hidden { display: none; }

.role-dd-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    padding: 7px 28px 7px 10px;
    background: transparent;
    border: none;
    border-radius: 5px;
    text-align: left;
    cursor: pointer;
    color: var(--text-body, var(--text-heading));
    font-size: 0.78rem;
    line-height: 1.3;
    position: relative;
    transition: background 0.12s ease;
}
.role-dd-option:hover,
.role-dd-option:focus-visible {
    background: color-mix(in srgb, var(--border-focus, #6366f1) 12%, transparent);
    outline: none;
}
.role-dd-option.is-selected {
    color: var(--border-focus, var(--accent-dark, var(--accent, #4f46e5)));
    font-weight: 600;
}
.role-dd-option-label {
    font-weight: 500;
}
.role-dd-option.is-selected .role-dd-option-label {
    font-weight: 600;
}
.role-dd-option-hint {
    font-size: 0.7rem;
    color: var(--text-muted, #64748b);
    font-weight: 400;
}
.role-dd-option-check {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--border-focus, var(--accent, #6366f1));
}


/* Edit-blocked dialog (Phase A2.5b) */
.edit-blocked-body {
    margin: 0;
    color: var(--text, var(--text-heading));
    font-size: 0.82rem;
    line-height: 1.5;
}
.edit-blocked-footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

/* Visual gates for read-only-shared-view state. The pushHistory hook
   is the functional wall (any mutation reverts + opens the modal);
   these rules just hide the most obvious edit triggers so anonymous
   viewers don't keep bumping into them.

   NOTE (2026-08-04): the create-task and row-delete selectors were dead — the
   button's id is #addTaskButton (not #addTaskBtn) and the Object Browser rows
   carry .task-row (not .task-list-row), so neither rule ever matched and viewers
   kept being offered both. Corrected below; #addEventBtn is kept only because
   the events row, not a button, is the create affordance now (it is JS-gated). */
body.shared-readonly #addTaskButton,
body.shared-readonly #addEventBtn,
body.shared-readonly .new-project-btn,
body.shared-readonly .project-picker-item-delete,
body.shared-readonly .collaborate-btn,
body.shared-readonly .undo-redo,
body.shared-readonly .project-cloud-status {
    display: none !important;
}
body.shared-readonly .task-row .task-row-delete {
    display: none !important;
}
/* Read-only viewers (anon / shared-viewer) can't rename the project — show the
   "no" cursor. The rename triggers are JS-gated too, so a click does nothing. */
body.shared-readonly #activeProjectName {
    cursor: not-allowed;
}
.new-project-btn {
    flex-shrink: 0;
}
.new-project-btn svg {
    margin-right: 0.05rem;
}

/* New Project dialog (Blank vs Import cards) */
.new-project-dialog {
    max-width: 480px;
}
.new-project-intro {
    margin: 0 0 0.85rem 0;
    color: var(--text-muted, #64748b);
    font-size: 0.78rem;
    line-height: 1.45;
}
.new-project-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
}
.new-project-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    gap: 0.4rem;
    padding: 1rem 0.9rem;
    background: var(--surface, var(--bg-elevated, #fff));
    border: 1.5px solid var(--border-input);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, transform 0.1s;
}
.new-project-card:hover {
    border-color: var(--accent, #6366f1);
    background: rgba(99, 102, 241, 0.04);
}
.new-project-card:active {
    transform: scale(0.985);
}
.new-project-card-icon {
    width: 30px;
    height: 30px;
    color: var(--accent, #6366f1);
}
.new-project-card-icon svg {
    width: 100%;
    height: 100%;
}
.new-project-card-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-heading, #1e293b);
}
.new-project-card-desc {
    font-size: 0.72rem;
    color: var(--text-muted, #64748b);
    line-height: 1.4;
}

@media (max-width: 540px) {
    .new-project-cards { grid-template-columns: 1fr; }
}

/* ── Tabbed Export modal ───────────────────────────────────── */
.export-tabbed-dialog {
    /* Another ~30% on both axes (1430×960 → 1860×1250), because the Print and
       Document panels grew enough controls to scroll again. The goal is no inner
       scrollbar at all on a normal desktop; the vh cap still wins on genuinely
       small windows, where .export-tab-panels scrolls as before.
       min-height stays 520px on purpose — it is a floor, and CSS gives min-height
       priority over max-height, so raising it would overflow short windows. */
    max-width: 1860px;
    width: 95vw;
    height: min(96vh, 1250px);
    max-height: 96vh;
    min-height: 520px;
    display: flex;
    flex-direction: column;
    /* Deepest layer of the modal: the settings cards and sidebar sit ON this,
       giving the panel-on-canvas depth (canvas < panels < active tile). */
    background: var(--bg-body);
}
/* The header and footer hold their size no matter what the body wants:
   they are flex children of a FIXED-height column, so without this a
   too-tall body puts them through the shrink phase (the zoomed preview
   canvas did exactly that — the header collapsed to its text). */
.export-tabbed-dialog > .dialog-header,
.export-tabbed-dialog > .dialog-footer {
    flex: none;
}
/* Chrome sits directly on the deep backdrop — no separate header fill. */
.export-tabbed-dialog > .dialog-header {
    background: transparent;
}
.export-tabbed-dialog > .dialog-footer {
    padding: 0.8rem 1.25rem;
}
.export-tabbed-dialog .btn-primary {
    padding: 0.55rem 1.5rem;
    font-weight: 600;
}
.export-tabbed-body {
    display: flex;
    /* Basis 0, not auto: the body's share is the leftover space between the
       header and footer, never its own content height — a 400%-zoomed preview
       canvas must scroll inside its pane, not inflate the flex layout. */
    flex: 1 1 0;
    min-height: 0;
    gap: 0;
    overflow: hidden;
}
.export-tabs {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 0 0 208px;
    padding: 0.85rem 0.6rem;
    border-right: 1px solid var(--border-card, var(--border-input));
    background: color-mix(in srgb, var(--bg-card) 55%, transparent);
    overflow-y: auto;
}
.export-tab {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.6rem 0.5rem 0.75rem;
    background: transparent;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    text-align: left;
    color: var(--text, var(--text-heading));
    transition: background 0.12s, color 0.12s;
    font: inherit;
    width: 100%;
}
/* Active indicator: a small accent bar hugging the item's left edge. */
.export-tab::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 0;
    border-radius: 999px;
    background: var(--title-accent);
    transition: height 0.15s ease;
}
.export-tab:hover {
    background: color-mix(in srgb, var(--text-body) 7%, transparent);
}
.export-tab.active {
    background: color-mix(in srgb, var(--title-accent) 10%, transparent);
}
.export-tab.active::before {
    height: 55%;
}
.export-tab.active .export-tab-label {
    color: var(--title-accent);
}
.export-tab-icon {
    width: 17px;
    height: 17px;
    flex-shrink: 0;
    color: var(--text-muted);
    transition: color 0.12s;
}
.export-tab.active .export-tab-icon { color: var(--title-accent); }
/* Print sits last, below the file formats, because it is an ACTION rather
   than a format — a rule above it and an accent-tinted icon set it apart
   without the standing green wash the old design used. */
.export-tab-print {
    margin-top: 0.5rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border-card);
    border-radius: 0 0 8px 8px;
}
.export-tab-print:not(.active) .export-tab-icon {
    color: var(--title-accent);
    opacity: 0.9;
}
.export-tab-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.15;
}
.export-tab-label {
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.export-tab-hint {
    font-size: 0.66rem;
    font-weight: 400;
    color: var(--text-muted, #64748b);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 1px;
}
.export-tab-panels {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    padding: 0;
}

/* Title-size control: label left, compact segmented control right. */
.export-titlesize-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    margin-top: 0.5rem;
}
.export-titlesize-label {
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--text-muted, #64748b);
    white-space: nowrap;
}
.export-titlesize-row .export-bg-row {
    margin-top: 0;
    width: auto;
    flex: 0 0 auto;
}
.export-titlesize-row .export-bg-chip {
    flex: 0 0 auto;
    min-width: 2.1rem;
    padding: 0.26rem 0.5rem;
}

/* ── Page-size dropdown ─────────────────────────────────────────
   The paper-format list outgrew a chip row (13 formats), so the size
   radios live in a themed popover. Items keep the [data-*-size] radio
   attributes, so the dialogs' generic chip wiring drives selection. */
.export-size-dd {
    position: relative;
    max-width: 300px;
}
.export-size-dd-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    margin-top: 0.35rem;
    padding: 0.48rem 0.75rem;
    background: var(--bg-input);
    color: var(--text, var(--text-heading));
    border: 1px solid var(--border-input);
    border-radius: 8px;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s;
}
.export-size-dd-btn:hover {
    border-color: var(--text-muted);
}
.export-size-dd-btn[aria-expanded="true"] {
    border-color: var(--title-accent);
}
.export-size-dd-btn svg {
    flex: none;
    opacity: 0.6;
    transition: transform 0.15s ease;
}
.export-size-dd-btn[aria-expanded="true"] svg {
    transform: rotate(180deg);
}
.export-size-dd-menu {
    position: absolute;
    top: calc(100% + 5px);
    left: 0;
    z-index: 40;
    min-width: 100%;
    max-height: 320px;
    overflow-y: auto;
    background: var(--bg-card);
    border: 1px solid var(--border-card, var(--border-input));
    border-radius: 10px;
    padding: 0.28rem;
    box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.4);
}
.export-size-dd-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.2rem;
    width: 100%;
    padding: 0.42rem 0.6rem;
    background: transparent;
    border: none;
    border-radius: 7px;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--text, var(--text-heading));
    cursor: pointer;
    text-align: left;
}
.export-size-dd-item:hover {
    background: color-mix(in srgb, var(--text-body) 7%, transparent);
}
.export-size-dd-item[aria-checked="true"] {
    background: color-mix(in srgb, var(--title-accent) 10%, transparent);
    color: var(--title-accent);
    font-weight: 600;
}
.export-size-dd-dims {
    font-size: 0.68rem;
    font-weight: 500;
    color: var(--text-muted, #64748b);
    white-space: nowrap;
}
.export-size-dd-item[aria-checked="true"] .export-size-dd-dims {
    color: inherit;
    opacity: 0.75;
}
.export-tab-panel { display: none; }
.export-tab-panel.active {
    display: block;
    /* Definite height, handed down to the panel's own dialog/body/grid below. */
    height: 100%;
    min-height: 0;
}

/* Each panel embeds an existing per-format overlay. Strip the embedded
   overlay's modal chrome (positioning, backdrop, header/footer) so the
   tabbed modal's own header/footer take over. */
.export-tab-panel > .dialog-overlay {
    position: static !important;
    background: transparent !important;
    padding: 0 !important;
    display: block !important;          /* neutralise old .hidden toggling */
    inset: auto !important;
    min-height: 0 !important;
    height: 100% !important;
}
.export-tab-panel > .dialog-overlay > .dialog {
    box-shadow: none !important;
    border-radius: 0 !important;
    max-width: none !important;
    width: 100% !important;
    margin: 0 !important;
    max-height: none !important;
    background: transparent !important;
    animation: none !important;            /* suppress dialogIn zoom on tab switch */
    border: none !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    min-height: 0 !important;
    /* The body owns the scrolling now (its controls column and preview box each
       scroll independently), so the dialog itself must not add a second one. */
    overflow: hidden !important;
}
.export-tab-panel > .dialog-overlay {
    animation: none !important;            /* suppress fadeIn on tab switch */
}
.export-tab-panel > .dialog-overlay > .dialog > .dialog-header {
    display: none;
}
.export-tab-panel > .dialog-overlay > .dialog > .dialog-footer {
    display: none;
}
.export-tab-panel > .dialog-overlay > .dialog > .dialog-body {
    padding: 1rem 1.25rem;
    flex: 1 1 auto;
    min-height: 0;
}
/* The form-only tabs (Spreadsheet / Project File / MS Project / TaskJuggler)
   have no preview pane; their content fills the tab as a full-height
   two-panel layout (.excel-layout: settings | data selection) instead of
   one small floating card lost in the empty canvas. */
.export-tab-panel > .dialog-overlay > .dialog > .dialog-body.excel-dialog-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 1rem 1.25rem;
}

@media (max-width: 760px) {
    .export-tabbed-body { flex-direction: column; }
    .export-tabs {
        flex-direction: row;
        flex: 0 0 auto;
        overflow-x: auto;
        overflow-y: hidden;
        border-right: none;
        border-bottom: 1px solid var(--border-card, var(--border-input));
        padding: 0.5rem;
    }
    .export-tab { flex: 0 0 auto; }
    /* The left-edge indicator bar only makes sense in the vertical list. */
    .export-tab::before { display: none; }
    .export-tab-print {
        margin-top: 0;
        padding-top: 0.5rem;
        border-top: none;
        border-radius: 8px;
    }
    .export-tab-hint { display: none; }
    .export-head-sub { display: none; }
}

/* Export modal header: title + live project-info subtitle
   ("<project> · N tasks · N milestones · <start> – <end>"). */
.export-head-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    gap: 1px;
}
.export-head-sub {
    margin: 0;
    font-size: 0.7rem;
    color: var(--text-muted, #64748b);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    animation: exportRiseIn 0.4s 0.1s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
.export-head-sub:empty { display: none; }
@keyframes exportRiseIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .export-head-sub { animation: none; } }
[data-a11y-motion="reduce"] .export-head-sub { animation: none; }

/* Universal Load File format picker modal */
.load-format-dialog {
    max-width: 640px;
}
.load-format-intro {
    margin: 0 0 0.9rem 0;
    color: var(--text-muted, #64748b);
    font-size: 0.78rem;
    line-height: 1.45;
}
.load-format-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.55rem;
}
/* Horizontal pill layout: icon left, label + hint stacked right, vertically
   centered. Border-radius 999px (from the pill refresh below) makes the
   wide rectangle a proper pill — content reads in the same direction as
   the curve. */
.load-format-card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    align-items: center;
    column-gap: 0.85rem;
    row-gap: 0.1rem;
    padding: 0.95rem 1.2rem;
    background: var(--surface, var(--bg-elevated, #fff));
    border: 1.5px solid var(--border-input);
    border-radius: 9px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, transform 0.1s;
    color: var(--text, var(--text-heading));
    font: inherit;
    text-align: left;
}
.load-format-card:hover {
    border-color: var(--accent, #6366f1);
    background: rgba(99, 102, 241, 0.05);
}
.load-format-card:active {
    transform: scale(0.985);
}
.load-format-card-icon {
    grid-row: 1 / span 2;
    grid-column: 1;
    width: 30px;
    height: 30px;
    color: var(--accent, #6366f1);
    align-self: center;
}
.load-format-card-icon svg {
    width: 100%;
    height: 100%;
}
.load-format-card-label {
    grid-column: 2;
    grid-row: 1;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-heading, #1e293b);
    line-height: 1.2;
}
.load-format-card-hint {
    grid-column: 2;
    grid-row: 2;
    font-size: 0.7rem;
    color: var(--text-muted, #64748b);
    line-height: 1.2;
}

/* The staged-file header inside the merged import dialog: which file is about
   to be imported, and a way back to the format cards. */
.import-staged-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding-bottom: 0.9rem;
    margin-bottom: 0.9rem;
    border-bottom: 1px solid var(--border-input);
}
.import-staged-file {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
}
.import-staged-icon {
    flex: none;
    width: 26px;
    height: 26px;
    color: var(--accent, #6366f1);
}
.import-staged-icon svg { width: 100%; height: 100%; }
.import-staged-file .import-file-name {
    display: block;
    font-weight: 600;
    color: var(--text-heading);
    overflow-wrap: anywhere;
}
.import-staged-file .label { display: block; }
/* Wider only while a preview table is on screen — the choosing phase is six
   pills and looks stranded at this width. */
.load-format-dialog.is-staged { max-width: 720px; }

/* "or" rule between the format cards and the drop zone. The line is drawn by
   the two flex children either side of the word, so it needs no pseudo-element
   trickery and re-centres itself at any dialog width. */
.load-format-or {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    margin: 1rem 0 0.85rem;
    color: var(--text-muted, #64748b);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.load-format-or::before,
.load-format-or::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border-input);
}
/* Inherits .import-drop-zone (dashed border, hover/drag-over states) — this
   only trims the padding, since here it sits under six cards rather than
   being the dialog's main event. */
.load-format-drop {
    padding: 0.9rem 1rem;
}

@media (max-width: 600px) {
    .load-format-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 380px) {
    .load-format-grid { grid-template-columns: 1fr; }
}

/* Import dialog: two-button footer (overwrite vs new project) */
.import-dialog-footer {
    gap: 0.5rem;
}
.import-dialog-footer #importDialogGoNew {
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1rem;
    font-size: 0.8rem;
    font-weight: 500;
    border-radius: 0.5rem;
    cursor: pointer;
    transition: all 0.2s;
    margin-left: 0.4rem;
    margin-right: auto;
    background: transparent;
    color: #6366f1;
    border: 1px solid #6366f1;
}
.import-dialog-footer #importDialogGoNew:hover:not(:disabled) {
    background: rgba(99, 102, 241, 0.08);
}
.import-dialog-footer #importDialogGoNew:disabled {
    background: transparent;
    color: var(--text-muted, #64748b);
    border-color: var(--border-input);
    opacity: 0.55;
    cursor: not-allowed;
}

@media (max-width: 560px) {
    .import-dialog-footer { flex-wrap: wrap; }
    .import-dialog-footer #importDialogGoNew { margin-right: 0; flex: 1 1 auto; }
}


/* ─────────────────────────────────────────────────────────────
   Collab feature-flag gate — hide all data-collab-only elements
   unless the bootstrap has added body.collab-enabled.
   ───────────────────────────────────────────────────────────── */
[data-collab-only] { display: none !important; }
body.collab-enabled [data-collab-only] { display: revert !important; }

/* ─────────────────────────────────────────────────────────────
   Live remote cursors (Phase C — #67). Overlay div pinned over the
   chart container. Each `.remote-cursor` is positioned via transform
   based on semantic chart coords broadcast by peers, re-derived by
   the receiver every render so it lands on the right task regardless
   of zoom. `pointer-events: none` so the overlay never steals input.
   ───────────────────────────────────────────────────────────── */
.cursor-layer {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    pointer-events: none;
    overflow: hidden;
    z-index: 4;
}
.remote-cursor {
    position: absolute;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    will-change: transform;
    transition: transform 0.08s linear;
}
.remote-cursor-arrow {
    position: absolute;
    top: 0;
    left: 0;
    width: 14px;
    height: 14px;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25));
}
.remote-cursor-label {
    position: absolute;
    top: 14px;
    left: 12px;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 10.5px;
    font-weight: 600;
    color: #fff;
    white-space: nowrap;
    line-height: 1.35;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
    user-select: none;
    pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────
   Busy tasks + events (#80). While a peer drags tasks or events,
   every other client dims those rows and dashes the bar outline in
   the dragger's cursor color (--remote-busy-color, set inline by
   app.js from the sanitized broadcast payload). Applied to the
   whole task-group / event-group so the bar, label, progress fill
   and handles all fade together; the dashed stroke goes on the
   row's main shape only. Purely visual — the row stays interactive
   (last write wins, as everywhere else).
   ───────────────────────────────────────────────────────────── */
.task-group.remote-busy,
.event-group.remote-busy {
    opacity: 0.35;
    transition: opacity 0.15s ease;
}
[data-a11y-motion="reduce"] .task-group.remote-busy,
[data-a11y-motion="reduce"] .event-group.remote-busy { transition: none; }
.task-group.remote-busy .task-bar,
.task-group.remote-busy .milestone-diamond,
.task-group.remote-busy .group-bar,
.event-group.remote-busy .event-bar {
    stroke: var(--remote-busy-color, #94a3b8);
    stroke-width: 1.5;
    stroke-dasharray: 4 3;
}

/* ─────────────────────────────────────────────────────────────
   Chart presence stack — other live collaborators on this chart.
   Pinned to the top-left corner of the gantt chart area. Populated
   by app.js when cloud-sync is on; one avatar per remote user,
   deduped by userId. Hover label is wired via [data-tooltip].
   ───────────────────────────────────────────────────────────── */
/* ── Collaborators presence ──────────────────────────
   Relocated from an absolute overlay on the chart to its own
   toolbar section beside Tools. 2×2 grid of small avatars; the
   whole section + its leading divider hide when you're alone. */
/* Editor's note while documenting: the two comments immediately above
   this describe DIFFERENT layouts -- "pinned to the top-left corner of
   the gantt chart area" vs. "relocated... to its own toolbar section
   beside Tools". They're sequential history, not alternatives: only the
   second (immediately above this line) matches the rules below. */
.presence-grid {
    display: grid;
    grid-template-columns: repeat(2, auto);
    gap: 4px;
    justify-items: center;
    align-content: center;
}
#collabStack.presence-collab-hidden,
#collabDivider.presence-collab-hidden { display: none !important; }

.presence-avatar,
.presence-more {
    width: 17px;
    height: 17px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 7.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: 1;
    text-transform: uppercase;
    user-select: none;
    box-shadow: 0 0 0 1.5px var(--bg-card, #fff);
    transition: transform 0.16s ease, opacity 0.18s ease;
    cursor: default;
}
.presence-avatar:hover {
    transform: translateY(-1px) scale(1.14);
    z-index: 2;
}
.presence-more {
    background: var(--text-muted, #6b7280);
    font-size: 8px;
    letter-spacing: 0;
    border: none;
    padding: 0;
    cursor: pointer;
}
.presence-more:hover { transform: scale(1.14); filter: brightness(1.1); }
.presence-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Soft join / leave instead of an instant pop. */
.presence-cell-enter { opacity: 0; transform: scale(0.4); }
.presence-cell-leave { opacity: 0; transform: scale(0.4); pointer-events: none; }

/* Overflow popover — full collaborator list with names. */
.presence-popover {
    position: fixed;
    z-index: 1200;
    min-width: 168px;
    max-width: 240px;
    max-height: 300px;
    overflow-y: auto;
    padding: 6px;
    border-radius: 10px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
    animation: presencePopIn 0.14s ease;
}
@keyframes presencePopIn {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}
.presence-popover-title {
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    padding: 4px 6px 6px;
}
.presence-popover-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 6px;
    border-radius: 6px;
    font-size: 0.78rem;
    color: var(--text);
}
.presence-popover-row:hover { background: var(--bg-hover); }
.presence-popover-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex: 0 0 auto;
    box-shadow: 0 0 0 1.5px var(--bg-card);
}
.presence-popover-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ─────────────────────────────────────────────────────────────
   Toolbar auth slot
   ───────────────────────────────────────────────────────────── */
.auth-wrapper {
    position: relative;
    display: inline-flex;
    align-items: center;
}
/* Sign in: the ink button; signed in: a 34px avatar circle in the same ink
   (the OAuth photo covers the initial when it loads). The ink is the heading
   colour, so in dark mode it is the white fill with dark text. */
.auth-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 34px;
    padding: 0 14px;
    border: 0;
    border-radius: 10px;
    background: var(--text-heading);
    color: var(--bg-card);
    font-size: 13px;
    font-weight: 600;
    font-family: inherit;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.15s ease;
}
.auth-btn:hover {
    background: color-mix(in srgb, var(--text-heading) 88%, var(--bg-card));
}
.auth-btn-icon { display: inline-flex; }
.auth-btn-icon svg { width: 15px; height: 15px; display: block; }
.auth-btn-label { white-space: nowrap; }
.auth-btn-avatar {
    display: none;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--text-heading);
    color: var(--bg-card);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.02em;
    align-items: center;
    justify-content: center;
    text-transform: uppercase;
    user-select: none;
    transition: background 0.15s ease;
}
.auth-btn[data-state="signed-in"] {
    padding: 0;
    border: 0;
    background: transparent;
    height: auto;
    border-radius: 50%;
}
.auth-btn[data-state="signed-in"]:hover { background: transparent; }
.auth-btn[data-state="signed-in"] .auth-btn-icon,
.auth-btn[data-state="signed-in"] .auth-btn-label { display: none; }
.auth-btn[data-state="signed-in"] .auth-btn-avatar { display: inline-flex; }
.auth-btn[data-state="signed-in"]:hover .auth-btn-avatar {
    background: color-mix(in srgb, var(--text-heading) 88%, var(--bg-card));
}
/* OAuth profile photo layered over the initials disc — covers the initials
   when it loads; on load error the img is removed and the initials remain. */
.auth-btn-avatar,
.auth-menu-avatar {
    position: relative;
    overflow: hidden;
}
.auth-avatar-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
}

/* ─────────────────────────────────────────────────────────────
   Toolbar auth menu (dropdown when signed in)
   ───────────────────────────────────────────────────────────── */
.auth-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 220px;
    padding: 6px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    z-index: 1000;
}
[data-theme$="-dark"] .auth-menu {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
.auth-menu-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--border-card);
}
.auth-menu-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--auth-cursor-color, #3b82f6);
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-transform: uppercase;
    flex: 0 0 auto;
}
.auth-menu-id {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}
.auth-menu-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-heading);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.auth-menu-email {
    font-size: 11px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.auth-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    background: transparent;
    border: 0;
    color: var(--text-body);
    font-size: 13px;
    font-family: inherit;
    text-align: left;
    border-radius: 6px;
    cursor: pointer;
    text-decoration: none;
}
.auth-menu-item:hover {
    background: var(--bg-hover);
}
[data-theme$="-dark"] .auth-menu-signout:hover {
    background: rgba(220, 38, 38, 0.18);
}
.auth-menu-item svg { width: 16px; height: 16px; flex: 0 0 auto; opacity: 0.7; }
.auth-menu-signout { color: var(--danger, #dc2626); }
.auth-menu-signout svg { opacity: 0.85; }
.auth-menu-signout:hover { background: rgba(220, 38, 38, 0.08); }

/* ─────────────────────────────────────────────────────────────
   Auth modal — split window: a dark panel that DEMOS what an
   account unlocks, beside the OAuth column.

   Two things to know before editing:
   1. The demo panel is dark in EVERY theme (that is the design),
      so its colours are literals, not theme tokens. The OAuth
      column on the right is fully tokenised and themes normally.
   2. Selectors are .auth-modal-prefixed on purpose. The "pill
      refresh" section further down this file rounds
      `.auth-oauth-btn` and `.auth-modal button` to 999px, and the
      dialog-radius rule sets 14px; the two-class selectors here
      outrank both so the design's 12px / 18px radii survive.

   Illustrations are pure CSS + inline SVG (no image assets) and
   every loop stops under prefers-reduced-motion.
   ───────────────────────────────────────────────────────────── */
.dialog.auth-modal {
    position: relative;
    max-width: min(760px, calc(100vw - 2rem));
    border-radius: 18px;
    display: grid;
    grid-template-columns: 330px 1fr;
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
}
[data-theme$="-dark"] .dialog.auth-modal {
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
}

/* ── Left: the demo panel ──
   Its own corners are rounded (inner radius = 18px shell - 1px border)
   rather than clipping the shell with overflow:hidden — the shell keeps
   .dialog's overflow-y:auto, so a short window scrolls instead of
   losing the bottom of the panel. */
.auth-demo {
    background: #0f172a;
    padding: 26px 24px;
    border-radius: 17px 0 0 17px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.auth-demo-title {
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    color: #ffffff;
    text-wrap: pretty;
    margin-bottom: 2px;
}
.auth-vig {
    border-radius: 12px;
    padding: 16px 16px 20px;
}
.auth-vig-cap {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #94a3b8;
    margin-bottom: 13px;
}
/* Each vignette sits on its own tint: slate, teal, indigo. */
.auth-vig-live    { background: #1e293b; border: 1px solid #334155; }
.auth-vig-save    { background: #16303a; border: 1px solid #2c4a56; padding-bottom: 18px; }
.auth-vig-devices { background: #252a40; border: 1px solid #3b4266; }

/* Vignette 1 — two teammates' cursors drifting over cascading bars */
.auth-art-live {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 11px;
    padding: 4px 0 8px;
}
.auth-bar { height: 7px; border-radius: 999px; background: #34d399; }
.auth-bar-1 { width: 80%; }
.auth-bar-2 { width: 56%; opacity: 0.65; margin-left: 12%; }
.auth-bar-3 { width: 44%; opacity: 0.4; margin-left: 30%; }
.auth-cursor {
    position: absolute;
    display: flex;
    align-items: flex-start;
    gap: 2px;
}
.auth-cursor svg { stroke: #0f172a; stroke-width: 1.5; }
.auth-cursor-tag {
    margin-top: 6px;
    padding: 2px 7px;
    border-radius: 999px;
    color: #0f172a;
    font-size: 9.5px;
    font-weight: 700;
}
.auth-cursor-a { top: 2px; left: 56%; animation: authCur1 5s ease-in-out infinite; }
.auth-cursor-a svg { fill: #f59e0b; }
.auth-cursor-a .auth-cursor-tag { background: #f59e0b; }
.auth-cursor-b { bottom: -4px; left: 16%; animation: authCur2 6.5s ease-in-out infinite; }
.auth-cursor-b svg { fill: #60a5fa; }
.auth-cursor-b .auth-cursor-tag { background: #60a5fa; }
@keyframes authCur1 {
    0%, 100% { transform: translate(0, 0); }
    50% { transform: translate(-34px, 12px); }
}
@keyframes authCur2 {
    0%, 100% { transform: translate(0, 0); }
    45% { transform: translate(28px, -10px); }
}

/* Vignette 2 — bars filling from the left beside a pulsing cloud */
.auth-art-save { display: flex; align-items: center; gap: 14px; }
.auth-fillbars { flex: 1; display: flex; flex-direction: column; gap: 11px; }
.auth-fill {
    height: 7px;
    border-radius: 999px;
    background: #34d399;
    transform-origin: left;
    animation: authFillbar 4s ease-in-out infinite;
}
.auth-fill-1 { width: 88%; }
.auth-fill-2 { width: 64%; opacity: 0.65; margin-left: 10%; animation-delay: 0.4s; }
.auth-fill-3 { width: 48%; opacity: 0.4; margin-left: 26%; animation-delay: 0.8s; }
.auth-cloud {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    border-radius: 50%;
    background: #34d399;
    color: #06281c;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: authCloudPulse 4s ease-in-out infinite;
}
@keyframes authFillbar {
    0% { transform: scaleX(0.15); }
    55%, 100% { transform: scaleX(1); }
}
@keyframes authCloudPulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.45); }
    60% { box-shadow: 0 0 0 9px rgba(52, 211, 153, 0); }
}

/* Vignette 3 — the same mini-chart on a desktop and a laptop, with a
   sync dot shuttling between them */
.auth-art-devices {
    position: relative;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 30px;
}
.auth-dev { display: flex; flex-direction: column; align-items: center; }
.auth-screen {
    box-sizing: border-box;
    border: 2px solid #94a3b8;
    border-radius: 6px;
    background: #0f172a;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
}
.auth-dev-desktop .auth-screen { width: 84px; height: 47px; padding: 8px; }
.auth-dev-laptop .auth-screen {
    width: 80px;
    height: 45px;
    padding: 7px 8px 8px;
    border-bottom: 0;
    border-radius: 6px 6px 0 0;
}
.auth-mini { height: 5px; border-radius: 999px; background: #34d399; }
.auth-mini-1 { width: 82%; }
.auth-mini-2 { width: 58%; opacity: 0.6; margin-left: 10%; }
.auth-mini-3 { width: 42%; opacity: 0.35; margin-left: 26%; }
.auth-stand {
    width: 12px;
    height: 9px;
    background: #94a3b8;
    clip-path: polygon(15% 0, 85% 0, 100% 100%, 0 100%);
}
.auth-base { width: 38px; height: 3px; border-radius: 999px; background: #94a3b8; }
.auth-deck {
    position: relative;
    width: 104px;
    height: 6px;
    background: #94a3b8;
    border-radius: 2px 2px 6px 6px;
}
.auth-notch {
    position: absolute;
    left: calc(50% - 9px);
    top: 0;
    width: 18px;
    height: 2.5px;
    border-radius: 0 0 3px 3px;
    background: #0f172a;
}
.auth-sync {
    position: absolute;
    left: calc(50% - 32px);
    bottom: -7px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #34d399;
    animation: authShuttle 3.5s ease-in-out infinite;
}
@keyframes authShuttle {
    0%, 15% { transform: translateX(0); }
    45%, 55% { transform: translateX(52px); }
    85%, 100% { transform: translateX(0); }
}

/* Ambient, never insistent: with reduced motion asked for, every loop
   parks at its resting frame. */
@media (prefers-reduced-motion: reduce) {
    .auth-cursor,
    .auth-fill,
    .auth-cloud,
    .auth-sync { animation: none; }
}

/* ── Right: the sign-in column ── */
.auth-pitch {
    position: relative;
    padding: 28px 30px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 22px;
}
/* Brand row above the headline. The column is centre-justified, so the
   negative top margin is what keeps the headline sitting where it did
   before the logo was added instead of the whole block dropping. */
.auth-brand {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: -26px;
}
.auth-brand-mark {
    width: 44px;
    height: 44px;
    border-radius: 12px;
    flex: 0 0 auto;
}
.auth-brand-name {
    font-size: 21px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-heading);
}
.auth-modal .auth-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 30px;
    height: 30px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 9px;
    background: transparent;
    color: var(--icon-color);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.auth-modal .auth-close:hover {
    background: var(--bg-input);
    color: var(--text-heading);
}
.auth-modal .auth-pitch-title {
    margin: 0;
    font-size: 24px;
    font-weight: 700;
    line-height: 1.28;
    color: var(--text-heading);
    text-wrap: pretty;
}
.auth-modal .auth-pitch-body {
    margin: 12px 0 0;
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-muted);
}
.auth-oauth-stack {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.auth-modal .auth-oauth-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    padding: 12px 0;
    background: var(--bg-card);
    color: var(--text-heading);
    border: 1px solid var(--border-input);
    border-radius: 12px;
    box-shadow: 0 2px 10px rgba(2, 6, 23, 0.06);
    font-size: 13.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
[data-theme$="-dark"] .auth-modal .auth-oauth-btn {
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
}
.auth-modal .auth-oauth-btn:hover {
    background: var(--bg-input);
    border-color: var(--border-input);
}
.auth-modal .auth-oauth-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.auth-oauth-icon { width: 17px; height: 17px; flex: 0 0 auto; }
.auth-modal .auth-consent {
    margin: 0;
    text-align: center;
    font-size: 10.5px;
    line-height: 1.5;
    color: var(--text-muted);
}
.auth-modal .auth-consent a {
    color: var(--accent);
    text-decoration: none;
}
.auth-modal .auth-consent a:hover { text-decoration: underline; }

/* Narrow window: one column, demo panel on top, and only the FIRST
   vignette (the design's stated fallback). Stacking all three would
   stretch the little charts across the full width and push the buttons
   below the fold. The card column keeps its 282px content width and
   centres, so the illustration stays the shape it was drawn at. */
@media (max-width: 820px) {
    .dialog.auth-modal { grid-template-columns: 1fr; }
    .auth-demo { border-radius: 17px 17px 0 0; padding: 22px 24px 24px; }
    .auth-demo > * { width: 100%; max-width: 282px; margin-inline: auto; }
    .auth-vig-save,
    .auth-vig-devices { display: none; }
    .auth-pitch { position: static; padding: 24px 22px 26px; }
    /* Stacked, the pitch column is sized to its content, so there is no
       centring slack for the brand row's negative margin to cancel. */
    .auth-brand { margin-top: 0; }
    /* With the pitch column no longer the containing block, the close
       button lands on the window's own corner — over the dark panel, so
       it takes the panel's muted slate instead of the theme's icon colour. */
    .auth-modal .auth-close { top: 10px; right: 10px; color: #94a3b8; }
    .auth-modal .auth-close:hover { background: rgba(255, 255, 255, 0.1); color: #ffffff; }
}
/* Short window: the first vignette carries the pitch on its own, centred
   in the panel so the dark column has no dead space below it. */
@media (max-height: 640px) {
    .auth-vig-save,
    .auth-vig-devices { display: none; }
    .auth-demo { padding: 20px 24px; justify-content: center; }
    .auth-modal .auth-pitch-title { font-size: 21px; }
    /* Short windows buy back the height the brand row costs. */
    .auth-brand { margin-top: 0; }
    .auth-brand-mark { width: 36px; height: 36px; border-radius: 10px; }
    .auth-brand-name { font-size: 18px; }
}

.auth-error {
    margin-top: 12px;
    padding: 8px 10px;
    font-size: 12px;
    color: var(--danger, #dc2626);
    background: rgba(220, 38, 38, 0.08);
    border: 1px solid rgba(220, 38, 38, 0.2);
    border-radius: 6px;
    line-height: 1.4;
}

/* ─────────────────────────────────────────────────────────────
   Account settings window (design handoff "Account Settings Window",
   option 1b, 2026-10). Replaces the old "Your profile" popover: a
   760 × 500 window, a 210px side nav (identity, four tabs, Sign out at
   the foot) beside the content pane (title, a scrolling body, a save
   bar on Profile and Notifications). The design is light-only and names
   exact colours; each one goes through an --ac-* token so the dark
   theme re-points them in one block further down. Selectors carry the
   .acct-window prefix where the later "pill refresh" pass would
   otherwise round the inputs and swatches.
   ───────────────────────────────────────────────────────────── */
.dialog.acct-window {
    --ac-ink: var(--text-heading);          /* #0f172a */
    --ac-body: var(--text-body);            /* #334155 */
    --ac-sec: #475569;
    --ac-muted: var(--text-muted);          /* #64748b */
    --ac-faint: #94a3b8;
    --ac-card: var(--bg-card);              /* #ffffff */
    --ac-nav: var(--bg-body);               /* #f8fafc */
    --ac-hover: #eef2f6;
    --ac-hover-2: var(--bg-input);          /* #f1f5f9 */
    --ac-border: var(--border-card);        /* #e2e8f0 */
    --ac-field: var(--border-input);        /* #cbd5e1 */
    --ac-hair: #eef2f6;
    --ac-accent: var(--btn-add-bg);         /* #059669 */
    --ac-accent-ink: var(--btn-add-text);   /* #ffffff */
    --ac-off: #cbd5e1;
    --ac-disabled: #e2e8f0;
    --ac-disabled-ink: #94a3b8;
    --ac-band: #f0fdf7;
    --ac-band-line: #d1fae5;
    --ac-band-label: #047857;
    --ac-danger: #dc2626;
    --ac-danger-line: #fecaca;
    --ac-danger-bg: #fffafa;
    --ac-danger-hover: #fef2f2;
    --ac-tile-shadow: 0 2px 10px rgba(2, 6, 23, 0.06);
    --ac-card-shadow: 0 2px 10px rgba(2, 6, 23, 0.04);
    --ac-card-shadow-hover: 0 6px 18px rgba(2, 6, 23, 0.08);
    --ac-knob-shadow: 0 1px 2px rgba(2, 6, 23, 0.2);
    --ac-swatch-edge: rgba(15, 23, 42, 0.08);
    width: min(760px, calc(100vw - 2rem));
    max-width: none;
    height: min(500px, calc(100dvh - 2rem));
    max-height: none;
    padding: 0;
    overflow: hidden;
    display: grid;
    grid-template-columns: 210px minmax(0, 1fr);
    background: var(--ac-card);
    border: 1px solid var(--ac-border);
    border-radius: 18px;
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
    color: var(--ac-body);
}
.acct-window button,
.acct-window input { font-family: inherit; }
/* Anything inside the window marked .hidden stays hidden, whatever
   display its own class sets. */
.acct-window .hidden { display: none; }

/* ── Side nav ── */
.acct-nav {
    background: var(--ac-nav);
    border-right: 1px solid var(--ac-border);
    padding: 18px 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: 0;
    overflow-y: auto;
}
.acct-ident {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 8px 16px;
    min-width: 0;
}
.acct-ident-avatar {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    background: #3b82f6;
    color: #ffffff;
    transition: background 0.15s ease, color 0.15s ease;
    user-select: none;
}
.acct-ident-text { min-width: 0; }
.acct-ident-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--ac-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.acct-ident-email {
    font-size: 10.5px;
    color: var(--ac-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.acct-tabs { display: flex; flex-direction: column; gap: 4px; }
.acct-nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    border-radius: 9px;
    background: transparent;
    font-size: 13px;
    font-weight: 500;
    color: var(--ac-sec);
    text-align: left;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.acct-nav-item svg { width: 15px; height: 15px; flex-shrink: 0; }
.acct-nav-item.is-active {
    background: var(--ac-card);
    color: var(--ac-ink);
    font-weight: 600;
}
.acct-nav-item:hover { background: var(--ac-hover); }
.acct-nav-item:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -2px; }
.acct-nav-spacer { flex: 1 1 auto; }
.acct-nav-signout { color: var(--ac-muted); }

/* ── Content pane ── */
.acct-main {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}
.acct-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 9px;
    background: transparent;
    color: var(--ac-faint);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease;
    z-index: 1;
}
.acct-close svg { width: 15px; height: 15px; }
.acct-close:hover { background: var(--ac-hover-2); }
.acct-close:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 1px; }
.acct-title {
    padding: 22px 56px 0 28px;
    font-size: 16px;
    font-weight: 700;
    color: var(--ac-ink);
}
.acct-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 18px 28px 24px;
    display: flex;
    flex-direction: column;
}
.acct-pane {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* ── Profile tab ── */
.acct-profile-grid {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: 28px;
    align-items: stretch;
}
.acct-col {
    display: flex;
    flex-direction: column;
    gap: 26px;
    min-width: 0;
}
.acct-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.acct-field-color { gap: 8px; position: relative; }
.acct-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--ac-ink);
}
.acct-input {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 44px;
    padding: 0 12px;
    border: 1px solid var(--ac-field);
    border-radius: 12px;
    background: var(--ac-card);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.acct-input:focus-within {
    border-color: var(--border-focus);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 16%, transparent);
}
.acct-window .acct-input input {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: 0;
    outline: 0;
    box-shadow: none;
    background: transparent;
    font-size: 13.5px;
    color: var(--ac-ink);
}
.acct-count {
    font-size: 11px;
    color: var(--ac-faint);
    font-variant-numeric: tabular-nums;
    user-select: none;
}
.acct-help { font-size: 11px; line-height: 1.45; color: var(--ac-muted); }
.acct-label-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
/* "Custom color" — the themed picker's trigger: a rainbow dot beside the
   label (so the swatch grid stays the design's 6 × 2); while a custom
   colour is chosen it shows that colour, ringed like a selected swatch. */
.acct-custom {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: conic-gradient(from 0deg, #ef4444, #f59e0b, #22c55e, #06b6d4, #6366f1, #ec4899, #ef4444);
    box-shadow: inset 0 0 0 1px var(--ac-swatch-edge);
    cursor: pointer;
    transition: box-shadow 0.15s ease;
}
.acct-custom:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 4px; }
.acct-custom.selected {
    background: var(--custom, #3b82f6);
    box-shadow: 0 0 0 2px var(--ac-card), 0 0 0 4px var(--custom, #3b82f6);
}
.acct-window .profile-color-grid {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 10px;
}
.acct-window .profile-color-swatch {
    width: 100%;
    height: auto;
    aspect-ratio: 1;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--sw);
    box-shadow: inset 0 0 0 1px var(--ac-swatch-edge);
    cursor: pointer;
    transition: box-shadow 0.15s ease;
}
.acct-window .profile-color-swatch.selected {
    box-shadow: 0 0 0 2px var(--ac-card), 0 0 0 4px var(--sw);
}
.acct-window .profile-color-swatch:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 4px; }
.acct-provider {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-size: 12.5px;
    color: var(--ac-body);
}
.acct-provider svg { display: none; width: 14px; height: 14px; flex-shrink: 0; color: var(--ac-ink); }
.acct-provider[data-provider="google"] svg[data-provider-icon="google"],
.acct-provider[data-provider="github"] svg[data-provider-icon="github"],
.acct-provider[data-provider="azure"]  svg[data-provider-icon="azure"] { display: block; }
.acct-provider-email { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Live preview — a slice of a shared chart. */
.acct-col-preview { gap: 8px; }
.acct-preview {
    position: relative;
    flex: 1 1 auto;
    min-height: 220px;
    border: 1px solid var(--ac-border);
    border-radius: 12px;
    background: var(--ac-nav);
    overflow: hidden;
    cursor: none;
}
.acct-pv-grid {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    pointer-events: none;
}
.acct-pv-grid span { border-right: 1px solid var(--ac-hair); }
.acct-pv-grid span:last-child { border-right: 0; }
.acct-pv-bars {
    position: absolute;
    inset: 24px 14px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    pointer-events: none;
}
.acct-pv-bar {
    height: 18px;
    padding-left: 8px;
    border-radius: 6px;
    display: flex;
    align-items: center;
    font-size: 10px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    user-select: none;
}
.acct-pv-b1 { width: 52%; margin-left: 2%;  background: #059669; color: #ffffff; }
.acct-pv-b2 { width: 56%; margin-left: 26%; background: #a7f3d0; color: #065f46; }
.acct-pv-b3 { width: 44%; margin-left: 50%; background: #e2e8f0; color: #475569; }
.acct-pv-b4 { width: 30%; margin-left: 66%; background: #e2e8f0; color: #475569; }
.acct-pv-cursor {
    position: absolute;
    display: flex;
    align-items: flex-start;
    gap: 2px;
    pointer-events: none;
}
.acct-pv-cursor svg { width: 13px; height: 13px; flex-shrink: 0; }
.acct-pv-pill {
    margin-top: 7px;
    padding: 2px 7px;
    border-radius: 999px;
    font-size: 9.5px;
    font-weight: 700;
    white-space: nowrap;
    user-select: none;
}
.acct-pv-mate { top: 46%; left: 12%; animation: acctDriftMate 7s ease-in-out infinite; }
.acct-pv-mate .acct-pv-pill { background: #6366f1; color: #ffffff; }
.acct-pv-you { top: 0; left: 0; }
.acct-pv-you.is-idle { top: 132px; left: 56%; animation: acctDriftYou 6s ease-in-out infinite; }
@keyframes acctDriftYou {
    0%, 100% { transform: translate(0, 0); }
    50% { transform: translate(-46px, 14px); }
}
@keyframes acctDriftMate {
    0%, 100% { transform: translate(0, 0); }
    50% { transform: translate(38px, -12px); }
}

/* ── Notifications tab ── */
.acct-notif,
.acct-notif-kinds { display: flex; flex-direction: column; gap: 4px; }
.acct-notif-row { display: flex; align-items: center; gap: 14px; cursor: pointer; }
.acct-notif-text { flex: 1 1 auto; min-width: 0; font-size: 13px; }
.acct-notif-master { padding: 6px 0 16px; border-bottom: 1px solid var(--ac-border); }
.acct-notif-master .acct-notif-text { font-weight: 600; color: var(--ac-ink); }
.acct-notif-kind { padding: 16px 0 16px 4px; border-bottom: 1px solid var(--ac-hair); }
.acct-notif-kind .acct-notif-text { color: var(--ac-body); }
.acct-notif-kinds { transition: opacity 0.15s ease; }
/* Master off: the kinds read as off and cannot be changed (each keeps its
   own value for when the master comes back). */
.acct-notif-kinds.kinds-disabled { opacity: 0.45; }
.acct-notif-kinds.kinds-disabled .acct-notif-row { cursor: default; }
.acct-switch {
    position: relative;
    flex-shrink: 0;
    width: 30px;
    height: 18px;
}
.acct-switch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: inherit;
}
.acct-switch-track {
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: var(--ac-off);
    pointer-events: none;
    transition: background 0.15s ease, box-shadow 0.15s ease;
}
.acct-switch-track::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #ffffff;
    box-shadow: var(--ac-knob-shadow);
    transition: transform 0.15s ease;
}
.acct-switch input:checked + .acct-switch-track { background: var(--ac-accent); }
.acct-switch input:checked + .acct-switch-track::after { transform: translateX(12px); }
.acct-switch input:focus-visible + .acct-switch-track {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 28%, transparent);
}
.acct-switch-lg { width: 34px; height: 20px; }
.acct-switch-lg .acct-switch-track::after { width: 16px; height: 16px; }
.acct-switch-lg input:checked + .acct-switch-track::after { transform: translateX(14px); }
.kinds-disabled .acct-switch input:checked + .acct-switch-track { background: var(--ac-off); }
.kinds-disabled .acct-switch input:checked + .acct-switch-track::after { transform: none; }

/* ── Usage tab ── */
.acct-usage { display: flex; flex-direction: column; gap: 14px; }
.acct-band {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    border: 1px solid var(--ac-band-line);
    border-radius: 14px;
    background: var(--ac-band);
    overflow: hidden;
}
.acct-band-half {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 20px;
    min-width: 0;
}
.acct-band-half + .acct-band-half { border-left: 1px solid var(--ac-band-line); }
.acct-band-tile {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    border-radius: 12px;
    background: var(--ac-card);
    box-shadow: var(--ac-tile-shadow);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--ac-ink);
}
.acct-band-tile svg { width: 18px; height: 18px; }
.acct-band-text { min-width: 0; }
.acct-micro {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ac-band-label);
}
.acct-band-value {
    margin-top: 2px;
    font-size: 20px;
    font-weight: 700;
    color: var(--ac-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.acct-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
}
.acct-stat {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
    padding: 16px;
    border: 1px solid var(--ac-border);
    border-radius: 14px;
    background: var(--ac-card);
    box-shadow: var(--ac-card-shadow);
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.acct-stat:hover { border-color: var(--ac-field); box-shadow: var(--ac-card-shadow-hover); }
.acct-stat-tile {
    width: 34px;
    height: 34px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--t-bg);
    color: var(--t-ink);
}
.acct-stat-tile svg { width: 16px; height: 16px; }
.acct-stat-value {
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
    color: var(--ac-ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.acct-stat-label { margin-top: 4px; font-size: 12px; color: var(--ac-muted); }
.acct-t-charts  { --t-bg: #eff6ff; --t-ink: #2563eb; }
.acct-t-shared  { --t-bg: #f5f3ff; --t-ink: #7c3aed; }
.acct-t-tasks   { --t-bg: #fffbeb; --t-ink: #b45309; }
.acct-t-events  { --t-bg: #fdf2f8; --t-ink: #be185d; }
.acct-t-mcp     { --t-bg: #eef2ff; --t-ink: #4f46e5; }
.acct-t-exports { --t-bg: #ecfeff; --t-ink: #0e7490; }

/* ── Your data tab ── */
.acct-data { display: flex; flex-direction: column; gap: 12px; }
.acct-data-card {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 22px;
    border: 1px solid var(--ac-border);
    border-radius: 12px;
}
.acct-data-danger { border-color: var(--ac-danger-line); background: var(--ac-danger-bg); }
.acct-data-text { flex: 1 1 auto; min-width: 0; }
.acct-data-title { font-size: 13px; font-weight: 600; color: var(--ac-ink); }
.acct-data-sub { margin-top: 3px; font-size: 12px; color: var(--ac-muted); }
.acct-btn-outline,
.acct-btn-danger {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex-shrink: 0;
    padding: 8px 14px;
    border-radius: 10px;
    background: transparent;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.15s ease;
}
.acct-btn-outline { border: 1px solid var(--ac-field); color: var(--ac-ink); }
.acct-btn-outline svg { width: 14px; height: 14px; }
.acct-btn-outline:hover { background: var(--ac-hover-2); }
.acct-btn-outline:disabled { opacity: 0.6; cursor: progress; }
.acct-btn-danger { border: 1px solid var(--ac-danger-line); color: var(--ac-danger); }
.acct-btn-danger:hover { background: var(--ac-danger-hover); }
.acct-btn-outline:focus-visible,
.acct-btn-danger:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.acct-window .profile-privacy-status { margin-top: 0; border-radius: 10px; }

/* ── Save bar (Profile + Notifications) ── */
.acct-savebar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
    padding: 12px 20px 12px 28px;
    border-top: 1px solid var(--ac-border);
}
.acct-status {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 11.5px;
    color: var(--ac-muted);
}
.acct-status-error { color: var(--ac-danger); font-weight: 600; }
.acct-btn-ghost,
.acct-btn-save {
    border: 0;
    border-radius: 10px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.acct-btn-ghost { padding: 8px 12px; background: transparent; color: var(--ac-body); }
.acct-btn-ghost:hover { background: var(--ac-hover-2); }
.acct-btn-save { padding: 8px 16px; background: var(--ac-accent); color: var(--ac-accent-ink); }
.acct-btn-save:disabled { background: var(--ac-disabled); color: var(--ac-disabled-ink); cursor: default; }
.acct-btn-ghost:focus-visible,
.acct-btn-save:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }

/* First signup: the Profile tab alone. */
.acct-first-run [data-acct-tab="notif"],
.acct-first-run [data-acct-tab="usage"],
.acct-first-run [data-acct-tab="data"] { display: none; }

/* ── Dark theme: the same window, re-pointed tokens ── */
[data-theme$="-dark"] .dialog.acct-window {
    --ac-sec: #a3a3a3;
    --ac-faint: #707070;
    --ac-hover: #1f1f1f;
    --ac-hair: #1f1f1f;
    --ac-off: #3a3a3a;
    --ac-disabled: #262626;
    --ac-disabled-ink: #707070;
    --ac-band: rgba(52, 211, 153, 0.07);
    --ac-band-line: rgba(52, 211, 153, 0.22);
    --ac-band-label: var(--accent-dark);
    --ac-danger: #f87171;
    --ac-danger-line: rgba(248, 113, 113, 0.35);
    --ac-danger-bg: rgba(248, 113, 113, 0.05);
    --ac-danger-hover: rgba(248, 113, 113, 0.12);
    --ac-tile-shadow: none;
    --ac-card-shadow: none;
    --ac-card-shadow-hover: 0 6px 18px rgba(0, 0, 0, 0.5);
    --ac-knob-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
    --ac-swatch-edge: rgba(255, 255, 255, 0.16);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
}
[data-theme$="-dark"] .acct-band-tile { background: var(--bg-input); }
[data-theme$="-dark"] .acct-pv-b2 { background: rgba(52, 211, 153, 0.22); color: #6ee7b7; }
[data-theme$="-dark"] .acct-pv-b3,
[data-theme$="-dark"] .acct-pv-b4 { background: #262626; color: #a3a3a3; }
[data-theme$="-dark"] .acct-t-charts  { --t-bg: rgba(59, 130, 246, 0.16); --t-ink: #60a5fa; }
[data-theme$="-dark"] .acct-t-shared  { --t-bg: rgba(139, 92, 246, 0.18); --t-ink: #a78bfa; }
[data-theme$="-dark"] .acct-t-tasks   { --t-bg: rgba(245, 158, 11, 0.16); --t-ink: #fbbf24; }
[data-theme$="-dark"] .acct-t-events  { --t-bg: rgba(236, 72, 153, 0.16); --t-ink: #f472b6; }
[data-theme$="-dark"] .acct-t-mcp     { --t-bg: rgba(99, 102, 241, 0.2);  --t-ink: #818cf8; }
[data-theme$="-dark"] .acct-t-exports { --t-bg: rgba(6, 182, 212, 0.16);  --t-ink: #22d3ee; }
/* The navy swatch's own ring would vanish on the dark window. */
[data-theme$="-dark"] .acct-window .profile-color-swatch[data-color="#0f172a"].selected {
    box-shadow: 0 0 0 2px var(--ac-card), 0 0 0 4px #64748b;
}

/* ── Narrow windows: the nav becomes a tab strip, columns stack ── */
@media (max-width: 680px) {
    .dialog.acct-window {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr);
        height: min(640px, calc(100dvh - 2rem));
    }
    .acct-nav {
        flex-direction: row;
        align-items: center;
        padding: 10px;
        border-right: 0;
        border-bottom: 1px solid var(--ac-border);
        overflow-x: auto;
    }
    .acct-ident,
    .acct-nav-spacer { display: none; }
    .acct-tabs { flex-direction: row; }
    .acct-nav-item { width: auto; white-space: nowrap; }
    .acct-profile-grid { grid-template-columns: minmax(0, 1fr); }
    .acct-window .profile-color-grid { max-width: 300px; }
    .acct-band { grid-template-columns: minmax(0, 1fr); }
    .acct-band-half + .acct-band-half { border-left: 0; border-top: 1px solid var(--ac-band-line); }
    .acct-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .acct-data-card { flex-wrap: wrap; }
}
@media (prefers-reduced-motion: reduce) {
    .acct-pv-mate,
    .acct-pv-you.is-idle { animation: none; }
}

/* Custom-color popover — themed replacement for the native picker.
   Anchored to the colour field (position: relative) under the "Custom
   color" control and opens DOWNWARD over the swatches: the body above is
   the name field, and the window's body scrolls. */
.profile-color-pop {
    position: absolute;
    top: 26px;
    right: -6px;              /* also covers the last swatch's selection ring */
    width: 234px;
    padding: 12px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 14px;
    box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.45);
    z-index: 40;
    animation: dialogIn 0.12s ease-out;
}
.pcp-sv {
    position: relative;
    height: 140px;
    border-radius: 10px;
    cursor: crosshair;
    touch-action: none;
    background:
        linear-gradient(to top, #000, rgba(0, 0, 0, 0)),
        linear-gradient(to right, #fff, var(--pcp-hue, #10b981));
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.pcp-knob {
    position: absolute;
    width: 16px; height: 16px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4), inset 0 0 0 1px rgba(0, 0, 0, 0.15);
    transform: translate(-50%, -50%);
    pointer-events: none;
}
.pcp-hue {
    position: relative;
    height: 14px;
    margin-top: 12px;
    border-radius: 999px;
    cursor: pointer;
    touch-action: none;
    background: linear-gradient(to right, #f00, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.pcp-hue .pcp-knob { top: 50%; width: 18px; height: 18px; }
.pcp-row { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.pcp-eyedrop {
    width: 30px; height: 30px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--border-card);
    border-radius: 9px;
    background: var(--accent-soft);
    color: var(--accent-dark);
    cursor: pointer;
    padding: 0;
    transition: border-color 0.15s ease, transform 0.15s ease;
}
.pcp-eyedrop:hover { border-color: var(--accent-dark); transform: scale(1.06); }
.pcp-eyedrop svg { width: 14px; height: 14px; }
.pcp-current {
    width: 30px; height: 30px; flex: 0 0 auto;
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 2px var(--card-bg), 0 0 0 3px var(--border-card);
}
.pcp-hex {
    flex: 1; min-width: 0; height: 30px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 12px;
}

@media (max-width: 560px) {
    .auth-modal { max-width: calc(100vw - 32px); }
    .auth-menu { right: -8px; min-width: 200px; }
}

/* ============================================================================
   pill refresh — visual-consistency pass
   ============================================================================
   Unifies every rectangular button + input + card across the app with the
   "Synced" badge design language (border-radius: 999px). Square icon-only
   controls keep their existing radius — they're handled below explicitly.
   Cascade-only override (no !important): later rules win at equal
   specificity, so the original declarations stay readable above.
   ========================================================================= */

.btn-export-sm,
.btn-export,
.btn-primary,
.btn-accent,
.btn-feedback,
.btn-zoom-fit,
.btn-about,
.btn-dialog-delete,
.btn-text-quiet,
.auth-oauth-btn,
.my-charts-btn,
.new-project-btn,
.collaborate-btn,
.project-picker-trigger,
.family-option,
/* (export dialog chips/cards left out on purpose: the redesigned export
   modal uses segmented controls and 10px cards, not pills.) */
.load-format-card,
.dialog-footer button,
.auth-modal button,
button[type="submit"],
/* Type chips, color swatch, and dialog dropdowns join the pill family
   so every interactive control in the Create/Edit Task dialog reads the same. */
.type-chip,
.color-swatch,
.dlg-select,
/* Small square icon controls — pill radius produces a clean circle on
   equal-size square buttons (close ×, edit, delete, lang flag, settings,
   theme toggle, L/U/TOD/EVT toggles, per-row delete). */
.icon-btn,
.icon-btn-edit,
.icon-btn-delete,
.lang-toggle,
.theme-toggle-sm,
.snap-toggle {
    border-radius: 999px;
}

/* Type chip padding now mirrors .priority-chip span so the two
   sibling-row chip families share the same height + horizontal breathing
   room. Without this, type chips read as a different family next to the
   priority pills above them. */
.type-chip {
    padding: 0.4rem 0.75rem;
    font-size: 0.75rem;
}

/* Inputs — pill radius + extra horizontal breathing room so text doesn't
   sit on the curve. Multi-line textareas excluded. */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
select {
    border-radius: 999px;
    padding-left: 0.95rem;
    padding-right: 0.95rem;
}

/* Selects carrying the .input class need an explicit pill rule — the bare
   `select` selector above (specificity 0,0,1) loses to `.input` (0,1,0),
   so `<select class="input">` would otherwise keep the rounded 0.5rem from
   the base .input rule. Match the same horizontal padding too. */
select.input {
    border-radius: 999px;
    padding-left: 0.95rem;
    padding-right: 0.95rem;
}

/* Date-trigger inputs (viewport start/end, task start/end pickers) — the
   value is short + symmetric in feel; centering reads cleaner inside the
   pill than left-aligned with empty right-side whitespace. */
.date-trigger {
    text-align: center;
}

/* Active project name span — pill background on hover to match the
   input/badge family. */
.active-project-name {
    border-radius: 999px;
    padding-left: 0.7rem;
    padding-right: 0.7rem;
}

/* Dialog / card corner harmonization — softer than the old 8px so the
   panel surfaces visually rhyme with their pill content. */
.dialog,
.auth-modal,
.project-picker-menu,
.new-project-card {
    border-radius: 14px;
}

/* ============================================================================
   Feedback dialog polish
   ========================================================================= */
.fb-label-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}
.fb-counter {
    font-size: 0.65rem;
    color: var(--text-faint);
    font-weight: 500;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.fb-counter.near-limit {
    color: var(--text-muted);
}
.fb-trust {
    font-size: 0.7rem;
    color: var(--text-faint);
    font-style: italic;
    margin: -0.15rem 0 0.4rem;
    line-height: 1.35;
}
.fb-spinner {
    width: 14px;
    height: 14px;
    margin-left: 4px;
    animation: fb-spin 0.8s linear infinite;
    flex-shrink: 0;
}
@keyframes fb-spin {
    to { transform: rotate(360deg); }
}
#feedbackSubmit {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.fb-thanks {
    text-align: center;
    align-items: center;
    justify-content: center;
    gap: 0;
    padding: 2.75rem 1.5rem 3rem;
    min-height: 232px;
}
.fb-thanks-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    margin-bottom: 1.2rem;
    border-radius: 50%;
    color: var(--accent);
    background: var(--accent-soft);
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 8%, transparent),
                0 6px 18px -8px color-mix(in srgb, var(--accent) 35%, transparent);
    animation: fbBadgePop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
[data-theme$="-dark"] .fb-thanks-badge { color: var(--accent-dark); }
.fb-thanks-badge svg {
    width: 38px;
    height: 38px;
}
.fb-thanks-badge svg polyline {
    stroke-dasharray: 40;
    stroke-dashoffset: 40;
    animation: fbCheckDraw 0.4s 0.22s cubic-bezier(0.65, 0, 0.35, 1) forwards;
}
@keyframes fbBadgePop {
    0%   { transform: scale(0.3); opacity: 0; }
    100% { transform: scale(1);   opacity: 1; }
}
@keyframes fbCheckDraw {
    to { stroke-dashoffset: 0; }
}
.fb-thanks h4,
.fb-thanks .fb-thanks-title {
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 0 0 0.4rem;
    color: var(--text-heading);
}
.fb-thanks p {
    color: var(--text-muted);
    font-size: 0.9rem;
    line-height: 1.5;
    margin: 0;
    max-width: 30ch;
}
@media (prefers-reduced-motion: reduce) {
    .fb-thanks-badge { animation: none; }
    .fb-thanks-badge svg polyline { animation: none; stroke-dashoffset: 0; }
}

/* ============================================================================
   Support center (a46) — the signed-in face of the feedback dialog: a
   two-pane window (thread-list sidebar + open conversation with chat
   bubbles). The anonymous form keeps the fb-* styles above. On narrow
   screens the panes stack: list first, .sc-has-thread swaps in the
   conversation and the back button returns.
   ========================================================================= */
.fb-signin-hint {
    font-size: 0.72rem;
    color: var(--text-muted);
    margin: -0.2rem 0 0;
    line-height: 1.35;
}
/* The dialog is narrow for the email form and a full window for the Message
   Center (JS toggles sc-center-open when the two-pane face shows). The center
   follows the 2026-09 design system: IBM Plex Sans, an 18px-radius window,
   a thread-card rail on --bg-body, ink user bubbles, card support bubbles,
   and exactly one green primary action per view. */
.fb-dialog { max-width: min(460px, calc(100vw - 2rem)); }
.fb-dialog.sc-center-open {
    max-width: min(1020px, calc(100vw - 2rem));
    display: flex;
    flex-direction: column;
    overflow: hidden;          /* panes scroll internally, not the dialog */
    padding: 0;
    border-radius: 18px;
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
    box-shadow: 0 24px 60px rgba(2, 6, 23, 0.22), 0 4px 12px rgba(2, 6, 23, 0.1);
}
/* The center carries its own header (icon tile + caption + close). */
.fb-dialog.sc-center-open .dialog-header { display: none; }
.sc-center {
    display: flex;
    flex-direction: column;
    min-height: 0;
    height: min(760px, calc(100dvh - 6rem));
}
.sc-center.hidden { display: none; }
.sc-flex { flex: 1 1 auto; }
.sc-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px 12px 18px;
    border-bottom: 1px solid var(--border-card);
    flex: none;
}
.sc-head-tile {
    width: 30px;
    height: 30px;
    border-radius: 10px;
    background: var(--bg-input);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-heading);
    flex: none;
}
.sc-head-text { flex: 1 1 auto; min-width: 0; }
.sc-head-title { font-size: 14.5px; font-weight: 700; color: var(--text-heading); }
.sc-head-cap { font-size: 10.5px; color: var(--text-muted); }
.sc-head-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    border: 0;
    background: transparent;
    color: var(--icon-color, var(--text-muted));
    cursor: pointer;
    flex: none;
}
.sc-head-x:hover { color: var(--text-heading); background: var(--bg-input); }
.sc-body { flex: 1 1 auto; min-height: 0; display: flex; }
.sc-list-pane {
    flex: 0 0 284px;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px;
    border-right: 1px solid var(--border-card);
    background: var(--bg-body);
}
.sc-center.sc-mode-empty .sc-list-pane { display: none; }
.sc-thread-pane {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.sc-new-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    width: 100%;
    padding: 9px 0;
    border: 0;
    border-radius: 10px;
    background: var(--btn-add-bg);
    color: var(--btn-add-text);
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    flex: none;
}
.sc-new-btn:hover { background: var(--accent-dark); }
#scEmptyNewBtn { width: auto; padding: 9px 18px; margin-top: 6px; }
.sc-thread-list {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    overflow-y: auto;
}
.sc-rail-foot {
    flex: none;
    font-size: 10.5px;
    color: var(--text-muted);
    text-align: center;
    padding: 0 8px 4px;
    line-height: 1.5;
    margin: 0;
}
.sc-thread-row {
    display: flex;
    gap: 11px;
    padding: 10px 11px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--bg-card);
    cursor: pointer;
    text-align: left;
    width: 100%;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.sc-thread-row:hover { border-color: var(--border-input); }
.sc-thread-row.is-active {
    border-color: var(--border-input);
    box-shadow: 0 2px 8px rgba(2, 6, 23, 0.07);
}
.sc-tile {
    width: 28px;
    height: 28px;
    border-radius: 9px;
    background: var(--bg-input);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    color: var(--text-heading);
}
.sc-thread-main { flex: 1 1 auto; min-width: 0; }
.sc-thread-l1 { display: flex; align-items: center; gap: 6px; }
.sc-thread-topic {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1 1 auto;
    min-width: 0;
}
.sc-thread-when { font-size: 10.5px; color: var(--text-muted); flex: none; }
.sc-thread-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 3px;
    font-size: 11px;
    color: var(--text-muted);
}
.sc-unread-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: none; }
.sc-status-pill {
    display: inline-flex;
    align-items: center;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    background: var(--accent-soft);
    color: var(--accent-dark);
    flex: none;
}
.sc-status-pill.is-closed { background: var(--bg-input); color: var(--text-muted); }
/* Back to the list — a narrow-screen affordance only; side-by-side panes
   need no back button. */
.sc-back {
    display: none;
    align-items: center;
    gap: 4px;
    align-self: flex-start;
    padding: 0.15rem 0.45rem;
    border: none;
    background: none;
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 600;
    border-radius: 7px;
    cursor: pointer;
}
.sc-back:hover { color: var(--accent-dark); background: var(--bg-hover); }
/* Conversation strip above the messages (thread mode). */
.sc-strip {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--border-card);
    flex: none;
}
.sc-strip-ico { display: inline-flex; color: var(--text-muted); flex: none; }
.sc-strip-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sc-strip-meta { margin-left: auto; font-size: 10.5px; color: var(--text-muted); flex: none; }
/* Compose mode: heading, topic chips, per-topic hint. */
.sc-compose-head { padding: 22px 26px 0; flex: none; }
.sc-compose-title { font-size: 15px; font-weight: 700; color: var(--text-heading); }
.sc-compose-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
#scTopicRow { padding: 18px 26px 0; flex: none; }
#scTopicRow.hidden { display: none; }
.sc-micro {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 8px;
}
.sc-chips { display: flex; gap: 7px; flex-wrap: wrap; }
.sc-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    padding: 7px 13px;
    border-radius: 999px;
    border: 1px solid var(--border-input);
    background: var(--bg-card);
    color: var(--text-body);
    cursor: pointer;
    font-family: inherit;
}
.sc-chip svg { color: var(--text-muted); }
.sc-chip:hover { background: var(--bg-input); }
.sc-chip.on {
    font-weight: 700;
    border-color: var(--text-heading);
    background: var(--text-heading);
    color: var(--bg-card);
}
.sc-chip.on svg { color: var(--bg-card); }
.sc-topic-hint { font-size: 11px; color: var(--text-muted); margin-top: 8px; line-height: 1.5; min-height: 17px; }
.sc-messages {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-height: 40px;
    overflow-y: auto;
    padding: 16px 18px;
}
.sc-messages.hidden { display: none; }
/* Hairline day dividers between message groups. */
.sc-divider { display: flex; align-items: center; gap: 10px; flex: none; }
.sc-divider i { flex: 1; height: 1px; background: var(--border-card); }
.sc-divider span {
    font-size: 10.5px;
    font-weight: 600;
    color: var(--text-muted);
    display: inline-flex;
    align-items: center;
    gap: 5px;
}
/* Bubbles: user = ink, right; support = card + 26px icon tile, left. */
.sc-msg { display: flex; flex-direction: column; gap: 4px; max-width: 78%; }
.sc-msg.from-user { align-self: flex-end; align-items: flex-end; }
.sc-msg.from-admin { align-self: flex-start; max-width: 82%; flex-direction: row; gap: 9px; }
.sc-msg.from-admin .sc-tile { width: 26px; height: 26px; margin-top: 2px; }
.sc-msg-col { display: flex; flex-direction: column; gap: 4px; min-width: 0; align-items: flex-start; }
.sc-msg.from-user .sc-msg-col { align-items: flex-end; }
.sc-msg-bubble {
    padding: 9px 13px;
    line-height: 1.45;
    font-size: 13.5px;
    /* Bodies are free text — rendered with textContent, wrapped here. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.sc-msg.from-user .sc-msg-bubble {
    background: var(--text-heading);
    color: var(--bg-card);
    border-radius: 16px 16px 4px 16px;
}
.sc-msg.from-admin .sc-msg-bubble {
    background: var(--bg-card);
    color: var(--text-body);
    border: 1px solid var(--border-card);
    border-radius: 16px 16px 16px 4px;
    box-shadow: 0 2px 10px rgba(2, 6, 23, 0.06);
    line-height: 1.5;
}
.sc-msg-time { font-size: 10.5px; color: var(--text-muted); }
/* Screenshot chips inside a bubble (click opens the image). */
.sc-msg-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.sc-msg-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    background: rgba(148, 163, 184, 0.28);
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}
.sc-msg-chip:hover { background: rgba(148, 163, 184, 0.42); }
/* Links the team writes into a bubble (the console's grammar, schema-a64)
   read as links: accent green, underlined, so they are not taken for text. */
.sc-msg.from-admin .sc-msg-bubble a:not(.sc-msg-chip) {
    color: var(--accent-dark);
    text-decoration: underline;
    text-underline-offset: 2px;
}
/* Empty state (no threads yet). */
.sc-empty {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    text-align: center;
}
.sc-empty-tile {
    width: 52px;
    height: 52px;
    border-radius: 16px;
    background: var(--bg-input);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-heading);
}
.sc-empty-title { font-size: 15px; font-weight: 700; color: var(--text-heading); }
.sc-empty-body { font-size: 12px; color: var(--text-muted); line-height: 1.55; max-width: 300px; }
/* Closed conversation: the composer swaps out for this bar. */
.sc-closedbar {
    flex: none;
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 14px 14px;
    border: 1px solid var(--border-card);
    border-radius: 14px;
    background: var(--bg-input);
    padding: 12px 14px;
    font-size: 12px;
    color: var(--text-body);
}
.sc-closedbar.hidden { display: none; }
.sc-closedbar > svg { color: var(--text-muted); flex: none; }
.sc-reopen {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 600;
    padding: 6px 13px;
    border-radius: 10px;
    border: 1px solid var(--border-input);
    background: var(--bg-card);
    color: var(--text-body);
    cursor: pointer;
    font-family: inherit;
}
.sc-reopen:hover { background: var(--bg-hover); }
.sc-empty-note {
    font-size: 0.78rem;
    color: var(--text-muted);
    text-align: center;
    padding: 0.8rem 0.4rem;
}
/* Screenshot attachments: pill strip + ghost Attach button under the composer.
   Shared by the Message Center composer and the anonymous form; the row stays
   hidden until the media Worker answers its health probe. */
.sc-attach-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding-top: 8px;
}
.sc-attach-row.hidden { display: none; }
.sc-attach-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border: 1px solid var(--border-card);
    border-radius: 999px;
    background: var(--bg-card);
    color: var(--text-body);
    font: inherit;
    font-size: 0.75rem;
    cursor: pointer;
}
.sc-attach-btn:hover { background: var(--bg-hover); }
.sc-attach-btn:disabled { opacity: 0.45; cursor: default; }
.sc-attach-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 220px;
    padding: 4px 6px 4px 9px;
    border: 1px solid var(--border-card);
    border-radius: 999px;
    background: var(--bg-input);
    font-size: 0.75rem;
    color: var(--text-heading);
}
.sc-attach-pill-icon { display: inline-flex; color: var(--text-muted); }
.sc-attach-pill-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
}
.sc-attach-pill.is-uploading .sc-attach-pill-name { opacity: 0.55; }
.sc-attach-pill.is-uploading .sc-attach-pill-icon { animation: scShotPulse 1s ease-in-out infinite; }
@keyframes scShotPulse { 50% { opacity: 0.3; } }
.sc-attach-pill-x {
    border: 0;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0 2px;
}
.sc-attach-pill-x:hover { color: var(--text-heading); }
.sc-drop-hot { outline: 2px dashed var(--border-focus); outline-offset: -2px; }
/* The composer: one input-toned card holding the textarea, staged screenshot
   pills, and the tool row (attach icons · counter · Send). Below the card sit
   the details toggle (compose, Bug report only) and the reassurance foot. */
.sc-composer { flex: none; padding: 0 14px 10px; }
.sc-composer.hidden { display: none; }
.sc-center.sc-mode-compose .sc-composer { padding: 14px 26px 16px; }
.sc-composer-card {
    border: 1px solid var(--border-input);
    border-radius: 14px;
    background: var(--bg-input);
    padding: 10px 12px;
    transition: border-color 0.15s;
}
.sc-composer-card.has-text { border-color: var(--border-focus); }
#scInput {
    width: 100%;
    display: block;
    border: 0;
    background: transparent;
    resize: none;
    outline: none;
    font-family: inherit;
    font-size: 13.5px;
    color: var(--text-input);
    line-height: 1.45;
    padding: 0;
    min-height: 21px;
    max-height: 130px;
}
.sc-center.sc-mode-compose #scInput { min-height: 110px; }
.sc-tools { display: flex; align-items: center; gap: 6px; margin-top: 10px; }
.sc-attach-ctl { display: inline-flex; gap: 2px; }
.sc-attach-ctl.hidden { display: none; }
.sc-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    border: 0;
    background: transparent;
    color: var(--icon-color, var(--text-muted));
    cursor: pointer;
}
.sc-icon-btn:hover { color: var(--text-heading); background: var(--bg-card); }
.sc-icon-btn:disabled { opacity: 0.4; cursor: default; }
#scCounter { font-size: 10.5px; color: var(--text-muted); }
.sc-send {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 16px;
    border-radius: 10px;
    border: 0;
    background: var(--btn-add-bg);
    color: var(--btn-add-text);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    font-family: inherit;
}
.sc-send:hover { background: var(--accent-dark); }
.sc-send:disabled { opacity: 0.6; cursor: default; }
.sc-details {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: 14px;
    padding: 10px 12px;
    border-radius: 12px;
    background: var(--bg-input);
    cursor: pointer;
}
.sc-details.hidden { display: none; }
.sc-toggle {
    width: 30px;
    height: 18px;
    border-radius: 999px;
    background: var(--border-input);
    position: relative;
    flex: none;
    transition: background 0.15s;
}
.sc-toggle.is-on { background: var(--accent); }
.sc-toggle i {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    transition: left 0.15s;
}
.sc-toggle.is-on i { left: 14px; }
.sc-details-text { font-size: 11.5px; color: var(--text-body); }
.sc-details-sub { color: var(--text-muted); }
.sc-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 7px 4px 0;
}
.sc-reply-note {
    font-size: 10.5px;
    color: var(--text-muted);
    font-style: italic;
    margin: 0;
    line-height: 1.3;
    text-align: left;
}
.sc-enter-hint { font-size: 10.5px; color: var(--text-muted); }
/* Right-pane modes — the class rides on #scCenter so both panes react. */
.sc-center.sc-mode-thread #scComposeHead,
.sc-center.sc-mode-thread #scTopicRow,
.sc-center.sc-mode-thread .sc-details { display: none; }
.sc-center.sc-mode-compose #scStrip,
.sc-center.sc-mode-compose #scMessages { display: none; }
.sc-center.sc-mode-empty #scStrip,
.sc-center.sc-mode-empty #scComposeHead,
.sc-center.sc-mode-empty #scTopicRow,
.sc-center.sc-mode-empty #scMessages,
.sc-center.sc-mode-empty .sc-composer,
.sc-center.sc-mode-empty .sc-closedbar { display: none; }
.sc-center:not(.sc-mode-empty) #scEmpty { display: none; }
@media (max-width: 720px) {
    .fb-dialog.sc-center-open { max-width: calc(100vw - 1rem); }
    .sc-center { height: calc(100dvh - 4rem); }
    .sc-list-pane { flex: 1 1 auto; border-right: none; }
    .sc-center.sc-has-thread .sc-list-pane { display: none; }
    .sc-center:not(.sc-has-thread):not(.sc-mode-empty) .sc-thread-pane { display: none; }
    .sc-center.sc-mode-empty .sc-thread-pane { display: flex; }
    .sc-back:not(.hidden) { display: inline-flex; margin: 8px 14px -4px; }
    .sc-center.sc-mode-compose .sc-composer { padding: 14px 14px 16px; }
    .sc-compose-head { padding: 16px 14px 0; }
    #scTopicRow { padding: 14px 14px 0; }
}

/* ── Touch & PWA ───────────────────────────────────────
   Mobile/touch interaction layer + installed-app (standalone) fit.    */

/* Stop pull-to-refresh from hijacking vertical drags (bar reorder, list
   scroll at the top edge) without breaking normal scrolling. */
html {
    overscroll-behavior-y: contain;
    -webkit-text-size-adjust: 100%;
    /* #35 a11y text scaling — 100% = the user's browser default, multiplied by
       the chosen scale. Cascades to every rem, including the SVG chart's text. */
    font-size: calc(100% * var(--a11y-font-scale, 1));
}

/* Installed-app / notched-phone fit: viewport-fit=cover lets the page run
   edge-to-edge; pad the UI back inside the safe area. All env() values are
   0 in a normal browser tab, so desktop is untouched. */
body {
    padding-top: env(safe-area-inset-top, 0px);
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
}

/* Kill the double-tap-to-zoom delay on every control so taps feel instant
   (and double-taps reach the app, e.g. rename on the project name). */
button, a, label, select, input, textarea,
[role="button"], .task-row, .icon-btn, .tool-tile,
#projectPickerName {
    touch-action: manipulation;
}

/* Chart gestures:
   - Empty canvas: horizontal swipe pans the timeline (d3 pan-drag),
     vertical swipe stays native scroll (pan-y) — no scroll-trap.
   - Interactive elements: the browser gets NO gestures; d3 drags own the
     touch entirely (incl. vertical reorder + resize). */
#ganttChart {
    touch-action: pan-y;
    -webkit-touch-callout: none;
}
#ganttChart .task-bar,
#ganttChart .milestone-diamond,
#ganttChart .group-bar,
#ganttChart .resize-handle,
#ganttChart .grip-hitarea,
#ganttChart .drag-grip,
#ganttChart .event-bar,
#ganttChart .event-group,
#ganttChart .events-row-bg,
#ganttChart .resize-left-evt,
#ganttChart .resize-right-evt,
#ganttChart .link-handle,
#ganttChart .dep-arrow-hit,
#ganttChart .dep-delete-badge {
    touch-action: none;
}
.gantt-chart-container {
    overscroll-behavior: contain;
}

/* Long-press (context menu) must not trigger text selection / iOS callout. */
#ganttChart text,
.task-row {
    user-select: none;
    -webkit-user-select: none;
}

/* ≤1300px (which includes the fixed 1200px mobile layout): the single-row
   header gets tight and the Install label collided with Templates — the
   button drops to icon-only, tooltip carries the label. */
@media (max-width: 1300px) {
    #installAppBtn span { display: none; }
    #installAppBtn { padding: 0.3rem 0.5rem; }
}

/* NOTE (final): phones render the DESKTOP layout via a fixed 1200px
   viewport meta in index.html — mobile looks exactly like desktop, scaled.
   The earlier coarse-pointer density trims, the 16px input-zoom floor and
   the phone "shell release" media queries were removed for that reason:
   control sizes and the 100dvh app shell must match desktop 1:1. The
   width-based stacking blocks above remain for genuinely narrow DESKTOP
   windows. Touch features (long-press menu, chart pinch zoom, widened
   handles, touch-action layering) are unaffected. */

/* CONTRADICTION FLAG (found while documenting this file, not fully
   chased down): index.html's <meta name="viewport"> currently reads
   `width=device-width, initial-scale=1, viewport-fit=cover` -- not a
   fixed 1200px width as the "NOTE (final)" comment just above claims.
   The "real mobile layout (coarse-pointer phones only)" block further
   down this file (search that heading) only has a chance to fire on a
   real phone if the viewport truly is responsive. Likely explanation:
   the NOTE above predates that later mobile-layout work and was never
   updated. Re-verify both before changing anything phone-related. */
/* Install-help dialog: muted benefit line, prominent platform step. */
.install-help-benefit {
    margin: 0;
    font-size: 0.8rem;
    color: var(--text-muted);
}
.install-help-steps {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-body);
    line-height: 1.5;
}

/* ── Install-app window enrichment ──────────────────────────
   Animated hero (the app tile "downloads" onto a phone home screen),
   benefit chips, and per-platform how-to cards. All motion is disabled
   under prefers-reduced-motion / the a11y reduce-motion switch. */
.install-help-dialog { max-width: min(440px, calc(100vw - 2rem)); }

.install-hero {
    position: relative;
    height: 172px;
    border-radius: 14px;
    border: 1px solid var(--border-card);
    overflow: hidden;
    background:
        linear-gradient(to right, var(--border-card) 1px, transparent 1px) 0 0 / 28px 100%,
        linear-gradient(to bottom, var(--border-card) 1px, transparent 1px) 0 0 / 100% 22px,
        var(--bg-input);
    animation: mcpStepIn 0.34s ease backwards;
}
/* Fade the chart-grid at the edges so it reads as texture, not a table. */
.install-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(120% 90% at 50% 40%, transparent 55%, var(--bg-input) 100%);
    pointer-events: none;
}

/* Soft download "beam" the tile falls through. */
.install-hero .ih-beam {
    position: absolute;
    left: 50%;
    top: 4px;
    width: 46px;
    height: 78px;
    transform: translateX(-50%);
    background: linear-gradient(to bottom, color-mix(in srgb, var(--accent) 42%, transparent), transparent);
    border-radius: 0 0 40% 40%;
    opacity: 0;
    z-index: 1;
    animation: ihBeam 4.4s ease-in-out infinite;
}

.install-hero .ih-device {
    position: absolute;
    left: 50%;
    bottom: 15px;
    transform: translateX(-50%);
    width: 168px;
    height: 108px;
    border: 2px solid color-mix(in srgb, var(--text-muted) 45%, transparent);
    border-radius: 18px;
    background: var(--bg-card);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.12), 0 6px 14px -8px rgba(0, 0, 0, 0.4);
    z-index: 2;
}
.install-hero .ih-apps {
    position: absolute;
    top: 20px;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
}
.install-hero .ih-app-dot {
    width: 26px;
    height: 26px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--text-muted) 22%, transparent);
}
.install-hero .ih-slot {
    position: relative;
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.install-hero .ih-tile {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 11px;
    background: linear-gradient(145deg, var(--accent), var(--accent-dark));
    box-shadow: 0 4px 10px -3px color-mix(in srgb, var(--accent) 55%, transparent);
    animation: ihDrop 4.4s cubic-bezier(0.4, 1.3, 0.5, 1) infinite;
}
.install-hero .ih-tile svg { width: 24px; height: 24px; }
.install-hero .ih-check {
    position: absolute;
    right: -6px;
    top: -6px;
    width: 17px;
    height: 17px;
    border-radius: 50%;
    background: var(--bg-card);
    color: var(--accent-dark);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    animation: ihCheck 4.4s ease-in-out infinite;
}
.install-hero .ih-check svg { width: 11px; height: 11px; }
.install-hero .ih-ripple {
    position: absolute;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    border: 2px solid var(--accent);
    opacity: 0;
    animation: ihRipple 4.4s ease-out infinite;
}
.install-hero .ih-home {
    position: absolute;
    left: 50%;
    bottom: 7px;
    transform: translateX(-50%);
    width: 42px;
    height: 4px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--text-muted) 40%, transparent);
}

@keyframes ihDrop {
    0%   { transform: translateY(-62px) scale(0.7);  opacity: 0; }
    10%  { opacity: 1; }
    30%  { transform: translateY(4px)   scale(1.04); opacity: 1; }
    38%  { transform: translateY(0)     scale(1); }
    88%  { transform: translateY(0)     scale(1);    opacity: 1; }
    100% { transform: translateY(-62px) scale(0.7);  opacity: 0; }
}
@keyframes ihCheck {
    0%, 32% { transform: scale(0);    opacity: 0; }
    42%     { transform: scale(1.28); opacity: 1; }
    50%     { transform: scale(1);    opacity: 1; }
    86%     { opacity: 1; }
    100%    { transform: scale(0);    opacity: 0; }
}
@keyframes ihBeam {
    0%, 6% { opacity: 0; }
    16%    { opacity: 0.85; }
    32%    { opacity: 0; }
    100%   { opacity: 0; }
}
@keyframes ihRipple {
    0%, 28% { transform: scale(0.6); opacity: 0; }
    34%     { opacity: 0.55; }
    58%     { transform: scale(1.7); opacity: 0; }
    100%    { opacity: 0; }
}

/* Benefit chips */
.install-benefits {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
.install-benefit-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.32rem 0.6rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--accent-dark);
    background: var(--accent-soft);
    border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
    animation: mcpStepIn 0.34s ease backwards;
    animation-delay: calc(var(--i, 0) * 0.07s + 0.1s);
}
.install-benefit-ico { width: 14px; height: 14px; flex: 0 0 auto; }

/* Per-platform how-to card */
.install-steps-wrap { display: flex; flex-direction: column; gap: 0.5rem; }
.install-steps-label {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.install-step-card {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.7rem 0.85rem;
    border-radius: 11px;
    background: var(--bg-input);
    border: 1px solid var(--border-card);
    animation: mcpStepIn 0.34s ease 0.16s backwards;
}
.install-step-ico {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    color: var(--accent-dark);
    background: var(--accent-soft);
}
.install-step-ico svg { width: 16px; height: 16px; }

@media (prefers-reduced-motion: reduce) {
    .install-hero, .install-benefit-chip, .install-step-card { animation: none; }
    .install-hero .ih-beam, .install-hero .ih-ripple { animation: none; opacity: 0; }
    .install-hero .ih-tile { animation: none; }
    .install-hero .ih-check { animation: none; transform: scale(1); opacity: 1; }
}
[data-a11y-motion="reduce"] .install-hero,
[data-a11y-motion="reduce"] .install-benefit-chip,
[data-a11y-motion="reduce"] .install-step-card { animation: none; }
[data-a11y-motion="reduce"] .install-hero .ih-beam,
[data-a11y-motion="reduce"] .install-hero .ih-ripple { animation: none; opacity: 0; }
[data-a11y-motion="reduce"] .install-hero .ih-tile { animation: none; }
[data-a11y-motion="reduce"] .install-hero .ih-check { animation: none; transform: scale(1); opacity: 1; }

/* ════ real mobile layout (coarse-pointer phones only) ════
   Desktop untouched: everything gates on pointer:coarse AND phone
   width/height. v2 (real-device feedback round): chart height now
   targets the CONTAINER (the controls panel shares .chart-section, so
   sizing the section starved the chart to a sliver), zoom slider
   un-broken, color swatch fixed, side panel slimmed, header/toolbar/
   footer/dialog density pass. */
.mobile-bar { display: none; }
/* This block only fires if index.html's viewport is truly responsive on
   phones -- see the "CONTRADICTION FLAG" comment near the Touch & PWA
   section above, which flags a stale-looking claim that phones instead
   get a fixed 1200px viewport. */

@media (pointer: coarse) and (max-width: 820px), (pointer: coarse) and (max-height: 520px) {
    /* 1 ── scrolling document instead of the locked shell */
    body.app-layout {
        display: block;
        height: auto;
        min-height: 100dvh;
        overflow: visible;
        padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    }
    /* The chart SCROLLER gets the height budget — not .chart-section,
       which also contains the whole controls panel. */
    .chart-section { height: auto; }
    .chart-section .gantt-chart-container {
        flex: none;
        height: min(58dvh, 520px);
        -webkit-overflow-scrolling: touch;
    }
    #taskList {
        max-height: 38dvh;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        touch-action: pan-y;
    }

    /* 2 ── touch targets + inputs (range inputs exempt — the 44px floor
       turned the zoom slider's track into a giant slab) */
    button, .btn-export-sm, .tool-tile, select { min-height: 44px; }
    button, .btn-export-sm { min-width: 44px; }
    a.btn-export-sm { display: inline-flex; align-items: center; justify-content: center; }
    input, select, textarea { min-height: 44px; font-size: 16px; }
    input[type="range"] { min-height: 0; }
    .zoom-slider { height: 18px; }
    .zoom-slider::-webkit-slider-thumb { width: 26px; height: 26px; }
    .zoom-slider::-moz-range-thumb { width: 26px; height: 26px; }
    /* 3 ── header: every control becomes a 34px tap tile on one wrapping row;
       labels, clock and divider go. */
    .app-header-compact { height: auto; min-height: 48px; padding: 6px 8px; gap: 6px; row-gap: 4px; flex-wrap: wrap; }
    .app-title-compact { font-size: 0.95rem; }
    .header-clock, .hdr-divider { display: none; }
    .app-header-compact .hdr-quiet > span,
    .app-header-compact .hdr-seg-btn > span { display: none; }
    .app-header-compact .hdr-quiet,
    .app-header-compact .hdr-seg-btn { padding: 0 9px; min-height: 34px; }

    /* 4 ── creation form: stack Task Properties over the object browser and
       lay the fields out compactly — priority · type · colour share ONE row
       instead of a tall full-width stack. */
    .top-panel { --panel-x: 0px; padding: 0 var(--panel-x); }
    .top-panel-right { gap: 6px; padding-bottom: 6px; }
    .flat-section { flex-direction: column; gap: 10px !important; }
    .flat-section > div:first-child { flex: 1 1 auto !important; min-width: 0 !important; padding-right: 0 !important; width: 100%; }
    .flat-section > div[style*="width: 6px"] { display: none; }   /* the vertical divider */
    #taskSection { padding-left: 0 !important; width: 100%; }

    /* object browser: one readable column, compact rows (no cramped 2-up) */
    #taskList {
        display: grid;
        grid-template-columns: 1fr;
        gap: 2px;
        align-items: stretch;
    }
    #taskList .task-row { min-height: 36px; padding: 2px 8px; }
    #taskList .task-row-name { font-size: 13px; min-width: 0; }
    #taskList .task-row-num,
    #taskList .task-row [class*="duration"] { display: none; }
    /* denser list: show more rows before the 38dvh scroll kicks in */
    #taskList { max-height: 34dvh; }

    /* 5 ── controls panel (PROJECT / zoom / viewport / tools) density */
    .viewport-controls { padding: 8px 10px; gap: 8px 10px; }
    .viewport-divider { display: none; }
    .viewport-field-label { font-size: 9.5px; letter-spacing: 0.05em; }
    .viewport-stack, .viewport-fields-block, .viewport-apply-stack { width: 100%; }
    .viewport-fields-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
    .viewport-span-row { font-size: 11px; text-align: center; }
    .viewport-apply-stack { display: grid; grid-template-columns: repeat(2, minmax(0, 150px)); justify-content: center; gap: 8px; }
    .viewport-apply-stack .viewport-apply, .viewport-apply-stack .btn-zoom-fit { width: 100%; min-height: 40px; font-size: 13px; padding: 6px 10px; }

    /* project strip: wrap cleanly, truncate the chart name */
    .project-picker-bar { width: 100%; }
    .my-charts-bar { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; align-items: center; }
    #activeProjectName { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .zoom-slider-inline { gap: 8px; }

    /* date-range pill (cluster redesign): the desktop inline pill is ~600px and
       had NO mobile rule, so it forced the page ~240px wider than the phone —
       collapsing the whole layout to ~63% scale. Make it fit + wrap its parts. */
    .viewport-range-bar {
        width: 100%;
        box-sizing: border-box;
        flex-wrap: wrap;
        justify-content: center;
        row-gap: 6px;
    }
    .viewport-range-bar .vrb-date { width: auto; flex: 1 1 7rem; min-width: 0; text-align: center; }
    .viewport-range-bar .viewport-span { flex-basis: 100%; width: auto; text-align: center; padding-left: 0; border-left: 0; margin-left: 0; }
    .viewport-tools-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
    .tool-tile { min-height: 56px; padding: 6px 4px; }
    .tool-tile-ico svg { width: 18px; height: 18px; }
    .tool-tile-name { font-size: 10px; }

    /* 6 ── chart: slim side panel (duration chips off; JS halves the
       reserve via getChartMargin's coarsePhone branch) + touch resizer */
    #ganttChart .duration-chip-chart, #ganttChart .duration-chip-text { display: none; }
    .side-panel-resizer { width: 22px; transform: translateX(-11px); }

    /* 7 ── dialogs: slimmer frame, denser My Charts grid */
    .dialog-overlay .dialog { max-width: calc(100vw - 12px); max-height: calc(100dvh - 12px); }
    .dialog-header { padding: 8px 12px; }
    #myChartsList { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
    .my-charts-item-name { font-size: 13px; }
    .my-charts-item-meta { font-size: 10.5px; }
    /* Toolbar controls grow to the 44px touch floor together (the global
       input/button floor in this block would otherwise burst the 38px
       search shell while its input stretches). */
    .my-charts-search, .my-charts-sort-btn, .my-charts-new-btn { height: 44px; }
    .my-charts-search input { min-height: 0; }
    .my-charts-item-action { width: 34px; height: 34px; min-width: 34px; min-height: 34px; }

    /* 8 ── footer: compact legal strip */
    .app-footer { padding: 3px 8px; }
    .app-footer-links { font-size: 10.5px; gap: 0 2px; }
    .app-footer-links a { display: inline-flex; align-items: center; min-height: 36px; padding: 0 6px; }

    /* 9 ── bottom action bar */
    .mobile-bar {
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: 1500; /* under dialogs (2000) and the cookie banner (1990) */
        display: flex;
        align-items: stretch;
        gap: 4px;
        background: var(--bg-card);
        border-top: 1px solid var(--border-card);
        box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.12);
        padding: 4px max(8px, env(safe-area-inset-left))
                 calc(4px + env(safe-area-inset-bottom, 0px))
                 max(8px, env(safe-area-inset-right));
    }
    .mb-btn {
        flex: 1;
        min-height: 52px;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        border: none;
        border-radius: 12px;
        background: transparent;
        color: var(--text-muted);
        font: inherit;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }
    .mb-btn:active { background: var(--bg-hover); color: var(--text-heading); }
    .mb-btn svg { width: 21px; height: 21px; }
    .mb-label { font-size: 10.5px; font-weight: 600; letter-spacing: 0.01em; }
    .mb-btn-primary { color: var(--border-focus); }

    /* landscape phones: shorter screens, chart gets most of it */
    @media (max-height: 520px) {
        .chart-section .gantt-chart-container { height: calc(100dvh - 120px); }
        #taskList { max-height: 30dvh; }
    }
}

/* ── Guided intro tour (spotlight coachmarks) ───────────────────────────── */
.gtour-mask { position: fixed; inset: 0; z-index: 9000; background: transparent; }
.gtour-spot {
    position: fixed; z-index: 9001; border-radius: 10px; pointer-events: none;
    box-shadow: 0 0 0 3px var(--accent), 0 0 0 100vmax rgba(2, 6, 23, 0.55);
    transition: left .3s ease, top .3s ease, width .3s ease, height .3s ease;
}
.gtour-tooltip {
    position: fixed; z-index: 9002; width: min(330px, calc(100vw - 24px));
    background: var(--bg-card); color: var(--text-body);
    border: 1px solid var(--border-card); border-radius: 14px;
    box-shadow: 0 12px 40px rgba(2, 6, 23, 0.28); padding: 16px 16px 14px;
}
.gtour-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.gtour-eyebrow { font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--accent-dark); }
.gtour-skip { background: none; border: none; color: var(--text-muted); font-size: 12.5px; cursor: pointer; padding: 2px 4px; font-family: inherit; }
.gtour-skip:hover { color: var(--text-heading); text-decoration: underline; }
.gtour-title { margin: 0 0 6px; font-size: 16px; font-weight: 700; color: var(--text-heading); }
.gtour-body { margin: 0 0 14px; font-size: 13.5px; line-height: 1.55; color: var(--text-body); }
.gtour-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gtour-dots { display: flex; gap: 6px; }
.gtour-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border-card); }
.gtour-dot.active { background: var(--accent); }
.gtour-btns { display: flex; gap: 8px; }
.gtour-back, .gtour-next { font-family: inherit; font-size: 13px; font-weight: 600; border-radius: 8px; padding: 7px 14px; cursor: pointer; border: 1px solid transparent; }
.gtour-back { background: transparent; color: var(--text-muted); border-color: var(--border-card); }
.gtour-back:hover { color: var(--text-heading); }
.gtour-next { background: var(--accent); color: #fff; }
.gtour-next:hover { background: var(--accent-dark); }
@media (prefers-reduced-motion: reduce) { .gtour-spot { transition: none; } }
/* Between-tour "continue?" prompt: full-screen dim + centered card. */
.gtour-mask.gtour-dim { background: rgba(2, 6, 23, 0.55); }
.gtour-prompt {
    position: fixed; z-index: 9003; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: min(380px, calc(100vw - 32px));
    background: var(--bg-card); color: var(--text-body);
    border: 1px solid var(--border-card); border-radius: 16px;
    box-shadow: 0 16px 50px rgba(2, 6, 23, 0.34); padding: 22px 22px 18px; text-align: center;
}
.gtour-prompt-title { margin: 0 0 8px; font-size: 19px; font-weight: 700; color: var(--text-heading); }
.gtour-prompt-body { margin: 0 0 18px; font-size: 14px; line-height: 1.55; color: var(--text-body); }
.gtour-prompt-btns { display: flex; gap: 10px; justify-content: center; }
.gtour-prompt-no, .gtour-prompt-yes { font-family: inherit; font-size: 14px; font-weight: 600; border-radius: 9px; padding: 9px 18px; cursor: pointer; border: 1px solid transparent; }
.gtour-prompt-no { background: transparent; color: var(--text-muted); border-color: var(--border-card); }
.gtour-prompt-no:hover { color: var(--text-heading); }
.gtour-prompt-yes { background: var(--accent); color: #fff; }
.gtour-prompt-yes:hover { background: var(--accent-dark); }

/* ═══════════════════════ #35 Accessibility ═══════════════════════ */

/* Reduce motion — opt-in kill switch for animations & transitions. */
[data-a11y-motion="reduce"] *,
[data-a11y-motion="reduce"] *::before,
[data-a11y-motion="reduce"] *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
}

/* Enhanced focus — always-visible, high-contrast focus rings for keyboard users.
   Universal `:focus` (not just :focus-visible) so it shows on click + keyboard;
   the halo makes it unmistakable over any background. */
[data-a11y-focus="on"] :focus,
[data-a11y-focus="on"] :focus-visible {
    outline: 3px solid var(--accent, #059669) !important;
    outline-offset: 2px !important;
    box-shadow: 0 0 0 4px rgba(5, 150, 105, 0.30) !important;
    border-radius: 3px;
}

/* High contrast — the whole UI is driven by CSS variables, so overriding the
   palette here cascades to every surface (light + dark variants). */
[data-a11y-contrast="high"] {
    --bg-body: #ffffff;
    --bg-card: #ffffff;
    --bg-input: #ffffff;
    --bg-hover: #e6e6e6;
    --border-card: #000000;
    --border-input: #000000;
    --border-focus: #000000;
    --text-heading: #000000;
    --text-body: #000000;
    --text-muted: #1c1c1c;
    --text-faint: #1c1c1c;
    --text-input: #000000;
    --grid-stroke: #5a5a5a;
    --axis-text: #000000;
    --axis-stroke: #000000;
    --yaxis-text: #000000;
}
[data-theme$="-dark"][data-a11y-contrast="high"] {
    --bg-body: #000000;
    --bg-card: #000000;
    --bg-input: #0b0b0b;
    --bg-hover: #1f1f1f;
    --border-card: #ffffff;
    --border-input: #ffffff;
    --border-focus: #ffffff;
    --text-heading: #ffffff;
    --text-body: #ffffff;
    --text-muted: #ededed;
    --text-faint: #ededed;
    --text-input: #ffffff;
    --grid-stroke: #9a9a9a;
    --axis-text: #ffffff;
    --axis-stroke: #ffffff;
    --yaxis-text: #ffffff;
}
[data-a11y-contrast="high"] .dialog,
[data-a11y-contrast="high"] .input,
[data-a11y-contrast="high"] .btn-export-sm,
[data-a11y-contrast="high"] .lang-toggle,
[data-a11y-contrast="high"] .a11y-seg-btn {
    border-width: 2px;
}

/* ── Accessibility dialog UI ── */
.a11y-dialog { max-width: min(440px, calc(100vw - 2rem)); }
.a11y-dialog-icon { width: 1.2rem; height: 1.2rem; color: var(--accent, #059669); flex: 0 0 auto; }
.a11y-body { gap: 0; }

.a11y-group { display: flex; flex-direction: column; gap: 0.6rem; padding: 0.85rem 0; }
.a11y-group-head { display: flex; flex-direction: column; gap: 0.12rem; }
.a11y-group-title,
.a11y-toggle-title { font-weight: 600; color: var(--text-heading); font-size: 0.9rem; }
.a11y-group-sub,
.a11y-toggle-sub { font-size: 0.75rem; color: var(--text-muted); }

.a11y-seg { display: flex; gap: 0.45rem; }
.a11y-seg-btn {
    flex: 1 1 0;
    display: flex; flex-direction: column; align-items: center; gap: 0.2rem;
    padding: 0.6rem 0.4rem;
    border: 1.5px solid var(--border-input);
    border-radius: 0.6rem;
    background: var(--bg-input);
    color: var(--text-body);
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s, box-shadow 0.12s;
}
.a11y-seg-btn:hover { border-color: var(--accent, #059669); }
.a11y-seg-btn.active {
    border-color: var(--accent, #059669);
    background: var(--bg-card);
    box-shadow: inset 0 0 0 1px var(--accent, #059669);
}
.a11y-seg-glyph { font-weight: 800; line-height: 1; color: var(--text-heading); }
.a11y-seg-btn.active .a11y-seg-glyph { color: var(--accent, #059669); }
.a11y-seg-lbl { font-size: 0.7rem; color: var(--text-muted); }

/* Switch rows */
.a11y-toggle {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    width: 100%; padding: 0.85rem 0;
    background: none; border: none; border-top: 1px solid var(--border-card);
    cursor: pointer; text-align: left; font-family: inherit;
}
.a11y-toggle-text { display: flex; flex-direction: column; gap: 0.12rem; }
.a11y-switch {
    flex: 0 0 auto; position: relative;
    width: 2.6rem; height: 1.5rem; border-radius: 999px;
    background: var(--border-input); transition: background 0.15s;
}
.a11y-toggle.on .a11y-switch { background: var(--accent, #059669); }
.a11y-switch-knob {
    position: absolute; top: 2px; left: 2px;
    width: calc(1.5rem - 4px); height: calc(1.5rem - 4px);
    border-radius: 50%; background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    transition: transform 0.15s;
}
.a11y-toggle.on .a11y-switch-knob { transform: translateX(1.1rem); }

/* Colour-blind mode — daltonization correction filter on the chart. The filter
   defs live in a hidden <svg> in index.html; only the chart SVG is filtered so
   fixed-position modals/dropdowns are untouched (a filter on html/body would
   break their positioning). */
[data-a11y-cvd="deuter"] #ganttChart { filter: url(#gcm-cvd-deuter); }
[data-a11y-cvd="protan"] #ganttChart { filter: url(#gcm-cvd-protan); }
[data-a11y-cvd="tritan"] #ganttChart { filter: url(#gcm-cvd-tritan); }

/* Themed dropdown for the colour-blind selector — a native <select>'s popup is
   browser-rendered and can't be themed, so this mirrors the .prop-select look
   using the same theme variables (matches every theme). Own classes keep it
   decoupled from app.js's .prop-select-trigger wiring; a11y.js drives it. */
/* ── colour-blind profile track ──────────────────────────
   One pill rail, four slots, an accent thumb that glides to the picked
   profile. Real radio inputs underneath: native arrow-key roving + proper
   "x of 4, selected" screen-reader announcements — the thumb position is
   pure CSS (:has) so a11y.js only persists state + syncs the caption. */
.a11y-track {
    position: relative;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    height: 2.5rem;
    padding: 0.2rem;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    border-radius: 999px;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.a11y-track:hover { border-color: var(--accent, #059669); }

.a11y-track:has(input:focus-visible) {
    border-color: var(--accent, #059669);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #059669) 16%, transparent);
}

.a11y-track-thumb {
    position: absolute;
    top: 0.2rem;
    bottom: 0.2rem;
    left: 0.2rem;
    width: calc((100% - 0.4rem) / 4);
    border-radius: 999px;
    background: var(--accent, #059669);
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.2),
        inset 0 1px 0 rgba(255, 255, 255, 0.2);
    transition: transform 0.3s cubic-bezier(0.3, 1, 0.3, 1);
}

.a11y-track:has(.a11y-track-slot:nth-of-type(1) input:checked) .a11y-track-thumb { transform: translateX(0); }
.a11y-track:has(.a11y-track-slot:nth-of-type(2) input:checked) .a11y-track-thumb { transform: translateX(100%); }
.a11y-track:has(.a11y-track-slot:nth-of-type(3) input:checked) .a11y-track-thumb { transform: translateX(200%); }
.a11y-track:has(.a11y-track-slot:nth-of-type(4) input:checked) .a11y-track-thumb { transform: translateX(300%); }

/* Respect BOTH the OS setting and the panel's own reduce-motion switch. */
[data-a11y-motion="reduce"] .a11y-track-thumb { transition: none; }
@media (prefers-reduced-motion: reduce) { .a11y-track-thumb { transition: none; } }

.a11y-track-slot {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    cursor: pointer;
}

.a11y-track-slot input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.a11y-track-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.82rem;
    font-weight: 700;
    line-height: 1;
    color: var(--text-muted);
    transition: color 0.18s, transform 0.18s;
}

.a11y-track-glyph svg { width: 0.95rem; height: 0.95rem; }

.a11y-track-slot:hover .a11y-track-glyph { color: var(--text-heading); }

.a11y-track-slot:has(input:checked) .a11y-track-glyph {
    color: var(--btn-add-text, #fff);
    transform: scale(1.08);
}

.a11y-track-caption {
    margin: 0.45rem 0 0;
    font-size: 0.75rem;
    color: var(--text-muted);
    min-height: 1.1em;
}

/* legacy .a11y-dd dropdown styles removed (control replaced) */
.a11y-kbd-hint {
    font-size: 0.75rem; color: var(--text-muted);
    margin: 0; padding-top: 0.85rem;
    border-top: 1px solid var(--border-card); line-height: 1.45;
}

/* ═══════ Donate animation — export-ready dialog pitch box ═══════ */
.donate-anim {
    position: relative;
    display: flex; align-items: center; justify-content: center;
    height: 42px; margin-bottom: 0.4rem;
}
.donate-anim-heart {
    position: relative; z-index: 1;
    width: 30px; height: 30px; color: var(--accent, #059669);
    transform-origin: center;
    animation: donateBeat 1.7s ease-in-out infinite;
}
.donate-anim-ring {
    position: absolute; width: 30px; height: 30px;
    border-radius: 50%; border: 2px solid var(--accent, #059669);
    opacity: 0;
    animation: donateRing 1.7s ease-out infinite;
}
.donate-anim-ring-2 { animation-delay: 0.6s; }
@keyframes donateBeat {
    0%, 100% { transform: scale(1); }
    14% { transform: scale(1.2); }
    28% { transform: scale(1); }
    42% { transform: scale(1.12); }
    56% { transform: scale(1); }
}
@keyframes donateRing {
    0%   { transform: scale(0.6); opacity: 0.55; }
    70%  { transform: scale(1.9); opacity: 0; }
    100% { transform: scale(1.9); opacity: 0; }
}

/* ── Version history (a42) ───────────────────────────────────────────
   The former sync badge now sits next to the project name and opens a
   savepoint list. Two consequences for the rules above: the synced states
   were `cursor: default` because the badge used to be inert, and they are
   interactive now; and the badge needs a positioned ancestor for the
   popover to anchor to. */
.version-history {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.project-cloud-status[data-state="saved"],
.project-cloud-status[data-state="idle"],
.project-cloud-status[data-state="saving"],
.project-cloud-status[data-state="error"] {
    cursor: pointer;
}
.project-cloud-status[data-state="saved"]:hover,
.project-cloud-status[data-state="idle"]:hover {
    background: rgba(34, 197, 94, 0.22);
}
.version-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 1200;
    width: 344px;
    max-width: calc(100vw - 32px);
    max-height: 60vh;
    overflow-y: auto;
    padding: 0.6rem;
    border: 1px solid var(--border-card, #e2e8f0);
    border-radius: 12px;
    background: var(--bg-card, #fff);
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.22);
    font-size: 0.78rem;
}
.version-menu-head {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.1rem 0.25rem 0.5rem;
    border-bottom: 1px solid var(--border-card, #e2e8f0);
}
.version-menu-title {
    font-weight: 700;
    font-size: 0.82rem;
    color: var(--text-heading, #0f172a);
}
/* The save-state words the badge used to show inline. They moved in here when
   the button became an icon, so nothing was silently dropped. */
.version-menu-sub {
    font-size: 0.72rem;
    color: var(--text-muted, #64748b);
}
.version-save-btn {
    display: block;
    width: 100%;
    margin: 0.5rem 0;
    padding: 0.42rem 0.6rem;
    border: 1px solid var(--border-card, #e2e8f0);
    border-radius: 8px;
    background: var(--bg-body, #f8fafc);
    color: var(--text-heading, #0f172a);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}
.version-save-btn:hover:not(:disabled) {
    border-color: var(--accent, #059669);
    color: var(--accent, #059669);
}
.version-save-btn:disabled { opacity: 0.55; cursor: default; }
.version-list { display: flex; flex-direction: column; }
.version-note {
    padding: 0.7rem 0.3rem;
    color: var(--text-muted, #64748b);
    line-height: 1.5;
}
.version-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.38rem 0.25rem;
    border-top: 1px solid var(--grid-line, #eef2f7);
}
.version-row:first-child { border-top: 0; }
.version-star {
    flex: none;
    display: inline-flex;
    padding: 2px;
    border: 0;
    background: none;
    color: var(--text-muted, #64748b);
    cursor: pointer;
    border-radius: 5px;
}
.version-star:hover { color: #d97706; }
.version-row.is-starred .version-star { color: #d97706; }
.version-no {
    flex: none;
    min-width: 2.1rem;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 0.72rem;
    color: var(--text-muted, #64748b);
}
.version-mid { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.version-label {
    cursor: text;
    color: var(--text-heading, #0f172a);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.version-label.is-untitled { color: var(--text-body, #334155); font-variant-numeric: tabular-nums; }
.version-label:hover { text-decoration: underline dotted; }
.version-when { font-size: 0.68rem; color: var(--text-muted, #64748b); }
.version-rename-input {
    width: 100%;
    padding: 0.15rem 0.3rem;
    border: 1px solid var(--accent, #059669);
    border-radius: 5px;
    background: var(--bg-body, #fff);
    color: var(--text-heading, #0f172a);
    font: inherit;
}
.version-restore {
    flex: none;
    padding: 0.22rem 0.5rem;
    border: 1px solid var(--border-card, #e2e8f0);
    border-radius: 7px;
    background: transparent;
    color: var(--text-body, #334155);
    font: inherit;
    font-size: 0.72rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}
.version-restore:hover { border-color: var(--accent, #059669); color: var(--accent, #059669); }

/* ── Version viewing (a42) ───────────────────────────────────────────
   The banner sits INSIDE the chart perimeter (Fotis's call) rather than in a
   page footer, because it is a statement about the chart on screen. Persistent
   by design — no dismiss — since it is the only signal that the chart is not
   the live one, and the only way back. z-index clears the chart SVG but stays
   under popovers; bottom offset clears the fullscreen button's row. */
.version-view-bar {
    position: absolute;
    left: 50%;
    bottom: 1rem;
    transform: translateX(-50%);
    z-index: 60;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    max-width: calc(100% - 5rem);
    padding: 0.7rem 1.1rem;
    border: 1px solid var(--border-focus);
    border-radius: 12px;
    background: var(--bg-card);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
    text-align: center;
}
.version-view-line {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-heading);
}
.version-view-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center; }
.version-view-btn {
    padding: 0.32rem 0.7rem;
    border: 1px solid var(--border-card);
    border-radius: 8px;
    background: transparent;
    color: var(--text-body);
    font: inherit;
    font-size: 0.76rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.version-view-btn:hover { border-color: var(--border-focus); color: var(--border-focus); }
.version-view-btn-primary {
    background: var(--border-focus);
    border-color: var(--border-focus);
    color: #fff;
}
.version-view-btn-primary:hover { filter: brightness(1.08); color: #fff; }

/* The "View" button shares the Restore button's shape so the pair reads as one
   control group; Restore stays the emphasised one. */
.version-view {
    flex: none;
    padding: 0.22rem 0.45rem;
    border: 1px solid transparent;
    border-radius: 7px;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: 0.72rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}
.version-view:hover { border-color: var(--border-card); color: var(--text-heading); }

/* Restore confirmation. Three ways out, so the buttons wrap rather than being
   squeezed — "Create a new project from this version" is a long label on purpose
   and must not be truncated into ambiguity. */
.version-confirm-overlay {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.45);
}
.version-confirm {
    width: 27rem;
    max-width: 100%;
    padding: 1.15rem 1.25rem;
    border: 1px solid var(--border-card);
    border-radius: 14px;
    background: var(--bg-card);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
}
.version-confirm h3 {
    margin: 0 0 0.5rem;
    font-size: 1rem;
    color: var(--text-heading);
}
.version-confirm p {
    margin: 0 0 0.6rem;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-body);
}
.version-confirm-note { color: var(--text-muted); }
/* Stacked, not a ragged wrap: the "create a new project from this version"
   label is long enough that a three-button flex row broke with Continue alone
   on a second line. Alternative action full-width on top, decision pair
   bottom-right underneath. */
.version-confirm-actions {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-top: 0.9rem;
}
.version-confirm-btn-wide { width: 100%; }
/* Even halves, not a hugging right-aligned pair: with the wide button above
   spanning the full content width, buttons at three different widths and two
   different alignments read as scattered. All three now share the same left and
   right edges, so the footer is a deliberate two-row grid. */
.version-confirm-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
}
.version-confirm-pair .version-confirm-btn { width: 100%; }
.version-confirm-btn {
    padding: 0.42rem 0.8rem;
    border: 1px solid var(--border-card);
    border-radius: 9px;
    background: transparent;
    color: var(--text-body);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.version-confirm-btn:hover { border-color: var(--border-focus); color: var(--border-focus); }
.version-confirm-btn-primary {
    background: var(--border-focus);
    border-color: var(--border-focus);
    color: #fff;
}
.version-confirm-btn-primary:hover { filter: brightness(1.08); color: #fff; }

/* Savepoint kind marker. Muted by default because most rows are automatic; the
   two exempt kinds are what the reader is actually looking for, so they are the
   ones that get colour. This exists to make the post-restore purge legible: a
   manual savepoint correctly surviving used to be indistinguishable from an
   automatic one that failed to go. */
.version-kind {
    flex: none;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: lowercase;
    color: var(--text-muted);
    opacity: 0.75;
}
.version-kind-manual { color: var(--border-focus); opacity: 1; }
.version-kind-pre_restore { color: #d97706; opacity: 1; }

/* The sync/version badge is a first-class button beside the project name now, so
   it takes the same pill geometry as its neighbours (My Charts, New Project) —
   see `.project-picker-bar .btn-export-sm` above for the shape it matches.
   The `.project-picker-bar .project-cloud-status[data-state]` rule further up
   deliberately flattened this element to plain inline text back when it was a
   quiet indicator trailing the row; it still matches by descendant and its
   `border-radius: 0` is what left a square green box. Scoped through
   .version-history here so it out-specifies that rule (4 classes vs 3) instead
   of relying on source order. */
.project-picker-bar .version-history .project-cloud-status,
.project-picker-bar .version-history .project-cloud-status[data-state] {
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 0.34rem 0.7rem;
    font-weight: 500;
}
.project-picker-bar .version-history .project-cloud-status[data-state="saved"],
.project-picker-bar .version-history .project-cloud-status[data-state="idle"] {
    background: rgba(34, 197, 94, 0.14);
}
.project-picker-bar .version-history .project-cloud-status[data-state="saved"]:hover,
.project-picker-bar .version-history .project-cloud-status[data-state="idle"]:hover {
    background: rgba(34, 197, 94, 0.22);
}
.project-picker-bar .version-history .project-cloud-status[data-state="saving"] {
    background: rgba(99, 102, 241, 0.14);
}
.project-picker-bar .version-history .project-cloud-status[data-state="error"] {
    background: rgba(239, 68, 68, 0.14);
}
/* Not-synced keeps its red outline: it is the one state that is a call to action
   rather than a status, and it stays visually distinct from the others. */
.project-picker-bar .version-history .project-cloud-status[data-state="off"] {
    background: rgba(239, 68, 68, 0.1);
    border-color: rgba(239, 68, 68, 0.3);
    font-weight: 600;
}
.project-picker-bar .version-history .project-cloud-status[data-state="off"]:hover {
    background: rgba(239, 68, 68, 0.18);
    border-color: rgba(239, 68, 68, 0.5);
}

/* The version-history trigger. Separated from the status label (Fotis,
   2026-08-04): the label reports sync state, this opens the savepoint list. Sized
   and shaped like the other ghost items in the project bar so it reads as one of
   them rather than as decoration attached to the badge. */
.version-history-btn {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-right: 2px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.version-history-btn:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}
.version-history-btn[aria-expanded="true"] {
    background: var(--bg-hover);
    border-color: var(--border-focus);
    color: var(--border-focus);
}
.version-history-btn svg { display: block; }
/* The label no longer carries the icon, so its left padding can match the right. */
.project-picker-bar .version-history .project-cloud-status,
.project-picker-bar .version-history .project-cloud-status[data-state] {
    padding: 0.34rem 0.7rem;
}

/* ── Undo / redo (Fotis, 2026-08-22) ──────────────────────────────────
   Ctrl+Z and Ctrl+Y as buttons, sitting immediately left of the
   version-history clock so the three time controls read as one cluster.
   Same 26px round-ghost geometry as .version-history-btn above — that is
   what makes them look like one group rather than three unrelated icons. */
.undo-redo {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 1px;
}
.undo-redo-btn {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, opacity 0.15s;
}
.undo-redo-btn:hover:not(:disabled) {
    background: var(--bg-hover);
    color: var(--text-heading);
}
.undo-redo-btn:active:not(:disabled) {
    background: var(--bg-hover);
    color: var(--border-focus);
}
/* Nothing left to undo / redo. Dimmed rather than hidden: the pair keeps its
   place in the row, so the toolbar never shifts sideways as history fills up. */
.undo-redo-btn:disabled {
    opacity: 0.32;
    cursor: default;
}
.undo-redo-btn svg { display: block; }
/* Stacked project popover: the parent column is align-items:stretch, so nudge
   the icons onto the same left edge as the pill rows' text. */
.viewport-controls.vc-compact-project .project-picker-bar .undo-redo {
    justify-content: flex-start;
    padding-left: 0.36rem;
}

/* Signed-in accounts keep Guides / Share / Install inside the account menu
   (see setupAuthMenuExtras in app.js); anonymous visitors keep them in the
   header. The body class flips with auth state. */
/* Guides stays in the header for accounts too (Fotis, 2026-10-12: "it should be
   next to templates"); only Share (the bell takes its slot) and Install app tuck. */
body.hdr-tucked #shareBtn,
body.hdr-tucked #installAppBtn { display: none; }

/* ═════════════════════════════════════════════════════════════
   CASCADE LOADER — the app's one loading animation
   (design handoff "Cascade loading animation (1a)", 2026-10-14)
   Three task bars draw in left to right in sequence, like a dependency
   chain; a milestone diamond lands after the last one; everything fades
   and the cycle loops. Green is used only on the milestone: it marks
   completion. buildCascadeLoader() in app.js builds the markup:
     <div class="gcm-loader-wrap" role="status" aria-live="polite">
       <div class="gcm-loader" aria-hidden="true">
         <span class="gcm-loader__bar"></span>  (x3)
         <span class="gcm-loader__milestone"></span>
       </div>
       <div class="gcm-loader-label">Loading…</div>
     </div>
   The spec size is 168 x 44 at --gcm-scale: 1 (8px bars on a 16px row
   pitch, a 10px diamond 6px after the last bar); every length is a
   multiple of the scale, so --gcm-scale: .5 is a faithful half-size copy.
   --gcm-speed: 2 runs the 2.6s cycle twice as fast. Ink and accent come
   from the theme tokens, so every palette and both modes are covered
   without rules of their own.
   ───────────────────────────────────────────────────────────── */
.gcm-loader {
    --gcm-scale: 1;
    --gcm-speed: 1;
    --gcm-ink: var(--text-heading, #0f172a);
    --gcm-accent: var(--accent, #059669);
    position: relative;
    flex-shrink: 0;
    width: calc(168px * var(--gcm-scale));
    height: calc(44px * var(--gcm-scale));
}
.gcm-loader__bar {
    position: absolute;
    height: calc(8px * var(--gcm-scale));
    border-radius: calc(4px * var(--gcm-scale));
    background: var(--gcm-ink);
    transform-origin: left center;
    transform: scaleX(0);
    animation: gcm-grow calc(2.6s / var(--gcm-speed)) cubic-bezier(.4, 0, .2, 1) infinite;
}
.gcm-loader__bar:nth-child(1) { top: 0; left: 0; width: 36%; animation-delay: 0s; }
.gcm-loader__bar:nth-child(2) { top: calc(16px * var(--gcm-scale)); left: 24%; width: 34%; animation-delay: calc(.22s / var(--gcm-speed)); }
.gcm-loader__bar:nth-child(3) { top: calc(32px * var(--gcm-scale)); left: 50%; width: 36%; animation-delay: calc(.44s / var(--gcm-speed)); }
.gcm-loader__milestone {
    position: absolute;
    top: calc(31px * var(--gcm-scale));
    left: calc(86% + 6px * var(--gcm-scale));
    width: calc(10px * var(--gcm-scale));
    height: calc(10px * var(--gcm-scale));
    border-radius: calc(2px * var(--gcm-scale));
    background: var(--gcm-accent);
    transform: rotate(45deg) scale(0);
    animation: gcm-pop calc(2.6s / var(--gcm-speed)) cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes gcm-grow {
    0%   { transform: scaleX(0); opacity: 1; }
    30%  { transform: scaleX(1); opacity: 1; }
    82%  { transform: scaleX(1); opacity: 1; }
    100% { transform: scaleX(1); opacity: 0; }
}
@keyframes gcm-pop {
    0%, 52%  { transform: rotate(45deg) scale(0); opacity: 1; }
    64%, 82% { transform: rotate(45deg) scale(1); opacity: 1; }
    100%     { transform: rotate(45deg) scale(1); opacity: 0; }
}
.gcm-loader-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 20px;
}
/* Compact spots (a menu list, a dialog row): drawing and label side by side. */
.gcm-loader-wrap--row { flex-direction: row; gap: 12px; }
.gcm-loader-label {
    font-size: 13px;
    line-height: 1.4;
    font-weight: 400;
    color: var(--text-muted, #64748b);
    text-align: center;
}
/* Reduced motion: a static, fully drawn chart (the handoff's own rule). */
@media (prefers-reduced-motion: reduce) {
    .gcm-loader__bar, .gcm-loader__milestone { animation: none; }
    .gcm-loader__bar { transform: none; }
    .gcm-loader__milestone { transform: rotate(45deg); }
}
[data-a11y-motion="reduce"] .gcm-loader__bar,
[data-a11y-motion="reduce"] .gcm-loader__milestone { animation: none; }
[data-a11y-motion="reduce"] .gcm-loader__bar { transform: none; }
[data-a11y-motion="reduce"] .gcm-loader__milestone { transform: rotate(45deg); }

/* The busy veil: a full-window wait with the loader in a small card, raised
   by showAppBusy() in app.js while a chart opens, a template or a shared chart
   loads, a file imports, a version restores or the sign-in redirect leaves.
   Above every dialog (2000 / 2100), below the tours (9000) and the consent
   layer. Kept up for at least APP_BUSY_MIN_MS so a short wait reads as a
   beat, not a flicker. */
.app-busy-veil {
    position: fixed;
    inset: 0;
    z-index: 2600;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--dialog-overlay);
    backdrop-filter: blur(4px);
    animation: fadeIn 0.15s ease-out;
}
.app-busy-veil.hidden { display: none; }
.app-busy-card {
    min-width: 236px;
    padding: 30px 36px 26px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 14px;
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
    animation: dialogIn 0.15s ease-out;
}

/* The import dialog while the chosen file is being read (setImportParsing). */
.import-parsing {
    display: flex;
    justify-content: center;
    padding: 1.6rem 0 1.2rem;
}
.import-parsing.hidden { display: none; }
/* ============================================================================
   PUBLISHED VIEW — /p/<token> (6.10.0)
   The read-only public page of a chart (docs/PUBLISHING_SPEC.md). theme-boot.js
   sets html.published-view BEFORE first paint, so these rules hide the editor
   chrome from the first frame: the header, the task form, the project / tools /
   collaborators groups of the chart header, the footer, and every editing
   affordance of the task list and the chart. What stays: the published bar,
   the zoom and timeline controls, the chart, and (when the owner allows it)
   the task list as a read-only list.
   ============================================================================ */
html.published-view .top-panel-right,
html.published-view .composer-pane,
html.published-view #projectStack,
html.published-view #toolsStack,
html.published-view #collabStack,
html.published-view #collabDivider,
html.published-view .viewport-divider,
html.published-view #appFooter,
html.published-view #versionViewBar,
html.published-view #bulkActions,
html.published-view .task-row-checkbox,
html.published-view .task-row-delete,
html.published-view .dep-edit-badge,
html.published-view .dep-badge-pill,
html.published-view .ai-fab,
html.published-view #aiBuilderPanel,
html.published-view #cursorLayer,
html.published-view .grip-hitarea,
html.published-view .drag-grip,
html.published-view .link-handle,
html.published-view .resize-handle,
html.published-view .dep-delete-badge,
html.published-view #cc-banner {
    display: none !important;
}
/* Belt and braces: the bar and the changed-pill exist in the shell for every
   page; outside a published page they never show. */
html:not(.published-view) #publishedBar,
html:not(.published-view) #publishedChanged {
    display: none !important;
}
/* Owner setting "Task list: hidden" removes the button AND the panel. */
html.published-view.published-no-list #publishedListBtn,
html.published-view.published-no-list #controlsPanel {
    display: none !important;
}
/* Owner setting "Show the name and dates" off: keep the bar's height so the
   controls on the right do not jump, hide the words. */
html.published-view.published-no-header .published-bar-main {
    visibility: hidden;
}
/* The task list is a LIST here: the row controls that edit a task take no
   clicks (CSS, because the rows are rebuilt by innerHTML on every render);
   expanding and collapsing a group keeps working. */
html.published-view .task-row-dot,
html.published-view .task-row-name,
html.published-view .task-row-duration,
html.published-view .task-row-priority,
html.published-view .task-row-progress {
    pointer-events: none;
}
html.published-view .task-row { cursor: default; }
/* The browser pane sits alone on its row (the composer is hidden): give it a
   real height. The row's drag handle still lets a viewer make it taller. */
html.published-view .browser-pane { min-height: 9.5rem; }
html.published-view .composer-split { padding: 0.5rem 6px 0.45rem; }
html.published-view .chart-section { padding-top: 0.35rem; }

/* The published page wears the app's header bar (2026-10-24): brand and clock
   on the left, the chart's name and the publish status in the middle, the
   utilities pill (language, accessibility, theme, task panel), Feedback,
   Report and Donate on the right, and "Made with Gantt Chart Maker" in the
   Sign in slot. The editor-only groups are hidden (the browse items by id
   too: the density ladder moves them into the pill when the row is tight);
   everything else is the bar's own markup and styles. */
html.published-view .app-header-compact .hdr-browse,
html.published-view .app-header-compact .hdr-divider,
html.published-view .app-header-compact #hdrChart,
html.published-view .app-header-compact #templatesBtn,
html.published-view .app-header-compact #guidesBtn,
html.published-view .app-header-compact #installAppBtn,
html.published-view .app-header-compact .share-header,
html.published-view .app-header-compact #changelogWrapper,
html.published-view .app-header-compact #authWrapper,
html.published-view .app-header-compact .panel-toggle { display: none !important; }
html:not(.published-view) #publishedListWrap,
html:not(.published-view) #publishedControlsWrap,
html:not(.published-view) #publishedReportBtn,
html:not(.published-view) #publishedBrand { display: none !important; }
html.published-view .title-clock-group { margin-right: 0; }
/* The middle takes the room between the brand and the controls; its floor is
   what lets the density ladder see a tight row and fold the labels. */
.hdr-published {
    display: flex;
    flex: 1 1 auto !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 160px;
    padding: 0 8px;
    text-align: center;
}
.hdr-published.hidden { display: none !important; }
.published-bar-main { min-width: 0; max-width: 100%; }
.published-title {
    display: block;
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--text-heading);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.published-sub {
    margin: 2px 0 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 2px 10px;
    font-size: 11.5px;
    line-height: 1.2;
    color: var(--text-muted);
    white-space: nowrap;
}
.published-status {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.published-status:empty { display: none; }
.published-status::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--text-faint);
    flex: none;
}
.published-status.is-live::before { background: #10b981; }
/* The task panel and zoom-row tiles read as pressed while their part shows. */
#publishedListBtn[aria-pressed="true"],
#publishedControlsBtn[aria-pressed="true"] { background: var(--bg-card); color: var(--text-heading); box-shadow: inset 0 0 0 1px var(--border-card); }
/* The zoom and timeline row, folded by the viewer or by the owner's default. */
html.published-view.published-controls-collapsed .chart-header { display: none !important; }
/* "Made with": the Sign in slot's dark pill, a link to the app; its label
   folds with the quiet labels. */
.published-brand { text-decoration: none; }
.published-brand:hover { text-decoration: none; color: var(--bg-card); }
.app-header-compact.hdr-fold-labels .published-brand .auth-btn-label { display: none; }
.app-header-compact.hdr-fold-labels .published-brand { padding: 0 10px; }
/* When the bar wraps (under 900px) the name and status take a row of their
   own under the controls, left-aligned like the brand. */
@media (max-width: 899px) {
    .hdr-published { order: 10; flex: 1 1 100% !important; align-items: flex-start; min-width: 0; padding: 0 2px; text-align: left; }
    .published-sub { justify-content: flex-start; }
}

/* The editor's "Published" marker, beside the sync badge: a chart with a public
   page says so where its owner and editors work. Same pill grammar as
   .project-cloud-status, in the published page's green. */
.published-marker {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-left: 0.4rem;
    padding: 0.18rem 0.55rem 0.18rem 0.45rem;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1;
    border-radius: 999px;
    background: rgba(16, 185, 129, 0.13);
    color: #059669;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.2s;
}
.published-marker:hover { background: rgba(16, 185, 129, 0.24); }
/* Snapshot mode with changes viewers cannot see yet: amber, so the owner
   notices before anyone asks why the page is old. */
.published-marker.is-stale { background: rgba(245, 158, 11, 0.16); color: #b45309; }
.published-marker.is-stale:hover { background: rgba(245, 158, 11, 0.28); }
html[data-theme$="-dark"] .published-marker.is-stale { color: #fbbf24; }
.published-marker.hidden { display: none; }
.published-marker svg { width: 11px; height: 11px; }
html[data-theme$="-dark"] .published-marker { color: #34d399; }

/* "This chart has changed" pill: the page's only live signal, pinned top
   centre above everything but the busy veil and the tours. */
.published-changed {
    position: fixed;
    top: calc(0.75rem + env(safe-area-inset-top, 0px));
    left: 50%;
    transform: translateX(-50%);
    z-index: 2500;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.45rem 0.5rem 0.45rem 1rem;
    border-radius: 999px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    box-shadow: 0 14px 34px -14px rgba(0, 0, 0, 0.4);
    font-size: 0.85rem;
    color: var(--text-heading);
    animation: dialogIn 0.18s ease-out;
    max-width: calc(100vw - 2rem);
}
.published-changed.hidden { display: none; }
.published-refresh {
    padding: 0.3rem 0.85rem;
    font-size: 0.8rem;
    border-radius: 999px;
}
@media (max-width: 640px) {
    /* The "Made with" pill keeps its icon alone, like the quiet buttons. */
    .published-brand .auth-btn-label { display: none; }
    .published-brand { padding: 0 10px; }
    /* The shared zoom + timeline row runs edge to edge on a phone (its 15px
       gutters were the few pixels that pushed it past a 390px screen). */
    html.published-view .chart-header { padding: 0 6px; }
}


/* ============================================================================
   SHARE WINDOW — redesign 2026-10-23 (design handoff "Chart Share Dialog
   Redesign", option 1c)
   A fixed 1120 × 630 window: a 56px header with the title, the two-tab pill
   (People / Publish to the web), Preview, Done and close; a two-column body
   (left white, right the input grey); no footer, nothing scrolls inside.
   Every colour is a theme token (the handoff's hex table maps 1:1 onto
   :root), so dark mode is the same rules re-pointed. Under 1160px the window
   scales down as one piece (--shr-scale, set by collab/share.js from the
   viewport); under ~840px it stacks into one scrolling column (.is-stacked).
   The "shr-" prefix keeps these rules clear of the generic .dialog rules;
   the people list keeps its share-collab-* class names (collab/share.js
   builds those rows) and is restyled here.
   ============================================================================ */
#shareCollabOverlay {
    /* bg-subtle in the handoff: between the card and the input grey. */
    --shr-subtle: color-mix(in srgb, var(--bg-input) 45%, var(--bg-card));
    --shr-ink-hover: color-mix(in srgb, var(--text-heading) 88%, var(--bg-card));
}
#shareCollabOverlay .shr-dialog {
    position: relative;
    width: 1120px;
    max-width: none;
    height: 630px;
    max-height: none;
    margin: 0;
    padding: 0;
    border-radius: 16px;
    border: 1px solid var(--border-card);
    background: var(--bg-card);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 24px 48px rgba(15, 23, 42, 0.12);
    display: grid;
    grid-template-rows: 56px minmax(0, 1fr);
    overflow: hidden;
    box-sizing: border-box;
    flex: none;                 /* a flex item of the overlay: never shrunk below the frame */
    font-family: 'IBM Plex Sans', system-ui, sans-serif;
    color: var(--text-body);
    transform: scale(var(--shr-scale, 1));
    transform-origin: center;
    animation: shrIn 0.15s ease-out;
}
@keyframes shrIn {
    from { transform: scale(calc(var(--shr-scale, 1) * 0.96)); opacity: 0; }
    to   { transform: scale(var(--shr-scale, 1)); opacity: 1; }
}
#shareCollabOverlay .shr-dialog * { box-sizing: border-box; }
#shareCollabOverlay .shr-dialog button { font-family: inherit; }
#shareCollabOverlay .hidden { display: none !important; }

/* ── Header ── */
.shr-head {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 14px 0 20px;
    border-bottom: 1px solid var(--border-card);
    cursor: move;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
    min-width: 0;
}
.shr-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 320px;
    cursor: move;
}
.shr-head-spacer { flex: 1; min-width: 0; }
.shr-tabs {
    display: flex;
    gap: 2px;
    height: 34px;
    padding: 0 4px;
    align-items: center;
    background: var(--bg-input);
    border-radius: 10px;
    flex: none;
    cursor: default;
}
.shr-tab {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 12px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    white-space: nowrap;
    transition: color 0.14s ease, background 0.14s ease;
}
.shr-tab:hover { color: var(--text-heading); }
.shr-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.shr-tab svg { width: 15px; height: 15px; flex: none; }
.shr-tab.is-active {
    background: var(--bg-card);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1);
    color: var(--text-heading);
    font-weight: 600;
}
.shr-tab-count {
    font-size: 11px;
    font-weight: 600;
    line-height: 16px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--border-card);
    color: var(--text-muted);
}
.shr-tab.is-active .shr-tab-count { color: var(--text-heading); }
.shr-tab-dot {
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background: var(--accent);
}
.shr-close {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--icon-color);
    display: grid;
    place-items: center;
    cursor: pointer;
    flex: none;
    transition: background 0.14s ease, color 0.14s ease;
}
.shr-close:hover { background: var(--bg-input); color: var(--text-heading); }
.shr-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.shr-close svg { width: 16px; height: 16px; }

/* ── Buttons ── */
.shr-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    height: 38px;
    padding: 0 14px;
    border: 0;
    border-radius: 10px;
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    flex: none;
    text-decoration: none;
    transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease, opacity 0.14s ease;
}
.shr-btn svg { width: 15px; height: 15px; flex: none; }
.shr-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.shr-btn:disabled { opacity: 0.5; cursor: default; }
.shr-btn-ink { background: var(--text-heading); color: var(--bg-card); }
.shr-btn-ink:hover:not(:disabled) { background: var(--shr-ink-hover); }
.shr-btn-ghost {
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    color: var(--text-body);
    font-weight: 500;
}
.shr-btn-ghost:hover:not(:disabled) { background: var(--bg-input); border-color: var(--border-input); }
.shr-btn-sm { height: 28px; padding: 0 12px; font-size: 12.5px; border-radius: 8px; }
.shr-btn-sm svg { width: 14px; height: 14px; }
.shr-head .shr-btn { height: 34px; }
.shr-head .shr-btn-ink { padding: 0 18px; }
.shr-head .shr-preview-btn { padding: 0 13px; }
.shr-btn-icon {
    width: 38px;
    height: 38px;
    border: 1px solid var(--border-card);
    border-radius: 10px;
    background: var(--bg-card);
    color: var(--text-body);
    display: grid;
    place-items: center;
    flex: none;
    cursor: pointer;
    transition: background 0.14s ease, color 0.14s ease;
}
.shr-btn-icon:hover { background: var(--shr-subtle); color: var(--text-heading); }
.shr-btn-icon:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.shr-btn-icon[aria-disabled="true"] { pointer-events: none; }
.shr-btn-icon svg { width: 15px; height: 15px; }

/* ── Body: two columns ── */
.shr-body {
    display: grid;
    grid-template-columns: 440px minmax(0, 1fr);
    min-height: 0;
}
.shr-body-publish { grid-template-columns: 400px minmax(0, 1fr); }
.shr-col {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 20px 22px;
    min-height: 0;
    min-width: 0;
}
.shr-col-left { border-right: 1px solid var(--border-card); }
.shr-col-spread { justify-content: space-between; }
.shr-col-right { background: var(--bg-input); gap: 10px; }
.shr-body-publish .shr-col-right { gap: 12px; }
.shr-group { display: flex; flex-direction: column; gap: 10px; }
.shr-h { font-size: 14px; font-weight: 600; color: var(--text-heading); }
.shr-h-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.shr-hint { font-size: 12px; color: var(--text-muted); line-height: 1.45; }
.shr-divider { height: 1px; background: var(--border-card); flex: none; }
.shr-count { font-size: 12px; color: var(--text-muted); white-space: nowrap; }

/* ── Invite row ── */
.shr-invite-row { display: flex; gap: 8px; align-items: stretch; }
.shr-field {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    padding: 0 4px 0 12px;
    border: 1px solid var(--border-card);
    border-radius: 10px;
    background: var(--bg-card);
    min-width: 0;
    transition: border-color 0.14s ease;
}
.shr-field:focus-within { border-color: var(--border-input); }
.shr-field-email { flex: 1; }
.shr-field-icon { color: var(--icon-color); display: flex; flex: none; }
.shr-field-icon svg { width: 15px; height: 15px; }
.shr-field-input {
    flex: 1;
    min-width: 0;
    height: 100%;
    border: 0;
    outline: 0;
    background: transparent;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--text-heading);
}
.shr-field-input::placeholder { color: var(--icon-color); font-weight: 400; }
.shr-btn-invite { height: 40px; padding: 0 16px; }
/* The role pickers in this window are grey chips (no border), the handoff's
   "Viewer ▾" inside the email field and on the Anyone-with-the-link row. */
.shr-role-chip .role-dd-trigger {
    height: 32px;
    min-width: 0;
    padding: 0 8px 0 10px;
    border: 0;
    border-radius: 8px;
    background: var(--bg-input);
    color: var(--text-body);
    font-size: 12.5px;
    font-weight: 500;
    box-shadow: none;
}
.shr-role-chip .role-dd-trigger:hover,
.shr-role-chip.is-open .role-dd-trigger { background: var(--border-card); box-shadow: none; }
.shr-role-chip .role-dd-chevron { color: currentColor; }
.shr-role-chip.is-disabled .role-dd-trigger { opacity: 1; color: var(--icon-color); cursor: default; }
.shr-role-chip.is-disabled .role-dd-trigger:hover { background: var(--bg-input); }
.shr-public-role { flex: none; margin-left: auto; }
.shr-public-role .role-dd-trigger { height: 30px; }
.share-collab-warning {
    padding: 8px 10px;
    font-size: 12px;
    line-height: 1.4;
    border-radius: 8px;
    background: rgba(239, 68, 68, 0.1);
    color: #b91c1c;
    border: 1px solid rgba(239, 68, 68, 0.3);
}
.share-collab-warning[data-level="info"] {
    background: var(--accent-soft);
    color: var(--accent-dark, var(--accent));
    border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}
.share-collab-warning[data-level="success"] {
    background: var(--accent-soft);
    color: var(--accent-dark, var(--accent));
    border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}

/* ── General access ── */
.shr-access {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    overflow: hidden;
}
.shr-access-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 18px 14px;
    transition: background 0.14s ease;
}
.shr-access-row + .shr-access-row { border-top: 1px solid var(--border-card); }
.shr-access-row:hover,
.shr-access-row:has(input[type="radio"]:checked) { background: var(--shr-subtle); }
.shr-access-row input[type="radio"] { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.shr-access-row:has(input[type="radio"]:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
.shr-access-main { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; cursor: pointer; }
.shr-radio {
    width: 16px;
    height: 16px;
    border-radius: 999px;
    border: 1.5px solid var(--border-input);
    flex: none;
    transition: border 0.14s ease;
}
.shr-access-row:has(input[type="radio"]:checked) .shr-radio { border: 5px solid var(--text-heading); }
.shr-tile {
    width: 34px;
    height: 34px;
    border-radius: 10px;
    background: var(--bg-input);
    color: var(--text-body);
    display: grid;
    place-items: center;
    flex: none;
    transition: background 0.14s ease, color 0.14s ease;
}
.shr-tile svg { width: 16px; height: 16px; }
.shr-access-row:has(input[type="radio"]:checked) .shr-tile { background: var(--text-heading); color: var(--bg-card); }
.shr-access-meta { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.shr-access-meta strong { font-size: 13.5px; font-weight: 600; color: var(--text-body); transition: color 0.14s ease; }
.shr-access-row:has(input[type="radio"]:checked) .shr-access-meta strong { color: var(--text-heading); }

/* ── Link rows (chart link, public URL) ── */
.shr-link-row { display: flex; gap: 8px; align-items: stretch; }
.shr-mono-field {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    height: 38px;
    padding: 0 12px;
    border: 1px solid var(--border-card);
    border-radius: 10px;
    background: var(--bg-card);
    min-width: 0;
}
.shr-mono-field-subtle { background: var(--shr-subtle); }
.shr-mono {
    font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px;
    font-weight: 400;
    color: var(--text-body);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.shr-mono.is-placeholder { font-family: inherit; color: var(--text-muted); }
.shr-link-hint { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.shr-link-hint svg { width: 13px; height: 13px; }
.shr-link-hint .shr-link-hint-globe { display: none; }
.shr-link-hint.is-public .shr-link-hint-lock { display: none; }
.shr-link-hint.is-public .shr-link-hint-globe { display: inline; }
.shr-pub-url-icon { color: var(--accent); display: flex; flex: none; }
.shr-pub-url-icon svg { width: 14px; height: 14px; }

/* ── Who has access ── */
.shr-people-card {
    flex: 1;
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
    overflow: hidden;
    min-height: 0;
}
.share-collab-collaborator-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    flex: 0 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}
.share-collab-collaborator-list::-webkit-scrollbar { width: 8px; }
.share-collab-collaborator-list::-webkit-scrollbar-thumb { background: var(--border-input); border-radius: 4px; }
.share-collab-collaborator-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    min-height: 60px;
    min-width: 0;
    flex-shrink: 0;
    transition: background-color 0.14s ease, opacity 0.18s ease, transform 0.18s ease;
}
.share-collab-collaborator-item + .share-collab-collaborator-item { border-top: 1px solid var(--border-card); }
.share-collab-collaborator-item:not(.is-owner):hover { background: var(--shr-subtle); }
.share-collab-collaborator-item.is-leaving { opacity: 0; transform: translateX(8px); }
.share-collab-collab-avatar {
    width: 36px;
    height: 36px;
    border-radius: 999px;
    background: var(--bg-input);
    color: var(--text-heading);
    font-size: 14px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.share-collab-collab-avatar.is-self { background: var(--text-heading); color: var(--bg-card); }
.share-collab-collab-avatar.is-pending { background: transparent; border: 1.5px dashed var(--border-input); color: var(--text-muted); }
.share-collab-collab-meta { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.share-collab-collab-name {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-heading);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.share-collab-collab-you-tag { font-weight: 400; color: var(--text-muted); }
.share-collab-collab-email {
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.share-collab-owner-badge { font-size: 12.5px; font-weight: 500; color: var(--text-muted); flex-shrink: 0; white-space: nowrap; }
.share-collab-invite-badge { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; border: 1px dashed var(--border-input); }
.share-collab-collab-role-slot { flex-shrink: 0; position: relative; border-radius: 6px; transition: box-shadow 0.4s ease; }
.share-collab-collab-role-slot .role-dd-trigger { font-size: 12.5px; color: var(--text-muted); }
.share-collab-collab-role-slot.save-tick-flash { box-shadow: 0 0 0 2px var(--accent-soft); animation: shareCollabFlash 1.4s ease; }
.share-collab-collab-role-slot.save-tick-flash::after {
    content: '';
    position: absolute;
    top: 50%;
    right: -22px;
    transform: translateY(-50%);
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #22c55e;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 8 L7 11 L12 5' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-size: 14px 14px;
    background-repeat: no-repeat;
    background-position: center;
    animation: shareCollabTickPop 1.4s ease;
    pointer-events: none;
}
@keyframes shareCollabFlash {
    0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
    20% { box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.35); }
    100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}
@keyframes shareCollabTickPop {
    0% { opacity: 0; transform: translateY(-50%) scale(0.6); }
    20% { opacity: 1; transform: translateY(-50%) scale(1); }
    80% { opacity: 1; transform: translateY(-50%) scale(1); }
    100% { opacity: 0; transform: translateY(-50%) scale(0.85); }
}
.share-collab-collab-remove-wrap { flex-shrink: 0; display: flex; align-items: center; }
.share-collab-collab-remove {
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.14s, color 0.14s;
}
.share-collab-collab-remove:hover,
.share-collab-collab-remove:focus-visible { background: rgba(239, 68, 68, 0.12); color: #ef4444; outline: none; }
.share-collab-collab-confirm { display: inline-flex; align-items: center; gap: 4px; }
.share-collab-confirm-yes,
.share-collab-confirm-no {
    border: 0;
    cursor: pointer;
    padding: 4px 9px;
    font-size: 12px;
    font-weight: 600;
    border-radius: 6px;
    line-height: 1.2;
    transition: background 0.14s, color 0.14s;
}
.share-collab-confirm-yes { background: rgba(239, 68, 68, 0.12); color: #b91c1c; }
.share-collab-confirm-yes:hover { background: rgba(239, 68, 68, 0.24); }
.share-collab-confirm-yes:disabled { opacity: 0.5; cursor: not-allowed; }
.share-collab-confirm-no { background: transparent; color: var(--text-muted); }
.share-collab-confirm-no:hover { background: var(--bg-input); color: var(--text-heading); }
.shr-empty {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    border-top: 1px solid var(--border-card);
    padding: 20px;
    text-align: center;
}
.shr-empty-tile {
    width: 44px;
    height: 44px;
    border-radius: 12px;
    background: var(--bg-input);
    color: var(--text-muted);
    display: grid;
    place-items: center;
}
.shr-empty-tile svg { width: 18px; height: 18px; }
.shr-empty-title { font-size: 13.5px; font-weight: 600; color: var(--text-heading); }
.shr-empty-sub { font-size: 12.5px; color: var(--text-muted); max-width: 280px; line-height: 1.45; }

/* ── Publish to the web: left column ── */
.shr-pub-head { display: flex; align-items: flex-start; gap: 12px; }
.shr-pub-head-text { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.shr-pub-body { display: flex; flex-direction: column; gap: 14px; flex: 1; min-height: 0; }
/* Publishing off: every option stays in view, greyed and inert (the
   controls also carry the disabled attribute, which is what stops them). */
.shr-pub-body.is-off > *,
.shr-pub-appearance.is-off { opacity: 0.55; }
.shr-switch {
    position: relative;
    width: 36px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--border-input);
    cursor: pointer;
    flex: none;
    margin-top: 2px;
    transition: background 0.18s ease;
}
.shr-switch-knob {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 999px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.2);
    transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
.shr-switch[aria-checked="true"] { background: var(--text-heading); }
.shr-switch[aria-checked="true"] .shr-switch-knob { transform: translateX(16px); background: var(--bg-card); }
/* The publish switch is the only green control in the window. */
.shr-switch-accent[aria-checked="true"] { background: var(--accent); }
.shr-switch-accent[aria-checked="true"] .shr-switch-knob { background: #fff; }
.shr-switch-sm { width: 32px; height: 18px; margin-top: 0; }
.shr-switch-sm .shr-switch-knob { width: 14px; height: 14px; }
.shr-switch-sm[aria-checked="true"] .shr-switch-knob { transform: translateX(14px); }
.shr-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.shr-switch:disabled { opacity: 0.5; cursor: default; }
.shr-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    padding: 10px 12px;
    background: var(--bg-card);
    flex: none;
    min-width: 0;
}
.shr-card-updates { gap: 6px; }
.shr-card-views { flex: 1; min-height: 0; padding: 12px 14px; }
.shr-card-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.shr-card-title { font-size: 13px; font-weight: 600; color: var(--text-heading); }
.shr-card-title-icon { display: flex; align-items: center; gap: 8px; }
.shr-card-title-icon svg { width: 15px; height: 15px; }
.shr-seg { display: flex; gap: 2px; padding: 3px; background: var(--bg-input); border-radius: 10px; flex: none; }
.shr-seg-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    height: 26px;
    padding: 0 10px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.14s ease, color 0.14s ease;
}
.shr-seg-btn svg { width: 13px; height: 13px; flex: none; }
.shr-seg-btn:hover:not(:disabled) { color: var(--text-heading); }
.shr-seg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.shr-seg-btn[aria-checked="true"] {
    background: var(--bg-card);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1);
    color: var(--text-heading);
    font-weight: 600;
}
.shr-seg-btn:disabled { cursor: default; }
.shr-snap-actions { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-top: 2px; }
.shr-snap-state { font-size: 11.5px; color: var(--text-muted); min-width: 0; }
.shr-snap-state.is-pending { color: #d97706; font-weight: 600; }
/* Password protection */
.shr-pw-head { display: flex; align-items: center; gap: 10px; }
.shr-tile-soft { width: 30px; height: 30px; border-radius: 9px; background: var(--bg-input); color: var(--text-heading); }
.shr-tile-soft svg { width: 15px; height: 15px; }
.shr-pw-text { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.shr-pw-set {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 38px;
    padding: 0 6px 0 12px;
    border-radius: 10px;
    background: var(--shr-subtle);
    border: 1px solid var(--border-card);
}
.shr-pw-dots { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 14px; font-weight: 500; letter-spacing: 0.12em; color: var(--text-heading); }
.shr-pw-note { flex: 1; font-size: 11.5px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shr-pw-edit { display: flex; flex-direction: column; gap: 8px; }
.shr-pw-row { display: flex; gap: 6px; }
.shr-pw-row .shr-btn { height: 38px; }
.shr-field-pw { flex: 1; height: 38px; border-color: var(--border-input); }
.shr-field-pw .shr-field-input { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 13px; }
.shr-field-pw .shr-field-input::placeholder { font-family: 'IBM Plex Sans', system-ui, sans-serif; }
.shr-pw-eye,
.pubpw-eye {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    display: grid;
    place-items: center;
    cursor: pointer;
    flex: none;
    transition: background 0.14s ease, color 0.14s ease;
}
.shr-pw-eye:hover,
.pubpw-eye:hover { background: var(--bg-input); color: var(--text-heading); }
.shr-pw-eye:focus-visible,
.pubpw-eye:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.shr-pw-eye svg,
.pubpw-eye svg { width: 15px; height: 15px; }
.shr-eye-shut { display: none; }
.shr-pw-eye.is-shown .shr-eye-open,
.pubpw-eye[aria-pressed="true"] .shr-eye-open { display: none; }
.shr-pw-eye.is-shown .shr-eye-shut,
.pubpw-eye[aria-pressed="true"] .shr-eye-shut { display: block; }
.shr-pw-meter-row { display: flex; align-items: center; gap: 10px; }
.shr-pw-meter { flex: 1; display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.shr-pw-meter span { height: 4px; border-radius: 999px; background: var(--border-card); transition: background 0.18s ease; }
.shr-pw-strength { font-size: 11.5px; font-weight: 600; color: var(--icon-color); min-width: 64px; text-align: right; white-space: nowrap; }
/* Total views */
.shr-views-nums { display: flex; align-items: baseline; gap: 8px; }
.shr-views-total { font-size: 26px; font-weight: 700; color: var(--text-heading); letter-spacing: -0.02em; line-height: 1.1; }
.shr-views-today { font-size: 12px; font-weight: 600; color: var(--accent-dark, var(--accent)); }
.shr-views-bars { flex: 1; display: flex; align-items: flex-end; gap: 4px; min-height: 24px; }
.shr-views-bar { flex: 1; min-height: 3px; border-radius: 3px 3px 0 0; background: var(--border-input); }
.shr-views-bar.is-today { background: var(--accent); }
.shr-views-axis { display: flex; justify-content: space-between; font-size: 11px; color: var(--icon-color); margin-top: -4px; }
.shr-views-state { margin-top: -2px; }

/* ── Publish to the web: right column (appearance) ── */
.shr-pub-appearance { display: flex; flex-direction: column; gap: 12px; flex: 1; min-height: 0; }
.shr-card-page { gap: 10px; padding: 12px 14px; }
.shr-stack { display: flex; flex-direction: column; gap: 6px; }
.shr-label { font-size: 12px; font-weight: 600; color: var(--text-muted); }
.shr-input {
    width: 100%;
    height: 36px;
    padding: 0 12px;
    border: 1px solid var(--border-input);
    border-radius: 10px;
    background: var(--bg-card);
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-heading);
    outline: 0;
    transition: border-color 0.14s ease, background 0.14s ease, color 0.14s ease;
}
.shr-input::placeholder { color: var(--icon-color); font-weight: 400; }
.shr-input:focus { border-color: var(--border-focus); }
.shr-input:disabled { background: var(--shr-subtle); color: var(--icon-color); }
.shr-rule { height: 1px; background: var(--bg-input); }
.shr-opt-label { font-size: 13px; font-weight: 500; color: var(--text-body); }
.shr-card-extras { flex: 1; min-height: 0; gap: 4px; padding: 12px 14px; }
/* Eleven rows share the fixed card: tighter than the default 6px. */
.shr-card-extras .shr-check { padding: 3px 0; }
.shr-extras { flex: 1; display: flex; flex-direction: column; justify-content: space-between; min-height: 0; }
.shr-check {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-heading);
    cursor: pointer;
    min-width: 0;
}
.shr-check input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.shr-check-box {
    width: 17px;
    height: 17px;
    border-radius: 5px;
    border: 1.5px solid var(--border-input);
    background: var(--bg-card);
    color: var(--bg-card);
    display: grid;
    place-items: center;
    flex: none;
    transition: background 0.14s ease, border-color 0.14s ease;
}
.shr-check-box svg { width: 11px; height: 11px; opacity: 0; }
.shr-check input:checked ~ .shr-check-box { background: var(--text-heading); border-color: var(--text-heading); }
.shr-check input:checked ~ .shr-check-box svg { opacity: 1; }
.shr-check input:focus-visible ~ .shr-check-box { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
.shr-check input:disabled ~ * { cursor: default; }
.shr-check-label { width: 170px; flex: none; }
.shr-check-desc { font-size: 12px; font-weight: 400; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
#sharePanelPublish.is-busy { opacity: 0.7; pointer-events: none; }

/* ── Preview popup ── */
.shr-preview {
    position: absolute;
    inset: 0;
    z-index: 5;
    background: rgba(15, 23, 42, 0.38);
    display: grid;
    place-items: center;
    border-radius: 16px;
}
.shr-preview-win {
    width: 980px;
    height: 560px;
    max-width: calc(100% - 40px);
    max-height: calc(100% - 40px);
    background: var(--bg-card);
    border-radius: 14px;
    box-shadow: 0 24px 64px rgba(15, 23, 42, 0.35);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.shr-preview-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 48px;
    padding: 0 10px 0 14px;
    background: var(--shr-subtle);
    border-bottom: 1px solid var(--border-card);
    flex: none;
    min-width: 0;
}
.shr-preview-title { font-size: 13px; font-weight: 600; color: var(--text-heading); white-space: nowrap; }
.shr-preview-addr {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    height: 32px;
    padding: 0 12px;
    border-radius: 999px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    min-width: 0;
}
.shr-preview-addr .shr-mono { font-size: 12px; color: var(--text-muted); }
.shr-preview-lock { color: var(--accent); display: flex; flex: none; }
.shr-preview-lock svg { width: 13px; height: 13px; }
.shr-preview-bar .shr-btn-sm { height: 32px; border-radius: 10px; }
.shr-preview-bar .shr-close { width: 32px; height: 32px; color: var(--text-muted); }
.shr-preview-bar .shr-close:hover { background: var(--border-card); color: var(--text-heading); }
.shr-preview-frame { flex: 1; width: 100%; min-height: 0; border: 0; background: var(--bg-card); }

/* ── Stacked (phones and narrow tablets): one scrolling column ── */
#shareCollabOverlay .shr-dialog.is-stacked {
    width: min(560px, calc(100vw - 1rem));
    height: auto;
    max-height: calc(100dvh - 1rem);
    grid-template-rows: auto minmax(0, 1fr);
    overflow: hidden;
    transform: none;
    animation: dialogIn 0.15s ease-out;
}
.is-stacked .shr-head { flex-wrap: wrap; height: auto; padding: 10px 12px; gap: 8px; cursor: default; }
.is-stacked .shr-title { max-width: 100%; flex: 1 1 100%; cursor: default; }
.is-stacked .shr-head-spacer { display: none; }
.is-stacked .shr-tabs { flex: 1 1 auto; }
.is-stacked .shr-body { grid-template-columns: 1fr; overflow-y: auto; }
.is-stacked .shr-col { padding: 16px; }
.is-stacked .shr-col-left { border-right: 0; border-bottom: 1px solid var(--border-card); }
.is-stacked .shr-col-spread { justify-content: flex-start; }
.is-stacked .shr-people-card { min-height: 200px; }
.is-stacked .shr-card-views { min-height: 150px; }
.is-stacked .shr-card-extras .shr-check-label { width: auto; }
.is-stacked .shr-check-desc { display: none; }
.is-stacked .shr-card-row { flex-wrap: wrap; }
.is-stacked .shr-preview { position: fixed; border-radius: 0; z-index: 2600; }
.is-stacked .shr-preview-win { width: calc(100vw - 1rem); height: calc(100dvh - 1rem); max-width: none; max-height: none; }
.is-stacked .shr-preview-bar .shr-btn-sm span { display: none; }

/* ============================================================================
   PUBLISHED PAGE — the extras and the password prompt (a72)
   ============================================================================ */
/* Owner setting "Grid lines" off: the same families the export drops. */
html.published-view.published-no-grid .grid-vertical,
html.published-view.published-no-grid .grid-horizontal,
html.published-view.published-no-grid .row-separator,
html.published-view.published-no-grid .month-separator {
    display: none !important;
}
.pubpw-dialog { max-width: min(400px, calc(100vw - 2rem)); border-radius: 16px; }
.pubpw-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 28px 28px 24px;
    text-align: center;
}
.pubpw-tile {
    width: 48px;
    height: 48px;
    border-radius: 14px;
    background: var(--bg-input);
    color: var(--text-heading);
    display: grid;
    place-items: center;
}
.pubpw-tile svg { width: 22px; height: 22px; }
.pubpw-title { margin: 6px 0 0; font-size: 17px; font-weight: 600; color: var(--text-heading); }
.pubpw-sub { margin: 0; font-size: 13px; color: var(--text-muted); line-height: 1.45; }
.pubpw-form { width: 100%; display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.pubpw-field {
    display: flex;
    align-items: center;
    height: 42px;
    padding: 0 4px 0 12px;
    border: 1px solid var(--border-input);
    border-radius: 10px;
    background: var(--bg-card);
    transition: border-color 0.14s ease;
}
.pubpw-field:focus-within { border-color: var(--border-focus); }
.pubpw-input {
    flex: 1;
    min-width: 0;
    height: 100%;
    border: 0;
    outline: 0;
    background: transparent;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-heading);
}
.pubpw-input::placeholder { font-family: 'IBM Plex Sans', system-ui, sans-serif; font-weight: 400; color: var(--text-placeholder); }
.pubpw-eye { width: 34px; height: 34px; }
.pubpw-error { font-size: 12.5px; color: #dc2626; text-align: left; line-height: 1.4; }
.pubpw-error.hidden { display: none; }
.pubpw-submit { height: 40px; border-radius: 10px; font-size: 13.5px; }
.pubpw-brand { margin-top: 6px; font-size: 12px; color: var(--text-muted); text-decoration: none; }
.pubpw-brand:hover { color: var(--text-heading); }

/* ============================================================================
   OBJECT BROWSER — design handoff "Object Browser 2A" (pack 9, 2026-10-24;
   first built 2026-10-23 to packs 7 and 8 of the same design)
   The pane is the frame: a 38px header (the task count at rest, the action
   bar once rows are ticked; both layers always mounted and cross-faded with
   a left-to-right stagger), the task grid at full width with its native
   scrollbar hidden and row scroll-snap, and a 64px paging gutter. Every hex
   in the handoff maps to a theme token of the same value; the few the theme
   lacks are the --ob-* tokens below, at the handoff's exact values. The rows
   keep their task-row-* class names (app.js renderTaskList builds them);
   the "ob-" prefix is the frame.
   ============================================================================ */
#taskSection.ob-frame {
    /* Handoff values with no theme token of the same value. Light is the
       handoff; the handoff has no dark mode, so dark is the theme's nearest. */
    --ob-fill-2: #f8fafc;                 /* header while selecting, row hover */
    --ob-danger-bg: #fef2f2;              /* Delete hover */
    --ob-ring: rgba(15, 23, 42, 0.08);    /* ring of the rename field and the name field */
    /* The header adapts to the pane's own width (icons only, then no Select
       all) with container queries below. */
    container-type: inline-size;
    padding: 0;
    /* The handoff sets every text with the `font:` shorthand, which resets
       line-height to normal; the app's body line-height (1.5) would make its
       pills and chips taller. */
    line-height: normal;
    /* header 38 + its 1px rule + three 30px rows + the frame's borders. The
       strip's 6px paddings are not counted: the snap pulls the first row to
       the strip's top, so the frame holds whole rows, and app.js sizes it to
       a whole number of them (fitBrowserPane: the rows that cover the row's
       height beside the task creator card, or the dragged count). */
    min-height: 131px;
    overflow: visible;
}
[data-theme$="-dark"] #taskSection.ob-frame {
    --ob-fill-2: color-mix(in srgb, var(--bg-input) 45%, var(--bg-card));
    --ob-danger-bg: var(--delete-hover-bg);
    --ob-ring: rgba(255, 255, 255, 0.12);
}
/* The priority palette is the theme's own (--priority-*): the handoff's
   values since 6.10.1, so the list, the chooser, the chart and the dialog
   agree (Fotis, 2026-10-24). */
.ob-inner {
    inset: 0;
    flex-direction: column;
    min-height: 0;
}

/* ── Header: two stacked layers ── */
.ob-head {
    position: relative;
    flex: none;
    box-sizing: content-box;
    height: 38px;
    border-bottom: 1px solid var(--border-card);
    border-radius: 12px 12px 0 0;
    background: var(--bg-card);
    /* Clips the action bar on a pane too narrow for it (the bar is absolutely
       positioned and would otherwise widen the page); the priority menu is
       positioned fixed by app.js, so the clip never catches it. */
    overflow: hidden;
    transition: background 0.25s ease;
}
.ob-head.is-selecting { background: var(--ob-fill-2); }
.ob-head-rest,
.ob-head-actions {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 10px 0 14px;
    min-width: 0;
}
.ob-head-rest {
    opacity: 1;
    transform: none;
    transition: opacity 0.2s ease, transform 0.25s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.ob-head.is-selecting .ob-head-rest {
    opacity: 0;
    transform: translateY(-6px);
    pointer-events: none;
}
.ob-title { font-size: 13px; font-weight: 600; color: var(--text-heading); white-space: nowrap; }
.ob-count-pill {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 11.5px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    background: var(--bg-input);
    padding: 1px 7px;
    border-radius: 99px;
}
.ob-hint {
    margin-left: auto;
    font-size: 12px;
    color: var(--icon-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.ob-head-actions { pointer-events: none; }
.ob-head.is-selecting .ob-head-actions { pointer-events: auto; }
/* Each action item enters on its own, left to right (60ms + i × 35ms); on
   the way out every item leaves at once. The transition values of the
   destination state are the ones that apply, so the delays live only on
   the selecting state. */
.ob-act {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.2s ease 0s, transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) 0s, background 0.15s;
}
.ob-head.is-selecting .ob-act {
    opacity: 1;
    transform: none;
    transition: opacity 0.2s ease var(--ob-d, 60ms), transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) var(--ob-d, 60ms), background 0.15s;
}
.ob-head-actions > :nth-child(1) { --ob-d: 60ms; }
.ob-head-actions > :nth-child(2) { --ob-d: 95ms; }
.ob-head-actions > :nth-child(3) { --ob-d: 130ms; }
.ob-head-actions > :nth-child(4) { --ob-d: 165ms; }
.ob-head-actions > :nth-child(5) { --ob-d: 200ms; }
.ob-head-actions > :nth-child(6) { --ob-d: 235ms; }
.ob-head-actions > :nth-child(7) { --ob-d: 270ms; }
/* Reduced motion: the fade only, no transforms and no delays. */
@media (prefers-reduced-motion: reduce) {
    .ob-head, .ob-head-rest, .ob-act, .ob-head.is-selecting .ob-act { transition: opacity 0.2s ease 0s; transform: none; }
}
[data-a11y-motion="reduce"] .ob-head,
[data-a11y-motion="reduce"] .ob-head-rest,
[data-a11y-motion="reduce"] .ob-act,
[data-a11y-motion="reduce"] .ob-head.is-selecting .ob-act { transition: opacity 0.2s ease 0s; transform: none; }

.ob-act-clear {
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    display: grid;
    place-items: center;
    cursor: pointer;
    flex: none;
}
.ob-act-clear:hover { background: var(--border-card); color: var(--text-heading); }
.ob-act-clear svg { width: 14px; height: 14px; }
.ob-sel-count {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-heading);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.ob-divider { width: 1px; height: 18px; background: var(--border-card); margin: 0 4px; flex: none; }
.ob-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-body);
    cursor: pointer;
    white-space: nowrap;
}
.ob-btn:hover:not(:disabled) { background: var(--bg-input); }
.ob-btn:focus-visible,
.ob-act-clear:focus-visible,
.ob-page:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ob-btn svg { width: 14px; height: 14px; flex: none; }
.ob-btn .ob-chev { width: 12px; height: 12px; }
.ob-btn-danger { color: var(--delete-hover-color); }
.ob-btn-danger:hover:not(:disabled) { background: var(--ob-danger-bg); }
.ob-btn-all { margin-left: auto; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.ob-btn-all:hover:not(:disabled) { color: var(--text-heading); }
.ob-pri-wrap { position: relative; }
.ob-pri-menu {
    /* app.js places it fixed from the button's rect when it opens. */
    position: fixed;
    z-index: 2600;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 10px;
    box-shadow: 0 12px 24px rgba(15, 23, 42, 0.12);
    padding: 4px;
    display: flex;
    flex-direction: column;
    min-width: 130px;
}
.ob-pri-menu.hidden { display: none; }
.ob-pri-item {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 30px;
    padding: 0 8px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-body);
    cursor: pointer;
    text-align: left;
    white-space: nowrap;
}
.ob-pri-item:hover,
.ob-pri-item:focus-visible { background: var(--bg-input); outline: none; }
.ob-badge {
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 99px;
    display: grid;
    place-items: center;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.03em;
    flex: none;
}
/* The custom priority: the user's own name, first three letters on a violet
   pill (single letters stay circles, three letters grow into a pill). */
.ob-badge.task-priority-custom,
.task-row-priority.task-priority-custom { border: 0; }
.ob-pri-sep { height: 1px; background: var(--bg-input); margin: 4px 2px; }
.ob-pri-custom { display: flex; flex-direction: column; gap: 4px; padding: 4px 6px 6px; }
.ob-pri-custom-label { font-size: 11px; font-weight: 500; color: var(--text-muted); cursor: auto; }
input.ob-pri-custom-input {
    /* `input.` beats the app's pill rule for text inputs (radius 999px). */
    height: 28px;
    width: 150px;
    padding: 0 8px;
    border: 1px solid var(--border-card);
    border-radius: 7px;
    outline: none;
    background: var(--bg-card);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 400;
    color: var(--text-heading);
}
input.ob-pri-custom-input:focus { border-color: var(--text-heading); box-shadow: 0 0 0 3px var(--ob-ring); }
.ob-pri-custom-hint { font-size: 11px; color: var(--icon-color); }

/* ── Body: the grid and the gutter ── */
.ob-body {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 64px;
}
#taskSection .task-list-compact {
    /* Three equal columns, the handoff's grid. A pane narrower than the
       1240px design frame drops to two columns, then one, before a column
       gets narrower than about 250px (the container queries below); the
       gutter's paging reads the real column count. */
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-rows: 30px;
    column-gap: 16px;
    row-gap: 0;
    align-content: start;
    padding: 6px 8px;
    height: 100%;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
    overflow-x: hidden;
    scroll-snap-type: y mandatory;
    scrollbar-width: none;
}
#taskSection .task-list-compact::-webkit-scrollbar { display: none; }
/* Phones (the pointer: coarse block near the top of this file) make the rows
   36px tap targets: the tracks follow, or every row overlaps the next by 6px
   and the frame cuts the last one. */
@media (pointer: coarse) and (max-width: 820px), (pointer: coarse) and (max-height: 520px) {
    #taskSection .task-list-compact { grid-auto-rows: 36px; }
}
.task-list-compact > .task-row {
    min-width: 0;
    scroll-snap-align: start;
    /* Big charts: a row scrolled out of the strip skips style, layout and
       paint until it scrolls in; the intrinsic size keeps the grid exact. */
    content-visibility: auto;
    contain-intrinsic-block-size: auto 30px;
}
.task-list-compact > .ob-empty {
    grid-column: 1 / -1;
    margin: 0;
    padding: 0 8px;
    font-size: 12.5px;
    color: var(--text-muted);
    align-self: center;
}
/* The row: checkbox, index, sub-task diamond, colour dot, name (with the
   pencil that shows on hover), duration chip, dependency chip, priority
   badge. The whole row ticks; the name and the pencil rename. Plain, as the
   handoff draws it: the subtle grey on hover, the input grey when selected,
   tasks and groups alike (the colour dot and the chart's caret say which rows
   are groups). */
.task-row {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 30px;
    padding: 0 8px;
    border-radius: 8px;
    background: transparent;
    cursor: pointer;
    min-width: 0;
    transition: background 0.12s;
}
.task-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.task-row:hover { background: var(--ob-fill-2); }
.task-row.selected { background: var(--bg-input); }
.task-row.selected:hover { background: var(--ob-fill-2); }
.task-row-checkbox {
    width: 15px;
    height: 15px;
    border-radius: 4px;
    border: 1.5px solid var(--border-input);
    background: var(--bg-card);
    color: #fff;
    display: grid;
    place-items: center;
    flex: none;
}
.task-row-checkbox svg { width: 10px; height: 10px; display: none; }
.task-row-checkbox.checked { background: var(--text-heading); border-color: var(--text-heading); color: var(--bg-card); }
.task-row-checkbox.checked svg { display: block; }
.task-row-num {
    flex: none;
    width: 16px;
    text-align: right;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 11px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--icon-color);
    user-select: none;
}
.task-row-dot {
    width: 10px;
    height: 10px;
    border-radius: 99px;
    flex: none;
}
/* The dot and the badge are buttons (the dot opens the colour chooser, the
   badge the priority chooser): a ring in their own colour under the pointer
   says so (Fotis, 2026-11-03); a locked chart, which changes neither, and
   the published page's list (their roles are img there) show none. */
.task-row-dot[role="button"] { cursor: pointer; transition: box-shadow 0.12s; }
.task-row-dot[role="button"]:hover { box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 3.5px var(--dot); }
.task-row-priority[role="button"] { transition: box-shadow 0.12s; }
.task-row-priority[role="button"]:hover { box-shadow: 0 0 0 1.5px currentColor; }
body.chart-locked .task-row-dot[role="button"],
body.chart-locked .task-row-priority[role="button"] { cursor: default; box-shadow: none; }
/* The type glyph (a bar, a diamond, a bracket: task, milestone, summary),
   in the place of the handoff's sub-task diamond, a button that opens the
   type chooser; it darkens under the pointer. */
.task-row-type { flex: none; width: 14px; height: 14px; display: grid; place-items: center; color: var(--text-muted); }
.task-row-type svg { width: 14px; height: 14px; display: block; }
.task-row-type[role="button"] { cursor: pointer; transition: color 0.12s; }
.task-row-type[role="button"]:hover { color: var(--text-heading); }
body.chart-locked .task-row-type[role="button"] { cursor: default; color: var(--text-muted); }
/* The list's colour and type choosers: the card's popovers (.tc-pop-color
   with its swatches and custom row, .tc-pop-list with its items), floating
   at the dot or the glyph. The card tokens they read are set here, the
   handoff's values and the dark theme's nearest. */
.ob-pop {
    position: fixed;
    top: auto;
    left: auto;
    z-index: 1500;
    animation: popIn 0.12s ease-out;
    --tc-subtle: #94a3b8;
    --tc-hover: #f1f5f9;
    --tc-surface: #f8fafc;
    --tc-pop-shadow: 0 2px 6px rgba(15, 23, 42, 0.06), 0 12px 32px rgba(15, 23, 42, 0.12);
}
[data-theme$="-dark"] .ob-pop {
    --tc-subtle: var(--text-muted);
    --tc-hover: var(--bg-hover);
    --tc-surface: color-mix(in srgb, var(--bg-input) 45%, var(--bg-card));
    --tc-pop-shadow: 0 2px 6px rgba(0, 0, 0, 0.4), 0 12px 32px rgba(0, 0, 0, 0.5);
}
.task-row-label {
    flex: 1 1 0%;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
}
.task-row-name {
    display: block;
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    appearance: none;
    font: inherit;
    font-size: 13px;
    font-weight: 400;
    text-align: left;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: text;
}
.task-row-pencil {
    width: 12px;
    height: 12px;
    flex: none;
    color: var(--icon-color);
    opacity: 0;
    transition: opacity 0.15s;
    cursor: pointer;
}
.task-row:hover .task-row-pencil { opacity: 1; }
.task-row-duration {
    flex: none;
    font-size: 11px;
    font-weight: 500;
    color: var(--yaxis-text);
    background: var(--bg-input);
    padding: 1px 7px;
    border-radius: 99px;
    white-space: nowrap;
}
.task-row-dep {
    flex: none;
    display: grid;
    place-items: center;
    padding: 3px 7px;
    border-radius: 99px;
    background: var(--bg-input);
    color: var(--text-muted);
}
.task-row-dep svg { width: 7px; height: 7px; }
.task-row-priority {
    flex: none;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 99px;
    display: grid;
    place-items: center;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: none;
    cursor: pointer;   /* opens the priority chooser for that task (Fotis, 2026-10-24) */
}
/* The inline rename field takes the place of the name and the pencil: 24px
   tall, its margin cancelling its padding so the text does not shift. */
.task-row .task-row-rename {
    flex: 1 1 0%;
    min-width: 0;
    height: 24px;
    box-sizing: border-box;
    margin: 0 -6px;
    padding: 0 6px;
    border: 1px solid var(--text-heading);
    border-radius: 6px;
    background: var(--bg-card);
    outline: none;
    font: inherit;
    font-size: 13px;
    font-weight: 400;
    color: var(--text-heading);
    box-shadow: 0 0 0 3px var(--ob-ring);
}

/* ── Paging gutter ── */
.ob-gutter {
    border-left: 1px solid var(--bg-input);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 0;
}
.ob-page {
    width: 28px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--text-body);
    display: grid;
    place-items: center;
    cursor: pointer;
}
.ob-page:hover { background: var(--bg-input); }
.ob-page[aria-disabled="true"] { color: var(--border-input); }
.ob-page svg { width: 14px; height: 14px; }
.ob-range {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 10.5px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    white-space: nowrap;
}
.ob-range-of {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 10px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--icon-color);
    white-space: nowrap;
}

/* Narrower panes than the design frame. Columns: two once a third column
   would be narrower than about 250px, one once a second would be. The
   action buttons keep their icons and drop their words (the aria-labels
   stay); narrower still, Select all and the rest hint go. */
@container (max-width: 861px) {
    #taskSection .task-list-compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@container (max-width: 620px) {
    .ob-btn > span { display: none; }
    .ob-btn .ob-chev { display: none; }
    .ob-btn { padding: 0 8px; }
    .ob-divider { display: none; }
}
@container (max-width: 595px) {
    #taskSection .task-list-compact { grid-template-columns: minmax(0, 1fr); }
}
@container (max-width: 420px) {
    .ob-btn-all, .ob-hint { display: none; }
}

/* Published page: a read-only list. No hint to tick rows, no selection, no
   rename. */
html.published-view .ob-hint,
html.published-view .ob-head-actions,
html.published-view .task-row-pencil { display: none !important; }
html.published-view .task-row { cursor: default; }
html.published-view .task-row-name { cursor: default; }
html.published-view .task-row-priority { cursor: default; }
/* The chart's priority badges open nothing on a published page. */
html.published-view .priority-badge-chart { cursor: default !important; }
