/* ┌───────────────────────────────────────────────────────────────┐
 * │           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-add-modern)                         │
 * │  1026  │  Task Properties Row                               │
 * │  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 ".props-row" grid (Name/Start-End/Color/
     Priority/Type/Create), fused date-range pill, "Keep type & priority"
     switch, themed dropdown selects, ~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: #d97706;
    --priority-med-bg: #fef3c7;
    --priority-med-border: #d97706;
    --priority-low-text: #16a34a;
    --priority-low-bg: #dcfce7;
    --priority-low-border: #16a34a;
    --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: #86efac;
    --priority-low-bg: #052e16;
    --priority-low-border: #16a34a;
    --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;
    padding: 0 1.5rem;
}

.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));
}

/* ── IMPROVEMENT 1: Compact Header ─────────────────────────────── */
.app-header-compact {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "left center right";
    align-items: center;
    column-gap: 0.75rem;
    row-gap: 0.4rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--border-card);
    margin-bottom: 0;
    position: relative;
    /* The decorative ::before/::after glows are masked to fade out inside
       their own box (see below), so nothing bleeds horizontally any more;
       the clip stays as a guard so nothing in the bar can ever widen the
       page scroll width. overflow-x:clip leaves overflow-y visible, so the
       header dropdowns (lang/theme/bell/account) still drop. */
    overflow-x: clip;
    --hx: -200px;
    --hy: 50%;
    --hdr-tracer: var(--title-accent, #6366f1);
    --hdr-tracer-soft: color-mix(in srgb, var(--title-accent, #6366f1) 50%, transparent);
    transition: --hdr-tracer 0.25s ease, --hdr-tracer-soft 0.25s ease;
}

/* Near the Donate button the tracer picks up the button's red (same
   semantic red in both themes, like #kofiBtn itself). */
.app-header-compact.header-donate-near {
    --hdr-tracer: #ef4444;
    --hdr-tracer-soft: color-mix(in srgb, #ef4444 55%, transparent);
}

/* The tracer's colors live in registered custom properties so the
   green→red flip near the Donate button (JS toggles .header-donate-near)
   animates as a smooth crossfade instead of a hard swap. */
@property --hdr-tracer {
    syntax: '<color>';
    inherits: true;
    initial-value: #6366f1;
}
@property --hdr-tracer-soft {
    syntax: '<color>';
    inherits: true;
    initial-value: transparent;
}

/* Cursor-tracking header glow, retuned 2026-09-04 (Fotis: "make it look
   okay", NOT remove it). The old glare peaked at 18% accent and faded only
   at the sides, so with the cursor parked near the title it slammed flat
   against the viewport's top edge and read as a broken green smear in the
   corner. Now: a gentler wash (11%) that fades on EVERY edge it can hit —
   top included — and longer side fades so corner parks stay soft. */
.app-header-compact::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    opacity: 0;
    transition: opacity 0.3s ease;
    background: radial-gradient(
        circle 200px at var(--hx) var(--hy),
        color-mix(in srgb, var(--title-accent, #6366f1) 11%, transparent) 0%,
        color-mix(in srgb, var(--title-accent, #6366f1) 4%, transparent) 50%,
        transparent 100%
    );
    mask-image:
        linear-gradient(to right, transparent, #000 3rem, #000 calc(100% - 3rem), transparent),
        linear-gradient(to bottom, transparent, #000 10px, #000 50%, transparent);
    mask-composite: intersect;
}

.app-header-compact.header-spotlight::before {
    opacity: 1;
}

/* Bottom border glow — tracks cursor along the bottom edge */
.app-header-compact::after {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 0;
    right: 0;
    height: 3px;
    pointer-events: none;
    z-index: 0;
    opacity: 0;
    transition: opacity 0.3s ease;
    background: radial-gradient(
        ellipse 160px 4px at var(--hx) 50%,
        var(--hdr-tracer) 0%,
        var(--hdr-tracer-soft) 40%,
        transparent 100%
    );
    filter: blur(0.5px);
    /* Same fade at the ends so the tracer never slices at the box edge. */
    mask-image: linear-gradient(to right, transparent, #000 3rem, #000 calc(100% - 3rem), transparent);
}

.app-header-compact.header-spotlight::after {
    opacity: 1;
}

.app-header-compact > * {
    position: relative;
    z-index: 1;
}

[data-theme$="-dark"] .app-header-compact::before {
    background: radial-gradient(
        circle 200px at var(--hx) var(--hy),
        color-mix(in srgb, var(--title-accent, #818cf8) 15%, transparent) 0%,
        color-mix(in srgb, var(--title-accent, #818cf8) 5%, transparent) 50%,
        transparent 100%
    );
}

.header-center-group {
    grid-area: center;
    justify-self: center;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: nowrap;
    z-index: 1;
}

/* The logo is a link to the blog (Fotis, 2026-08-07) — it must LOOK exactly
   like the plain heading did, so the anchor inherits everything. */
.app-title-link {
    color: inherit;
    background: inherit;
    background-clip: inherit;
    -webkit-background-clip: inherit;
    -webkit-text-fill-color: inherit;
    text-decoration: none;
    cursor: pointer;
}
.app-title-compact {
    font-size: 1.25rem;
    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;
    position: relative;
    white-space: nowrap;
}

.title-clock-group {
    grid-area: left;
    justify-self: start;
    min-width: 0;
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
}

.header-clock {
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--text-muted);
    white-space: nowrap;
    letter-spacing: 0.01em;
    font-variant-numeric: tabular-nums;
}

.header-actions {
    grid-area: right;
    justify-self: end;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

/* ── Header density ladder ────────────────────────────────────────
   .app-header-compact is a grid whose centre track is 1fr with overflow-x:clip,
   so when the row runs short the middle cluster is CLIPPED rather than reflowed
   ("Donat…" cut in half). setupTopHeaderDensity() measures the real fit and adds
   these classes one stage at a time: labels off first, then whole groups folded
   behind one icon. Every collapsed form keeps its tooltip, so nothing becomes
   unidentifiable. */
/* Header controls must NOT shrink. Left to flex-shrink, they squeeze their own
   labels into an ellipsis-free clip ("Donat…" cut mid-word) while the row still
   reports as fitting; with flex:none the groups genuinely overflow instead, which
   is both honest and what makes the collision test below detectable. */
.header-center-group > *,
.header-actions > * { flex: none; }

.hdr-icon-alt { display: none; }
.hdr-icon-alt,
.hdr-collapsed-btn svg {
    width: 15px;
    height: 15px;
    flex: none;
}
.hdr-menu-wrap {
    position: relative;
    display: none;
}
.app-header-compact.hdr-compact-feedback #feedbackBtn > span { display: none; }
.app-header-compact.hdr-compact-feedback #feedbackBtn {
    padding: 0.35rem 0.5rem;
    display: inline-flex;
    align-items: center;
}
.app-header-compact.hdr-compact-feedback #feedbackBtn .hdr-icon-alt { display: block; }

.app-header-compact.hdr-compact-kofi #kofiBtn > span,
.app-header-compact.hdr-compact-kofi #kofiBtn .hdr-icon-main { display: none; }
.app-header-compact.hdr-compact-kofi #kofiBtn .hdr-icon-alt { display: block; }
.app-header-compact.hdr-compact-kofi #kofiBtn { padding: 0.4rem 0.5rem; }

.app-header-compact.hdr-compact-library #templatesBtn,
.app-header-compact.hdr-compact-library #guidesBtn { display: none; }
.app-header-compact.hdr-compact-library #libraryWrap { display: inline-flex; }

.app-header-compact.hdr-compact-files #loadFileBtn,
.app-header-compact.hdr-compact-files #exportBtn { display: none; }
.app-header-compact.hdr-compact-files #filesWrap { display: inline-flex; }

.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 { text-decoration: none; }

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

/* Export button spacing (replaces old .export-wrapper margin) */
#exportBtn { margin-right: 1.4rem; }

/* Donate: inverted on purpose — the heart's red fills the pill and the heart
   and label turn white, so the one button that asks for support is the one
   that stands out. Same colours in both themes (the red is semantic, not a
   surface token); the compact header keeps the fill around the lone heart. */
#kofiBtn {
    background: #ef4444;
    border-color: #ef4444;
    color: #ffffff;
    font-weight: 600;
    box-shadow: 0 2px 8px color-mix(in srgb, #ef4444 35%, transparent);
    animation: donatePulse 3s ease-in-out infinite;
}
#kofiBtn:hover {
    background: #dc2626;
    border-color: #dc2626;
    color: #ffffff;
    box-shadow: 0 4px 14px color-mix(in srgb, #ef4444 50%, transparent);
    animation: none;
}
#kofiBtn:focus-visible { outline: 2px solid #ef4444; outline-offset: 2px; }

/* Same breathing glow as the feedback pill, in the donate red. */
@keyframes donatePulse {
    0%, 100% { box-shadow: 0 2px 8px color-mix(in srgb, #ef4444 35%, transparent); }
    50% { box-shadow: 0 2px 14px color-mix(in srgb, #ef4444 55%, transparent); }
}

/* ── Feedback Button ───────────────────────────────────────────── */
.btn-feedback {
    padding: 0.35rem 0.85rem;
    background: linear-gradient(135deg, var(--accent, #6366f1), var(--accent-dark, #4f46e5));
    color: var(--btn-add-text, white);
    font-size: 0.7rem;
    font-weight: 600;
    border-radius: 9999px;
    border: none;
    cursor: pointer;
    transition: all 0.2s;
    white-space: nowrap;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--accent, #6366f1) 35%, transparent);
    animation: feedbackPulse 3s ease-in-out infinite;
}

.btn-feedback:hover {
    transform: scale(1.05);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--accent, #6366f1) 50%, transparent);
    animation: none;
}

.btn-feedback:active {
    transform: scale(0.97);
}

@keyframes feedbackPulse {
    0%, 100% { box-shadow: 0 2px 8px color-mix(in srgb, var(--accent, #6366f1) 35%, transparent); }
    50% { box-shadow: 0 2px 14px color-mix(in srgb, var(--accent, #6366f1) 55%, transparent); }
}


/* ── Language Selector ──────────────────────────────────────────── */
.lang-wrapper {
    position: relative;
}

.lang-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(--toggle-bg);
    color: var(--toggle-icon);
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    transition: all 0.15s;
}

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

.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;
    margin-right: 1.4rem;
}

.changelog-bell {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    border: 1px solid var(--border-input);
    cursor: pointer;
    background: var(--toggle-bg);
    color: var(--toggle-icon);
    transition: all 0.15s;
    position: relative;
    flex-shrink: 0;
}

.changelog-bell:hover {
    border-color: var(--border-focus);
    color: var(--text-heading);
}

.changelog-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 14px;
    height: 14px;
    padding: 0 3px;
    background: var(--changelog-badge-bg);
    color: var(--changelog-badge-text);
    border-radius: 999px;
    font-size: 0.55rem;
    font-weight: 800;
    line-height: 14px;
    text-align: center;
    border: 1.5px solid var(--bg-body);
    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-refresh-warning {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.75rem;
    background: var(--priority-med-bg);
    color: var(--priority-med-text);
    border-bottom: 1px solid var(--border-card);
    font-size: 0.65rem;
    font-weight: 500;
}

.changelog-refresh-warning kbd {
    background: var(--bg-input);
    color: var(--text-body);
    border: 1px solid var(--border-input);
    border-radius: 3px;
    padding: 0.1rem 0.3rem;
    font-size: 0.6rem;
    font-family: inherit;
    white-space: nowrap;
    flex-shrink: 0;
}

.changelog-refresh-warning.hidden {
    display: 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: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 9999px;
    border: 1px solid var(--border-input);
    cursor: pointer;
    background: var(--toggle-bg);
    color: var(--toggle-icon);
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

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

.panel-toggle svg {
    width: 14px;
    height: 14px;
    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;
    align-items: stretch;
    gap: 1rem;
}
.composer-pane {
    flex: 0 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: 1;
    min-width: 0;
    position: relative;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 12px;
    padding: 0.6rem 0.95rem 0.65rem;
}

/* Pinned to the pane's padding box: the browser pane contributes NO height
   to the composer row (the composer alone sets it), so the bulk-action bar
   sliding open can never grow this card and leave dead space under the
   composer — the task list flexes smaller instead and scrolls. */
.browser-pane-inner {
    position: absolute;
    inset: 0.6rem 0.95rem 0.65rem;
    display: flex;
    /* Row: the bulk-action stack slides in on the LEFT (a top bar ate the
       list's scarce height; width is plentiful), the list fills the rest. */
    flex-direction: row;
    min-height: 0;
}
#taskSection .task-list-compact {
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    max-height: none;
}

/* 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);
}

.custom-priority-field {
    height: 2.25rem;
    width: 6rem;
    padding: 0.3rem 0.6rem;
    border-radius: 9999px;
    border: 1px solid #d946ef;
    background: var(--bg-input);
    color: var(--text-input);
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    outline: none;
    text-align: center;
}

.custom-priority-field::placeholder {
    text-transform: none;
    font-weight: 400;
    color: var(--text-placeholder);
}

.priority-custom-chip span {
    color: var(--chip-text);
    background: var(--chip-bg);
}
.priority-custom-chip:has(:checked) span {
    color: #d946ef;
    background: rgba(217, 70, 239, 0.12);
    border-color: #d946ef;
}
[data-theme$="-dark"] .priority-custom-chip:has(:checked) span {
    color: #f0abfc;
    background: rgba(217, 70, 239, 0.15);
    border-color: rgba(217, 70, 239, 0.5);
}

/* ── 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 Properties row ───────────────────────────────────────── */
/* v4.4.0: 2-row CSS grid. Name spans cols 1–2 row 1; Start/End sit in cols 1
   and 2 row 2; Color and Create span both rows. Priority (row 1) and Type
   (row 2) share col 4. */
.props-row {
    display: grid;
    grid-template-columns:
        minmax(5rem, 7rem)
        minmax(5rem, 7rem)
        auto
        auto
        auto;
    grid-template-rows: auto auto;
    grid-template-areas:
        "name  name  color priority add"
        "dates dates color type     add";
    column-gap: 0.6rem;
    row-gap: 0.45rem;
    width: 100%;
    align-items: center;
    justify-content: start;
}

/* The divider folding ladder (Fotis, 2026-08-07). RULE: the slack under the
   composer's controls must never grow taller than one control row, so the
   grid refolds in one-row steps — and each stage class is applied by the
   drag handler at that layout's OWN natural height, never earlier, so
   engaging a stage can never move the row (see the composer-row height drag
   in app.js). Heights climb ~124 → 168 → 214 → 258 → 300 while the composer
   narrows, handing the freed width to the task browser at every step.

   Stage 1 (.composer-fold1): the Create row (button + keep-switch, now side
   by side) drops under the compact grid. */
.composer-split.composer-fold1 .props-row {
    grid-template-columns: minmax(5rem, 7rem) minmax(5rem, 7rem) auto auto;
    grid-template-rows: repeat(3, auto);
    grid-template-areas:
        "name  name  color priority"
        "dates dates color type"
        "add   add   add   add";
    align-content: start;
}

/* Stage 2 (.composer-fold2): Priority and Type tuck under Name and the
   dates; the colour pill and the Create column keep their places. */
.composer-split.composer-fold2 .props-row {
    grid-template-columns: minmax(5rem, 7rem) minmax(5rem, 7rem) auto auto;
    grid-template-rows: repeat(4, auto);
    grid-template-areas:
        "name     name     color add"
        "dates    dates    color add"
        "priority priority color add"
        "type     type     color add";
    align-content: start;
}

/* Stage 3 (.composer-fold3): stage 2 with the Create row dropped under. */
.composer-split.composer-fold3 .props-row {
    grid-template-columns: minmax(5rem, 7rem) minmax(5rem, 7rem) auto;
    grid-template-rows: repeat(5, auto);
    grid-template-areas:
        "name     name     color"
        "dates    dates    color"
        "priority priority color"
        "type     type     color"
        "add      add      add";
    align-content: start;
}

/* Stage 4 (.composer-fold4): the full single-column stack — fields first,
   colour + the Create row at the bottom. The drag CAPS at this layout's
   natural height; past it there is nothing further to rearrange. */
.composer-split.composer-fold4 .props-row {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: repeat(6, auto);
    grid-template-areas:
        "name     name"
        "dates    dates"
        "priority priority"
        "type     type"
        "color    color"
        "add      add";
    align-content: start;
}
.composer-split.composer-fold4 .prop-color { align-self: center; }
/* The desktop swatch is a VERTICAL pill (width 2rem, height auto, flex 1) —
   in the stacked grid its row is auto-height, so it collapsed to nothing and
   the colour control vanished. Same fixed horizontal chip the narrow media
   query uses. */
.composer-split.composer-fold4 .props-row .prop-color .color-swatch {
    width: 100%;
    max-width: 4rem;
    height: 2.25rem;
    flex: 0 0 auto;
    margin: 0 auto;
    align-self: center;
}

/* Whenever the Create area is a full-width ROW (stages 1/3/4), the button
   and the keep-switch sit side by side, centred; the &nbsp; label that
   aligns the column form is dead weight here. */
.composer-split.composer-fold1 .prop-add,
.composer-split.composer-fold3 .prop-add,
.composer-split.composer-fold4 .prop-add {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 0.9rem;
    align-self: stretch;
}
.composer-split.composer-fold1 .prop-add .label,
.composer-split.composer-fold3 .prop-add .label,
.composer-split.composer-fold4 .prop-add .label {
    display: none;
}

.prop-field {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    min-width: 0;
}

/* Visually hide field labels (TASK/START/END/COLOR/PRIORITY/TYPE) and the
   "Task Properties" section heading — placeholders + chip text are enough.
   Kept in DOM for screen readers. */
.prop-field .label,
.section-label[data-i18n="taskProperties"] {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.prop-field .input,
.prop-field .input-color {
    width: 100%;
}

/* Chip groups inside prop-priority / prop-type must stay on one line —
   never split "Low" from Med/High/Custom, never orphan Task/Milestone/Group. */
.prop-priority > div,
.prop-type > div {
    flex-wrap: nowrap;
    white-space: nowrap;
}

.prop-name      { grid-area: name;     }
.prop-daterange { grid-area: dates;    }
.prop-color     { grid-area: color; align-self: stretch; align-items: center; justify-content: center; }
.prop-priority  { grid-area: priority; min-width: 11rem; }
.prop-type      { grid-area: type; min-width: 11rem; }
.prop-add       { grid-area: add; align-self: center; }

/* ── composer polish — fused date-range control ──────────
   Start + End live inside ONE pill (calendar glyph · start → end) so the
   range reads as a single value. The two original inputs keep their ids /
   .date-trigger wiring; they just render as flat zones inside the shell. */
.prop-daterange {
    flex-direction: row;
    align-items: center;
    gap: 0;
    height: 2.25rem;
    padding: 0 0.2rem 0 0.65rem;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    border-radius: 999px;
    transition: border-color 0.15s, box-shadow 0.15s, background 0.2s;
}

.prop-daterange:hover {
    border-color: var(--border-focus);
}

.prop-daterange:focus-within {
    border-color: var(--border-focus);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 16%, transparent);
}

.daterange-ico {
    display: inline-flex;
    width: 0.85rem;
    height: 0.85rem;
    flex-shrink: 0;
    color: var(--text-placeholder);
    margin-right: 0.15rem;
    transition: color 0.15s;
}

.prop-daterange:focus-within .daterange-ico {
    color: var(--border-focus);
}

.daterange-ico svg { width: 100%; height: 100%; }

.prop-daterange .prop-date {
    display: flex;
    flex: 1 1 0;
    min-width: 0;
}

/* The two date fields become quiet zones: transparent until hovered,
   an inner pill wash on hover, no double borders. */
.props-row .prop-daterange .input {
    height: 1.85rem;
    background: transparent;
    border: none;
    border-radius: 999px;
    padding: 0 0.4rem;
    font-size: 0.8rem;
    transition: background 0.15s, color 0.15s;
}

.props-row .prop-daterange .input:hover {
    background: var(--bg-hover);
}

.props-row .prop-daterange .input:focus {
    background: var(--bg-hover);
    box-shadow: none;
    border: none;
}

.daterange-sep {
    display: inline-flex;
    width: 0.8rem;
    height: 0.8rem;
    flex-shrink: 0;
    color: var(--text-faint);
    margin: 0 0.05rem;
}

.daterange-sep svg {
    width: 100%;
    height: 100%;
    transition: transform 0.18s ease, color 0.18s;
}

.prop-daterange:hover .daterange-sep svg {
    transform: translateX(2px);
    color: var(--border-focus);
}

/* Milestones are single-date events — collapse the End zone and the arrow;
   Start stretches across the whole pill. (Dialog rule lives below.) */
.props-row.is-milestone .prop-end,
.props-row.is-milestone .daterange-sep { display: none; }

/* Name field joins the same focus language as the range + selects. */
.props-row .prop-name .input:hover {
    border-color: var(--border-focus);
}

.props-row .prop-name .input:focus {
    border-color: var(--border-focus);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 16%, transparent);
}

/* Coarse pointers: let the fused pill grow with the 42px+ touch inputs, and
   hand the swatch back to the labelled 38px chip the touch layout expects
   (these would otherwise lose to the higher-specificity well rules above). */
@media (pointer: coarse) {
    .prop-daterange { height: auto; min-height: 2.75rem; }
    .props-row .prop-daterange .input { height: auto; }
    .props-row .prop-color .color-swatch {
        width: 38px;
        height: 38px;
        flex: none;
        margin: 0;
        border-radius: 10px;
        align-self: flex-start;
    }
}
#taskDialog.is-milestone #dlgEndField { display: none; }

/* #91 — "Keep type & priority" pill switch, tucked under the Create Task
   button. When on, Type + Priority carry to the next Create Task instead of
   resetting; the explanation lives in the hover tooltip. */
.keep-switch {
    display: inline-flex;
    align-items: center;
    align-self: center;
    gap: 0.45rem;
    margin-top: 0.5rem;
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}
.keep-switch input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}
.keep-switch-track {
    position: relative;
    flex-shrink: 0;
    width: 2rem;
    height: 1.125rem;
    border-radius: 999px;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.15s;
}
.keep-switch-track::after {
    content: '';
    position: absolute;
    top: calc(50% - 0.375rem);
    left: 0.125rem;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 50%;
    background: var(--text-faint);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
    transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.18s ease;
}
.keep-switch:hover .keep-switch-track {
    border-color: var(--border-focus);
}
.keep-switch input:focus-visible ~ .keep-switch-track {
    border-color: var(--border-focus);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 16%, transparent);
}
.keep-switch input:checked ~ .keep-switch-track {
    background: var(--btn-add-bg);
    border-color: var(--btn-add-bg);
}
.keep-switch input:checked ~ .keep-switch-track::after {
    background: var(--btn-add-text);
    transform: translateX(0.875rem);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
}
.keep-switch-text {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-muted);
    white-space: nowrap;
    transition: color 0.15s;
}
.keep-switch:hover .keep-switch-text,
.keep-switch.is-on .keep-switch-text {
    color: var(--text-heading);
}
/* ── v4.4.0 dropdown selector for priority + type ─────────────── */
.prop-select {
    position: relative;
    width: 100%;
}

.prop-select-trigger {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    width: 100%;
    height: 2.25rem;
    padding: 0 0.6rem;
    background: var(--bg-input);
    border: 1px solid var(--border-input);
    border-radius: 0.55rem;
    cursor: pointer;
    color: var(--text-heading);
    font-size: 0.8rem;
    font-weight: 500;
    text-align: left;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.prop-select-trigger:hover,
.prop-select-trigger[aria-expanded="true"] {
    border-color: var(--border-focus);
}

.prop-select-trigger[aria-expanded="true"] {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 16%, transparent);
}

.prop-select-trigger:focus-visible {
    outline: none;
    border-color: var(--border-focus);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus) 16%, transparent);
}

.prop-select-trigger:hover .prop-select-caret {
    opacity: 0.85;
}

.prop-select-trigger.is-placeholder .prop-select-label {
    color: var(--text-placeholder);
    font-weight: 400;
}

.prop-select-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.prop-select-caret {
    width: 0.85rem;
    height: 0.85rem;
    flex-shrink: 0;
    opacity: 0.55;
    transition: transform 0.15s;
}

.prop-select.open .prop-select-caret {
    transform: rotate(180deg);
}

.prop-select-menu {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    right: 0;
    z-index: 200;
    display: none;
    flex-direction: column;
    gap: 0.15rem;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: 0.6rem;
    padding: 0.3rem;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
    min-width: 100%;
}

.prop-select.open .prop-select-menu {
    display: flex;
}

.prop-select-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.55rem;
    border-radius: 0.45rem;
    cursor: pointer;
    user-select: none;
    color: var(--text-heading);
    font-size: 0.8rem;
    transition: background 0.1s;
}

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

.prop-select-option:has(:checked) {
    background: var(--bg-hover);
    font-weight: 600;
}

.prop-select-dot {
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    flex-shrink: 0;
    border: 1px solid var(--border-input);
    background: transparent;
}

.prop-select-dot-empty {
    border-style: dashed;
}

.prop-select-dot-low    { background: var(--priority-low-border);  border-color: var(--priority-low-border);  }
.prop-select-dot-med    { background: var(--priority-med-border);  border-color: var(--priority-med-border);  }
.prop-select-dot-high   { background: var(--priority-high-border); border-color: var(--priority-high-border); }
.prop-select-dot-custom { background: var(--accent, #6366f1);                     border-color: var(--accent, #6366f1);                     }

.prop-select-icon-slot {
    width: 0.9rem;
    height: 0.9rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--text-placeholder);
}

.prop-select-trigger:not(.is-placeholder) .prop-select-icon-slot {
    color: var(--text-heading);
}

.prop-select-icon-slot svg {
    width: 100%;
    height: 100%;
}

.prop-select-menu .custom-priority-field {
    margin-top: 0.25rem;
    width: 100%;
}

/* Color — the whole capsule IS the chosen color (JS keeps painting the
   inline background). A faint inset ring + top sheen keep pale colors
   readable against the card without adding chrome. */
.props-row .prop-color .color-swatch {
    position: relative;
    width: 2rem;
    height: auto;
    flex: 1 1 auto;
    align-self: center;
    border-radius: 999px;
    border: none;
    box-shadow:
        inset 0 0 0 1px rgba(0, 0, 0, 0.12),
        inset 0 1px 0 rgba(255, 255, 255, 0.22),
        0 1px 2px rgba(0, 0, 0, 0.10);
    transition: box-shadow 0.15s, filter 0.15s;
}

.props-row .prop-color .color-swatch:hover {
    filter: brightness(1.07) saturate(1.05);
    transform: none;
    box-shadow:
        inset 0 0 0 1px rgba(0, 0, 0, 0.12),
        inset 0 1px 0 rgba(255, 255, 255, 0.22),
        0 0 0 3px color-mix(in srgb, var(--border-focus) 18%, transparent),
        0 1px 2px rgba(0, 0, 0, 0.12);
}

/* Create button: slim, centered between the two input rows. */
.prop-add .btn-add-modern {
    height: 2.5rem;
    align-self: center;
}

.btn-add-modern {
    position: relative;
    display: inline-flex;
    align-items: center;
    align-self: center;
    flex: 0 0 auto;
    width: auto;
    max-width: 14rem;
    gap: 0.55rem;
    height: 2.25rem;
    padding: 0.25rem 0.95rem 0.25rem 0.3rem;
    background: var(--btn-add-bg);
    color: var(--btn-add-text);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    border-radius: 9999px;
    border: none;
    cursor: pointer;
    transition: filter 0.03s ease-out, transform 0.02s ease-out, box-shadow 0.05s ease-out;
    white-space: nowrap;
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.07),
        0 2px 6px color-mix(in srgb, var(--btn-add-bg) 28%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.18);
    overflow: hidden;
}

.btn-add-modern::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0) 55%);
    pointer-events: none;
    border-radius: inherit;
}

.btn-add-modern .btn-add-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.65rem;
    height: 1.65rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.96);
    color: var(--btn-add-bg);
    flex-shrink: 0;
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.12),
        inset 0 0 0 1px rgba(255, 255, 255, 0.65);
    z-index: 1;
    transform-origin: center;
}

.btn-add-modern .btn-add-icon svg {
    width: 0.95rem;
    height: 0.95rem;
    stroke-width: 2.75;
}

.btn-add-modern .btn-add-label {
    position: relative;
    z-index: 1;
}

.btn-add-modern:hover {
    filter: brightness(1.06);
    transform: translateY(-1px);
    box-shadow:
        0 1px 3px rgba(0, 0, 0, 0.09),
        0 4px 10px color-mix(in srgb, var(--btn-add-bg) 38%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.22);
}

.btn-add-modern:active {
    transform: translateY(0);
    filter: brightness(0.97);
    box-shadow:
        0 1px 1px rgba(0, 0, 0, 0.07),
        0 1px 4px color-mix(in srgb, var(--btn-add-bg) 22%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.btn-add-modern:focus-visible {
    outline: 2px solid var(--btn-add-bg);
    outline-offset: 2px;
}

.btn-add-modern.pop {
    animation: addConfirm 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.btn-add-modern.pop .btn-add-icon {
    animation: iconPop 0.22s cubic-bezier(0.34, 1.6, 0.64, 1);
}

@keyframes addConfirm {
    0% {
        box-shadow:
            0 1px 2px rgba(0, 0, 0, 0.07),
            0 2px 6px color-mix(in srgb, var(--btn-add-bg) 28%, transparent),
            inset 0 1px 0 rgba(255, 255, 255, 0.18);
    }
    45% {
        box-shadow:
            0 1px 2px rgba(0, 0, 0, 0.07),
            0 0 0 5px color-mix(in srgb, var(--btn-add-bg) 24%, transparent),
            0 4px 14px color-mix(in srgb, var(--btn-add-bg) 44%, transparent),
            inset 0 1px 0 rgba(255, 255, 255, 0.28);
    }
    100% {
        box-shadow:
            0 1px 2px rgba(0, 0, 0, 0.07),
            0 2px 6px color-mix(in srgb, var(--btn-add-bg) 28%, transparent),
            inset 0 1px 0 rgba(255, 255, 255, 0.18);
    }
}

@keyframes iconPop {
    0%   { transform: scale(1) rotate(0deg); }
    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);
}

/* ── IMPROVEMENT 5: Compact Object Browser List ────────────────── */
.task-list-compact {
    /* Grid, row-major: tasks fill LEFT→RIGHT and a partial last row stays
       left-aligned under the columns above (Fotis, 2026-08-06 — the old
       centered flex-wrap folded the last row to the middle, which read as
       misalignment, and its fixed-basis columns wasted both side gutters).
       auto-fill + 1fr spends the pane's whole width, and when the
       bulk-action stack narrows the pane the column count recomputes on
       the same rule. */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
    column-gap: 0.35rem;
    row-gap: 2px;
    align-content: start;
    /* 3 rows max in multi-column. Past that, scroll instead of expanding
       the toolbar panel. */
    max-height: 5.5rem;
    overflow-y: auto;
    scrollbar-width: thin;
    width: 100%;
    max-width: 100%;
}
.task-list-compact > .task-row {
    /* Grid items fill their track; min-width 0 lets long names ellipsise
       instead of forcing the track wider. */
    min-width: 0;
}
.task-list-compact > .ob-empty {
    /* "No objects" spans every track so its text-align centres on the pane. */
    grid-column: 1 / -1;
}

.task-list-compact.single-column {
    /* 6 rows max in single-column = 6 tasks visible. */
    max-height: 11rem;
    grid-template-columns: 1fr;
}

.task-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.25rem 0.5rem;
    border-radius: 0.35rem;
    transition: background 0.1s;
    cursor: default;
    min-width: 0;
    min-height: 1.6rem; /* pin: inline rename / re-render can't change row height */
    overflow: hidden;
}

/* Hover / selection on a TASK row is NEUTRAL GREY, not the emerald accent
   (Fotis, 2026-08-04): in the object browser colour carries one meaning only —
   group identity — so a transient state on a plain task must not borrow it.
   Mixed from --text-body rather than a fixed grey so it lands correctly in every
   theme family and in high contrast without needing a token per palette:
   --text-body is dark on light themes and light on dark ones, so a small
   percentage is always a subtle wash of the right polarity.
   Group rows opt back into the accent further down. */
.task-row:hover {
    background: color-mix(in srgb, var(--text-body) 8%, transparent);
}

/* Group membership tint: rows inside a group carry a faint wash of that
   group's colour (--group-tint, set per row by renderTaskList) plus a slim
   edge, so the cluster reads as one block. Selection styles still win. */
.task-row.in-group:not(.selected) {
    background: color-mix(in srgb, var(--group-tint) 10%, transparent);
    box-shadow: inset 2px 0 0 color-mix(in srgb, var(--group-tint) 45%, transparent);
}

/* Hovering a group MEMBER also goes neutral grey (Fotis, 2026-08-04) — this rule
   out-specifies the plain .task-row:hover above, so without it a member kept a
   coloured hover while an ungrouped task went grey. The group-coloured left edge
   from the resting rule above still shows through (only `background` is
   overridden here), so membership is not lost while the pointer is over it. */
.task-row.in-group:not(.selected):hover {
    background: color-mix(in srgb, var(--text-body) 8%, transparent);
}

.task-row-num {
    flex-shrink: 0;
    min-width: 1.4rem;
    padding-right: 0.25rem;
    font-size: 0.65rem;
    font-weight: 600;
    color: var(--text-faint);
    text-align: right;
    font-variant-numeric: tabular-nums;
    border-right: 1px solid var(--border-card);
    margin-right: 0.15rem;
    user-select: none;
}

.task-row-dot {
    width: 0.65rem;
    height: 0.65rem;
    border-radius: 50%;
    flex-shrink: 0;
    cursor: pointer;
    transition: transform 0.1s, box-shadow 0.1s;
}

.task-row-dot:hover {
    transform: scale(1.3);
    box-shadow: 0 0 0 2px var(--border-focus);
}

.task-row-name {
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--text-body);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex: 0 1 auto;
    cursor: pointer;
    border-bottom: 1px dashed transparent;
    transition: border-color 0.1s;
}

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

.task-row-duration {
    font-size: 0.65rem;
    font-weight: 600;
    color: var(--text-faint);
    white-space: nowrap;
    cursor: pointer;
    padding: 0.08rem 0.4rem;
    border-radius: 9999px;
    background: rgba(100, 116, 139, 0.08);
    transition: background 0.1s, color 0.1s;
    flex-shrink: 0;
}

.task-row-duration:hover {
    background: rgba(100, 116, 139, 0.16);
    color: var(--text-muted);
}

.task-row-priority {
    font-size: 0.5rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.08rem 0.3rem;
    border-radius: 9999px;
    cursor: pointer;
    transition: opacity 0.1s;
    flex-shrink: 0;
}

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

.task-row-delete {
    width: 0.9rem;
    height: 0.9rem;
    border-radius: 50%;
    background: var(--delete-hover-bg);
    color: var(--delete-hover-color);
    border: none;
    font-size: 0.6rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: transform 0.1s, opacity 0.1s;
    line-height: 1;
    flex-shrink: 0;
    opacity: 0;
    pointer-events: none;
}

.task-row:hover .task-row-delete {
    opacity: 1;
    pointer-events: auto;
}

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

/* Checkbox: always in flow (reserves space), invisible until hover */
.task-row-checkbox {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0.2rem;
    border: 1.5px solid var(--border-input);
    background: var(--bg-card);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.5rem;
    color: transparent;
    transition: opacity 0.1s, background 0.1s, border-color 0.1s;
    flex-shrink: 0;
    opacity: 0;
    pointer-events: none;
}

.task-row:hover .task-row-checkbox,
.task-row-checkbox.always-show {
    opacity: 1;
    pointer-events: auto;
}

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

.task-row.selected {
    background: color-mix(in srgb, var(--text-body) 11%, transparent);
    box-shadow: inset 3px 0 0 color-mix(in srgb, var(--text-body) 42%, transparent);
}

.task-row.selected:hover {
    background: color-mix(in srgb, var(--text-body) 16%, transparent);
}

/* A GROUP row keeps the accent treatment it has always had — colour is the
   group's language. Higher specificity than the neutral rules above, so the
   order of these blocks is not load-bearing. */
.task-row.is-group:hover {
    background: var(--bg-hover);
}

.task-row.is-group.selected {
    background: color-mix(in srgb, var(--border-focus) 12%, transparent);
    box-shadow: inset 3px 0 0 var(--border-focus);
}

.task-row.is-group.selected:hover {
    background: color-mix(in srgb, var(--border-focus) 18%, transparent);
}

/* 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: #d946ef; background: rgba(217, 70, 239, 0.12); border: 1px solid rgba(217, 70, 239, 0.3); }
[data-theme$="-dark"] .task-priority-custom { color: #f0abfc; background: rgba(217, 70, 239, 0.15); border: 1px solid rgba(217, 70, 239, 0.3); }

/* ── Bulk actions ──────────────────────────────────────────────── */
/* Vertical stack on the LEFT of the object browser (Fotis: a top bar made
   the browser too slim — height is scarce there, width is not). Same reveal
   language as .my-charts-bulkbar, rotated: keep display and animate
   max-WIDTH + fade + a small slide-in, flipping visibility at the edges.
   Lives inside .browser-pane-inner (fixed-height row), so the animated
   width is absorbed by the flexing task list — the pane, and the composer
   card stretched to match it, never change height.
   flex: none on the stack AND its buttons: in a fixed box next to a
   content-sized list, default flex-shrink crushed the bar to a sliver. */
.bulk-actions {
    display: flex;
    flex: none;
    flex-direction: column;
    justify-content: center;
    gap: 0.3rem;
    margin-right: 0.6rem;
    max-width: 13rem;
    overflow: hidden;
    visibility: visible;
    opacity: 1;
    transform: translateX(0);
    transition:
        max-width 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-right 0.28s cubic-bezier(0.2, 0.7, 0.3, 1),
        visibility 0s linear 0s;
}

/* Tight line-height: three stacked buttons must fit the composer-defined
   box (~81px); nowrap so labels slide in whole instead of re-wrapping. */
.bulk-actions .bulk-btn {
    flex: none;
    white-space: nowrap;
    line-height: 1;
}

.bulk-actions.hidden {
    display: flex;
    max-width: 0;
    opacity: 0;
    transform: translateX(-6px);
    margin-right: 0;
    pointer-events: none;
    visibility: hidden;
    transition:
        max-width 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-right 0.24s cubic-bezier(0.4, 0, 0.7, 1),
        visibility 0s linear 0.24s;
}

@media (prefers-reduced-motion: reduce) {
    .bulk-actions,
    .bulk-actions.hidden { transition: none; }
}
[data-a11y-motion="reduce"] .bulk-actions,
[data-a11y-motion="reduce"] .bulk-actions.hidden { transition: none; }

.bulk-btn {
    font-size: 0.7rem;
    font-weight: 600;
    padding: 0.3rem 0.7rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border-input);
    cursor: pointer;
    transition: all 0.1s;
    background: var(--bg-input);
    color: var(--text-body);
}

.bulk-btn-delete {
    background: #dc2626;
    color: white;
    border-color: #dc2626;
}

.bulk-btn-delete:hover {
    background: #b91c1c;
    border-color: #b91c1c;
}

.bulk-btn-priority:hover,
.bulk-btn-group:hover {
    background: var(--bg-hover);
    color: var(--icon-hover-color);
    border-color: var(--border-focus);
}

/* ── 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;
}

.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 {
    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-section { margin-top: 1.2rem; }
.profile-privacy-divider { border: none; border-top: 1px solid var(--border-card); margin: 0 0 1rem; }
.profile-privacy-title { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); margin: 0 0 0.5rem; }
/* data/privacy actions as descriptive rows instead of two bare buttons */
.profile-privacy-rows { display: flex; flex-direction: column; gap: 8px; }
.profile-privacy-row {
    display: flex; align-items: center; gap: 10px;
    width: 100%; text-align: left;
    padding: 9px 12px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--card-bg);
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.profile-privacy-row:hover { border-color: var(--accent-dark); background: var(--hover-bg, rgba(0, 0, 0, 0.03)); }
.profile-privacy-row:disabled { opacity: 0.6; cursor: progress; }
.profile-privacy-ico {
    width: 30px; height: 30px; flex: 0 0 auto;
    border-radius: 9px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-dark);
}
.profile-privacy-ico svg { width: 15px; height: 15px; }
.profile-privacy-texts { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.profile-privacy-row-title { font-size: 0.78rem; font-weight: 600; color: var(--text-heading); }
.profile-privacy-row-sub { font-size: 0.68rem; color: var(--text-muted); line-height: 1.35; }
.profile-privacy-chevron { width: 14px; height: 14px; flex: 0 0 auto; color: var(--text-muted); opacity: 0.7; }
.profile-privacy-row-danger .profile-privacy-ico { background: rgba(220, 38, 38, 0.12); color: #dc2626; }
.profile-privacy-row-danger .profile-privacy-row-title { color: #dc2626; }
.profile-privacy-row-danger:hover { border-color: rgba(220, 38, 38, 0.45); background: rgba(220, 38, 38, 0.04); }
.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 { color: #b91c1c; font-family: 'IBM Plex Mono', monospace; }
.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;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.35);
    color: white;
    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, stacks flat-section but 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;
        padding: 0 0.75rem;
    }

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

    /* Header: stack into 3 rows — title / center cluster / actions — each full-width */
    .app-header-compact {
        grid-template-columns: 1fr;
        grid-template-areas:
            "left"
            "center"
            "right";
        row-gap: 0.4rem;
    }

    .title-clock-group {
        justify-self: center;
        justify-content: center;
    }

    .app-title-compact {
        font-size: 1.15rem;
        white-space: normal;
    }

    .header-clock {
        display: none;
    }

    .header-center-group {
        justify-self: center;
        flex-wrap: wrap;
        justify-content: center;
    }

    .app-header-compact .kofi-btn img,
    a.kofi-btn img {
        height: 28px;
        max-height: 28px;
    }

    .btn-feedback {
        padding: 0.35rem 0.65rem;
        font-size: 0.75rem;
    }

    /* Label hiding used to live here as a fixed breakpoint. It is gone on purpose:
       setupTopHeaderDensity() now folds these groups by MEASURED fit (and merges
       Templates+Guides and Load+Export behind one icon each), which a media query
       cannot do — it fired at 1099px while a wide-but-crowded window was already
       clipping labels mid-word. */
    .header-actions {
        gap: 0.25rem;
        flex-wrap: wrap;
        justify-self: center;
        justify-content: center;
    }

    /* 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%;
    }

    /* Mobile: stack the v4.4.0 grid into single column.
       Order: name → start/end → color → priority → type → Create. */
    .props-row {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: repeat(6, auto);
        grid-template-areas:
            "name     name"
            "dates    dates"
            "color    color"
            "priority priority"
            "type     type"
            "add      add";
    }

    .prop-color    { align-self: center; }
    .prop-add      { align-self: stretch; }
    .props-row .prop-color .color-swatch {
        width: 100%;
        max-width: 4rem;
        height: 2.25rem;
        flex: 0 0 auto;      /* stacked layout: back to a fixed horizontal chip */
        margin: 0 auto;
        align-self: center;
    }
    .prop-add .btn-add-modern {
        width: 100%;
        max-width: none;
        height: 2.5rem;
        justify-content: center;
    }

    /* 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 {
        padding: 0.25rem 1rem;
    }

    /* Header: title+actions on row 1, center cluster (feedback/share/Ko-fi) on row 2 */
    .app-header-compact {
        grid-template-columns: auto 1fr;
        grid-template-areas:
            "left right"
            "center center";
    }
    .header-center-group {
        justify-self: center;
    }
    .header-clock {
        display: none;
    }

    /* Stack flat-section vertically — Object Browser drops below Task Properties */
    .flat-section {
        flex-direction: column;
        gap: 0.5rem !important;
    }
    /* Override inline flex: 0 0 auto on task-properties wrapper so it can shrink */
    .flat-section > div:first-child {
        flex: 1 1 auto !important;
        min-width: 0 !important;
        padding-right: 0 !important;
        width: 100%;
    }
    /* Hide vertical divider when stacked */
    .flat-section > div[style*="width: 6px"] {
        display: none;
    }
    #taskSection {
        padding-left: 0 !important;
        width: 100%;
    }

    /* Tablet: keep the v4.4.0 2-row grid with tighter columns. */
    .props-row {
        grid-template-columns:
            minmax(4.5rem, 6rem)
            minmax(4.5rem, 6rem)
            auto
            auto
            auto;
        column-gap: 0.5rem;
    }
    .btn-add-modern {
        padding: 0 1.25rem;
    }

    /* 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;
    }

    /* Label hiding used to live here as a fixed breakpoint. It is gone on purpose:
       setupTopHeaderDensity() now folds these groups by MEASURED fit (and merges
       Templates+Guides and Load+Export behind one icon each), which a media query
       cannot do — it fired at 1099px while a wide-but-crowded window was already
       clipping labels mid-word. */
    .header-actions {
        gap: 0.25rem;
    }

    /* 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;
    }

    .task-list-compact {
        max-height: 4rem;
    }

    .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;
}

/* ── NEW: Object Browser hierarchy indent ──────────────────── */
.task-row-indent {
    display: inline-block;
    width: 1rem;
    flex-shrink: 0;
}
.task-row-collapse {
    width: 1rem;
    height: 1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.6rem;
    line-height: 1;
    color: var(--text-faint);
    cursor: pointer;
    flex-shrink: 0;
    border-radius: 3px;
    transition: background 0.1s;
}
.task-row-collapse:hover {
    background: var(--bg-hover);
    color: var(--text-heading);
}
.task-row-type-icon {
    flex-shrink: 0;
    width: 0.75rem;
    height: 0.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    line-height: 1;
    color: var(--text-faint);
}
.task-row-progress {
    font-size: 0.6rem;
    color: var(--text-faint);
    min-width: 2rem;
    text-align: right;
    flex-shrink: 0;
}

/* ── 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;
    z-index: 3500;
}
/* THE HEADER POPOVER TRAP: every direct header child is a z-index:1 stacking
   context (`.app-header-compact > *`), so a popover's own 9999 is trapped at
   effective level 1 — and later-DOM elements with any positive z (the Create
   Task button's z:1 internals, the browser divider's 15, .prop-select-menu's
   200) painted OVER open header menus. Promote the child that currently
   contains an OPEN popover above every composer-level z instead. */
.app-header-compact > *:has(#authMenu:not(.hidden)),
.app-header-compact > *:has(#langDropdown:not(.hidden)),
.app-header-compact > *:has(#changelogDropdown:not(.hidden)) {
    z-index: 4000;
}
.share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    font-size: 0;
    font-weight: 500;
    color: var(--title-accent);
    background: var(--bg-card);
    border: 1px solid var(--border-input);
    border-radius: 50%;
    cursor: pointer;
    transition: all 0.2s;
    font-family: inherit;
    box-shadow: none;
}
[data-theme$="-dark"] .share-btn {
    background: var(--bg-input);
    box-shadow: none;
}
.share-btn:hover {
    color: var(--text-heading);
    border-color: var(--border-focus);
    transform: none;
    box-shadow: none;
}
.share-btn svg {
    width: 14px;
    height: 14px;
}
/* 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;
}
.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 modal (TODO #1). Animated feature tour for
   the 6.0 era + full version history. Auto-opens once per version.
   ───────────────────────────────────────────────────────────── */
.whatsnew-modal {
    /* 4:3, at 70% of what the viewport allows (Fotis, 2026-08-24: first "most
       of the window", then "30% smaller"). The width formula already caps by
       height, so aspect-ratio always holds. */
    width: calc(min(100vw - 2rem, (100dvh - 2rem) * 4 / 3) * 0.7);
    max-width: none;
    aspect-ratio: 4 / 3;
    max-height: calc((100dvh - 2rem) * 0.7);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.whatsnew-modal .dialog-header,
.whatsnew-modal .dialog-footer { flex: none; }
.whatsnew-modal .whatsnew-body { flex: 1; min-height: 0; overflow-y: auto; }
/* The body is itself a flex column; once the shell has a fixed height its
   children start SHRINKING before the body scrolls, and the hero (overflow
   hidden for the watermark) collapses first. Scroll, never squash. */
.whatsnew-modal .whatsnew-body > * { flex-shrink: 0; }
/* A 4:3 box on a portrait phone is a short letterbox — below the same 760px
   break the grids already collapse at, fall back to the normal dialog shape. */
@media (max-width: 760px) {
    .whatsnew-modal {
        width: min(880px, calc(100vw - 2rem));
        aspect-ratio: auto;
        max-height: calc(100dvh - 2rem);
        display: block;
        overflow-y: auto;
    }
}
.whatsnew-modal .dialog-footer { justify-content: flex-end; }
.whatsnew-body { padding: 1.15rem 1.4rem 1.4rem; display: flex; flex-direction: column; gap: 1rem; }
.whatsnew-header-title { display: flex; align-items: center; gap: 8px; }
.whatsnew-version-pill {
    padding: 1px 9px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent-dark);
    font-size: 0.66rem; font-weight: 700;
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}

/* Hero banner — branded 6.0, animated glow drift + giant watermark numeral */
.whatsnew-hero {
    position: relative;
    overflow: hidden;
    padding: 1.35rem 1.4rem 1.3rem;
    border-radius: 16px;
    border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
    background:
        radial-gradient(90% 150% at 8% 0%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 60%),
        radial-gradient(80% 140% at 95% 110%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 60%),
        var(--card-bg);
}
.whatsnew-hero::after {
    content: '';
    position: absolute; inset: -40%;
    background: radial-gradient(28% 38% at 50% 50%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 70%);
    animation: wnHeroGlow 9s ease-in-out infinite alternate;
    pointer-events: none;
}
@keyframes wnHeroGlow {
    0%   { transform: translate(-12%, -8%); }
    100% { transform: translate(14%, 10%); }
}
.whatsnew-hero-title {
    margin: 0;
    font-size: 1.65rem; font-weight: 800; letter-spacing: -0.015em;
    color: var(--text-heading);
    position: relative; z-index: 1;
}
.whatsnew-hero-sub {
    margin: 0.35rem 0 0;
    font-size: 0.84rem; line-height: 1.5;
    color: var(--text-muted);
    max-width: 30rem;
    position: relative; z-index: 1;
}
.whatsnew-hero-num {
    position: absolute; right: 18px; bottom: -32px;
    font-size: 7.5rem; font-weight: 900; line-height: 1;
    color: transparent;
    -webkit-text-stroke: 2px color-mix(in srgb, var(--accent) 38%, transparent);
    user-select: none; pointer-events: none;
    animation: wnNumFloat 6s ease-in-out infinite alternate;
}
@keyframes wnNumFloat {
    0%   { transform: translateY(0); }
    100% { transform: translateY(-9px); }
}

/* Layout grids — three aces side by side, six minis beneath */
.whatsnew-grid-hero { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.whatsnew-grid-mini { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.whatsnew-section-label { margin-top: 4px; }
@media (max-width: 760px) {
    .whatsnew-grid-hero { grid-template-columns: 1fr; }
    .whatsnew-grid-mini { grid-template-columns: repeat(2, 1fr); }
    .whatsnew-hero-num { display: none; }
}

/* Hero feature cards — animation strip on top, copy below */
.whatsnew-card {
    display: flex; flex-direction: column;
    border: 1px solid var(--border-card);
    border-radius: 14px;
    background: var(--card-bg);
    overflow: hidden;
    animation: mcpStepIn 0.35s ease backwards;
    transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
.whatsnew-card:hover {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border-card));
    box-shadow: 0 10px 24px -14px rgba(0, 0, 0, 0.35);
}
.whatsnew-grid-hero .whatsnew-card:nth-child(1) { animation-delay: 0.05s; }
.whatsnew-grid-hero .whatsnew-card:nth-child(2) { animation-delay: 0.12s; }
.whatsnew-grid-hero .whatsnew-card:nth-child(3) { animation-delay: 0.19s; }
.whatsnew-card-text { padding: 10px 13px 12px; min-width: 0; }
.whatsnew-card-title { margin: 0; font-size: 0.86rem; font-weight: 700; color: var(--text-heading); }
.whatsnew-card-sub { margin: 0.22rem 0 0; font-size: 0.74rem; line-height: 1.45; color: var(--text-muted); }
.whatsnew-tour-btn {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 10px; padding: 5px 12px;
    font-size: 0.72rem; font-weight: 600; line-height: 1;
    color: var(--accent); background: transparent;
    border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
    border-radius: 999px; cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.whatsnew-tour-btn svg { width: 12px; height: 12px; flex: none; }
.whatsnew-tour-btn:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: var(--accent); }
.whatsnew-tour-btn:disabled { opacity: 0.45; cursor: default; }
.whatsnew-tour-btn:disabled:hover { background: transparent; border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

.whatsnew-visual {
    position: relative;
    width: 100%; height: 112px;
    overflow: hidden;
    border-bottom: 1px solid var(--border-card);
    background:
        linear-gradient(to right, var(--border-card) 1px, transparent 1px) 0 0 / 34px 100%,
        linear-gradient(to bottom, var(--border-card) 1px, transparent 1px) 0 0 / 100% 24px,
        var(--bg-input);
}

/* Mini feature cards — small visual + one-line title, all animated */
.whatsnew-mini {
    display: flex; flex-direction: column;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--card-bg);
    overflow: hidden;
    animation: mcpStepIn 0.32s ease backwards;
    transition: transform 0.18s ease, border-color 0.18s ease;
}
.whatsnew-mini:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 40%, var(--border-card)); }
.whatsnew-grid-mini .whatsnew-mini:nth-child(1) { animation-delay: 0.22s; }
.whatsnew-grid-mini .whatsnew-mini:nth-child(2) { animation-delay: 0.27s; }
.whatsnew-grid-mini .whatsnew-mini:nth-child(3) { animation-delay: 0.32s; }
.whatsnew-grid-mini .whatsnew-mini:nth-child(4) { animation-delay: 0.37s; }
.whatsnew-grid-mini .whatsnew-mini:nth-child(5) { animation-delay: 0.42s; }
.whatsnew-grid-mini .whatsnew-mini:nth-child(6) { animation-delay: 0.47s; }
.whatsnew-mini-visual {
    position: relative;
    height: 64px;
    display: flex; align-items: center; justify-content: center;
    border-bottom: 1px solid var(--border-card);
    background: var(--bg-input);
    overflow: hidden;
}
.whatsnew-mini-title {
    padding: 8px 11px 9px;
    font-size: 0.72rem; font-weight: 600; color: var(--text-heading);
    line-height: 1.3;
}

/* Long-tail chips */
.whatsnew-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.whatsnew-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 11px;
    border-radius: 999px;
    border: 1px solid var(--border-card);
    background: var(--card-bg);
    font-size: 0.72rem; font-weight: 600; color: var(--text-heading);
}
.whatsnew-chip svg { width: 11px; height: 11px; color: var(--accent-dark); flex: 0 0 auto; }

/* Older updates — collapsed accordion + paginated history */
.whatsnew-older { border-top: 1px solid var(--border-card); padding-top: 0.7rem; }
.whatsnew-older-btn {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 13px;
    border-radius: 999px;
    border: 1px solid var(--border-card);
    background: var(--card-bg);
    font-size: 0.74rem; font-weight: 600; color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.whatsnew-older-btn:hover { color: var(--text-heading); border-color: var(--accent-dark); }
.whatsnew-older-chevron { width: 12px; height: 12px; transition: transform 0.2s ease; }
.whatsnew-older-btn[aria-expanded="true"] .whatsnew-older-chevron { transform: rotate(90deg); }
.whatsnew-history-wrap { margin-top: 0.8rem; }
/* History as a vertical release timeline — an emerald-dotted spine with one
   full-width node per release. Bullets flow in two balanced text columns so
   a 20-bullet release stays shallow. (Replaces the two-column card grid,
   whose uneven card heights tore holes in the layout.) */
.whatsnew-history-entries {
    position: relative;
    padding-left: 26px;
}
.whatsnew-history-entries::before {
    content: '';
    position: absolute;
    left: 5px; top: 8px; bottom: 6px;
    width: 2px;
    border-radius: 1px;
    background: linear-gradient(to bottom,
        var(--accent) 0, var(--border-card) 80px,
        var(--border-card) calc(100% - 50px), transparent);
}
.whatsnew-history-entries .changelog-entry {
    position: relative;
    padding: 0 0 1.15rem;
}
.whatsnew-history-entries .changelog-entry:last-child { padding-bottom: 0.25rem; }
/* Node dot — background fill punches a hole over the spine. */
.whatsnew-history-entries .changelog-entry::before {
    content: '';
    position: absolute;
    left: -25px; top: 4px;
    width: 10px; height: 10px;
    border-radius: 50%;
    box-sizing: border-box;
    background: var(--bg-card);
    border: 2px solid var(--accent);
}
/* Major releases (N.0.0) — filled, haloed node to anchor the eras. */
.whatsnew-history-entries .changelog-entry:has(.changelog-entry-version-major)::before {
    left: -26px; top: 3px;
    width: 12px; height: 12px;
    background: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.whatsnew-history-entries .changelog-entry-header { margin-bottom: 0.45rem; }
.whatsnew-history-entries .changelog-entry-version { font-size: 0.86rem; }
.whatsnew-history-entries .changelog-entry-date { font-size: 0.7rem; }
.whatsnew-history-entries .changelog-entry-list {
    display: block;                  /* neutralise the bell-era flex column */
    columns: 2;
    column-gap: 2.4rem;
}
.whatsnew-history-entries .changelog-entry-list li {
    font-size: 0.78rem;
    line-height: 1.5;
    padding-left: 1rem;
    margin-bottom: 0.32rem;          /* replaces the flex gap */
    break-inside: avoid;
}
/* Admin announcements keep a soft card so they lead distinctly. */
.whatsnew-history-entries .announcement-entry {
    background: var(--accent-soft);
    border-radius: 10px;
    padding: 0.7rem 0.9rem 0.75rem;
    margin-bottom: 1.15rem;
}
.whatsnew-history-entries .announcement-entry::before {
    background: var(--accent);
    top: 14px;
}
.whatsnew-history-wrap:not(.hidden) .changelog-entry { animation: wn-hist-in 0.28s ease both; }
@keyframes wn-hist-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}
@media (max-width: 760px) {
    .whatsnew-history-entries .changelog-entry-list { columns: 1; }
}
.whatsnew-loadmore { margin-top: 0.7rem; margin-left: 26px; }

/* ══ What's New visuals (6.5) ══════════════════════════════════════════════
   Headliners: print-to-any-paper, version timeline.
   Export section: poster tiling (the wide card), rebuilt window, live preview.
   Minis: fast grouping, right-click grouping, mono printing, export picks.
   Every animation is frozen on a readable frame under prefers-reduced-motion at
   the end of this block — add new ones there too. */

/* The poster card runs the full width of the body with its visual beside the
   copy, because tiling is the release's headline trick and a 112px strip can't
   show it. Stacks under 760px like everything else. */
.whatsnew-card-wide { flex-direction: row; align-items: stretch; }
.whatsnew-card-wide .whatsnew-visual {
    flex: 0 0 46%;
    width: 46%; height: auto; min-height: 148px;
    border-bottom: 0;
    border-right: 1px solid var(--border-card);
}
.whatsnew-card-wide .whatsnew-card-text { align-self: center; padding: 14px 18px; }
@media (max-width: 760px) {
    .whatsnew-card-wide { flex-direction: column; }
    .whatsnew-card-wide .whatsnew-visual {
        flex: 0 0 auto; width: 100%; min-height: 132px;
        border-right: 0;
        border-bottom: 1px solid var(--border-card);
    }
}

/* hero — print: three nested sheets (A2/A3/A4) take turns lighting up around the
   same little chart, so one chart on any paper reads at a glance */
.wn-pr-sheet {
    position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    border: 1.5px solid var(--border-card);
    border-radius: 3px;
    animation: wnPrLit 6.6s ease-in-out infinite;
}
.wn-pr-sheet b {
    position: absolute; top: 2px; left: 4px;
    font-size: 8px; font-weight: 800; letter-spacing: 0.03em;
    color: var(--text-muted);
}
.wn-pr-sheet-1 { width: 138px; height: 94px; }
.wn-pr-sheet-2 { width: 102px; height: 70px; animation-delay: 2.2s; }
.wn-pr-sheet-3 { width: 68px;  height: 48px; animation-delay: 4.4s; }
@keyframes wnPrLit {
    0%, 4%    { border-color: var(--border-card); box-shadow: none; }
    11%, 30%  { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 13%, transparent); }
    37%, 100% { border-color: var(--border-card); box-shadow: none; }
}
.wn-pr-bars {
    position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 50px;
    display: flex; flex-direction: column; gap: 4px;
}
.wn-pr-bars i { height: 5px; border-radius: 999px; background: var(--accent); opacity: 0.85; }
.wn-pr-bars i:nth-child(1) { width: 100%; }
.wn-pr-bars i:nth-child(2) { width: 72%; margin-left: 14%; }
.wn-pr-bars i:nth-child(3) { width: 48%; margin-left: 30%; }

/* hero — version history: savepoints land along a timeline one after another, one
   of them starred (kept forever), with the 5-minute cadence spelled out */
.wn-ver-line { position: absolute; left: 13%; right: 13%; top: 57px; height: 2px; border-radius: 999px; background: var(--border-card); }
.wn-ver-dot {
    position: absolute; top: 51px;
    width: 14px; height: 14px; margin-left: -7px;
    border-radius: 50%;
    border: 2px solid var(--border-card);
    background: var(--bg-card);
    animation: wnVerDot 5.6s ease-in-out infinite;
}
.wn-ver-dot-1 { left: 18%; }
.wn-ver-dot-2 { left: 39%; animation-delay: 0.45s; }
.wn-ver-dot-3 { left: 60%; animation-delay: 0.9s; }
.wn-ver-dot-4 { left: 81%; animation-delay: 1.35s; }
@keyframes wnVerDot {
    0%, 8%    { border-color: var(--border-card); background: var(--bg-card); transform: scale(1); }
    16%, 76%  { border-color: var(--accent); background: var(--accent); transform: scale(1.16); }
    90%, 100% { border-color: var(--border-card); background: var(--bg-card); transform: scale(1); }
}
.wn-ver-star {
    position: absolute; left: 60%; top: 26px;
    width: 14px; height: 14px; margin-left: -7px;
    color: var(--accent-dark);
    animation: wnVerStar 5.6s ease-in-out infinite;
}
.wn-ver-star svg { width: 100%; height: 100%; display: block; }
@keyframes wnVerStar {
    0%, 22%   { opacity: 0; transform: translateY(4px) scale(0.7); }
    34%, 78%  { opacity: 1; transform: none; }
    92%, 100% { opacity: 0; transform: translateY(4px) scale(0.7); }
}
.wn-ver-pill {
    position: absolute; right: 11px; bottom: 9px;
    padding: 2px 7px;
    border-radius: 999px;
    border: 1px solid var(--border-card);
    background: var(--bg-card);
    font-size: 8px; font-weight: 800; letter-spacing: 0.03em;
    color: var(--text-muted);
}

/* export — poster: eight dashed sheets in a 4x2 wall, task bars running straight
   over the seams, each sheet lighting in turn as it prints. The scissors snips. */
.wn-po-grid {
    position: absolute; inset: 15px 17px;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
}
.wn-po-grid i {
    border: 1px dashed color-mix(in srgb, var(--accent) 45%, transparent);
    background: var(--bg-card);
    animation: wnPoLit 5.6s ease-in-out infinite;
}
.wn-po-grid i:nth-child(1) { animation-delay: 0s; }
.wn-po-grid i:nth-child(2) { animation-delay: 0.2s; }
.wn-po-grid i:nth-child(3) { animation-delay: 0.4s; }
.wn-po-grid i:nth-child(4) { animation-delay: 0.6s; }
.wn-po-grid i:nth-child(5) { animation-delay: 0.8s; }
.wn-po-grid i:nth-child(6) { animation-delay: 1s; }
.wn-po-grid i:nth-child(7) { animation-delay: 1.2s; }
.wn-po-grid i:nth-child(8) { animation-delay: 1.4s; }
@keyframes wnPoLit {
    0%, 7%    { background: var(--bg-card); }
    14%, 26%  { background: color-mix(in srgb, var(--accent) 20%, var(--bg-card)); }
    34%, 100% { background: var(--bg-card); }
}
.wn-po-bars { position: absolute; inset: 15px 17px; pointer-events: none; }
.wn-po-bars b { position: absolute; height: 7px; border-radius: 999px; background: var(--accent); opacity: 0.82; }
.wn-po-bars b:nth-child(1) { left: 5%;  top: 15%; width: 62%; }
.wn-po-bars b:nth-child(2) { left: 21%; top: 35%; width: 66%; }
.wn-po-bars b:nth-child(3) { left: 11%; top: 60%; width: 47%; }
.wn-po-bars b:nth-child(4) { left: 39%; top: 79%; width: 52%; }
.wn-po-cut {
    position: absolute; right: 11px; bottom: 9px;
    width: 15px; height: 15px;
    color: var(--accent-dark); opacity: 0.75;
    animation: wnPoCut 5.6s ease-in-out infinite;
}
.wn-po-cut svg { width: 100%; height: 100%; display: block; }
@keyframes wnPoCut {
    0%, 62%   { transform: rotate(0deg); }
    72%       { transform: rotate(-16deg); }
    82%, 100% { transform: rotate(0deg); }
}

/* export — rebuilt window: four tabs take turns while the options stay put */
.wn-visual-exportui { display: flex; align-items: center; justify-content: center; }
.wn-eu-win {
    width: 74%;
    border: 1px solid var(--border-card);
    border-radius: 7px;
    background: var(--bg-card);
    overflow: hidden;
}
.wn-eu-tabs { display: flex; gap: 3px; padding: 5px 6px; border-bottom: 1px solid var(--border-card); }
.wn-eu-tabs b {
    flex: 1; height: 8px; border-radius: 3px;
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    animation: wnEuTab 6s ease-in-out infinite;
}
.wn-eu-tabs b:nth-child(2) { animation-delay: 1.5s; }
.wn-eu-tabs b:nth-child(3) { animation-delay: 3s; }
.wn-eu-tabs b:nth-child(4) { animation-delay: 4.5s; }
@keyframes wnEuTab {
    0%, 3%    { background: color-mix(in srgb, var(--accent) 16%, transparent); }
    9%, 24%   { background: var(--accent); }
    30%, 100% { background: color-mix(in srgb, var(--accent) 16%, transparent); }
}
.wn-eu-rows { display: flex; flex-direction: column; gap: 5px; padding: 7px 8px 9px; }
.wn-eu-rows i { height: 5px; border-radius: 999px; background: var(--text-muted); opacity: 0.28; }
.wn-eu-rows i:nth-child(1) { width: 100%; }
.wn-eu-rows i:nth-child(2) { width: 76%; }
.wn-eu-rows i:nth-child(3) { width: 54%; }

/* export — live preview: the framed chart breathes in and out under a zoom pill */
.wn-visual-preview { display: flex; align-items: center; justify-content: center; }
.wn-pv-frame {
    width: 66%; height: 74px;
    border: 1px solid var(--border-card);
    border-radius: 7px;
    background: var(--bg-card);
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
}
.wn-pv-chart {
    width: 74%;
    display: flex; flex-direction: column; gap: 6px;
    animation: wnPvZoom 6.4s ease-in-out infinite;
}
.wn-pv-chart i { height: 6px; border-radius: 999px; background: var(--accent); opacity: 0.8; }
.wn-pv-chart i:nth-child(1) { width: 100%; }
.wn-pv-chart i:nth-child(2) { width: 66%; }
.wn-pv-chart i:nth-child(3) { width: 44%; }
@keyframes wnPvZoom {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.18) translateX(-3px); }
}
.wn-pv-zoom {
    position: absolute; right: 11px; bottom: 9px;
    width: 15px; height: 15px;
    color: var(--accent-dark);
    animation: wnPvPulse 6.4s ease-in-out infinite;
}
.wn-pv-zoom svg { width: 100%; height: 100%; display: block; }
@keyframes wnPvPulse {
    0%, 100% { transform: scale(1); opacity: 0.7; }
    50%      { transform: scale(1.15); opacity: 1; }
}

/* mini — fast grouping: two rows light up as selected, then a brace claims them */
.wn-visual-groupfast { flex-direction: column; gap: 5px; }
.wn-gf-bar { width: 44px; height: 6px; border-radius: 999px; background: var(--text-muted); opacity: 0.32; }
.wn-gf-bar-2, .wn-gf-bar-3 { background: var(--accent); animation: wnGfSel 4.4s ease-in-out infinite; }
.wn-gf-bar-3 { animation-delay: 0.12s; }
@keyframes wnGfSel {
    0%, 16%   { opacity: 0.32; }
    27%, 88%  { opacity: 1; }
    97%, 100% { opacity: 0.32; }
}
.wn-gf-brace {
    position: absolute; left: calc(50% - 30px); top: 29px;
    width: 3px; height: 0;
    border-radius: 999px;
    background: var(--accent-dark);
    animation: wnGfBrace 4.4s ease-in-out infinite;
}
@keyframes wnGfBrace {
    0%, 30%   { height: 0; opacity: 0; }
    42%, 88%  { height: 17px; opacity: 1; }
    97%, 100% { height: 0; opacity: 0; }
}

/* mini — grouping from the right-click menu: the cursor arrives, the Group row lights */
.wn-gm-menu {
    width: 42px;
    padding: 3px;
    border: 1px solid var(--border-card);
    border-radius: 4px;
    background: var(--bg-card);
    display: flex; flex-direction: column; gap: 3px;
}
.wn-gm-menu i { height: 4px; border-radius: 2px; background: var(--text-muted); opacity: 0.3; }
.wn-gm-hit { animation: wnGmHit 4s ease-in-out infinite; }
@keyframes wnGmHit {
    0%, 28%   { background: var(--text-muted); opacity: 0.3; }
    40%, 86%  { background: var(--accent); opacity: 1; }
    96%, 100% { background: var(--text-muted); opacity: 0.3; }
}
.wn-gm-cursor {
    position: absolute; left: 57%; top: 36px;
    width: 11px; height: 11px;
    color: var(--text-heading);
    animation: wnGmCur 4s ease-in-out infinite;
}
.wn-gm-cursor svg { width: 100%; height: 100%; display: block; }
@keyframes wnGmCur {
    0%, 6%    { transform: translate(6px, 6px); opacity: 0; }
    26%, 88%  { transform: none; opacity: 1; }
    98%, 100% { transform: translate(6px, 6px); opacity: 0; }
}

/* mini — mono printing: three coloured bars drop to greys and back */
.wn-visual-bw { flex-direction: column; gap: 5px; }
.wn-bw-bar { width: 40px; height: 6px; border-radius: 999px; }
.wn-bw-bar-1 { animation: wnBw1 5s ease-in-out infinite; }
.wn-bw-bar-2 { animation: wnBw2 5s ease-in-out infinite; }
.wn-bw-bar-3 { animation: wnBw3 5s ease-in-out infinite; }
@keyframes wnBw1 { 0%, 36% { background: #e11d48; } 50%, 88% { background: #6b7280; } 100% { background: #e11d48; } }
@keyframes wnBw2 { 0%, 36% { background: #2563eb; } 50%, 88% { background: #9ca3af; } 100% { background: #2563eb; } }
@keyframes wnBw3 { 0%, 36% { background: #16a34a; } 50%, 88% { background: #4b5563; } 100% { background: #16a34a; } }

/* mini — export picks: two switches flick off, the third holds */
.wn-visual-pick { gap: 7px; }
.wn-pk-switch { position: relative; width: 22px; height: 12px; border-radius: 999px; background: var(--accent); }
.wn-pk-switch i { position: absolute; top: 2px; left: 2px; width: 8px; height: 8px; border-radius: 50%; background: #fff; }
.wn-pk-switch-1, .wn-pk-switch-2 { animation: wnPkOff 4.8s ease-in-out infinite; }
.wn-pk-switch-1 i, .wn-pk-switch-2 i { animation: wnPkKnob 4.8s ease-in-out infinite; }
.wn-pk-switch-2, .wn-pk-switch-2 i { animation-delay: 0.3s; }
@keyframes wnPkOff {
    0%, 22%   { background: var(--accent); }
    34%, 84%  { background: color-mix(in srgb, var(--text-muted) 38%, transparent); }
    96%, 100% { background: var(--accent); }
}
@keyframes wnPkKnob {
    0%, 22%   { transform: translateX(10px); }
    34%, 84%  { transform: translateX(0); }
    96%, 100% { transform: translateX(10px); }
}

/* ── 6.6 What's New visuals: phases that move and resize as one ────────── */

/* card — a group rule and its two tasks slide together, cursor leading */
.wn-gb-set {
    position: absolute; left: calc(50% - 56px); top: 33px;
    width: 96px; height: 46px;
    animation: wnGbSlide 4.8s ease-in-out infinite;
}
.wn-gb-rule {
    position: absolute; left: 0; top: 3px;
    width: 78px; height: 5px;
    border-radius: 2px; background: var(--accent);
}
.wn-gb-rule i {
    position: absolute; top: 0; width: 3px; height: 11px;
    border-radius: 1px; background: var(--accent-dark);
}
.wn-gb-rule i:first-child { left: 0; }
.wn-gb-rule i:last-child  { right: 0; }
.wn-gb-task {
    position: absolute; height: 7px; border-radius: 999px;
    background: var(--text-muted); opacity: 0.5;
}
.wn-gb-task-1 { left: 8px;  top: 19px; width: 40px; }
.wn-gb-task-2 { left: 30px; top: 31px; width: 44px; }
@keyframes wnGbSlide {
    0%, 14%   { transform: translateX(0); }
    46%, 82%  { transform: translateX(18px); }
    98%, 100% { transform: translateX(0); }
}
.wn-gb-cursor {
    position: absolute; left: calc(50% - 34px); top: 24px;
    width: 13px; height: 13px; color: var(--text-heading); opacity: 0.85;
    animation: wnGbSlide 4.8s ease-in-out infinite;
}
.wn-gb-cursor svg { width: 100%; height: 100%; display: block; }

/* card — the right end stretches, the tasks inside hold their place */
.wn-ge-rule {
    position: absolute; left: calc(50% - 52px); top: 34px;
    height: 5px; border-radius: 2px; background: var(--accent);
    animation: wnGeStretch 4.6s ease-in-out infinite;
}
.wn-ge-rule i {
    position: absolute; top: 0; width: 3px; height: 11px;
    border-radius: 1px; background: var(--accent-dark);
}
.wn-ge-rule i:first-child { left: 0; }
.wn-ge-rule i:last-child  { right: 0; }
@keyframes wnGeStretch {
    0%, 16%   { width: 56px; }
    44%, 84%  { width: 88px; }
    97%, 100% { width: 56px; }
}
.wn-ge-grip {
    position: absolute; top: 29px;
    width: 4px; height: 15px;
    border-radius: 2px; background: var(--text-heading); opacity: 0.5;
}
.wn-ge-grip-l { left: calc(50% - 56px); }
.wn-ge-grip-r { left: calc(50% + 2px); animation: wnGeGrip 4.6s ease-in-out infinite; }
@keyframes wnGeGrip {
    0%, 16%   { transform: translateX(0); opacity: 0.5; }
    44%, 84%  { transform: translateX(32px); opacity: 1; }
    97%, 100% { transform: translateX(0); opacity: 0.5; }
}
.wn-ge-task {
    position: absolute; height: 7px; border-radius: 999px;
    background: var(--text-muted); opacity: 0.5;
}
.wn-ge-task-1 { left: calc(50% - 44px); top: 54px; width: 30px; }
.wn-ge-task-2 { left: calc(50% - 26px); top: 68px; width: 26px; }

/* mini — a padlock closes over the phase bar, then opens */
.wn-gl-bar {
    position: absolute; left: calc(50% - 22px); top: 42px;
    width: 44px; height: 5px;
    border-radius: 2px; background: var(--accent);
}
.wn-gl-lock {
    position: absolute; left: calc(50% - 9px); top: 11px;
    width: 18px; height: 18px; color: var(--accent-dark);
    animation: wnGlLock 4.4s ease-in-out infinite;
}
.wn-gl-lock svg { width: 100%; height: 100%; display: block; }
@keyframes wnGlLock {
    0%, 20%   { opacity: 0.3; transform: translateY(-3px); }
    34%, 86%  { opacity: 1; transform: translateY(0); }
    96%, 100% { opacity: 0.3; transform: translateY(-3px); }
}

/* mini — a three-row block hops over its neighbours as one unit */
.wn-gr-row {
    position: absolute; left: calc(50% - 20px);
    width: 40px; height: 5px;
    border-radius: 999px; background: var(--text-muted); opacity: 0.32;
}
.wn-gr-row-1 { top: 9px; }
.wn-gr-row-2 { top: 19px; }
.wn-gr-block {
    position: absolute; left: calc(50% - 20px); top: 29px;
    display: flex; flex-direction: column; gap: 3px;
    animation: wnGrHop 4.6s ease-in-out infinite;
}
.wn-gr-block i {
    display: block; width: 40px; height: 5px;
    border-radius: 999px; background: var(--accent);
}
.wn-gr-block i:not(:first-child) { width: 32px; margin-left: 8px; opacity: 0.7; }
@keyframes wnGrHop {
    0%, 18%   { transform: translateY(0); }
    46%, 84%  { transform: translateY(-20px); }
    97%, 100% { transform: translateY(0); }
}

/* mini — the rule grows while the two tasks under it never move */
.wn-gk-rule {
    position: absolute; left: calc(50% - 25px); top: 14px;
    height: 5px; border-radius: 2px; background: var(--accent);
    animation: wnGkGrow 4.4s ease-in-out infinite;
}
@keyframes wnGkGrow {
    0%, 18%   { width: 30px; }
    44%, 86%  { width: 50px; }
    97%, 100% { width: 30px; }
}
.wn-gk-task {
    position: absolute; height: 6px; border-radius: 999px;
    background: var(--text-muted); opacity: 0.5;
}
.wn-gk-task-1 { left: calc(50% - 23px); top: 30px; width: 24px; }
.wn-gk-task-2 { left: calc(50% - 9px);  top: 42px; width: 22px; }

/* mini — a bar glides, leaving two fading after-images */
.wn-gs-bar, .wn-gs-trail {
    position: absolute; left: calc(50% - 28px); top: 29px;
    width: 34px; height: 7px;
    border-radius: 999px; background: var(--accent);
    animation: wnGsGlide 3.6s ease-in-out infinite;
}
.wn-gs-trail-1 { opacity: 0.35; animation-delay: 0.12s; }
.wn-gs-trail-2 { opacity: 0.16; animation-delay: 0.24s; }
@keyframes wnGsGlide {
    0%, 12%   { transform: translateX(0); }
    50%, 84%  { transform: translateX(24px); }
    98%, 100% { transform: translateX(0); }
}

/* Reduced motion — freeze every What's New animation on its "shown" frame */
@media (prefers-reduced-motion: reduce) {
    .wn-gb-set, .wn-gb-cursor, .wn-ge-rule, .wn-ge-grip-r, .wn-gl-lock,
    .wn-gr-block, .wn-gk-rule, .wn-gs-bar, .wn-gs-trail-1, .wn-gs-trail-2 { animation: none; }
    .wn-gb-set { transform: translateX(16px); }
    .wn-gb-cursor { transform: translateX(16px); }
    .wn-ge-rule { width: 88px; }
    .wn-ge-grip-r { transform: translateX(32px); opacity: 1; }
    .wn-gl-lock { opacity: 1; transform: translateY(0); }
    .wn-gr-block { transform: translateY(-20px); }
    .wn-gk-rule { width: 50px; }
    .wn-gs-bar { transform: translateX(22px); }
    .wn-gs-trail-1, .wn-gs-trail-2 { opacity: 0; }
    .whatsnew-card, .whatsnew-mini, .whatsnew-hero::after, .whatsnew-hero-num,
    .whatsnew-history-wrap:not(.hidden) .changelog-entry,
    .wn-pr-sheet, .wn-ver-dot, .wn-ver-star, .wn-po-grid i, .wn-po-cut,
    .wn-eu-tabs b, .wn-pv-chart, .wn-pv-zoom,
    .wn-gf-bar-2, .wn-gf-bar-3, .wn-gf-brace, .wn-gm-hit, .wn-gm-cursor,
    .wn-bw-bar-1, .wn-bw-bar-2, .wn-bw-bar-3,
    .wn-pk-switch-1, .wn-pk-switch-2, .wn-pk-switch-1 i, .wn-pk-switch-2 i { animation: none; }
    .wn-pr-sheet-2 { border-color: var(--accent); }
    .wn-ver-dot-1, .wn-ver-dot-2, .wn-ver-dot-3 { border-color: var(--accent); background: var(--accent); }
    .wn-ver-star { opacity: 1; }
    .wn-po-grid i:nth-child(3) { background: color-mix(in srgb, var(--accent) 20%, var(--bg-card)); }
    .wn-eu-tabs b:nth-child(1) { background: var(--accent); }
    .wn-gf-bar-2, .wn-gf-bar-3 { opacity: 1; }
    .wn-gf-brace { height: 17px; opacity: 1; }
    .wn-gm-hit { background: var(--accent); opacity: 1; }
    .wn-bw-bar-1 { background: #6b7280; }
    .wn-bw-bar-2 { background: #9ca3af; }
    .wn-bw-bar-3 { background: #4b5563; }
    .wn-pk-switch-1, .wn-pk-switch-2 { background: color-mix(in srgb, var(--text-muted) 38%, transparent); }
    .wn-pk-switch-1 i, .wn-pk-switch-2 i { transform: translateX(0); }
}


/* ── 6.7 What's New visuals: shared editing that keeps everything ───────
   Container notes that cost an afternoon the last time: .whatsnew-visual is
   112px tall and NOT a flex container, so every child needs an explicit `top`
   and is centred with `left: calc(50% - halfWidth)`. .whatsnew-mini-visual is
   64px and IS flex-centred, but a visual with TWO children still positions them
   absolutely. Never centre with translateX here: `transform` belongs to the
   animations. */

/* card — a message, a reply, then the bell that carries it back */
.wn-sp-bub {
    position: absolute; width: 48px; height: 25px;
    border-radius: 9px;
    border: 1px solid var(--border-card);
    animation: wnSpPop 5.2s ease-in-out infinite;
}
.wn-sp-bub i {
    position: absolute; left: 7px; height: 3px;
    border-radius: 999px; background: var(--text-muted); opacity: 0.55;
}
.wn-sp-bub i:first-child { top: 8px;  width: 28px; }
.wn-sp-bub i:last-child  { top: 15px; width: 18px; }
.wn-sp-bub-1 {
    left: calc(50% - 54px); top: 18px;
    background: var(--bg-card);
    border-bottom-left-radius: 3px;
}
.wn-sp-bub-2 {
    left: calc(50% + 6px); top: 46px;
    background: color-mix(in srgb, var(--accent) 14%, var(--bg-card));
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    border-bottom-right-radius: 3px;
    animation-delay: 0.9s;
}
@keyframes wnSpPop {
    0%, 8%    { opacity: 0; transform: translateY(6px); }
    22%, 88%  { opacity: 1; transform: translateY(0); }
    98%, 100% { opacity: 0; transform: translateY(6px); }
}
.wn-sp-bell {
    position: absolute; left: calc(50% - 9px); top: 82px;
    width: 18px; height: 18px; color: var(--accent-dark);
    animation: wnSpBell 5.2s ease-in-out infinite;
}
.wn-sp-bell svg { width: 100%; height: 100%; display: block; }
@keyframes wnSpBell {
    0%, 52%          { opacity: 0.25; transform: rotate(0deg); }
    60%              { opacity: 1; transform: rotate(-12deg); }
    66%              { transform: rotate(10deg); }
    72%, 100%        { opacity: 1; transform: rotate(0deg); }
}

/* card — the two toolbar arrows, and the bar one of them takes back */
.wn-ur-btn {
    position: absolute; top: 24px;
    width: 28px; height: 28px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 999px;
    border: 1px solid var(--border-card);
    background: var(--bg-card);
    color: var(--text-heading);
}
.wn-ur-btn svg { width: 15px; height: 15px; display: block; }
.wn-ur-btn-u { left: calc(50% - 32px); animation: wnUrPress 4.6s ease-in-out infinite; }
/* Redo sits dimmed: with a fresh edit there is nothing to redo yet, which is
   exactly what the real toolbar shows. */
.wn-ur-btn-r { left: calc(50% + 4px); opacity: 0.32; }
@keyframes wnUrPress {
    0%, 26%   { transform: scale(1); border-color: var(--border-card); }
    38%, 52%  { transform: scale(0.9); border-color: var(--accent); }
    64%, 100% { transform: scale(1); border-color: var(--border-card); }
}
.wn-ur-bar {
    position: absolute; left: calc(50% - 30px); top: 70px;
    width: 60px; height: 7px;
    border-radius: 999px; background: var(--accent);
    animation: wnUrBack 4.6s ease-in-out infinite;
}
@keyframes wnUrBack {
    0%, 34%   { transform: translateX(20px); }
    54%, 100% { transform: translateX(0); }
}

/* card — a dropped link closes itself, then the missed edits stream in */
.wn-sh-seg {
    position: absolute; top: 44px; height: 6px;
    border-radius: 999px; background: var(--accent);
}
.wn-sh-seg-l { left: calc(50% - 48px); width: 36px; }
.wn-sh-seg-r { width: 36px; animation: wnShClose 5s ease-in-out infinite; }
@keyframes wnShClose {
    0%, 18%   { left: calc(50% + 16px); opacity: 0.4; }
    44%, 100% { left: calc(50% - 12px); opacity: 1; }
}
.wn-sh-spark {
    position: absolute; left: calc(50% - 4px); top: 40px;
    width: 8px; height: 14px;
    border-radius: 999px; background: var(--accent-dark);
    animation: wnShSpark 5s ease-in-out infinite;
}
@keyframes wnShSpark {
    0%, 40%   { opacity: 0; transform: scale(0.4); }
    52%       { opacity: 1; transform: scale(1); }
    70%, 100% { opacity: 0; transform: scale(0.6); }
}
.wn-sh-dot {
    position: absolute; top: 70px;
    width: 7px; height: 7px; border-radius: 999px;
    background: #6366f1;
    animation: wnShCatch 5s ease-in-out infinite;
}
.wn-sh-dot-1 { left: calc(50% - 44px); }
.wn-sh-dot-2 { left: calc(50% - 44px); animation-delay: 0.35s; }
@keyframes wnShCatch {
    0%, 56%   { opacity: 0; transform: translateX(0); }
    68%       { opacity: 1; transform: translateX(24px); }
    88%, 100% { opacity: 0; transform: translateX(56px); }
}

/* mini — your bar rewinds, your teammate's holds its place */
.wn-um-mine, .wn-um-theirs {
    position: absolute; height: 6px; width: 34px;
    border-radius: 999px;
}
.wn-um-mine {
    left: calc(50% - 20px); top: 22px; background: var(--accent);
    animation: wnUmBack 4.4s ease-in-out infinite;
}
.wn-um-theirs { left: calc(50% - 8px); top: 36px; background: #6366f1; }
@keyframes wnUmBack {
    0%, 32%   { transform: translateX(16px); }
    54%, 100% { transform: translateX(0); }
}

/* mini (6.7.1) — an envelope for someone who is not here yet: the new-person mark pops in */
.wn-iv-env {
    position: absolute; left: calc(50% - 22px); top: 20px;
    width: 24px; height: 24px; color: var(--text-muted);
}
.wn-iv-env svg { width: 100%; height: 100%; display: block; }
.wn-iv-plus {
    position: absolute; left: calc(50% + 4px); top: 22px;
    width: 18px; height: 18px; color: var(--accent);
    animation: wnIvPop 4.2s ease-in-out infinite;
}
.wn-iv-plus svg { width: 100%; height: 100%; display: block; }
@keyframes wnIvPop {
    0%, 18%   { opacity: 0; transform: scale(0.5); }
    36%, 80%  { opacity: 1; transform: scale(1); }
    96%, 100% { opacity: 0; transform: scale(0.5); }
}

/* mini — the account menu, with the support row lighting up */
.wn-sm-menu {
    position: absolute; left: calc(50% - 21px); top: 16px;
    width: 42px; height: 32px;
    border-radius: 6px; border: 1px solid var(--border-card); background: var(--bg-card);
}
.wn-sm-menu i {
    position: absolute; left: 6px; width: 30px; height: 4px;
    border-radius: 999px; background: var(--text-muted); opacity: 0.4;
}
.wn-sm-menu i:nth-child(1) { top: 6px; }
.wn-sm-menu i:nth-child(2) { top: 14px; }
.wn-sm-menu i:nth-child(3) { top: 22px; }
.wn-sm-hit { animation: wnSmHit 4s ease-in-out infinite; }
@keyframes wnSmHit {
    0%, 24%   { background: var(--text-muted); opacity: 0.4; }
    40%, 86%  { background: var(--accent); opacity: 1; }
    98%, 100% { background: var(--text-muted); opacity: 0.4; }
}

/* mini — an envelope with an unread mark */
.wn-re-env {
    position: absolute; left: calc(50% - 13px); top: 20px;
    width: 26px; height: 26px; color: var(--text-muted);
}
.wn-re-env svg { width: 100%; height: 100%; display: block; }
.wn-re-dot {
    position: absolute; left: calc(50% + 8px); top: 16px;
    width: 9px; height: 9px; border-radius: 999px;
    background: var(--accent);
    animation: wnRePing 3.6s ease-in-out infinite;
}
@keyframes wnRePing {
    0%, 20%   { opacity: 0; transform: scale(0.4); }
    34%, 82%  { opacity: 1; transform: scale(1); }
    96%, 100% { opacity: 0; transform: scale(0.4); }
}

/* mini — an envelope handed on */
.wn-se-env {
    position: absolute; left: calc(50% - 20px); top: 20px;
    width: 24px; height: 24px; color: var(--text-muted);
}
.wn-se-env svg { width: 100%; height: 100%; display: block; }
.wn-se-arrow {
    position: absolute; left: calc(50% + 6px); top: 24px;
    width: 16px; height: 16px; color: var(--accent);
    animation: wnSeGo 3.8s ease-in-out infinite;
}
.wn-se-arrow svg { width: 100%; height: 100%; display: block; }
@keyframes wnSeGo {
    0%, 22%   { opacity: 0.3; transform: translateX(-4px); }
    46%, 78%  { opacity: 1; transform: translateX(2px); }
    96%, 100% { opacity: 0.3; transform: translateX(-4px); }
}

/* Reduced motion: freeze each 6.7 visual on the frame that carries its point.
   Same discipline as the 6.5 / 6.6 blocks above. */
@media (prefers-reduced-motion: reduce) {
    .wn-sp-bub-1, .wn-sp-bub-2, .wn-sp-bell,
    .wn-ur-btn-u, .wn-ur-bar,
    .wn-sh-seg-r, .wn-sh-spark, .wn-sh-dot-1, .wn-sh-dot-2,
    .wn-um-mine, .wn-sm-hit, .wn-re-dot, .wn-se-arrow, .wn-iv-plus { animation: none; }
    .wn-sp-bub-1, .wn-sp-bub-2 { opacity: 1; transform: translateY(0); }
    .wn-sp-bell { opacity: 1; }
    .wn-ur-bar { transform: translateX(0); }
    .wn-sh-seg-r { left: calc(50% - 12px); opacity: 1; }
    .wn-sh-spark { opacity: 0; }
    .wn-sh-dot-1 { opacity: 1; transform: translateX(24px); }
    .wn-sh-dot-2 { opacity: 1; transform: translateX(40px); }
    .wn-um-mine { transform: translateX(0); }
    .wn-sm-hit { background: var(--accent); opacity: 1; }
    .wn-re-dot { opacity: 1; transform: scale(1); }
    .wn-se-arrow { opacity: 1; transform: translateX(2px); }
    .wn-iv-plus { opacity: 1; transform: scale(1); }
}


/* Per-kind email switches in the profile (a49). Indented under the master
   toggle; when the master is off the kinds are moot and read as inert. */
.profile-email-kinds {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 8px 0 2px 26px;
    padding-left: 12px;
    border-left: 2px solid var(--border-card);
}
.profile-email-kinds .keep-switch-text { font-size: 0.82rem; color: var(--text-muted); }
.profile-email-kinds.kinds-disabled { opacity: 0.45; pointer-events: none; }


/* ── 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; }
/* Quiet spinner above the loading line. Scoped to .is-loading so the error
   message that reuses this element doesn't get a spinner beside it. */
.my-charts-status.is-loading::before {
    content: '';
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2.5px solid var(--border-card);
    border-top-color: var(--accent);
    animation: mcSpin 0.7s linear infinite;
}
@keyframes mcSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .my-charts-status.is-loading::before { animation: none; }
    .my-charts-list { transition: none; }
    .my-charts-pop { animation: none; }
}
[data-a11y-motion="reduce"] .my-charts-status.is-loading::before { 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;
}
.export-progress-spinner { animation: export-progress-spin 0.85s linear infinite; }
.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; }

@keyframes export-progress-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* 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;
}

/* Share dialog — invite warning banner + collaborator list (Phase A2.4b) */
/* ─────────────────────────────────────────────────────────────
   Share dialog — redesign (collaborator list + owner row)
   Theme-aware via CSS variables, all 6 themes verified.
   ───────────────────────────────────────────────────────────── */
.share-collab-warning {
    margin-top: 0.6rem;
    padding: 0.55rem 0.7rem;
    font-size: 0.74rem;
    line-height: 1.4;
    border-radius: 6px;
    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, rgba(99, 102, 241, 0.08));
    color: var(--accent-dark, var(--accent, #4f46e5));
    border-color: rgba(99, 102, 241, 0.25);
}

/* ONE clean list container: hairline outer border, rows separated by
   subtle dividers — no per-row boxes (they read as box-in-a-box clutter;
   Fotis, 2026-08-06). max-height = four full rows, so 1–4 people never
   grow a scrollbar and a 5th starts the scroll. */
.share-collab-collaborator-list {
    list-style: none;
    margin: 0.85rem 0 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-input, rgba(0,0,0,0.08));
    border-radius: 10px;
    max-height: 217px; /* 4 × 54px rows + container border */
    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, rgba(0,0,0,0.15));
    border-radius: 4px;
}

.share-collab-collaborator-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    min-height: 54px;
    background: transparent;
    min-width: 0;
    flex-shrink: 0;
    transition: background-color 0.14s ease, opacity 0.18s ease, transform 0.18s ease;
}
.share-collab-collaborator-item:not(:last-child) {
    border-bottom: 1px solid var(--border-input, rgba(0,0,0,0.06));
}
.share-collab-collaborator-item:hover {
    background: var(--bg-hover, rgba(0,0,0,0.025));
}
/* The owner is an ordinary person in the list — the muted badge says the
   rest. (The old accent-tinted owner box was part of the colour overuse.) */
.share-collab-collaborator-item.is-leaving {
    opacity: 0;
    transform: translateX(8px);
}

.share-collab-collab-avatar {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--bg-hover, rgba(127,127,127,0.12));
    border: 1px solid var(--border-input, rgba(0,0,0,0.1));
    color: var(--text-heading, var(--text-body));
    font-size: 0.7rem;
    font-weight: 650;
    letter-spacing: 0.02em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.share-collab-collab-avatar.is-self {
    background: var(--accent-soft, rgba(16, 185, 129, 0.12));
    border-color: color-mix(in srgb, var(--accent, #059669) 35%, transparent);
    color: var(--accent-dark, var(--accent, #047857));
}
/* Pending invitation (a51): a dashed, hollow avatar and a dashed tag say
   "not here yet"; everything else on the row matches a real collaborator. */
.share-collab-collab-avatar.is-pending {
    background: transparent;
    border-style: dashed;
    color: var(--text-muted, #64748b);
}
.share-collab-invite-badge {
    border-style: dashed;
}

.share-collab-collab-meta {
    display: flex;
    flex-direction: column;
    gap: 1px;
    flex: 1;
    min-width: 0;
}
.share-collab-collab-name {
    font-size: 0.83rem;
    font-weight: 500;
    color: var(--text-heading, var(--text-body));
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.share-collab-collab-you-tag {
    font-weight: 400;
    font-size: 0.75rem;
    color: var(--text-muted, #64748b);
    margin-left: 2px;
}
.share-collab-collab-email {
    font-size: 0.7rem;
    color: var(--text-muted, #64748b);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Muted, quiet tag — never a neon chip. */
.share-collab-owner-badge {
    font-size: 0.64rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 2.5px 8px;
    border-radius: 999px;
    background: var(--bg-hover, rgba(127,127,127,0.1));
    border: 1px solid var(--border-input, rgba(0,0,0,0.1));
    color: var(--text-muted, #64748b);
    flex-shrink: 0;
    line-height: 1.25;
}

.share-collab-collab-role-slot {
    flex-shrink: 0;
    position: relative;
    border-radius: 6px;
    transition: box-shadow 0.4s ease;
}
.share-collab-collab-role-slot.save-tick-flash {
    box-shadow: 0 0 0 2px var(--accent-soft, rgba(34, 197, 94, 0.3));
    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;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: var(--text-muted, #64748b);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.14s, color 0.14s, opacity 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-remove.hidden { display: none; }
.share-collab-collab-confirm {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.share-collab-collab-confirm.hidden { display: none; }
.share-collab-confirm-yes,
.share-collab-confirm-no {
    border: 0;
    cursor: pointer;
    padding: 4px 9px;
    font-size: 0.72rem;
    font-weight: 600;
    border-radius: 5px;
    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, #64748b);
}
.share-collab-confirm-no:hover {
    background: var(--bg-hover, rgba(0,0,0,0.05));
    color: var(--text-heading, var(--text-body));
}

.share-collab-empty-row {
    list-style: none;
    padding: 16px 12px;
    font-size: 0.76rem;
    color: var(--text-muted, #64748b);
    text-align: center;
    background: transparent;
}

/* ─────────────────────────────────────────────────────────────
   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));
}

/* The slot div that holds a mounted dropdown — used so the dropdown
   sits in the right flex layout regardless of variant. */
.share-collab-role-slot { display: inline-block; }
.share-collab-collab-role-slot { display: inline-block; }

/* 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; }
}

/* ─────────────────────────────────────────────────────────────
   Share Collaboration Dialog (Phase A scaffolding)
   ───────────────────────────────────────────────────────────── */
.share-collab-section + .share-collab-section {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid var(--border-input, rgba(0, 0, 0, 0.08));
}
.share-collab-invite-row {
    display: flex;
    gap: 8px;
    align-items: stretch;
    margin-top: 8px;
}
.share-collab-invite-row > .input { flex: 1; min-width: 0; }
.share-collab-invite-row > .share-collab-role-slot { flex: 0 0 auto; }
.share-collab-invite-row > .btn-primary { flex: 0 0 auto; }

/* List + items live in the share-collab-* block above (redesign).
   The legacy duplicate block that used to live here had dead selectors
   (`.share-collab-collaborator-name` / `-remove`) that never matched the
   JS-built markup and was overriding the canonical block via cascade. */

/* General access — two COMPACT radio rows in one grouped container, the
   same list architecture as the people list (Fotis, 2026-08-06 — the two
   tall accent-ringed cards were the "massive cards" problem). The native
   radios stay focusable for keyboards/SRs; the visible dot + text state
   are driven via :has(). */
.share-access-group {
    margin-top: 8px;
    border: 1px solid var(--border-input, rgba(0,0,0,0.08));
    border-radius: 10px;
    overflow: hidden;
}
.share-access-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    min-height: 50px;
    background: transparent;
    transition: background 0.14s ease;
}
.share-access-row:not(:last-child) {
    border-bottom: 1px solid var(--border-input, rgba(0,0,0,0.06));
}
.share-access-row:hover { background: var(--bg-hover, rgba(0,0,0,0.025)); }
.share-access-row input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.share-access-row:has(input[type="radio"]:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.share-access-main {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    min-width: 0;
    cursor: pointer;
}
/* Custom radio dot — the ONLY selected-state accent in this section. */
.share-access-dot {
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 1.5px solid var(--border-input, rgba(0,0,0,0.25));
    background: transparent;
    transition: border-color 0.14s ease, box-shadow 0.14s ease;
}
.share-access-row:has(input[type="radio"]:checked) .share-access-dot {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 3.5px var(--accent);
}
.share-access-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--bg-hover, rgba(127, 127, 127, 0.12));
    color: var(--text-muted);
}
.share-access-icon svg { width: 14px; height: 14px; }
.share-access-meta {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}
.share-access-meta strong {
    font-weight: 600;
    font-size: 13px;
    color: var(--text-muted, currentColor);
    transition: color 0.14s ease;
}
.share-access-row:has(input[type="radio"]:checked) .share-access-meta strong {
    color: var(--text-heading, currentColor);
}
.share-collab-radio-hint { font-size: 11.5px; opacity: 0.65; line-height: 1.35; }
/* The link-role picker sits flush right on the "Anyone with the link" row. */
.share-collab-public-role { flex: 0 0 auto; margin-left: auto; }
.share-collab-public-role .role-dd-popover { left: auto; right: 0; }

.share-collab-warning[data-level="success"] {
    background: var(--accent-soft, rgba(16, 185, 129, 0.1));
    color: var(--accent-dark, #047857);
    border-color: rgba(16, 185, 129, 0.3);
}

/* Copy link = polished ghost/secondary. The vibrant accent belongs to ONE
   button down here — Done (btn-primary). */
.share-collab-copylink {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 14px;
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--text-body, #1e293b);
    background: var(--bg-hover, rgba(127,127,127,0.08));
    border: 1px solid var(--border-input, rgba(0,0,0,0.12));
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.14s ease, border-color 0.14s ease;
}
.share-collab-copylink:hover {
    background: color-mix(in srgb, var(--text-body, #1e293b) 10%, transparent);
    border-color: var(--text-muted, #64748b);
}
.share-collab-copylink:focus-visible {
    outline: none;
    border-color: var(--border-focus, var(--accent));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--border-focus, var(--accent, #059669)) 22%, transparent);
}
.share-collab-copylink svg { flex: none; color: var(--text-muted, #64748b); }

.share-collab-footer { justify-content: space-between; }

/* Share dialog sizing, scoped to the overlay: the three invite controls
   share one exact height and radius (a cohesive inline group), sections
   breathe without the old oversized pill styling (superseded 2026-08-06). */
/* The window itself: wide, header/footer PINNED, only the body may scroll
   (the old full-height dialog scrollbar — "the big slider" — is gone), and
   it is draggable by the header + resizable by the corner grip (share.js).
   overflow:hidden is safe for the role dropdowns: their popovers are
   position:fixed, which escapes clipping. */
#shareCollabOverlay .dialog {
    /* Default size per Fotis's reference screenshot (2026-08-06): a fixed
       ~695×625 window (his capture, corrected for 125% display scaling) —
       the footer pins to the bottom and spare space sits below General
       Access. Still draggable/resizable; every open resets to this. */
    width: min(695px, calc(100vw - 2rem));
    max-width: none;
    height: min(625px, calc(100dvh - 2rem));
    max-height: none;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
}
#shareCollabOverlay .dialog-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}
#shareCollabOverlay .dialog-header {
    flex: 0 0 auto;
    cursor: move;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
}
#shareCollabOverlay .dialog-header .icon-btn { cursor: pointer; }
#shareCollabOverlay .dialog-footer { flex: 0 0 auto; }
.share-collab-resize {
    position: absolute;
    right: 2px;
    bottom: 2px;
    width: 18px;
    height: 18px;
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;
    padding: 0 3px 3px 0;
    color: var(--text-muted, #64748b);
    opacity: 0.55;
    cursor: nwse-resize;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}
.share-collab-resize:hover { opacity: 1; }
#shareCollabOverlay .dialog-body { padding: 1.35rem 1.5rem 1.45rem; }
#shareCollabOverlay .share-collab-section + .share-collab-section { margin-top: 22px; padding-top: 20px; }
#shareCollabOverlay .share-collab-section > .label { margin-bottom: 4px; }
#shareCollabOverlay .share-collab-invite-row { gap: 8px; margin-top: 10px; align-items: center; }
#shareCollabOverlay .share-collab-invite-row .input,
#shareCollabOverlay .share-collab-invite-row .btn-primary,
#shareCollabOverlay .share-collab-invite-row .role-dd-trigger {
    height: 38px;
    border-radius: 8px;
    padding-top: 0;
    padding-bottom: 0;
}
#shareCollabOverlay .share-collab-invite-row .btn-primary { padding-left: 16px; padding-right: 16px; }
#shareCollabOverlay .input { padding-left: 0.8rem; padding-right: 0.8rem; }

@media (max-width: 560px) {
    .share-collab-invite-row { flex-wrap: wrap; }
    .share-collab-invite-row > .input,
    .share-collab-invite-row > .share-collab-role-slot,
    .share-collab-invite-row > .btn-primary { flex: 1 1 100%; max-width: none; }
    .share-collab-invite-row > .share-collab-role-slot .role-dd-trigger { width: 100%; }
    .share-collab-link-row { flex-wrap: wrap; }
    .share-collab-link-input { flex: 1 1 100%; }
    .share-collab-collaborator-item { flex-wrap: wrap; }
    .share-collab-collab-meta { width: calc(100% - 44px); }
}

/* ─────────────────────────────────────────────────────────────
   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;
}
.auth-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 12px;
    border: 1px solid var(--border-input, rgba(0, 0, 0, 0.1));
    background: transparent;
    color: var(--text-color, currentColor);
    font-size: 12px;
    font-weight: 500;
    font-family: inherit;
    border-radius: 9999px;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
    line-height: 1;
}
.auth-btn:hover {
    background: var(--hover-bg, rgba(0, 0, 0, 0.04));
    border-color: var(--border-color, rgba(0, 0, 0, 0.15));
}
.auth-btn-icon svg { width: 14px; height: 14px; display: block; }
.auth-btn-label { white-space: nowrap; }
.auth-btn-avatar {
    display: none;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--auth-cursor-color, #3b82f6);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    align-items: center;
    justify-content: center;
    text-transform: uppercase;
    user-select: none;
}
.auth-btn[data-state="signed-in"] {
    padding: 0;
    border: 0;
    background: transparent;
    height: auto;
}
.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 {
    outline: 2px solid var(--accent, #6366f1);
    outline-offset: 1px;
}
/* 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
   ───────────────────────────────────────────────────────────── */
.auth-modal .auth-modal-subtitle {
    font-size: 14px;
    color: var(--text, #1f2937);
    margin: 4px 0 14px;
    line-height: 1.5;
    font-weight: 500;
}
.auth-perks {
    list-style: none;
    padding: 0;
    margin: 0 0 18px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: max-content;
    max-width: 100%;
    margin-inline: auto;
}
.auth-perks li {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    color: var(--text-muted, #4b5563);
    line-height: 1.35;
}
.auth-perk-check {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--accent, #6366f1);
}
.auth-consent {
    margin: 16px 0 0;
    text-align: center;
    font-size: 11px;
    color: var(--text-muted, #6b7280);
    line-height: 1.5;
}
.auth-consent a {
    color: var(--accent, #6366f1);
    text-decoration: none;
}
.auth-consent a:hover { text-decoration: underline; }
.auth-oauth-stack {
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* Stack width = widest child's intrinsic width, then centered in the
       modal. Each button stretches to fill the stack, so all 3 are uniform
       and only as wide as the longest label ("Continue with Microsoft"). */
    width: max-content;
    max-width: 100%;
    margin-inline: auto;
}
.auth-oauth-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 10px 16px;
    background: var(--input-bg, #fff);
    color: var(--text-color, currentColor);
    border: 1px solid var(--border-input, rgba(0, 0, 0, 0.15));
    border-radius: 8px;
    font-size: 14px;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.auth-oauth-btn:hover {
    background: var(--hover-bg, rgba(0, 0, 0, 0.04));
    border-color: var(--border-color, rgba(0, 0, 0, 0.25));
}
.auth-oauth-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.auth-oauth-icon { width: 20px; height: 20px; flex: 0 0 auto; }

.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;
}

/* ─────────────────────────────────────────────────────────────
   Profile modal (redesign — identity card, live cursor
   preview, compact swatches + custom color, workspace stats)
   ───────────────────────────────────────────────────────────── */
.profile-setup-modal .label { margin-top: 14px; }
.profile-setup-modal .label:first-of-type { margin-top: 0; }

/* Identity card */
.profile-id-card {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--border-card);
    border-radius: 14px;
    background: linear-gradient(135deg, var(--accent-soft), transparent 70%);
}
.profile-id-avatar {
    width: 46px; height: 46px; flex: 0 0 auto;
    position: relative; border-radius: 50%;
    box-shadow: 0 0 0 2px var(--card-bg), 0 0 0 4px var(--pp-color, #3b82f6);
    transition: box-shadow 0.2s ease;
}
.profile-id-avatar img {
    display: none; width: 100%; height: 100%;
    border-radius: 50%; object-fit: cover;
}
.profile-id-avatar.has-img img { display: block; }
.profile-id-avatar span {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--pp-color, #3b82f6);
    color: #fff; font-size: 16px; font-weight: 700; letter-spacing: 0.02em;
    transition: background 0.2s ease;
    user-select: none;
}
.profile-id-avatar.has-img span { display: none; }
.profile-id-meta { min-width: 0; flex: 1; }
.profile-id-name {
    font-size: 14px; font-weight: 600; color: var(--text-heading);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.profile-id-email {
    font-size: 12px; color: var(--text-muted); margin-top: 1px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.profile-id-pill {
    display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
    padding: 3px 10px;
    border: 1px solid var(--border-card);
    border-radius: 999px;
    background: var(--card-bg);
    font-size: 11px; font-weight: 600; color: var(--text-heading);
    white-space: nowrap;
}
.profile-id-pill svg { display: none; width: 12px; height: 12px; }
.profile-id-pill[data-provider="google"] svg[data-provider-icon="google"],
.profile-id-pill[data-provider="github"] svg[data-provider-icon="github"],
.profile-id-pill[data-provider="azure"]  svg[data-provider-icon="azure"] { display: block; }

/* Live cursor preview — mimics .remote-cursor exactly, but local */
.profile-preview-block { margin-top: 14px; }
.profile-preview {
    position: relative; height: 96px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    overflow: hidden;
    cursor: none;
    background:
        linear-gradient(to right, var(--border-card) 1px, transparent 1px) 0 0 / 38px 100%,
        linear-gradient(to bottom, var(--border-card) 1px, transparent 1px) 0 0 / 100% 24px,
        var(--bg-input);
}
.profile-preview-bar { position: absolute; height: 12px; border-radius: 999px; pointer-events: none; }
.pp-bar-1 { left: 7%;  top: 16px; width: 36%; background: var(--accent); opacity: 0.55; }
.pp-bar-2 { left: 27%; top: 42px; width: 44%; background: rgba(128, 128, 128, 0.35); }
.pp-bar-3 { left: 49%; top: 68px; width: 31%; background: rgba(128, 128, 128, 0.22); }
.profile-preview-cursor {
    position: absolute; top: 0; left: 0; width: 0; height: 0;
    pointer-events: none;
    transition: left 0.06s linear, top 0.06s linear;
}
.profile-preview-cursor.is-idle { animation: ppDrift 9s ease-in-out infinite alternate; }
@keyframes ppDrift {
    0%   { left: 14%; top: 22px; }
    30%  { left: 56%; top: 56px; }
    60%  { left: 32%; top: 68px; }
    100% { left: 70%; top: 26px; }
}
.profile-preview-hint {
    position: absolute; right: 9px; bottom: 6px;
    font-size: 10px; color: var(--text-muted); opacity: 0.75;
    pointer-events: none; user-select: none;
}

/* Display name + character counter */
.profile-name-row { position: relative; }
.profile-name-row .input { padding-right: 56px; }
.profile-name-count {
    position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
    font-size: 10.5px; color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    pointer-events: none; user-select: none;
}

/* Swatches — compact circles, ✓ on selected, conic "custom" wheel */
.profile-color-grid {
    display: flex; flex-wrap: wrap; gap: 8px;
    margin-top: 8px;
    position: relative;
}
.profile-color-swatch {
    width: 26px; height: 26px;
    border: 2px solid transparent;
    border-radius: 50%;
    cursor: pointer;
    position: relative;
    transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
    padding: 0;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
.profile-color-swatch:hover { transform: scale(1.18); }
.profile-color-swatch.selected {
    border-color: var(--card-bg, #fff);
    transform: scale(1.08);
    box-shadow: 0 0 0 2px var(--text-heading);
}
.profile-color-swatch.selected::after {
    content: '✓';
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 12px; font-weight: 800;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
.profile-color-custom {
    background: conic-gradient(from 0deg, #ef4444, #f59e0b, #22c55e, #06b6d4, #6366f1, #ec4899, #ef4444);
}

/* Custom-color popover — themed replacement for the native picker.
   Anchored to the swatch grid (position: relative), opens upward so it
   never extends the dialog's scroll area. */
.profile-color-pop {
    position: absolute;
    bottom: calc(100% + 10px);
    right: 0;
    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;
}

/* Workspace stats — hidden during first-signup */
.profile-stats {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
    margin-top: 16px;
}
.profile-stat {
    display: flex; flex-direction: column; gap: 2px; min-width: 0;
    padding: 8px 10px;
    border: 1px solid var(--border-card);
    border-radius: 12px;
    background: var(--card-bg);
}
.profile-stat-value {
    font-size: 14px; font-weight: 700; color: var(--text-heading);
    font-variant-numeric: tabular-nums;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.profile-stat-label {
    font-size: 9.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.profile-first-run .profile-stats { display: none; }

@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-add-modern,
.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,
.prop-select-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;
}

/* Task-creation color swatch — pill on a vertical axis. Already tall +
   narrow (~2.5rem × 100%), so 999px rounds top + bottom into a vertical
   pill that visually rhymes with the horizontal pills around it without
   flipping orientation. */
.prop-color .color-swatch {
    border-radius: 999px;
}

/* 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 {
    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); }
/* 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; }
    /* color: a labelled left-aligned chip, not an orphaned centred circle.
       Keep the column direction so the label sits above the swatch, both flush left. */
    .prop-color { align-items: flex-start; justify-content: flex-start; gap: 4px; }
    .prop-color .label {
        position: static !important; width: auto; height: auto; margin: 0;
        clip: auto; overflow: visible; white-space: nowrap;
        font-size: 11px; font-weight: 600; letter-spacing: 0.05em;
        text-transform: uppercase; color: var(--text-muted);
    }
    .prop-color .color-swatch {
        width: 38px; height: 38px; flex: none; margin: 0;
        border-radius: 10px; align-self: flex-start;
    }

    /* 3 ── header: the row of chunky grey bordered circles read as oversized,
       so FLATTEN the icon buttons into light borderless tap-icons and shrink
       them. Only the feedback pill + Sign-in/avatar keep a filled shape. */
    .app-header-compact { row-gap: 2px; column-gap: 2px; padding: 6px 6px 2px; }
    .app-title-compact { font-size: 0.9rem; }
    .app-header-compact .btn-feedback { padding: 3px 10px; font-size: 10px; min-height: 28px; }
    .app-header-compact button, .app-header-compact a { min-height: 30px; min-width: 30px; }
    .app-header-compact svg { width: 18px; height: 18px; }
    .app-header-compact .btn-export-sm,
    .app-header-compact .share-btn,
    .app-header-compact .lang-toggle,
    .app-header-compact .changelog-bell,
    .app-header-compact .panel-toggle {
        background: transparent !important;
        border-color: transparent !important;
        box-shadow: none !important;
        width: 34px; height: 34px; min-width: 34px; min-height: 34px; padding: 0 !important;
    }
    .app-header-compact a[href*="ko-fi"] span,
    .app-header-compact #installAppBtn span { display: none; }

    /* 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 { padding: 0 8px; }
    .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%; }

    .props-row {
        grid-template-columns: 1fr 1fr auto;
        grid-template-areas:
            "name     name  name"
            "dates    dates dates"
            "priority type  color"
            "add      add   add";
        column-gap: 8px;
        row-gap: 8px;
    }
    .top-panel .prop-field .input, .top-panel .prop-select-trigger { min-height: 36px; }
    #taskName.input { font-size: 16px; }   /* 16px so iOS Safari doesn't zoom on focus */
    .prop-priority, .prop-type { min-width: 0; }   /* beat the desktop 11rem so 2-up fits */
    .prop-color { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; align-self: end; justify-content: flex-end; }
    .props-row .prop-color .color-swatch {
        width: 36px; height: 36px; flex: none; align-self: flex-start; border-radius: 9px;
    }
    .btn-add-modern { min-height: 40px; font-size: 14px; }

    /* 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. */
body.hdr-tucked #shareBtn,
body.hdr-tucked #installAppBtn,
body.hdr-tucked #guidesBtn { display: none; }

/* ── What's New 6.8 visuals ──────────────────────────────────────────────
   Five scenes for Fotis's 6.8 picks (2026-09-13), same discipline as the
   6.5-6.7 blocks above: grid-paper .whatsnew-visual stage, token colors
   only, one gentle infinite loop each, frozen on the meaningful frame
   under reduced motion. */

/* My Charts workspace: search pill over three chart cards; the middle
   card lifts like a hovered card. */
.wn-mch-search {
    position: absolute; left: 50%; top: 11px; transform: translateX(-50%);
    display: flex; align-items: center; gap: 5px;
    width: 118px; height: 20px; padding: 0 8px;
    border-radius: 999px;
    background: var(--bg-card);
    border: 1px solid var(--border-input);
    color: var(--text-muted);
}
.wn-mch-search svg { width: 10px; height: 10px; flex: 0 0 auto; }
.wn-mch-search i { height: 3px; width: 52px; border-radius: 999px; background: var(--text-muted); opacity: 0.45; }
.wn-mch-card {
    position: absolute; top: 42px; width: 44px; height: 52px;
    border-radius: 7px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    animation: wnMchPop 5.6s ease-in-out infinite;
}
.wn-mch-card b {
    position: absolute; left: 6px; top: 9px; width: 22px; height: 4px;
    border-radius: 2px; background: var(--accent); opacity: 0.85;
}
.wn-mch-card i {
    position: absolute; left: 6px; top: 18px; width: 30px; height: 4px;
    border-radius: 2px; background: var(--text-muted); opacity: 0.35;
}
.wn-mch-card-1 { left: calc(50% - 76px); }
.wn-mch-card-2 {
    left: calc(50% - 22px);
    animation: wnMchLift 5.6s ease-in-out infinite;
}
.wn-mch-card-2 b { width: 28px; background: color-mix(in srgb, var(--accent) 55%, var(--text-muted)); }
.wn-mch-card-3 { left: calc(50% + 32px); animation-delay: 0.35s; }
.wn-mch-card-3 b { width: 16px; }
@keyframes wnMchPop {
    0%, 6%    { opacity: 0; transform: translateY(8px); }
    16%, 90%  { opacity: 1; transform: translateY(0); }
    98%, 100% { opacity: 0; transform: translateY(8px); }
}
@keyframes wnMchLift {
    0%, 26%   { transform: translateY(0); box-shadow: none; }
    38%, 62%  { transform: translateY(-4px); box-shadow: 0 6px 14px rgba(2, 6, 23, 0.16); }
    76%, 100% { transform: translateY(0); box-shadow: none; }
}

/* Screenshots in support: a photo tile glides into the message bubble. */
.wn-sht-bub {
    position: absolute; left: calc(50% - 6px); top: 26px;
    width: 74px; height: 52px;
    border-radius: 10px; border-bottom-right-radius: 3px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
}
.wn-sht-bub i {
    position: absolute; left: 9px; height: 3px;
    border-radius: 999px; background: var(--text-muted); opacity: 0.5;
}
.wn-sht-bub i:first-child { top: 10px; width: 42px; }
.wn-sht-bub i:last-child  { top: 18px; width: 28px; }
.wn-sht-photo {
    position: absolute; left: calc(50% - 70px); top: 32px;
    width: 46px; height: 40px; border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
    color: var(--accent-dark);
    /* Above the bubble: the tile must dock INTO the message, never slide
       behind it (the bubble is later in the DOM and would otherwise cover it). */
    z-index: 1;
    animation: wnShtSlide 5.2s ease-in-out infinite;
}
.wn-sht-photo svg { width: 20px; height: 20px; }
/* Dashed attachment slot under the two text lines — the tile's destination. */
.wn-sht-bub .wn-sht-slot {
    position: absolute; left: 9px; top: 26px;
    width: 30px; height: 24px;
    border-radius: 5px;
    border: 1px dashed var(--border-input);
}
@keyframes wnShtSlide {
    0%, 4%    { transform: translate(0, 0) scale(1); opacity: 0; }
    12%       { opacity: 1; transform: translate(0, 0) scale(1); }
    30%, 88%  { transform: translate(65px, 12px) scale(0.6); opacity: 1; }
    96%, 100% { transform: translate(0, 0) scale(1); opacity: 0; }
}

/* Message Center: a fresh conversation row appears above an older one;
   the green new-chat button gives a little pulse. */
.wn-mce-row {
    position: absolute; left: calc(50% - 62px);
    width: 124px; height: 24px;
    border-radius: 8px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
}
.wn-mce-row b {
    position: absolute; left: 8px; top: 7px; width: 10px; height: 10px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--accent) 30%, var(--bg-input));
}
.wn-mce-row i {
    position: absolute; left: 26px; top: 10px; width: 66px; height: 3px;
    border-radius: 999px; background: var(--text-muted); opacity: 0.5;
}
.wn-mce-row-1 {
    top: 22px;
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
    animation: wnMceNew 5.4s ease-in-out infinite;
}
.wn-mce-row-2 { top: 54px; }
.wn-mce-plus {
    position: absolute; right: 24px; top: 74px;
    width: 26px; height: 26px; border-radius: 999px;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent);
    color: var(--btn-add-text, #ffffff);
    animation: wnMcePulse 5.4s ease-in-out infinite;
}
.wn-mce-plus svg { width: 13px; height: 13px; }
@keyframes wnMceNew {
    0%, 14%  { opacity: 0; transform: translateY(-6px); }
    26%, 92% { opacity: 1; transform: none; }
    100%     { opacity: 0; transform: translateY(-6px); }
}
@keyframes wnMcePulse {
    0%, 6%    { transform: scale(0.92); }
    12%       { transform: scale(1.1); }
    20%, 100% { transform: scale(1); }
}

/* Notifications split: a New row whose unread dot settles once read,
   above a quiet Earlier row. */
.wn-bsp-tag { position: absolute; left: calc(50% - 58px); height: 5px; border-radius: 999px; }
.wn-bsp-tag-new { top: 15px; width: 26px; background: var(--accent); opacity: 0.9; }
.wn-bsp-tag-old { top: 65px; width: 34px; background: var(--text-muted); opacity: 0.4; }
.wn-bsp-row {
    position: absolute; left: calc(50% - 58px);
    width: 116px; height: 22px;
    border-radius: 7px;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
}
.wn-bsp-row b {
    position: absolute; left: 8px; top: 7px; width: 8px; height: 8px;
    border-radius: 999px; background: var(--accent);
}
.wn-bsp-row i {
    position: absolute; left: 24px; top: 9px; width: 70px; height: 3px;
    border-radius: 999px; background: var(--text-muted); opacity: 0.5;
}
.wn-bsp-row-1 { top: 27px; border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.wn-bsp-row-1 b { animation: wnBspRead 5s ease-in-out infinite; }
.wn-bsp-row-2 { top: 77px; }
.wn-bsp-row-2 b { background: var(--text-muted); opacity: 0.35; }
@keyframes wnBspRead {
    0%, 55%  { opacity: 1; transform: scale(1); }
    70%, 86% { opacity: 0.25; transform: scale(0.7); }
    100%     { opacity: 1; transform: scale(1); }
}

/* Invite by email: an envelope, dots marching across, and the new
   collaborator popping in on the other side. */
.wn-ivw-env,
.wn-ivw-user {
    position: absolute; top: 50%; transform: translateY(-50%);
    width: 44px; height: 44px; border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    color: var(--text-muted);
}
.wn-ivw-env { left: calc(50% - 92px); }
.wn-ivw-user {
    left: calc(50% + 48px);
    color: var(--accent-dark);
    background: color-mix(in srgb, var(--accent) 12%, var(--bg-card));
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    animation: wnIvwPop 4.6s ease-in-out infinite;
}
.wn-ivw-env svg, .wn-ivw-user svg { width: 20px; height: 20px; }
.wn-ivw-dots {
    position: absolute; left: calc(50% - 36px); top: 50%;
    transform: translateY(-50%);
    display: flex; gap: 11px;
}
.wn-ivw-dots i {
    width: 6px; height: 6px; border-radius: 999px;
    background: var(--accent); opacity: 0.25;
    animation: wnIvwDot 4.6s ease-in-out infinite;
}
.wn-ivw-dots i:nth-child(2) { animation-delay: 0.35s; }
.wn-ivw-dots i:nth-child(3) { animation-delay: 0.7s; }
@keyframes wnIvwDot {
    0%, 18%   { opacity: 0.25; }
    28%, 44%  { opacity: 1; }
    58%, 100% { opacity: 0.25; }
}
@keyframes wnIvwPop {
    0%, 42%   { transform: translateY(-50%) scale(1); }
    54%       { transform: translateY(-50%) scale(1.1); }
    64%, 100% { transform: translateY(-50%) scale(1); }
}

/* Reduced motion: freeze each 6.8 visual on the frame that carries its
   point, same discipline as the 6.5-6.7 blocks above. */
@media (prefers-reduced-motion: reduce) {
    .wn-mch-card, .wn-mch-card-2,
    .wn-sht-photo,
    .wn-mce-row-1, .wn-mce-plus,
    .wn-bsp-row-1 b,
    .wn-ivw-dots i, .wn-ivw-user { animation: none; }
    .wn-mch-card { opacity: 1; transform: translateY(0); }
    .wn-sht-photo { transform: translate(65px, 12px) scale(0.6); opacity: 1; }
    .wn-mce-row-1 { opacity: 1; transform: none; }
    .wn-ivw-dots i { opacity: 1; }
}
[data-a11y-motion="reduce"] .wn-mch-card,
[data-a11y-motion="reduce"] .wn-mch-card-2,
[data-a11y-motion="reduce"] .wn-sht-photo,
[data-a11y-motion="reduce"] .wn-mce-row-1,
[data-a11y-motion="reduce"] .wn-mce-plus,
[data-a11y-motion="reduce"] .wn-bsp-row-1 b,
[data-a11y-motion="reduce"] .wn-ivw-dots i,
[data-a11y-motion="reduce"] .wn-ivw-user { animation: none; }
[data-a11y-motion="reduce"] .wn-mch-card { opacity: 1; transform: translateY(0); }
[data-a11y-motion="reduce"] .wn-sht-photo { transform: translate(65px, 12px) scale(0.6); opacity: 1; }
[data-a11y-motion="reduce"] .wn-mce-row-1 { opacity: 1; transform: none; }
[data-a11y-motion="reduce"] .wn-ivw-dots i { opacity: 1; }
