/* ============================================================
   KIT-PTH stylesheet — Redesign 2026 (premium SaaS pass).
   Token system and component recipes per docs/design-brief-2026.md.

   This rewrite supersedes the Phase 19 Arial/baby-blue spec.
   Brand authorization to flex CLAUDE.md §10 granted 2026-05-24.
   ============================================================ */

/* -----------------------------------------------------------
   1. Design tokens
   ----------------------------------------------------------- */

:root {
  /* Accent — navy ramp. navy-700 is the KIT brand base.
     The full ramp unlocks tinted surfaces, borders, dark mode,
     and active states without ad-hoc hex codes. */
  --navy-50:  #F4F5FB;
  --navy-100: #E5E7F4;
  --navy-200: #C6CBE6;
  --navy-300: #9098CC;
  --navy-400: #5963B0;
  --navy-500: #2E3893;
  --navy-600: #1A2278;
  --navy-700: #0F145B;
  --navy-800: #0A0E45;
  --navy-900: #06092E;

  /* Baby blue — KIT's secondary brand colour (#74C0EB). Promoted in
     Redesign 2026.1 as a light accent (active-tab underline, etc.) so the
     brand stays recognisable as navy mass is dialled back. Light enough
     to read on both light and dark surfaces, so it needs no dark flip. */
  --sky-300: #B8DFF5;
  --sky-400: #74C0EB;
  --sky-500: #4FA8D8;
  --accent-sky: var(--sky-400);

  /* Neutrals — cool grey (slate family). */
  --grey-50:  #F7F8FA;
  --grey-100: #EEF0F3;
  --grey-200: #E1E4EA;
  --grey-300: #CBD0D9;
  --grey-400: #9AA1AE;
  --grey-500: #6B7280;
  --grey-600: #4A5160;
  --grey-700: #2F3543;
  --grey-800: #1C2030;
  --grey-900: #0E111B;

  /* Semantic — Linear-muted, not Material-bright. */
  --success:        #16A34A;
  --success-bg:     #ECFDF3;
  --warning:        #D97706;
  --warning-bg:     #FFF7ED;
  --error:          #DC2626;
  --error-bg:       #FEF2F2;
  --info:           #0284C7;
  --info-bg:        #F0F9FF;
  /* Extra accent (beyond the semantic set) for the My-work high-priority
     tile — a violet that sits well next to the navy ramp. */
  --accent-violet:    #7C3AED;
  --accent-violet-bg: #F5F3FF;

  /* Status colour system — vibrant + distinct (Redesign 2026.1). Single
     source of truth so a status reads the SAME colour everywhere: the
     pill tint recipe consumes the *hue*, and the dashboard donut consumes
     the *solid* fill. Hues are spread far apart (blue / amber / red /
     green) so "open" and "in progress" no longer both read as dull blue. */
  --status-open-h:      210;   /* blue   — not started   */
  --status-progress-h:  42;    /* amber  — in flight     */
  --status-blocked-h:   2;     /* red    — blocked       */
  --status-done-h:      150;   /* green  — complete      */
  --status-open:        #2D7FF9;
  --status-progress:    #F59E0B;
  --status-blocked:     #E5484D;
  --status-done:        #16A34A;

  /* Categorical chart palette — distinct, evenly-spaced hues for
     multi-category charts (e.g. the dashboard "hours by project" bars,
     which were previously one monochrome navy). Read by dashboard-charts.js
     via getComputedStyle; the hex fallbacks there mirror these. */
  --chart-1: #2D7FF9;  /* blue   */
  --chart-2: #16A34A;  /* green  */
  --chart-3: #F59E0B;  /* amber  */
  --chart-4: #7C3AED;  /* violet */
  --chart-5: #06B6D4;  /* cyan   */
  --chart-6: #E5484D;  /* red    */
  --chart-7: #DB2777;  /* pink   */
  --chart-8: #0F766E;  /* teal   */

  /* Semantic role tokens — assignable to surfaces.
     2026.2 — the page is a clean NEUTRAL light (not blue-tinted) so white
     surfaces/cards visibly FLOAT above it (depth) without any navy cast.
     Surfaces stay pure white. Override per-user via the Background picker.
     Default = Snow (near-white) per product decision 2026-07-13; the
     Background picker still overrides per-user via [data-bg]. */
  --bg-app:         var(--bg-shade-snow);
  --bg-surface:     #FFFFFF;
  --bg-raised:      #FFFFFF;
  --bg-sunken:      var(--grey-100);
  --bg-hover:       var(--grey-50);
  /* Redesign 2026.1 — was navy-50 (a blue wash on every selected row,
     active nav item and rail icon, high-frequency + high-area blue). Now
     a neutral grey selection surface; navy returns only as the active
     *text/icon* ink (--text-accent), so navy is a dose, not an area. */
  --bg-selected:    var(--grey-100);

  --text-strong:    var(--grey-900);
  --text:           var(--grey-800);
  --text-muted:     var(--grey-500);
  --text-subtle:    var(--grey-400);
  --text-inverse:   #FFFFFF;

  /* Brand accent — one indirection that reskins primary buttons, links,
     active nav and focus. Default = navy-600; the user-menu accent picker
     overrides --accent inline (localStorage 'kit-accent') and the derived
     shades + repointed role tokens below all follow. Every picker swatch is
     dark enough for white --accent-contrast.
     Default = Azure (#1746A2, the picker's "Azure" swatch) per product
     decision 2026-07-13; the Accent picker still overrides per-user. */
  --accent:          #1746A2;
  --accent-hover:    color-mix(in srgb, var(--accent) 86%, #000);
  --accent-active:   color-mix(in srgb, var(--accent) 74%, #000);
  --accent-contrast: #FFFFFF;
  --accent-soft:     color-mix(in srgb, var(--accent) 12%, var(--bg-surface));

  --text-link:      var(--accent);
  --text-link-hover:var(--accent-hover);
  /* Accent text for selected / active / hover states that sit on an
     adaptive surface (--bg-selected / --bg-hover / --bg-surface). Tracks
     --accent in light; lifts in dark (below) so it clears the dark surface. */
  --text-accent:    var(--accent);

  --border:         var(--grey-200);
  --border-strong:  var(--grey-300);
  --divider:        var(--grey-100);
  --focus-ring:     var(--accent);

  /* Typography — Inter Variable + Arial fallback for exports/old browsers.
     The mono uses the same Inter for legibility in dense tables; fallback
     to a true mono for code blocks via --font-mono. */
  --font-sans: 'Inter Variable', 'Inter', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
  --font-mono: ui-monospace, 'JetBrains Mono', 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace;

  /* Type scale (px). Headings get negative tracking; body stays at 0. */
  --t-caption:    12px;
  --t-body-sm:    13px;
  --t-body:       14px;
  --t-body-md:    15px;
  --t-heading-sm: 16px;
  --t-heading-md: 20px;
  --t-heading-lg: 24px;
  --t-display:    30px;

  /* Spacing — 4px base, with 2px and 80px additions. */
  --space-px: 1px;
  --space-0_5: 2px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;

  /* Radius. */
  --radius-sm:   6px;
  --radius-md:   8px;
  --radius-lg:   12px;
  --radius-xl:   16px;
  --radius-full: 9999px;

  /* Shadows — navy-tinted, two-layer, Linear-subtle. */
  --shadow-xs:  0 1px 2px   rgba(15, 20, 91, 0.04);
  --shadow-sm:  0 1px 3px   rgba(15, 20, 91, 0.06), 0 1px 2px rgba(15, 20, 91, 0.04);
  --shadow-md:  0 4px 8px   rgba(15, 20, 91, 0.06), 0 2px 4px rgba(15, 20, 91, 0.04);
  --shadow-lg:  0 10px 20px rgba(15, 20, 91, 0.08), 0 4px 8px  rgba(15, 20, 91, 0.04);
  --shadow-xl:  0 24px 48px rgba(15, 20, 91, 0.12), 0 8px 16px rgba(15, 20, 91, 0.06);

  /* Motion. */
  --duration-fast:    120ms;
  --duration-default: 200ms;
  --duration-slow:    320ms;
  --ease-standard:    cubic-bezier(0.2, 0, 0, 1);
  --ease-decelerate:  cubic-bezier(0, 0, 0.2, 1);
  --ease-accelerate:  cubic-bezier(0.4, 0, 1, 1);

  /* Layout dimensions. */
  --topbar-h:       48px;
  --page-header-h:  64px;
  --rail-w:         56px;
  --sidebar-w:      240px;
  --sidebar-w-collapsed: 56px;

  /* Rail-local tokens. Redesign 2026.1 — the rail was a dark navy slab
     (#0B0E1A) that made the app read as "too blue". It is now a light
     surface that merges visually with the workspace panel into one calm
     light nav. The tokens point at adaptive role tokens so the rail also
     stays correct in dark mode without a [data-theme] override. Navy
     survives only as the active *icon* ink (a small, deliberate dose) via
     --text-accent (navy in light, navy-300 in dark so it clears the dark
     surface). Keeps §10's "no hex outside site.css" rule intact. */
  --rail-bg:           var(--bg-surface);
  --rail-border:       var(--divider);
  --rail-text:         var(--text-muted);
  --rail-text-hover:   var(--text-strong);
  --rail-active-bg:    var(--bg-selected);
  --rail-active-text:  var(--text-accent);
  --rail-badge-bg:     var(--error);
  --rail-badge-text:   #FFFFFF;
}

/* Dark-mode tokens. Resolved pre-paint by the _Layout head script from
   localStorage('kit-theme') then prefers-color-scheme; toggled in-app
   via the user menu and the Cmd+K palette. Only role + semantic tokens
   flip here — the navy and grey ramps keep their light values, so
   component CSS must consume role tokens (never a raw ramp step) to
   adapt. */
[data-theme="dark"] {
  --bg-app:         #0B0D14;
  --bg-surface:     #11141D;
  --bg-raised:      #171B26;
  --bg-sunken:      #0E111B;
  --bg-hover:       #1E2331;
  --bg-selected:    #1E2640;

  --text-strong:    #FFFFFF;
  --text:           #D8DCE6;
  --text-muted:     #8B92A3;
  --text-subtle:    #6B7280;
  /* Lift the accent-derived ink toward white so links / active text / focus
     clear the dark surface, whatever accent is chosen (mirrors the old
     navy-700→navy-400/300 lift, now accent-relative). */
  --accent-soft:    color-mix(in srgb, var(--accent) 22%, var(--bg-surface));
  --text-link:      color-mix(in srgb, var(--accent) 55%, #FFF);
  --text-link-hover:color-mix(in srgb, var(--accent) 70%, #FFF);
  --text-accent:    color-mix(in srgb, var(--accent) 60%, #FFF);

  --border:         #262B3A;
  --border-strong:  #323849;
  --divider:        #1E2331;
  --focus-ring:     color-mix(in srgb, var(--accent) 60%, #FFF);

  --success:    #22C55E;  --success-bg: #052E16;
  --warning:    #F59E0B;  --warning-bg: #422006;
  --error:      #F87171;  --error-bg:   #450A0A;
  --info:       #38BDF8;  --info-bg:    #082F49;
  --accent-violet: #A78BFA;  --accent-violet-bg: #2E1065;
}

/* Page background shades (user-menu "Background" picker). Each tone is declared
   ONCE as a --bg-shade-* token: both the applied --bg-app (below) and the menu
   swatch preview (user-menu.js sets --sw: var(--bg-shade-<key>)) reference it,
   so the preview can never drift from the real background. Light-mode only —
   the :not([data-theme="dark"]) guard keeps the chosen light tone from leaking
   into dark mode (which keeps its own --bg-app). Default (no data-bg) = the cool
   --bg-app in :root. Surfaces stay white, so they float on any shade. */
:root {
  /* Light near-white tones only — white, snow, and a few barely-there steps.
     No grey/dark shades. Clean white family; the accent is the only colour. */
  --bg-shade-white:      #FFFFFF;
  --bg-shade-snow:       #FCFDFE;
  --bg-shade-porcelain:  #FAFBFC;
  --bg-shade-pearl:      #F8F9FB;
  --bg-shade-alabaster:  #F6F7F9;
  --bg-shade-mist:       #F4F5F7;
}
:root[data-bg="white"]:not([data-theme="dark"])      { --bg-app: var(--bg-shade-white); }
:root[data-bg="snow"]:not([data-theme="dark"])       { --bg-app: var(--bg-shade-snow); }
:root[data-bg="porcelain"]:not([data-theme="dark"])  { --bg-app: var(--bg-shade-porcelain); }
:root[data-bg="pearl"]:not([data-theme="dark"])      { --bg-app: var(--bg-shade-pearl); }
:root[data-bg="alabaster"]:not([data-theme="dark"])  { --bg-app: var(--bg-shade-alabaster); }
:root[data-bg="mist"]:not([data-theme="dark"])       { --bg-app: var(--bg-shade-mist); }

/* The Background picker only affects light mode (the rules above are
   dark-guarded), so hide the section in dark mode rather than offer a
   control that silently can't take effect. */
[data-theme="dark"] .user-menu__bg { display: none; }

/* -----------------------------------------------------------
   2. Reset + base
   ----------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  -moz-tab-size: 4;
  tab-size: 4;
  font-family: var(--font-sans);
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
  /* Slim the chunky 15px native scrollbar to the thin variant modern sites
     use. Native browser scrollbar, just narrower — keeps the OS's neutral
     colour, no custom styling or library. */
  scrollbar-width: thin;
}

body {
  font: 400 var(--t-body)/20px var(--font-sans);
  color: var(--text);
  background: var(--bg-app);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

a {
  color: var(--text-link);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-standard);
}
a:hover { color: var(--text-link-hover); }
a:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 2px;
}

code, pre, .mono, [data-mono] {
  font-family: var(--font-mono);
  font-feature-settings: 'tnum', 'cv02', 'cv03';
}
code {
  background: var(--bg-sunken);
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  font-size: 0.92em;
  color: var(--text-strong);
}

/* Tabular nums for any element that renders numeric data. */
.tabular, time, [data-tabular] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}

hr {
  border: none;
  border-top: 1px solid var(--divider);
  margin: var(--space-8) 0;
}

/* -----------------------------------------------------------
   3. Typography
   ----------------------------------------------------------- */

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-sans);
  color: var(--text-strong);
  margin: 0 0 var(--space-3);
  line-height: 1.25;
  font-weight: 600;
}
h1 { font-size: var(--t-heading-lg); line-height: 30px; letter-spacing: -0.018em; }
h2 { font-size: var(--t-heading-md); line-height: 28px; letter-spacing: -0.015em; }
h3 { font-size: var(--t-heading-sm); line-height: 22px; letter-spacing: -0.01em; }
h4 { font-size: var(--t-body-md);    line-height: 22px; letter-spacing: -0.005em; }

p { margin: 0 0 var(--space-3); }
small, .caption { font-size: var(--t-caption); color: var(--text-muted); }
.muted { color: var(--text-muted); }
.subtle { color: var(--text-subtle); }

/* -----------------------------------------------------------
   4. Layout shell
   ----------------------------------------------------------- */

.site-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: var(--rail-w) var(--sidebar-w) 1fr;
  grid-template-rows: var(--topbar-h) 1fr auto;
  grid-template-areas:
    "header header header"
    "rail   side   main"
    "rail   side   footer";
  background: var(--bg-app);
}

.site-header {
  grid-area: header;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--divider);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-5);
  gap: var(--space-4);
  position: sticky;
  top: 0;
  z-index: 50;
}

.site-header .brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 600;
  font-size: var(--t-body-md);
  color: var(--text-strong);
  text-decoration: none;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.site-header .brand::before {
  content: '';
  width: 24px; height: 24px;
  background: url('../img/logo.png') center / contain no-repeat;
  flex-shrink: 0;
}
.site-header .brand:hover { color: var(--text-strong); }

.topbar-cluster {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1;
  min-width: 0;
}
.topbar-cluster--right {
  justify-content: flex-end;
  flex: 0 0 auto;
}
.topbar-spacer { flex: 1; }

.site-rail {
  grid-area: rail;
  background: var(--rail-bg);
  border-right: 1px solid var(--rail-border);
  overflow: hidden;
  padding: var(--space-2) 0;
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  display: flex;
  flex-direction: column;
}

.site-side {
  grid-area: side;
  background: var(--bg-surface);
  border-right: 1px solid var(--divider);
  overflow-y: auto;
  padding: var(--space-3) 0 var(--space-4);
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
}

.site-main {
  grid-area: main;
  padding: var(--space-5) var(--space-6);
  overflow-x: auto;
  min-width: 0;
}

/* Sidebar collapse — the dark rail stays visible; the light workspace
   panel slides to zero. Triggered by sidebar-toggle.js (`[` key or the
   toggle button), or restored pre-paint from kit-sidebar in localStorage. */
[data-sidebar="collapsed"] .site-shell {
  grid-template-columns: var(--rail-w) 0 1fr;
}
[data-sidebar="collapsed"] .site-side {
  width: 0;
  min-width: 0;
  overflow: hidden;
  border-right: 0;
  padding: 0;
}

/* Attention badge on a nav item (e.g. My work overdue count). Error
   tint so it reads as "needs attention" without alarm; role tokens flip
   in dark mode. margin-left:auto pins it to the link's trailing edge.
   Hidden when the rail is collapsed (no room beside the icon). */
.side-nav-badge {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-full);
  background: var(--error-bg);
  color: var(--error);
  font: 600 var(--t-caption)/18px var(--font-sans);
  text-align: center;
  flex-shrink: 0;
}
/* Toggle button: rotate chevron to point right when collapsed. */
[data-sidebar="collapsed"] .side-nav-toggle-icon,
[data-sidebar="collapsed"] .rail-toggle-icon {
  transform: rotate(180deg);
}

.site-footer {
  grid-area: footer;
  border-top: 1px solid var(--divider);
  padding: var(--space-3) var(--space-6);
  color: var(--text-muted);
  font-size: var(--t-caption);
  text-align: center;
  background: var(--bg-surface);
}

/* -----------------------------------------------------------
   4b. Responsive shell (PRD §10.5 — usable down to 360px).
   Desktop is the fixed 3-column grid above. At ≤900px the rail +
   workspace sidebar become a single off-canvas drawer toggled from
   the topbar hamburger; main goes full-width. At ≤560px the topbar
   sheds the brand text, Help button, and Cmd+K label so the timer
   pill (a named must-work surface) always fits. Everything here is
   behind the media queries — desktop is untouched.
   ----------------------------------------------------------- */

/* These two live only on small screens; hidden by default so they
   never affect the desktop grid. The hamburger is scoped under
   .site-header so it out-specifies the later `.icon-btn` base rule
   (both are single-class; source order would otherwise un-hide it). */
.site-header .mobile-nav-toggle { display: none; }
.mobile-nav-scrim { display: none; }

@media (max-width: 900px) {
  /* Collapse to a single content column. Override the desktop
     sidebar-collapsed rule too, since a stale kit-sidebar=collapsed
     from a desktop session must not zero the drawer here. */
  .site-shell,
  [data-sidebar="collapsed"] .site-shell {
    /* minmax(0, 1fr) — NOT 1fr. A bare 1fr track's minimum is its
       content's min-content, so the header cluster / wide task rows would
       blow the column past the viewport (and then get clipped). The 0
       minimum pins the column to the viewport; wide content scrolls
       inside .site-main (which is overflow-x:auto) instead. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "header"
      "main"
      "footer";
    /* Clip (not hidden — preserves the sticky header) so the off-screen
       drawers parked beyond the right edge don't create a horizontal
       page scrollbar at narrow widths. */
    overflow-x: clip;
  }

  /* Rail + sidebar become a fixed off-canvas drawer, sitting below
     the sticky header. visibility:hidden (not just the off-screen
     transform) keeps their links out of the tab order + AT tree when
     closed — a keyboard/SR user must not land in an invisible drawer. */
  .site-rail,
  .site-side,
  [data-sidebar="collapsed"] .site-side {
    position: fixed;
    top: var(--topbar-h);
    height: calc(100vh - var(--topbar-h));
    z-index: 60;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform var(--duration-default) var(--ease-standard),
                visibility var(--duration-default) var(--ease-standard);
  }
  .site-rail { left: 0; width: var(--rail-w); }
  /* Restore the sidebar's chrome that the desktop collapsed rule strips. */
  .site-side,
  [data-sidebar="collapsed"] .site-side {
    left: var(--rail-w);
    width: var(--sidebar-w);
    min-width: var(--sidebar-w);
    border-right: 1px solid var(--divider);
    padding: var(--space-3) 0 var(--space-4);
  }

  [data-mobile-nav="open"] .site-rail,
  [data-mobile-nav="open"] .site-side {
    transform: translateX(0);
    visibility: visible;
  }

  /* Header above the drawer + scrim so the hamburger stays tappable. */
  .site-header { z-index: 70; }

  .site-header .mobile-nav-toggle { display: inline-flex; }

  .mobile-nav-scrim {
    display: block;
    position: fixed;
    top: var(--topbar-h);
    left: 0; right: 0; bottom: 0;
    z-index: 55;
    background: color-mix(in srgb, var(--navy-900) 45%, transparent);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--duration-default) var(--ease-standard),
                visibility var(--duration-default) var(--ease-standard);
  }
  [data-mobile-nav="open"] .mobile-nav-scrim {
    opacity: 1;
    visibility: visible;
  }

  /* Lock the page behind the open drawer so it doesn't scroll under. */
  [data-mobile-nav="open"] body { overflow: hidden; }

  /* Topbar reflow: let the Cmd+K trigger shrink and drop its shortcut
     hint. Scoped under .site-header to beat the later .cmdk-trigger base. */
  .site-header .cmdk-trigger { min-width: 0; }
  .site-header .cmdk-trigger .kbd-hint { display: none; }

  /* Tighter content gutters on small screens. */
  .site-main { padding: var(--space-4); }
}

@media (max-width: 560px) {
  /* Keep the logo mark (.brand::before), drop the wordmark text. */
  .site-header .brand { font-size: 0; gap: 0; }
  /* Help is a keyboard-shortcuts modal — low value on touch; drop it.
     Scoped under .site-header to out-specify the later .icon-btn base. */
  .site-header [data-module="help"] { display: none; }
  /* Cmd+K collapses to an icon-only square so the timer pill always fits. */
  .site-header .cmdk-trigger {
    flex: 0 0 auto;
    width: 32px;
    padding: 0;
    justify-content: center;
  }
  .site-header .cmdk-trigger > span:not(.kbd-hint) { display: none; }
}

/* -----------------------------------------------------------
   5a. Primary rail — dark icon-only rail (ClickUp pattern).
   Always visible, in both light + dark mode. Consumes rail-local
   tokens (--rail-bg, --rail-text, …) which don't flip with theme.
   ----------------------------------------------------------- */

.rail-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  padding: 0 var(--space-1);
}

.rail-spacer { flex: 1 1 auto; }

.rail-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--rail-text);
  text-decoration: none;
  border: none;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
}
.rail-link:hover {
  background: var(--rail-active-bg);
  color: var(--rail-text-hover);
  text-decoration: none;
}
.rail-link.active {
  background: var(--rail-active-bg);
  color: var(--rail-active-text);
}
.rail-link:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--rail-active-text),
              0 0 0 4px color-mix(in srgb, var(--rail-active-text) 30%, transparent);
}

.rail-icon { width: 18px; height: 18px; flex-shrink: 0; }
/* Colourful rail icons (2026.2) — each destination carries a distinct hue at
   rest (ClickUp-style energy), independent of the accent. Hover/active still
   lift the row background. Hues hold their own on both light + dark rails. */
.rail-link[data-icon="home"]      .rail-icon { color: #6366F1; }
.rail-link[data-icon="mywork"]    .rail-icon { color: #2563EB; }
.rail-link[data-icon="timer"]     .rail-icon { color: #D97706; }
.rail-link[data-icon="activity"]  .rail-icon { color: #0D9488; }
.rail-link[data-icon="timesheet"] .rail-icon { color: #DB2777; }
.rail-link[data-icon="reports"]   .rail-icon { color: #16A34A; }
.rail-link[data-icon="dashboard"] .rail-icon { color: #7C3AED; }
.rail-link[data-icon="manager"]   .rail-icon { color: #0891B2; }
.rail-link[data-icon="admin"]     .rail-icon { color: #E11D48; }
.rail-link[data-icon="config"]    .rail-icon { color: #64748B; }

/* Attention badge: small numeric pip pinned to the icon's top-right.
   No labels in the rail, so the badge floats over the icon. */
.rail-badge {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-full);
  background: var(--rail-badge-bg);
  color: var(--rail-badge-text);
  font: 600 11px/16px var(--font-sans);
  text-align: center;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--rail-bg);
}

/* Collapse toggle as a rail item — sits at the bottom of the rail. */
.rail-toggle-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  transition: transform var(--duration-default) var(--ease-standard);
}

/* -----------------------------------------------------------
   5. Sidebar / workspace nav
   ----------------------------------------------------------- */

.side-nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: 0 var(--space-2);
}

.side-nav-section {
  display: flex;
  flex-direction: column;
  padding: var(--space-1) 0;
  border-bottom: none;  /* dividers between groups feel heavy; rely on spacing */
  gap: 1px;
}
.side-nav-section + .side-nav-section { margin-top: var(--space-3); }
.side-nav-footer { margin-top: auto; padding-top: var(--space-3); }

.side-nav-heading {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
  padding: var(--space-2) var(--space-3) var(--space-1);
}

.side-nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-3);
  height: 30px;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  border-radius: var(--radius-sm);
  transition:
    background var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.side-nav-link:hover {
  background: var(--bg-hover);
  color: var(--text-strong);
  text-decoration: none;
}
.side-nav-link.active,
.side-nav-item.active .side-nav-workspace {
  background: var(--accent-soft);
  color: var(--text-accent);
  font-weight: 600;
}

.side-nav-meta { font-weight: 500; }

.side-nav-list { list-style: none; margin: 0; padding: 0; }
.side-nav-item { margin: 0; }

.side-nav-empty {
  padding: var(--space-2) var(--space-3);
  font-size: var(--t-body-sm);
  color: var(--text-muted);
  margin: 0;
}

/* Collapse toggle row — sits at the very top of the nav. */
.side-nav-toggle-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: var(--space-1) var(--space-2);
  margin-bottom: var(--space-1);
}

/* Toggle button — ghost style, icon-button dimensions. */
.side-nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: var(--radius-sm);
  color: var(--text-subtle);
  border: none;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
  flex-shrink: 0;
}
.side-nav-toggle:hover {
  background: var(--bg-hover);
  color: var(--text-strong);
}
.side-nav-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring),
              0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}

/* Chevron rotates smoothly on collapse/expand. */
.side-nav-toggle-icon {
  flex-shrink: 0;
  transition: transform var(--duration-default) var(--ease-standard);
}

/* Nav item icons — 16px, colour inherits from the link. */
.side-nav-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  opacity: 0.7;
  transition: opacity var(--duration-fast) var(--ease-standard);
}
.side-nav-link:hover .side-nav-icon,
.side-nav-link.active .side-nav-icon,
.side-nav-item.active > .side-nav-link .side-nav-icon {
  opacity: 1;
}

/* Workspace letter-avatar badge — legacy hook from when the sidebar
   narrowed to a 56px rail; the dark icon rail now lives separately
   (see §5b). Kept as `display:none` so any straggler markup stays
   invisible. Safe to remove once Razor templates drop the span. */
.side-nav-ws-avatar { display: none; }

/* -----------------------------------------------------------
   Sidebar project tree (ClickUp space → list). A disclosure
   caret + workspace link share a row; the project list expands
   below. The active workspace's tree is server-rendered open;
   others lazy-load via sidebar-tree.js. The workspace link
   keeps flex:1 so it fills the row beside the caret.
   ----------------------------------------------------------- */
.side-nav-ws-row {
  display: flex;
  align-items: center;
  gap: var(--space-0_5);
}
.side-nav-ws-row .side-nav-workspace { flex: 1 1 auto; min-width: 0; }

/* Drag handle for workspace reorder (admins only). Hover/focus-revealed 6-dot
   grip, mirrors the task-list .row-drag-handle. SortableJS uses it as the
   handle so the disclosure toggle / links stay clickable. */
.side-nav-drag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 12px;
  color: var(--text-subtle);
  cursor: grab;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-standard);
}
.side-nav-item:hover .side-nav-drag,
.side-nav-item:focus-within .side-nav-drag { opacity: 0.7; }
.side-nav-drag:hover { opacity: 1; }
.side-nav-drag:active { cursor: grabbing; }
.side-nav-item--ghost { opacity: 0.5; background: var(--bg-selected); }

.side-nav-ws-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 28px;
  padding: 0;
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: var(--text-subtle);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color var(--duration-fast) var(--ease-standard),
              background var(--duration-fast) var(--ease-standard);
}
.side-nav-ws-toggle:hover { color: var(--text); background: var(--bg-hover); }
.side-nav-ws-toggle:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.side-nav-ws-caret { transition: transform var(--duration-fast) var(--ease-standard); }
.side-nav-ws-toggle[aria-expanded="true"] .side-nav-ws-caret { transform: rotate(90deg); }

/* Workspace row as a single disclosure button (caret + name). Wears the
   .side-nav-link look; these reset the base <button> chrome (class beats the
   element-selector base) and lay out caret + ellipsised label full-width. */
.side-nav-ws-btn {
  width: 100%;
  height: 30px;
  justify-content: flex-start;
  /* <button> defaults to text-align:center; the flex:1 label span would
     inherit it and centre short workspace names within the row. Pin left
     so every name aligns to the caret regardless of length. */
  text-align: left;
  border: none;
  background: transparent;
  letter-spacing: 0;
}
.side-nav-ws-btn:hover { background: var(--bg-hover); }
.side-nav-ws-btn .side-nav-ws-caret { flex: 0 0 auto; color: var(--text-subtle); }
.side-nav-ws-btn .side-nav-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.side-nav-ws-btn[aria-expanded="true"] .side-nav-ws-caret { transform: rotate(90deg); }
/* The grid link in the tree (replaces the old name→grid navigation). */
.side-nav-tree-action--grid { font-weight: 500; color: var(--text); }

.side-nav-project-tree {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px 0 var(--space-1) var(--space-5);
}
/* The display:flex above (class specificity 0,1,0) outranks the UA
   [hidden]=display:none, so collapsing via the hidden attribute wouldn't
   hide the tree. Restate it at 0,2,0 (same trap/fix as .drawer-body,
   .quick-add-row--full, .mywork-tags__clear). */
.side-nav-project-tree[hidden] { display: none; }
.side-nav-tree-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
/* Cap the inline project list so a workspace with many (50+) projects scrolls
   within the tree instead of pushing the pinned "+ New"/"All …" and the
   secondary actions far down the rail. ~13 rows before an inner scrollbar. */
.side-nav-tree-list { max-height: 40vh; overflow-y: auto; }
.side-nav-tree-item {
  display: block;
  height: 26px;
  line-height: 26px;
  padding: 0 var(--space-2);
  font-size: var(--t-body-sm);
  font-weight: 400;
  color: var(--text-muted);
  text-decoration: none;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.side-nav-tree-item:hover { text-decoration: none; color: var(--text-strong); background: var(--bg-hover); }
.side-nav-tree-item.active { color: var(--text-accent); font-weight: 600; background: var(--bg-selected); }
.side-nav-tree-empty {
  padding: 0 var(--space-2);
  height: 26px;
  line-height: 26px;
  font-size: var(--t-body-sm);
  color: var(--text-subtle);
}
/* Loading shimmer shown while a non-active workspace's project tree
   lazy-loads (sidebar-tree.js). Sized to sit on the 26px tree-item
   rhythm; staggered widths read as "loading" rather than a single bar. */
.side-nav-tree-skel {
  display: block;
  height: 14px;
  margin: 6px var(--space-2);
}
.side-nav-tree-skel--a { width: 78%; }
.side-nav-tree-skel--b { width: 60%; }
.side-nav-tree-actions {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: var(--space-0_5);
}
/* Primary actions ("All …", "+ New …") pinned above the project list: no gap
   above (they sit right under the workspace row) and a small gap below to
   separate them from the list. */
.side-nav-tree-actions--primary { margin-top: 0; margin-bottom: var(--space-0_5); }
.side-nav-tree-action {
  display: flex;
  align-items: center;
  height: 26px;
  padding: 0 var(--space-2);
  font-size: var(--t-body-sm);
  font-weight: 400;
  color: var(--text-subtle);
  text-decoration: none;
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.side-nav-tree-action:hover { text-decoration: none; color: var(--text-accent); background: var(--bg-hover); }

/* Workspace-rail live filter — rendered only when > 8 workspaces.
   Sits above the workspace list; styled to match the rail's white
   surface without standing out as a heavy search bar. Role tokens
   only — switches correctly in dark mode. */
.side-nav-filter {
  padding: var(--space-1) var(--space-2) var(--space-1_5);
}
.side-nav-filter__input {
  display: block;
  width: 100%;
  height: 28px;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: var(--t-body-sm)/1 var(--font-sans);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow   var(--duration-fast) var(--ease-standard);
}
.side-nav-filter__input::placeholder { color: var(--text-subtle); }
.side-nav-filter__input:focus-visible {
  outline: none;
  border-color: color-mix(in srgb, var(--focus-ring) 55%, var(--border-strong));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
/* Hide the native ×-clear button in WebKit/Blink — low visual priority
   in this compact rail; the Escape handler covers the clear use-case. */
.side-nav-filter__input::-webkit-search-cancel-button { display: none; }


/* -----------------------------------------------------------
   6. Top-bar elements
   ----------------------------------------------------------- */

/* Cmd+K trigger — input-styled button, wired by cmdk.js (opens the
   palette on click + binds ⌘/Ctrl+K). Grows to fill the top bar so
   the global jump-to reads as the primary surface it is (ClickUp/
   Linear give search generous central width). */
.cmdk-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 32px;
  padding: 0 var(--space-3);
  flex: 1 1 auto;
  min-width: 220px;
  max-width: 560px;
  /* Clip the label to the button. Below 900px min-width drops to 0 and
     the trigger flex-shrinks when the brand + right cluster (timer pill
     grows with the hour count) leave no room; without this the
     placeholder text escaped the box and overlapped the brand wordmark.
     Box-shadow (focus ring) is painted outside the box and is NOT
     clipped by overflow, so focus styling is unaffected. */
  overflow: hidden;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font: 400 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
/* Keep the search glyph put while the label yields. */
.cmdk-trigger > svg { flex-shrink: 0; }
/* The label is the shrinkable item: it must be allowed below its
   content width (min-width:0) to ellipsize instead of spilling. At the
   tightest sizes it collapses to nothing and only the icon shows —
   mirroring the explicit ≤560px icon-only collapse below. */
.cmdk-trigger > span:not(.kbd-hint) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.cmdk-trigger:hover {
  background: var(--bg-surface);
  border-color: var(--border-strong);
  color: var(--text);
}
.cmdk-trigger:focus-visible {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 22%, transparent);
}
.cmdk-trigger .kbd-hint { margin-left: auto; }

/* Persistent "+ New" quick-create button in the top bar. A PM tool's
   most-used affordance — previously only reachable via the C shortcut
   or the palette. Opens the command palette (Create section) by
   triggering the [data-cmdk-open] delegate wired in cmdk.js. Navy,
   compact; the icon-only collapse on narrow screens keeps the bar tidy. */
.topbar-new {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 32px;
  padding: 0 var(--space-3);
  flex-shrink: 0;
  /* Redesign 2026.1 — was a solid navy block, the loudest "too blue"
     offender in the chrome. Now a neutral outline button (matches
     .btn-secondary) so the top bar stays all-white; navy is reserved for
     the single primary CTA inside each page, not the global + New. */
  background: var(--bg-surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.topbar-new:hover { background: var(--bg-hover); border-color: var(--border-strong); color: var(--text-strong); }
.topbar-new:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}
.topbar-new svg { flex-shrink: 0; }
@media (max-width: 720px) {
  .topbar-new__label { display: none; }
  .topbar-new { padding: 0 var(--space-2); }
}

.kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 4px;
  font: 600 11px/1 var(--font-mono);
  color: var(--text-muted);
}

/* Header-mounted timer pill + popover. */
.timer-cluster {
  position: relative;
  display: inline-flex;
}
.timer-indicator {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 30px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-full);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  color: var(--text);
  font: 500 var(--t-body-sm)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition: all var(--duration-fast) var(--ease-standard);
}
.timer-indicator::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--grey-400);
  flex: 0 0 auto;
}
.timer-indicator[data-state="running"] {
  background: color-mix(in srgb, var(--success) 8%, var(--bg-surface));
  border-color: color-mix(in srgb, var(--success) 30%, var(--border));
  color: var(--success);
}
.timer-indicator[data-state="running"]::before {
  background: var(--success);
  animation: pulse-dot 1.6s var(--ease-standard) infinite;
}
.timer-indicator[data-state="paused"] {
  background: var(--warning-bg);
  border-color: color-mix(in srgb, var(--warning) 30%, var(--border));
  color: var(--warning);
}
.timer-indicator[data-state="paused"]::before { background: var(--warning); }

/* Runaway/overrun — a timer past the idle threshold (App:TimerIdleHours),
   running OR paused. Error-tinted with the same dot pulse the running
   state uses, so a forgotten timer is noticeable. Overrides running/paused. */
.timer-indicator.timer-pill--overrun {
  background: var(--error-bg);
  border-color: color-mix(in srgb, var(--error) 35%, var(--border));
  color: var(--error);
}
.timer-indicator.timer-pill--overrun::before {
  background: var(--error);
  animation: pulse-dot 1.6s var(--ease-standard) infinite;
}
.timer-pill.timer-pill--overrun:hover {
  background: color-mix(in srgb, var(--error) 14%, var(--bg-surface));
}

/* Click-target flavor of the pill. */
.timer-pill {
  cursor: pointer;
  user-select: none;
  font-family: var(--font-mono);
}
.timer-pill:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}
.timer-pill[data-state="running"]:hover {
  background: color-mix(in srgb, var(--success) 14%, var(--bg-surface));
}
.timer-pill[data-state="paused"]:hover {
  background: color-mix(in srgb, var(--warning) 14%, var(--bg-surface));
}
.timer-pill:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring),
              0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}
.timer-pill__label { color: inherit; }

@keyframes pulse-dot {
  0%, 100% { opacity: 1;    transform: scale(1);    }
  50%      { opacity: 0.55; transform: scale(1.25); }
}

.timer-popover {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 50;
  min-width: 280px;
  max-width: 340px;
  padding: var(--space-4);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-sans);
  color: var(--text);
  animation: timer-popover-in var(--duration-fast) var(--ease-standard);
}
.timer-popover[hidden] { display: none; }
.timer-popover__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.timer-popover__title {
  font: 600 var(--t-heading-sm)/1.2 var(--font-sans);
  color: var(--text-strong);
  letter-spacing: -0.01em;
}
.timer-popover__code {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  font: 600 var(--t-caption)/1 var(--font-mono);
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.timer-popover__elapsed {
  font: 600 var(--t-heading-md)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
}
.timer-popover__elapsed[data-state="running"] { color: var(--success); }
.timer-popover__elapsed[data-state="paused"]  { color: var(--warning); }
.timer-popover__task {
  font: 600 var(--t-body)/1.35 var(--font-sans);
  color: var(--text-strong);
  margin-bottom: var(--space-1);
  letter-spacing: -0.01em;
}
.timer-popover__meta {
  font: 400 var(--t-caption)/1.4 var(--font-sans);
  color: var(--text-muted);
  margin-bottom: var(--space-4);
}
.timer-popover__actions {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.timer-popover__actions .btn-sm {
  flex: 1 1 auto;
}
.timer-popover__empty {
  font: 400 var(--t-body-sm)/1.5 var(--font-sans);
  color: var(--text-muted);
  margin: 0;
}
.timer-popover__notes {
  font: 400 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}
.timer-popover__notes > summary {
  cursor: pointer;
  user-select: none;
  padding: var(--space-1) 0;
  color: var(--text-muted);
}
.timer-popover__notes > summary:hover { color: var(--text); }
.timer-popover__label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: var(--space-2);
  font: 500 var(--t-caption)/1 var(--font-sans);
  color: var(--text-muted);
}
.timer-popover__label textarea {
  width: 100%;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: 400 var(--t-body-sm)/1.4 var(--font-sans);
  resize: vertical;
}
.timer-popover__label textarea:focus-visible {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 2px var(--focus-ring);
}
/* Activity / Tracker pickers in the Stop dialog (timer.js log-options slot). */
.timer-popover__log { display: flex; flex-direction: column; gap: var(--space-1); }
.timer-popover__log select {
  width: 100%; height: 32px; padding: 0 var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-surface); color: var(--text);
  font: 400 var(--t-body-sm)/1 var(--font-sans);
}
.timer-popover__log select:focus-visible {
  outline: none; border-color: var(--focus-ring); box-shadow: 0 0 0 2px var(--focus-ring);
}
.timer-popover__checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font: 400 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
  cursor: pointer;
}
.timer-popover__footer {
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
  font: 400 var(--t-caption)/1 var(--font-sans);
}
.timer-popover__link {
  color: var(--text-link);
  text-decoration: none;
}
.timer-popover__link:hover { color: var(--text-link-hover); text-decoration: underline; }

@keyframes timer-popover-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0);    }
}

/* Icon-only header buttons (notifications, settings, user). */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.icon-btn:hover { background: var(--bg-hover); color: var(--text-strong); }
.icon-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring), 0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}

/* -----------------------------------------------------------
   7. Page header + tabs + breadcrumbs
   ----------------------------------------------------------- */

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  /* Compacted 2026-06-05: every list/dashboard surface moved to
     .list-toolbar; the only remaining .page-header users are forms +
     simple/degenerate pages, which read better tighter. */
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--divider);
}
.page-header > .page-title-block { min-width: 0; }
.page-eyebrow {
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin-bottom: var(--space-1);
}
.page-title {
  font-size: var(--t-heading-md);
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 26px;
  color: var(--text-strong);
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.page-title small {
  font: 400 var(--t-body)/1 var(--font-sans);
  color: var(--text-muted);
  letter-spacing: 0;
}
.page-subtitle {
  margin: var(--space-1) 0 0;
  color: var(--text-muted);
  font-size: var(--t-body-sm);
}
.page-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}

/* ── Condensed list toolbar ──────────────────────────────────────────
   List/index surfaces only (forms keep .page-header). One ~48px band that
   replaces the stacked breadcrumb + eyebrow + 24px title + count subtitle —
   the same workspace name + "Workspace" label were each stated twice. Pinned
   under the top bar so actions + filters stay reachable as the list scrolls.
   top:0 — NOT --topbar-h: the scroll container is .site-main (its overflow-x
   promotes overflow-y to auto), and its scrollport top already sits below the
   global top bar, so an extra topbar-h offset double-counts it and shoves the
   bar down over the filter row. Pins at the scrollport top instead. */
.list-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: var(--space-2) 0;
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--divider);
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--bg-app);
}
.list-toolbar__back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  flex: 0 0 auto;
  padding: var(--space-1) var(--space-2);
  margin-left: calc(-1 * var(--space-2));
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: var(--t-body-sm);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.list-toolbar__back:hover { background: var(--bg-hover); color: var(--text); }
.list-toolbar__back svg { flex: 0 0 auto; }
.list-toolbar__sep {
  flex: 0 0 auto;
  color: var(--text-subtle);
  font-size: var(--t-body);
  user-select: none;
}
.list-toolbar__title {
  margin: 0;
  font-size: var(--t-heading-sm);   /* 16px — right-sized for a dense list */
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.list-toolbar__count {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  background: var(--bg-sunken);
  color: var(--text-muted);
  font-size: var(--t-caption);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.list-toolbar__spacer { flex: 1 1 auto; }
/* Tight variant — for pages whose toolbar holds only a title (no actions),
   so the 48px action-row height reads as wasted space. Trims the row down
   to the title's own height. */
.list-toolbar--tight {
  min-height: 0;
  padding: var(--space-1) 0;
  margin-bottom: var(--space-2);
}
/* Narrow widths: drop the back-link label (chevron still navigates) and the
   "/" separator so the title keeps its room instead of ellipsing to nothing. */
@media (max-width: 640px) {
  .list-toolbar__back span { display: none; }
  .list-toolbar__sep { display: none; }
}

/* (Breadcrumb styling lives in section N — see `.breadcrumbs ol/li/__sep`
   near the bottom of this file. The earlier duplicate block here — which
   targeted dead `.sep`/`.current` classes no markup emits — was removed.) */

/* Backlinks ("← Workspaces") rendered as a subtle ghost button. */
.backlink {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--t-body-sm);
  color: var(--text-muted);
  text-decoration: none;
  margin-bottom: var(--space-3);
  padding: 4px 6px;
  margin-left: -6px;
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.backlink:hover { background: var(--bg-hover); color: var(--text-strong); text-decoration: none; }

/* Tabs — underline style. */
.tabs {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  border-bottom: 1px solid var(--divider);
  margin-bottom: var(--space-5);
  padding: 0;
  list-style: none;
}
.tabs--block { display: flex; }
.tab,
.tabs > a {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 var(--space-3);
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.tab:hover,
.tabs > a:hover { color: var(--text-strong); text-decoration: none; }
.tab.active,
.tabs > a.active,
.tabs > a[aria-current="page"] {
  color: var(--text-accent);
  /* Baby-blue underline (brand secondary) under navy text — a lighter,
     friendlier active cue than a full navy bar. Redesign 2026.1. */
  border-bottom-color: var(--accent-sky);
}

/* Segmented control — for view switchers when a tab feels too heavy. */
.segmented {
  display: inline-flex;
  align-items: center;
  padding: 2px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.segmented > * {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--space-3);
  font-size: var(--t-body-sm);
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  border-radius: 6px;
  transition: all var(--duration-fast) var(--ease-standard);
}
.segmented > *:hover { color: var(--text-strong); }
.segmented > .active,
.segmented > [aria-current="page"] {
  background: var(--bg-surface);
  color: var(--text-strong);
  box-shadow: var(--shadow-xs);
}

/* -----------------------------------------------------------
   8. Buttons
   ----------------------------------------------------------- */

.btn,
button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
  padding: 0 var(--space-3);
  font: 500 var(--t-body)/1 var(--font-sans);
  letter-spacing: -0.005em;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--text-strong);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard);
}
.btn:hover,
button:hover { background: var(--bg-hover); }
.btn:focus-visible,
button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--focus-ring);
}
.btn:disabled,
button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Primary — single most important action per surface. Tracks --accent. */
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--accent-contrast);
}
.btn-primary:active { background: var(--accent-active); }
a.btn-primary, a.btn-primary:hover { color: var(--text-inverse); text-decoration: none; }

/* Secondary — ghost-style. */
.btn-secondary {
  background: var(--bg-surface);
  border-color: var(--border-strong);
  color: var(--text-strong);
}
.btn-secondary:hover { background: var(--bg-hover); border-color: var(--border-strong); }

/* Ghost — transparent. */
.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text);
}
.btn-ghost:hover { background: var(--bg-hover); }

/* Destructive — only in confirmation contexts. */
.btn-danger {
  background: var(--error);
  border-color: var(--error);
  color: var(--text-inverse);
}
.btn-danger:hover { background: color-mix(in srgb, var(--error) 88%, black); border-color: inherit; color: var(--text-inverse); }
a.btn-danger, a.btn-danger:hover { color: var(--text-inverse); text-decoration: none; }

/* Ghost button that *reads* destructive — used for in-row "Remove" /
   "Delete" actions where a full .btn-danger would be too loud. Text
   stays red; hover lifts the error tint just enough to telegraph the
   action without committing to the destructive surface. */
.btn-ghost--danger { color: var(--error); }
.btn-ghost--danger:hover { background: var(--error-bg); color: var(--error); }

/* Size variants. */
.btn-sm { height: 28px; padding: 0 var(--space-2); font-size: var(--t-body-sm); }
.btn-lg { height: 38px; padding: 0 var(--space-4); font-size: var(--t-body-md); }

/* -----------------------------------------------------------
   Bulk-action toolbar (Table view, Phase 56). Sits directly above
   the grid; revealed only when rows are selected (the [hidden]
   attribute drives display). Role tokens keep it correct in dark
   mode without a [data-theme] selector.
   ----------------------------------------------------------- */
/* ClickUp-style floating dock: fixed bottom-center pill, slides up on
   reveal. Sits above the rest of the page (z-index 60 clears the
   topbar's 50). Uses a raised role-token surface so it adapts in dark
   mode without authoring a second skin. The bar tucks above the
   footer/scrollbar with vw-aware padding so it never crowds page-end
   content. */
.bulk-bar {
  position: fixed;
  left: 50%;
  bottom: var(--space-6);
  transform: translateX(-50%);
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-lg);
  max-width: calc(100vw - var(--space-8));
  animation: bulk-bar-in var(--duration-default) var(--ease-decelerate);
}
.bulk-bar[hidden] { display: none; }
@keyframes bulk-bar-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
/* Selection count rendered as a filled navy chip on the left. */
.bulk-bar__count {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: var(--t-body-sm);
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.bulk-bar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: nowrap;
  overflow-x: auto;
}
.bulk-bar__select {
  width: auto;
  height: 28px;
  /* Keep the right padding the global select rule reserves for its
     chevron (28px); only tighten the left to match .btn-sm. */
  padding-left: var(--space-2);
  font-size: var(--t-body-sm);
  max-width: 160px;
}

/* -----------------------------------------------------------
   Phase 60 — bulk-select affordances for List + Kanban. The
   Table view drives selection through AG Grid's built-in
   checkbox column; List + Kanban stamp their own checkboxes via
   `_TaskListTable.cshtml` / `Kanban/Index.cshtml` and the
   `task-bulk-select.js` module.
   ----------------------------------------------------------- */

/* Compact checkbox sized to sit cleanly in a list row's leading
   cell without inflating row height. Native control — no custom
   look — so dark mode + focus ring track the platform. */
.row-select {
  width: 14px;
  height: 14px;
  margin: 0;
  vertical-align: middle;
  cursor: pointer;
  accent-color: var(--accent);
}
.row-select__cell {
  width: 28px;
  padding-right: var(--space-1);
}

/* ── Click-to-complete (Task List, leftmost cell) ──────────────
   ClickUp / Asana convention: an always-visible round checkbox
   per row that flips the task between done and not-done. Sits
   immediately left of the bulk-select checkbox so the two never
   visually compete (the bulk-select hides at rest below). */
.row-complete-cell {
  width: 28px;
  padding-right: var(--space-1);
  text-align: center;
}
.row-complete {
  width: 16px;
  height: 16px;
  margin: 0;
  border-radius: 50%;
  appearance: none;
  -webkit-appearance: none;
  border: 1.5px solid var(--border-strong);
  background: var(--bg-surface);
  cursor: pointer;
  vertical-align: middle;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
  position: relative;
}
.row-complete:hover { border-color: var(--success); }
.row-complete:checked {
  background: var(--success);
  border-color: var(--success);
}
.row-complete:checked::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid var(--text-inverse);
  border-width: 0 1.8px 1.8px 0;
  transform: rotate(45deg);
}
.row-complete:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.row-complete[disabled] {
  cursor: progress;
  opacity: 0.6;
}

/* The per-row bulk-select hides at rest so the round complete
   checkbox is unambiguous as the primary affordance. It reveals
   on row hover, when the row is selected, when the row has
   keyboard focus inside it, OR when the header select-all is
   on. Header checkbox stays always visible. */
.task-list-row .row-select { opacity: 0; transition: opacity var(--duration-fast) var(--ease-standard); }
.task-list-row:hover .row-select,
.task-list-row:focus-within .row-select,
.task-list-row .row-select:checked,
[data-bulk-select-all]:checked ~ * .row-select,
.task-list-row[data-bulk-selected] .row-select { opacity: 1; }

/* Kanban card checkbox lives in the top-left corner. Always
   visible (matches ClickUp) — the cards are tall enough that a
   14px box doesn't crowd the code/title. pointer-events ensures
   the checkbox stays clickable even when the card itself is a
   detail-drawer trigger; the JS module stops the click from
   bubbling to the drawer. */
.card-select {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  width: 14px;
  height: 14px;
  margin: 0;
  cursor: pointer;
  accent-color: var(--accent);
  /* Below the priority/billable chips in z-order so they still
     read on hover, but always clickable. */
  z-index: 1;
}

/* The card is positioned so the absolute checkbox anchors to it. */
.kanban-card { position: relative; }

/* Selected row / card visual treatment — subtle navy tint that
   doesn't fight the existing hover state. Uses role tokens so
   dark mode comes for free. */
.task-list-row.is-bulk-selected,
tr.is-bulk-selected {
  background: var(--bg-selected);
}
.kanban-card.is-bulk-selected {
  background: var(--bg-selected);
  border-color: var(--accent);
}

/* B6 — hint text in the pinned AG Grid add-task row's title cell. */
.ag-add-row-hint {
  color: var(--text-subtle);
  font-style: italic;
  pointer-events: none;
  user-select: none;
}

/* C6 — pointer cursor on AG Grid code cells that open the detail drawer. */
.ag-cell.cell--clickable {
  cursor: pointer;
}

/* "Plain" form-action buttons — secondary style by default if no class.
   Avoids the legacy navy-fill-on-every-submit problem. */
form > button:not([class]),
form button:not([class]) {
  background: var(--bg-surface);
  border-color: var(--border-strong);
  color: var(--text-strong);
  height: 32px;
  padding: 0 var(--space-3);
}
form > button:not([class]):hover,
form button:not([class]):hover { background: var(--bg-hover); }

/* -----------------------------------------------------------
   9. Forms
   ----------------------------------------------------------- */

form { max-width: 640px; }

.form-group,
form > div { margin-bottom: var(--space-4); }

label {
  display: block;
  font: 500 var(--t-body-sm)/18px var(--font-sans);
  color: var(--text-strong);
  margin-bottom: 6px;
}
form small {
  display: block;
  color: var(--text-muted);
  font-size: var(--t-caption);
  line-height: 16px;
  margin-top: 4px;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="search"],
input[type="url"],
input[type="tel"],
select,
textarea {
  width: 100%;
  max-width: 480px;
  height: 36px;
  padding: 0 12px;
  font: 400 var(--t-body)/1 var(--font-sans);
  color: var(--text-strong);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
textarea {
  height: auto;
  min-height: 84px;
  padding: 10px 12px;
  line-height: 1.5;
  resize: vertical;
}
select {
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path fill='%236B7280' d='M3 4.5l3 3 3-3z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 28px;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  /* Calm focus everywhere: a softened (mixed) accent border + a translucent
     halo, instead of a solid dark-navy border that read as a heavy box. */
  border-color: color-mix(in srgb, var(--focus-ring) 55%, var(--border-strong));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
input::placeholder, textarea::placeholder { color: var(--text-subtle); }
input[readonly], input:disabled, select:disabled, textarea:disabled {
  background: var(--bg-sunken);
  color: var(--text-muted);
  cursor: not-allowed;
}

input[type="checkbox"], input[type="radio"] {
  width: 16px; height: 16px;
  accent-color: var(--accent);
  margin-right: 6px;
  vertical-align: -2px;
}

/* -----------------------------------------------------------
   Entity create/edit form — title-first hero + compact 2-col
   property grid. Scoped to .entity-form so the global form
   styles are untouched. Denser, more ClickUp-like than the
   default stacked form.
   ----------------------------------------------------------- */
.entity-form { max-width: 760px; }
/* Hero name/title field — large, full-width, the visual lead. */
.entity-form__hero { margin-bottom: var(--space-4); }
.entity-form__hero label { color: var(--text-muted); font-weight: 500; }
.entity-form__hero input {
  max-width: none;
  height: 44px;
  font: 600 var(--t-heading-md) / 1 var(--font-sans);
  letter-spacing: -0.01em;
}
/* Secondary properties in a 2-column grid — Status / Assignee /
   Priority / dates sit side by side instead of a tall stack. */
.entity-form__grid {
  display: grid;
  /* Auto-reflow: as many ~230px columns as the form's actual width allows
     (container-responsive, not viewport-bound — so it adapts to the real
     content width whether the sidebar is open or collapsed). auto-FIT (not
     auto-fill) collapses empty trailing tracks and lets the row's fields
     grow to fill the width — so a short last row spreads out instead of
     leaving a ragged band of dead space on the right. */
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}
.entity-form__grid .form-group { margin-bottom: 0; }
.entity-form__grid select,
.entity-form__grid input { max-width: none; }
.entity-form__full { max-width: none; }
.entity-form__full textarea { max-width: none; }
@media (max-width: 560px) {
  .entity-form__grid { grid-template-columns: 1fr; }
}

/* Compact vertical rhythm so create/edit forms fit on one screen without
   scrolling — tighter section margins, grid row gap, label spacing, and a
   shorter default description box (still resizable; min-height was 84px). */
.entity-form__hero { margin-bottom: var(--space-3); }
.entity-form__grid { gap: var(--space-2) var(--space-4); margin-bottom: var(--space-3); }
.entity-form > .form-group,
.entity-form > fieldset { margin-bottom: var(--space-3); }
.entity-form label { margin-bottom: var(--space-1); }
.entity-form__full textarea { min-height: 52px; }

/* -----------------------------------------------------------
   Quick-create refinements (Projects/Tasks create form).
   Sectioned layout + collapsible Details + sticky action bar so
   the fast path is "name → Create" while the full field set stays
   one click away. Additive; the base .entity-form rules still apply.
   ----------------------------------------------------------- */
/* Fluid up to a generous cap — the form scales with the content area
   (filling the old dead space on wide screens) while staying readable; the
   auto-fill grid above turns the extra width into more columns, not stretched
   single inputs. */
.entity-form { max-width: 1280px; }

.entity-form__section { margin-bottom: var(--space-4); }
.entity-form__section-head {
  display: flex; align-items: baseline; gap: var(--space-2);
  margin: 0 0 var(--space-3);
}
.entity-form__section-title {
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-strong);
  letter-spacing: -0.01em;
}
.entity-form__section-hint {
  font: var(--t-caption)/1 var(--font-sans);
  color: var(--text-subtle);
}

/* Full-width cell inside the 2-col grid — tall pickers + notes. */
.entity-form__grid .form-group--wide { grid-column: 1 / -1; }
/* Checkbox row has no field above it — center it against its taller
   neighbour instead of pinning to the top of the cell. */
.entity-form__grid .form-group--checkbox { align-self: center; }
.entity-form__grid .form-group--checkbox label { margin-bottom: 0; }

/* Collapsible "Details" — native <details>, a quiet panel so the
   quick path stays uncluttered. Opens automatically when a field is
   required or a validation error landed inside it (see the view). */
.entity-form__details {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  margin-bottom: var(--space-4);
}
.entity-form__details > summary {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  cursor: pointer; list-style: none; user-select: none;
  font: 600 var(--t-body-sm)/1 var(--font-sans); color: var(--text-strong);
}
.entity-form__details > summary::-webkit-details-marker { display: none; }
.entity-form__details > summary::after {
  content: ""; width: 7px; height: 7px; margin-left: auto;
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  transform: rotate(45deg);
  transition: transform var(--duration-fast) var(--ease-standard);
}
.entity-form__details[open] > summary::after { transform: rotate(-135deg); }
.entity-form__details > summary:focus-visible {
  outline: none; box-shadow: inset 0 0 0 2px var(--focus-ring);
  border-radius: var(--radius-md);
}
.entity-form__details-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 var(--space-1);
  background: var(--bg-sunken); color: var(--text-muted);
  border-radius: var(--radius-full); font: 600 var(--t-caption)/1 var(--font-sans);
}
.entity-form__details-hint { margin-left: auto; color: var(--text-subtle);
  font: var(--t-caption)/1 var(--font-sans); }
.entity-form__details[open] .entity-form__details-hint { display: none; }
.entity-form__details-body { padding: 0 var(--space-4) var(--space-4); }
.entity-form__details-body .entity-form__grid { margin-bottom: 0; }

/* Sticky action bar — Create/Cancel stay in reach on a long form.
   Bleeds to the card edges (card padding = --space-4). */
.entity-form__actions {
  position: sticky; bottom: 0; z-index: 1;
  display: flex; align-items: center; gap: var(--space-2);
  /* Actions sit on the RIGHT, uniformly across every form. */
  justify-content: flex-end;
  margin: var(--space-2) calc(-1 * var(--space-4)) calc(-1 * var(--space-4));
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border-top: 1px solid var(--divider);
  border-bottom-left-radius: var(--radius-md);
  border-bottom-right-radius: var(--radius-md);
}

/* One quiet entrance on load — refined, not flashy. */
@media (prefers-reduced-motion: no-preference) {
  .entity-form {
    animation: entityFormIn var(--duration-default) var(--ease-standard) both;
  }
}
@keyframes entityFormIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-4);
  background: var(--bg-surface);
}
fieldset legend {
  font-weight: 600;
  font-size: var(--t-body-sm);
  color: var(--text-strong);
  padding: 0 var(--space-2);
}
fieldset label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: var(--space-4);
  font-weight: 400;
  font-size: var(--t-body);
  color: var(--text);
  margin-bottom: 0;
}

.field-validation-error {
  display: block;
  color: var(--error);
  font-size: var(--t-caption);
  margin-top: 4px;
}
.validation-summary-errors {
  background: var(--error-bg);
  border: 1px solid color-mix(in srgb, var(--error) 30%, var(--border));
  border-left: 3px solid var(--error);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font-size: var(--t-body-sm);
  margin-bottom: var(--space-4);
}
.validation-summary-errors ul { margin: 0; padding-left: var(--space-4); }

/* -----------------------------------------------------------
   10. Cards
   ----------------------------------------------------------- */

.card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  /* Soft lift so cards float on the cool page (2026.2, was --shadow-xs). */
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.card--hoverable:hover { box-shadow: var(--shadow-md); border-color: var(--border-strong); }
/* Card that hosts a list/table directly — zero padding on the outer
   shell so the inner table's own padding carries the rhythm. The
   inner table loses its own border (the card already has one) and
   the card clips its rounded corners around the table. */
.card--flush { padding: 0; overflow: hidden; }
.card--flush > table { border: none; margin: 0; }
.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.card-title {
  margin: 0;
  font-size: var(--t-heading-sm);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.card-subtitle {
  margin: var(--space-1) 0 0;
  font: 400 var(--t-body-sm)/1.5 var(--font-sans);
  color: var(--text-muted);
}
.card-body { color: var(--text); font-size: var(--t-body); }
.card-footer { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--divider); }

/* ============================================================
   Task row — used on Home (Today/Up next/Past due), Tasks/Index,
   and any future list view. ClickUp/Monday-style: code + title
   (click-to-edit) + status pill on top, meta + actions below.
   The title takes most of the width and never wraps under the
   pill. Row hover highlights the whole row; the Start affordance
   only appears on hover/focus to keep the resting state calm.
   ============================================================ */
.task-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}
/* Home dashboard lists (Today / Up next / Past due) can run long —
   Past due routinely holds dozens of rows. Cap each list's height so
   it scrolls inside its own card rather than stretching the whole
   page. Negative inline margin + matching padding keeps the row
   hover/divider full-bleed while reserving space for the scrollbar. */
.task-list--scroll {
  max-height: 26rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-inline: calc(-1 * var(--space-2));
  padding-inline: var(--space-2);
}
.task-list--scroll::-webkit-scrollbar { width: 10px; }
.task-list--scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid var(--bg-surface);
  border-radius: var(--radius-full);
}
.task-list--scroll::-webkit-scrollbar-thumb:hover { background: var(--text-subtle); }
.task-list--scroll { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
.task-row {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--space-3);
  row-gap: var(--space-0_5);
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-sm);
  border-top: 1px solid var(--divider);
  transition: background var(--duration-fast) var(--ease-standard);
}
.task-row:first-child { border-top: none; }
.task-row:hover { background: var(--bg-hover); }
.task-row__main {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  grid-column: 1;
  grid-row: 1;
}
.task-row__code {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 var(--space-1_5, 6px);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  font: 600 var(--t-caption)/1 var(--font-mono);
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.task-row__title {
  flex: 1 1 auto;
  min-width: 0;
  font: 500 var(--t-body)/1.4 var(--font-sans);
  color: var(--text-strong);
  letter-spacing: -0.005em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: text;
  border-radius: var(--radius-sm);
  padding: var(--space-0_5) var(--space-1);
  margin: calc(-1 * var(--space-0_5)) calc(-1 * var(--space-1));
  transition: background var(--duration-fast) var(--ease-standard);
}
.task-row__title:hover { background: var(--bg-selected); }
.task-row__meta {
  grid-column: 1;
  grid-row: 2;
  font: 400 var(--t-caption)/1.4 var(--font-sans);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 var(--space-1);
}
.task-row__meta-link {
  color: inherit;
  text-decoration: none;
}
.task-row__meta-link:hover { color: var(--text); text-decoration: underline; }
.task-row__actions {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  /* The timer button is the always-visible primary affordance now
     (play on any active task, pause when it's the running one), so the
     dashboard rows show it at rest rather than only on hover. */
  opacity: 1;
}

.row-start {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  padding: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  color: var(--success);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-standard);
}
.row-start:hover {
  background: color-mix(in srgb, var(--success) 10%, var(--bg-surface));
  border-color: color-mix(in srgb, var(--success) 50%, var(--border));
  transform: scale(1.05);
}
.row-start:active { transform: scale(0.96); }
.row-start:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}
.row-start:disabled,
.row-start[data-busy] {
  opacity: 0.5;
  cursor: progress;
  transform: none;
}

/* Always-visible timer affordance. Pulled OUT of the hover-revealed
   .row-quick-actions group so a green play button shows on every active
   task row at rest (the subtask "+" stays hover-only). The wrapper just
   carries spacing + keeps the button on the title baseline. */
.row-timer {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-1);
  vertical-align: middle;
}

/* On a Kanban card the timer sits at the right edge of the footer. */
.kanban-card-timer { margin-left: auto; }

/* Play / pause icon swap — both SVGs ship in the markup; [data-running]
   (set by row-actions.js when this task is the live timer) decides which
   shows. Default = play (green); running = filled navy with the pause
   glyph, so the active task reads as "click to pause". */
.row-start__icon--pause { display: none; }
.row-start[data-running] .row-start__icon--play { display: none; }
.row-start[data-running] .row-start__icon--pause { display: inline; }
.row-start[data-running] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.row-start[data-running]:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  transform: scale(1.05);
}

/* In-table inline-edit hint: hover lifts the cell with a faint
   bg-hover + inset border, so the click affordance reads clearly
   without shouting on a 50-row grid (advisor: no per-cell pencil
   glyph — too noisy). Keyboard focus gets a proper outline. */
.inline-edit-cell {
  cursor: text;
  border-radius: var(--radius-sm);
  padding: var(--space-0_5) var(--space-1);
  margin: calc(-1 * var(--space-0_5)) calc(-1 * var(--space-1));
  transition: background var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.inline-edit-cell:hover {
  background: var(--bg-hover);
  box-shadow: inset 0 0 0 1px var(--border);
}
.inline-edit-cell:focus-visible {
  outline: none;
  background: var(--bg-surface);
  box-shadow: 0 0 0 2px var(--focus-ring);
}

/* Empty editable cell → a "click to fill" affordance instead of a dead
   em-dash. The cell renders truly empty (server Razor and inline-edit.js both
   emit ""), so :empty matches. A faint "+" at rest keeps the cell from reading
   as "didn't render"; hovering the row lifts it, and hovering/focusing the cell
   itself spells out "+ Add". Scoped to table rows so non-list inline cells
   (e.g. the detail drawer) stay clean. */
tr .inline-edit-cell:empty::after {
  content: "+";
  color: var(--text-subtle);
  opacity: 0.35;
  font-weight: 500;
  transition: opacity var(--duration-fast) var(--ease-standard);
}
tr:hover .inline-edit-cell:empty::after { opacity: 0.6; }
tr .inline-edit-cell:empty:hover::after,
tr .inline-edit-cell:empty:focus-within::after {
  content: "+ Add";
  opacity: 1;
}

.row-actions-cell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Hover-reveal the row actions (ClickUp-style clean row at rest). The
   inline quick-actions (subtask +, start timer) sit RIGHT NEXT TO the
   title and slide in from the title text on row hover. The row-end
   (…) overflow menu fades in from the right. The row also gets a
   subtle lift on hover so the surfaced actions read as part of an
   active row, not a tooltip floating in space. The (…) trigger stays
   visible while its menu is open (aria-expanded). */
.task-list-row {
  transition: background var(--duration-fast) var(--ease-standard);
}
.task-list-row:hover {
  background: var(--bg-hover);
}

/* Title cell needs to keep the title, tags, and quick-actions on one
   line — otherwise the cluster wraps under the title at narrow column
   widths. Long titles fall to ellipsis. Position: relative reserves an
   absolute-positioning context for any future overlay (none today). */
.task-title-cell {
  display: flex;
  align-items: center;
  min-width: 0;
  overflow: hidden;
  position: relative;
}
/* Only the title itself flexes and truncates. Every other affordance in the
   cluster — the always-visible timer ▶, hover quick-actions, expand toggle,
   tags, badges — keeps its natural width and stays visible. Previously the
   cell was a plain nowrap+ellipsis box, so a long title filled the width and
   pushed the trailing timer button past the clipped edge, hiding it (the
   "start timer not visible for long task names" bug). */
.task-title-cell > * { flex: none; }
.task-title-cell > .task-title-text,
.task-title-cell > .inline-edit-cell[data-field="title"] {
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The inline quick-create row puts a text input in this cell — it should fill
   the available width like it did before the cell became a flex row. */
.task-title-cell > input {
  min-width: 0;
  flex: 1 1 auto;
}

/* Inline cluster inside the title cell. Stays out of flow when not
   hovered (pointer-events: none + opacity 0) so the title's tags
   remain the visual focus. The cluster gets a 4px left margin so the
   leftmost icon doesn't bump into the recurring glyph / last tag. */
.row-quick-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  margin-left: var(--space-1);
  vertical-align: middle;
  opacity: 0;
  transform: translateX(-4px);
  pointer-events: none;
  transition:
    opacity var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
}
tbody tr:hover .row-quick-actions,
tbody tr:focus-within .row-quick-actions {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}

.row-actions-cell .row-menu-trigger {
  opacity: 0;
  transform: translateX(4px);
  transition:
    opacity var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
}
tbody tr:hover .row-actions-cell .row-menu-trigger,
tbody tr:focus-within .row-actions-cell .row-menu-trigger,
.row-actions-cell .row-menu-trigger[aria-expanded="true"] {
  opacity: 1;
  transform: translateX(0);
}

/* Touch / no-hover devices — reveal the row affordances that are
   otherwise hover-gated. Without this, bulk-select, the inline
   quick-actions, the row "⋯" menu, and the calendar day "+" are
   invisible and unreachable on a phone/tablet (no hover event fires).
   The responsive shell reflowed the layout; this makes the row-level
   actions actually operable on touch. */
@media (hover: none), (pointer: coarse) {
  .task-list-row .row-select,
  .row-quick-actions,
  .row-actions-cell .row-menu-trigger,
  .task-row__actions,
  .calendar-day-add {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }
}

/* "+ Subtask" row affordance — same chrome recipe as .row-menu-trigger
   (square 26px button, ghost background) but with the navy hue on
   hover so it reads as the constructive action, not a neutral one. */
.row-subtask {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.row-subtask:hover { background: var(--bg-selected); color: var(--text-accent); }
.row-subtask:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }

/* The row's "Log time" quick action. Shares .row-subtask's geometry so the
   hover cluster reads as one control group; it's an <a>, not a <button>, so
   the link-colour and underline defaults need clearing. */
.row-logtime {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  text-decoration: none;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.row-logtime:hover { background: var(--bg-selected); color: var(--text-accent); text-decoration: none; }
.row-logtime:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }

/* The row's hidden "Move to workspace…" opener (row-menu.js / bulk bar fire a
   synthetic click on it). It carries the `hidden` attribute, but the global
   `button { display: inline-flex }` rule (author) outranks the UA `[hidden]`
   `display:none` — so without this it renders as an empty white bordered box
   beside the "…". A synthetic .click() still fires on a display:none element,
   so hiding it doesn't break the openers. (Same trap as the sidebar tree.) */
[data-open-move] { display: none; }

.row-menu-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.row-menu-trigger:hover { background: var(--bg-hover); color: var(--text); }
.row-menu-trigger:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.row-menu-trigger[aria-expanded="true"] { background: var(--bg-selected); color: var(--text); }

/* Row overflow menu — anchored dropdown built by row-menu.js. Mirrors
   the .user-menu token recipe so light/dark both work. */
.row-menu {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  min-width: 208px;
  padding: var(--space-1);
  z-index: 42;
}
.row-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font: inherit;
  color: var(--text);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.row-menu__item:hover { background: var(--bg-hover); }
.row-menu__item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}
/* Leading glyph — muted at rest, inherits the row's tint on hover/danger. */
.row-menu__ico { flex: 0 0 auto; width: 15px; height: 15px; color: var(--text-subtle); }
.row-menu__item:hover .row-menu__ico { color: var(--text); }
/* Separator before the destructive action. */
.row-menu__sep { height: 1px; margin: var(--space-1) var(--space-1); background: var(--divider); }
/* Destructive item (Archive) — red ink + red-tint hover, red glyph. */
.row-menu__item--danger { color: var(--error); }
.row-menu__item--danger .row-menu__ico { color: var(--error); }
.row-menu__item--danger:hover { background: var(--error-bg); color: var(--error); }

/* Drag-to-reorder grip (Sort = Manual only). Lives inside the select
   cell so no extra column is added; revealed on row hover/focus like
   the other row affordances. SortableJS uses it as the drag handle, so
   the checkbox + cell clicks stay unaffected. */
.row-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  margin-right: var(--space-1);
  color: var(--text-subtle);
  cursor: grab;
  opacity: 0;
  vertical-align: middle;
  transition: opacity var(--duration-fast) var(--ease-standard);
}
tbody tr:hover .row-drag-handle,
tbody tr:focus-within .row-drag-handle { opacity: 1; }
.row-drag-handle:active { cursor: grabbing; }
.task-list-row--ghost { opacity: 0.5; background: var(--bg-selected); }

/* Task List row chrome (ClickUp-style): due-date semantic colouring +
   assignee avatar chip. The classes are computed server-side
   (TaskDueDate) and refreshed client-side by inline-edit.js's
   redecorate hook after an inline edit. All colour via semantic / role
   tokens — overdue shouts (error), today emphasised (warning), the
   rest recede (muted / subtle). */
.due-cell { font-variant-numeric: tabular-nums; }
.due--overdue { color: var(--error); font-weight: 500; }
.due--today   { color: var(--warning); font-weight: 500; }
.due--soon    { color: var(--text); }
.due--future  { color: var(--text-muted); }
.due--none    { color: var(--text-subtle); }
/* Completed task: the date is history, not a deadline — muted, struck
   through, never the red/amber urgency colours (a done task is never
   "overdue"). Set by TaskDueDate.CssClass(..., isDone) + task-completion.js. */
.due--done    { color: var(--text-subtle); text-decoration: line-through; font-weight: 400; }

/* Assignee cell: 20px avatar chip + name, inline-flex so the avatar and
   name share a baseline. The inline-edit hover/padding still applies
   because the cell keeps its .inline-edit-cell class. */
.assignee-cell {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

/* ClickUp-style priority flag — icon + label coloured by level via
   semantic tokens (no hardcoded hex). Replaces the priority pill on the
   List; the inline-edit redecorate hook rebuilds it after an edit. */
.prio-flag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: 500 var(--t-caption)/1 var(--font-sans);
  white-space: nowrap;
}
.prio-flag__icon { flex-shrink: 0; }
.prio-flag--critical { color: var(--error); }
.prio-flag--high     { color: var(--warning); }
.prio-flag--medium   { color: var(--text-muted); }
.prio-flag--low      { color: var(--text-subtle); }
.prio-flag--none     { color: var(--text-subtle); }
.prio-flag--none .prio-flag__icon { opacity: 0.45; }

/* ============================================================
   Inline-edit input — generic style for the click-to-edit
   pattern. Mirrors the cell font/colour and adds a focus ring.
   ============================================================ */
.inline-edit-input {
  width: 100%;
  margin: calc(-1 * var(--space-0_5)) 0;
  padding: var(--space-0_5) var(--space-1);
  background: var(--bg-surface);
  border: 1px solid var(--focus-ring);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font: inherit;
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}

/* ============================================================
   Searchable lookup combobox (lookup-select.js)
   Progressive enhancement over a native <select>. The native
   control is hidden but kept in the DOM for form submission; the
   visible UI is this combobox. All colours come from role tokens
   so light + dark mode work with no extra selectors.
   ============================================================ */
.lookup-select {
  position: relative;
  display: block;
  width: 100%;
}

/* The enhanced native <select> stays in the DOM (posts its value)
   but is visually removed. Not display:none — keep it focusable for
   the rare a11y fallback, but out of the layout. */
select.lookup-select__native {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.lookup-select__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-1);
}
.lookup-select__chips:empty { display: none; }

.lookup-select__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  padding: 2px var(--space-1) 2px var(--space-2);
  background: var(--bg-selected);
  color: var(--text-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  font-size: var(--t-body-sm);
  line-height: 1.4;
}
.lookup-select__chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 16px;
  height: 16px;
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}
.lookup-select__chip-remove:hover { background: var(--bg-hover); color: var(--text-strong); }
.lookup-select__chip-remove:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}

.lookup-select__field {
  position: relative;
  display: flex;
  align-items: center;
}

.lookup-select__input {
  width: 100%;
  padding: var(--space-1) var(--space-6) var(--space-1) var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font: inherit;
}
.lookup-select__input::placeholder { color: var(--text-subtle); }
.lookup-select__input:focus {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 22%, transparent);
}
.lookup-select__input:disabled {
  background: var(--bg-sunken);
  color: var(--text-muted);
  cursor: not-allowed;
}

.lookup-select__clear {
  position: absolute;
  right: var(--space-1);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-subtle);
  cursor: pointer;
}
.lookup-select__clear:hover { background: var(--bg-hover); color: var(--text-strong); }
.lookup-select__clear:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}

.lookup-select__list {
  /* Fixed, not absolute: the combobox is often inside a clipping/scrolling
     ancestor (an inline-edit table cell, the new .table-scroll wrapper),
     which would crop an absolutely-positioned dropdown. lookup-select.js
     sets left/top/width from the field's viewport rect on open + scroll. */
  position: fixed;
  z-index: 1000;
  min-width: 220px;
  max-width: 360px;
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  max-height: 240px;
  overflow-y: auto;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.lookup-select__list[hidden] { display: none; }

/* Workspace list: the name is a link into that workspace's projects. Reads as
   a strong title until hovered, then shows the link affordance. */
.ws-name-link {
  color: var(--text-strong);
  font-weight: 500;
  text-decoration: none;
}
.ws-name-link:hover {
  color: var(--text-link);
  text-decoration: underline;
}

/* ── UI/UX audit 2026-06-13 — Wave 1/2 supporting styles ─────────────── */

/* Skip-to-content link: off-screen until focused (keyboard a11y). */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: -3rem;
  z-index: 200;
  padding: var(--space-2) var(--space-3);
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  transition: top var(--duration-fast) var(--ease-standard);
}
.skip-link:focus { top: var(--space-2); }

/* Caption legend above the workspace status/column builder control strips. */
.builder-legend {
  display: flex;
  gap: var(--space-3);
  margin: 0 0 var(--space-1);
  padding-left: calc(var(--space-4) + var(--space-2));
}

/* "Needs the full form" hint in the project inline-create row (required
   column types the inline row can't render). */
.qc-fullform-hint { color: var(--text-link); text-decoration: none; white-space: nowrap; }
.qc-fullform-hint:hover { color: var(--text-link-hover); text-decoration: underline; }

/* Kanban keyboard-move hint row. */
.kanban-kbd-hint {
  margin: 0 0 var(--space-3);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1);
}

/* Skeleton shown where the AG-Grid table mounts (self-clears on createGrid). */
.skeleton-table {
  display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-2);
}

/* Calendar day chips are now <button>s — neutralise UA button styling so
   .calendar-chip / .pill styling wins. */
button.calendar-chip {
  appearance: none; -webkit-appearance: none;
  border: 0; background: none; font: inherit; text-align: inherit;
  cursor: pointer; width: 100%;
}

/* Portfolio card stat list (definition list). */
.portfolio-card__stats {
  display: grid; grid-template-columns: 1fr auto;
  row-gap: var(--space-1); column-gap: var(--space-3);
  margin: var(--space-2) 0 0;
}
.portfolio-card__stats dt, .portfolio-card__stats dd { margin: 0; }
h2.report-card__title { margin: 0; }

/* View-switcher / toolbar rows wrap instead of overflowing on narrow screens. */
@media (max-width: 600px) {
  .row.row--between { flex-wrap: wrap; gap: var(--space-2); }
}

/* Right-aligned form action row for plain (non-entity-form) forms — gives
   Save/Cancel a uniform right placement WITHOUT the sticky bleed the
   .entity-form__actions bar uses (which only suits the entity-form card). */
.form-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  flex-wrap: wrap;
}

/* ── UI/UX audit 2026-06-13 — Wave 3 feature styles ──────────────────── */

/* Dashboard filter bar — a single dense toolbar row (workspace + date
   window + quick-range presets), wrapping on narrow screens. */
/* It's a <form>, so it inherits the global `form { max-width: 640px }` cap —
   opt out so the scope controls lay out on one full-width toolbar row (matching
   the full-width content below) instead of cramping and wrapping. */
.dashboard-filter { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3);
  margin-bottom: var(--space-4); max-width: none; }
/* margin:0 — the field is a <label>, which carries a global bottom margin;
   left in, that margin inflates the flex line and drops the label-less Apply
   button below the input row. */
.dashboard-filter__field { display: flex; flex-direction: column; gap: var(--space-1); margin: 0; }
.dashboard-filter__label { font-size: var(--t-caption); color: var(--text-muted); }
/* All three controls share one height so the labelled fields and the Apply
   button line up on the row. 36px matches the app's global input height — the
   date inputs are locked there by `input[type=date]{height:36px}` (a higher-
   specificity rule than this class), so the select + button match it rather
   than fight it. */
.dashboard-filter__select,
.dashboard-filter__input { height: 36px; padding: 0 var(--space-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-surface); color: var(--text); font: inherit; }
.dashboard-filter__apply { height: 36px; }
/* Vertical hairline separating the date window from the preset pills. Fixed
   height + bottom-aligned so it matches the control row without stretching the
   flex line taller than the fields (which pushed the Apply button 6px low). */
.dashboard-filter__divider { width: 1px; height: 36px; align-self: flex-end;
  background: var(--divider); margin: 0 var(--space-1); }
.dashboard-filter__presets { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
@media (max-width: 720px) {
  /* On a phone the hairline reads as clutter once the row wraps. */
  .dashboard-filter__divider { display: none; }
  .dashboard-filter__presets { width: 100%; }
}
.dashboard-chart-scope { margin-top: var(--space-0_5); }

/* KPI strip extras — a delta badge + live/overdue accents inside .stat-chip. */
.stat-chip__value-row { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.kpi-delta { font: 600 var(--t-caption)/1 var(--font-sans); font-variant-numeric: tabular-nums;
  white-space: nowrap; }
.kpi-delta--up { color: var(--success); }
.kpi-delta--down { color: var(--error); }
/* A small pulsing dot signalling timers are live right now. */
.kpi-live { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--error);
  display: inline-block; align-self: center; box-shadow: 0 0 0 0 var(--error);
  animation: kpi-pulse 1.8s var(--ease-standard) infinite; }
@keyframes kpi-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--error) 55%, transparent); }
  70%  { box-shadow: 0 0 0 6px color-mix(in srgb, var(--error) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--error) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .kpi-live { animation: none; }
}

/* Report results pager */
.report-pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-3); margin-top: var(--space-3); }
.report-pager__controls { display: flex; align-items: center; gap: var(--space-2); }
.report-pager__status { color: var(--text-muted); font-size: var(--t-body-sm); }
.report-pager__page { white-space: nowrap; }
.btn.is-disabled { opacity: 0.5; pointer-events: none; cursor: default; }

/* AdminRoles permission editor */
/* It's a <form>, so it inherits the global `form { max-width: 640px }` cap —
   opt out so the permission card fills its grid column (its groups then tile
   multi-column) instead of leaving a big empty band to its right. */
.perm-editor { max-width: none; }
.perm-editor__toolbar { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-4); }
.perm-editor__toolbar[hidden] { display: none; }
.perm-editor__search { position: relative; flex: 1; min-width: 220px; }
.perm-editor__search-icon {
  position: absolute; left: var(--space-2); top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-subtle); pointer-events: none;
}
.perm-editor__search input[type="search"] { width: 100%; padding-left: calc(var(--space-2) + 16px + var(--space-2)); }
.perm-editor__summary { white-space: nowrap; }
/* Groups tile multi-column so the permission list fills the card width
   instead of a single left strip (the "empty middle"). auto-fill collapses
   to one column when narrow; search-hidden groups reflow via [hidden]. */
.perm-editor__groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--space-3);
  align-items: start;
}
.perm-editor__group {
  border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3) var(--space-3);
  background: var(--bg-surface); box-shadow: var(--shadow-xs);
}
.perm-editor__group[hidden] { display: none; }
.perm-editor__legend {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; box-sizing: border-box;
  padding: 0 0 var(--space-2); margin-bottom: var(--space-1);
  border-bottom: 1px solid var(--divider);
  font-weight: 600; color: var(--text-strong);
}
.perm-editor__legend [data-perm-group-all] { margin: 0; cursor: pointer; }
.perm-editor__legend-label { cursor: pointer; }
.perm-editor__group-count { margin-left: auto; font-variant-numeric: tabular-nums; }
.perm-editor__row {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-2); border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard);
}
.perm-editor__row:hover { background: var(--bg-hover); }
.perm-editor__row[hidden] { display: none; }
/* Checked rows read as "on" at a glance (:has already used elsewhere). */
.perm-editor__row:has(input:checked) { background: var(--bg-selected); }
.perm-editor__row:has(input:checked) .perm-editor__key { color: var(--navy-700); }
.perm-editor__row input[type="checkbox"] { margin-top: 2px; flex: none; }
.perm-editor__row-body { display: flex; flex-direction: column; gap: var(--space-0_5); min-width: 0; }
.perm-editor__row-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.perm-editor__key { color: var(--text-strong); }
.perm-editor__desc { line-height: 1.4; }
/* Sensitive rows get an amber left rail so they scan instantly. */
.perm-editor__row--sensitive { border-left: 2px solid var(--warning); }
.perm-editor__row--sensitive .perm-editor__key { color: var(--warning); }
.perm-editor__sensitive {
  display: inline-flex; align-items: center; gap: var(--space-0_5);
  font-size: var(--t-caption); font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--warning); background: var(--warning-bg);
  padding: 1px var(--space-1); border-radius: var(--radius-sm);
}
.perm-editor__sensitive svg { width: 12px; height: 12px; }

/* Sticky dirty-state save bar */
.perm-editor__savebar {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  margin: var(--space-4) calc(-1 * var(--space-4)) calc(-1 * var(--space-4));
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface); border-top: 1px solid var(--border);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}
.perm-editor__savebar[hidden] { display: none; }
.perm-editor__savebar.is-dirty { border-top-color: var(--navy-400); box-shadow: var(--shadow-sm); }
.perm-editor__savebar-status { color: var(--text-muted); font-size: var(--t-body-sm); }
.perm-editor__savebar.is-dirty .perm-editor__savebar-status { color: var(--text-strong); font-weight: 600; }
.perm-editor__savebar-actions { display: flex; align-items: center; gap: var(--space-2); }
.perm-editor__fallback-actions[hidden] { display: none; }

/* ── AdminRoles console (Index) ── */
.roles-console__toolbar {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  margin-bottom: var(--space-3);
}
.roles-console__search { position: relative; flex: 1; min-width: 220px; max-width: 420px; }
.roles-console__search-icon {
  position: absolute; left: var(--space-2); top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-subtle); pointer-events: none;
}
.roles-console__search input[type="search"] { width: 100%; padding-left: calc(var(--space-2) + 16px + var(--space-2)); }
.roles-console__count { white-space: nowrap; margin-left: auto; }
.roles-console__no-results { padding: var(--space-8) var(--space-4); }
/* Let the table scroll horizontally within the card on narrow viewports
   (mobile) rather than pushing the row-action buttons off-screen. */
.roles-table-scroll { overflow-x: auto; margin: 0 calc(-1 * var(--space-1)); }
.roles-table { min-width: 520px; }
/* Defensive: the search filter hides rows via the [hidden] attribute. If a
   future rule ever sets `display` on these rows at class specificity it would
   outrank the UA [hidden]=none (same trap as .side-nav-project-tree etc.), so
   restate the collapse at higher specificity now. */
.roles-table tr[hidden] { display: none; }

.roles-table__name { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.roles-table__name-link { font-weight: 600; }
.roles-table__desc { margin-top: 2px; max-width: 52ch; }
.roles-table__coverage-col { width: 34%; min-width: 180px; }
.roles-table__actions-col { width: 1%; white-space: nowrap; text-align: right; }
.roles-table__actions { display: inline-flex; gap: var(--space-1); justify-content: flex-end; }
.role-coverage { display: flex; align-items: center; gap: var(--space-2); }
.role-coverage__track {
  flex: 1; height: 6px; border-radius: var(--radius-full);
  background: var(--bg-sunken); overflow: hidden; min-width: 60px;
}
.role-coverage__fill { height: 100%; border-radius: var(--radius-full); background: var(--navy-500); }
.role-coverage__label { font-size: var(--t-caption); color: var(--text-muted); white-space: nowrap; }

/* ── AdminRoles detail layout ── */
.roles-detail-grid {
  /* Main fills the width (its groups are multi-column); the side is a fixed
     calm rail rather than a stretchy 1fr that left the middle empty. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 360px);
  gap: var(--space-5); align-items: start;
}
.roles-detail-main { min-width: 0; }
.roles-detail-side { display: flex; flex-direction: column; gap: var(--space-4); }
@media (max-width: 1024px) {
  .roles-detail-grid { grid-template-columns: 1fr; }
}

/* Members drill-down list */
.roles-members { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.roles-members__item {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-2) 0; border-bottom: 1px solid var(--divider);
}
.roles-members__item:last-child { border-bottom: 0; }
.roles-members__item--inactive { opacity: 0.65; }
.roles-members__avatar {
  flex: none; width: 28px; height: 28px; border-radius: var(--radius-full);
  display: grid; place-items: center;
  background: var(--bg-selected); color: var(--navy-700);
  font-size: var(--t-caption); font-weight: 600;
}
.roles-members__id { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.roles-members__name { display: flex; align-items: center; gap: var(--space-1); color: var(--text-strong); font-weight: 500; }
.roles-members__email { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One row per person; their workspace scopes render as a wrapping chip
   cluster below the email (calmer than a row-per-scope in the narrow rail). */
.roles-members__scopes { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }
/* Per-member "Remove global" action, pushed to the row's trailing edge. */
.roles-members__action { flex: none; align-self: center; }
/* "Grant globally" picker row at the top of the Members card. */
.roles-assign-global { display: flex; gap: var(--space-2); align-items: center; margin-bottom: var(--space-1); }
.roles-assign-global__select { flex: 1; min-width: 0; }
.roles-assign-global__hint { margin: 0 0 var(--space-3); }
.roles-danger { border-color: color-mix(in srgb, var(--error) 40%, var(--border)); }

/* Workspace members role popover */
.members-table__bulk { width: 1%; white-space: nowrap; }
.members-roles { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.members-roles[hidden] { display: none; }
.members-roles__pills { display: flex; gap: var(--space-1); flex-wrap: wrap; }
.members-table td:nth-child(4) { position: relative; }
.members-popover { position: absolute; z-index: 42; min-width: 240px; margin-top: var(--space-1);
  display: flex; flex-direction: column; gap: var(--space-1); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-md); padding: var(--space-2); }
.members-popover[hidden] { display: none; }
.members-popover__title { color: var(--text-muted); padding: 0 var(--space-1) var(--space-1); }
.members-popover__row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-1) var(--space-1); }
.members-popover__row:hover { background: var(--bg-hover); border-radius: var(--radius-sm); }
.members-popover__form { margin: 0; }
[data-member-inline-forms][hidden] { display: none; }

/* Project-list column-visibility picker */
.cols-menu { display: inline-flex; }
.cols-menu__trigger { display: inline-flex; align-items: center; gap: var(--space-1); }
.cols-menu__popover { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); min-width: 200px; max-height: 60vh; overflow-y: auto; padding: var(--space-2); z-index: 50; }
.cols-menu__popover[hidden] { display: none; }
.cols-menu__title { margin: 0 0 var(--space-1); padding: 0 var(--space-1); color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .04em; font-size: var(--t-caption); }
.cols-menu__item { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm); font-size: var(--t-body-sm); color: var(--text); cursor: pointer; }
.cols-menu__item:hover { background: var(--bg-hover); }

/* Sortable list headers */
.th-sortable { cursor: pointer; user-select: none; }
.th-sortable:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--focus-ring); }
.th-sortable[aria-sort="ascending"]::after  { content: " ↑"; color: var(--text-muted); }
.th-sortable[aria-sort="descending"]::after { content: " ↓"; color: var(--text-muted); }

/* Server-side sortable header link (projects list). The <th> carries
   aria-sort; the <a> round-trips ?sort=&dir=. Caret on the active column. */
.th-sort { white-space: nowrap; }
.th-sort__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.th-sort__link:hover { color: var(--text-strong); }
.th-sort__link:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
  border-radius: var(--radius-sm);
}
.th-sort[aria-sort="ascending"] .th-sort__link::after  { content: "↑"; color: var(--text-muted); }
.th-sort[aria-sort="descending"] .th-sort__link::after { content: "↓"; color: var(--text-muted); }

.lookup-select__option {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--text);
  cursor: pointer;
  font-size: var(--t-body);
}
.lookup-select__option.is-active { background: var(--bg-hover); }
.lookup-select__option.is-selected { color: var(--text-strong); font-weight: 600; }
.lookup-select__option-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Colour dot for option metadata (e.g. status). The element also carries
   the pill class (.pill .pill--done …) which supplies the colour; these
   higher-specificity rules shrink that pill box down to a 9px circle. */
.lookup-select__option .lookup-select__dot {
  display: inline-block;
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: var(--radius-full);
  line-height: 0;
  font-size: 0;
}
.lookup-select__tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  flex: 0 0 14px;
  color: var(--text-accent);
}

.lookup-select__empty {
  padding: var(--space-1) var(--space-2);
  color: var(--text-muted);
  font-size: var(--t-body-sm);
}

/* -----------------------------------------------------------
   11. Tables
   ----------------------------------------------------------- */

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  font-size: var(--t-body-sm);
  margin: var(--space-3) 0 var(--space-5);
}
table thead th {
  background: var(--bg-sunken);
  color: var(--text-muted);
  font: 500 var(--t-caption)/1 var(--font-sans);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 10px 14px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
}
table tbody td {
  /* 8 + 20 + 8 = 36px row height — the Linear/ClickUp target the
     2026 brief calls out at §4 "Table density". Was 10/18 → ~38px;
     the 2px drop is what makes a long list feel scannable. */
  padding: 8px 14px;
  font-size: var(--t-body-sm);
  line-height: 20px;
  color: var(--text);
  border-bottom: 1px solid var(--divider);
  vertical-align: middle;
  background: var(--bg-surface);
  transition: background var(--duration-fast) var(--ease-standard);
}
table tbody tr:last-child td { border-bottom: none; }
table tbody tr:hover td { background: var(--bg-hover); }
table tbody td a {
  color: var(--text-strong);
  font-weight: 500;
}
table tbody td a:hover { color: var(--text-accent); text-decoration: none; }
table tbody td code {
  font-size: var(--t-caption);
  background: transparent;
  padding: 0;
  color: var(--text-muted);
}

table .num,
table td[align="right"],
table th[align="right"],
table td[style*="text-align: right"] {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Totals row — the last data row drops its border, so without this the
   bold total touches the last row and reads as overlapping it. Give the
   totals a clear top rule + a sunken fill so it reads as a summary. */
table tfoot td {
  border-top: 2px solid var(--border-strong);
  background: var(--bg-sunken);
  padding: 10px 14px;
  color: var(--text-strong);
}

/* -----------------------------------------------------------
   11a. Projects list — wide-table containment. Many workspaces
   carry a dozen header custom columns, so the list is wider than
   the viewport. Confine the horizontal scroll to the table itself
   (so the page chrome doesn't scroll sideways) and freeze the
   checkbox / Code / Name columns so context is never lost.
   Scoped to .projects-table so other tables are untouched.
   ----------------------------------------------------------- */
.table-scroll {
  overflow: auto;
  /* Subtrahend tracks the chrome above the grid (topbar + breadcrumb +
     page-header + filter bar). Trimmed from 19rem→17rem when that chrome
     was tightened, so the reclaimed space becomes visible rows, not a gap
     below the grid. */
  max-height: calc(100vh - 17rem);
  /* Float the grid as a white card on the cool page (2026.2). */
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  margin: var(--space-3) 0 var(--space-5);
}
.projects-table {
  /* the wrapper owns the frame now */
  border: none;
  border-radius: 0;
  overflow: visible;
  margin: 0;
}
/* Frozen leading columns: checkbox (1), Code (2), Name (3). */
.projects-table thead th:nth-child(-n+3),
.projects-table tbody td:nth-child(-n+3),
.projects-table tfoot td:nth-child(-n+3) {
  position: sticky;
  background: var(--bg-surface);
}
/* Left offsets come from --frozen-2/-3, which Index.cshtml measures from
   the real header widths — fixed px can't work because the Code column
   sizes to its content (long codes vary the width), which left gaps where
   scrolled content bled through between the frozen columns. */
.projects-table th:nth-child(1), .projects-table td:nth-child(1) { left: 0;                     width: 44px; z-index: 1; }
.projects-table th:nth-child(2), .projects-table td:nth-child(2) { left: var(--frozen-2, 44px);  white-space: nowrap; z-index: 1; }
.projects-table th:nth-child(3), .projects-table td:nth-child(3) { left: var(--frozen-3, 140px); min-width: 200px; z-index: 1;
  box-shadow: 1px 0 0 var(--divider); }      /* hairline marking the frozen edge */
/* Header corner cells ride above the body's frozen cells. */
.projects-table thead th { z-index: 2; }
.projects-table thead th:nth-child(-n+3) { z-index: 3; background: var(--bg-sunken); }
/* Frozen cells must stay opaque on row hover (scrolled content sits under them). */
.projects-table tbody tr:hover td:nth-child(-n+3) { background: var(--bg-hover); }
/* Headers stop wrapping into two lines. */
.projects-table thead th { white-space: nowrap; }
/* Long text columns truncate with an ellipsis instead of ballooning. */
.projects-table td.col-text { max-width: 240px; }
.projects-table td.col-text > .inline-edit-cell {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
/* While inline-editing a lookup cell, give the combobox a usable width. */
.projects-table td [data-inline-edit-active] .lookup-select { min-width: 200px; }
/* Empty-cell placeholder reads as muted, not a full-contrast dash field. */
.cell-empty { color: var(--text-subtle); }

/* Frozen Name column: a leading "open tasks" icon link so a project's
   tasks are one click away without scrolling right to the row-menu. The
   icon is flex-fixed at the left, so it stays visible regardless of name
   length; the name takes the remaining width. */
.proj-name-cell { display: flex; align-items: center; gap: var(--space-2); }
/* Cap the project name so a long name can't balloon the frozen Name column
   (which widened the frozen block and ate the horizontal scroll room for the
   other columns). Applies in BOTH densities — the column is unbounded (it can
   grow under the table's horizontal scroll), so even compact's ellipsis never
   engaged without this width cap. `> span:not(.pill)` covers the editable
   .inline-edit-cell name span and the archived read-only span, but not the
   Template pill. */
.proj-name-cell > span:not(.pill) { max-width: 260px; min-width: 0; }
/* Comfortable density: wrap the name to (at most) 2 lines instead of a single
   ellipsised line. Compact keeps its single-line ellipsis (rule further down). */
html:not([data-density="compact"]) .proj-name-cell > span:not(.pill) {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.3;
  /* A definite width so the two lines actually use the room (a bare max-width
     lets the auto-layout column collapse to its min, wrapping the name too
     early). Short names simply leave trailing space — a consistent, aligned
     Name column. */
  width: 230px;
}
/* Two-line name: top-align the leading tasks icon so it sits by the first line. */
html:not([data-density="compact"]) .proj-name-cell { align-items: flex-start; }
html:not([data-density="compact"]) .proj-name-cell .row-open-tasks { margin-top: 1px; }
/* Per-row categorical colour dot in the Code cell — a stable per-project hue
   (hashed id → a --chart-* slot in the view) that lets the eye chunk the grid,
   like Linear's project dots. Subtle: identity, not noise. */
.proj-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: var(--space-2);
  vertical-align: middle;
  background: var(--dot, var(--grey-400));
}
.row-open-tasks {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  /* Subtle chip so it reads as a tappable "go to tasks" control, not a
     decorative bullet — the stakeholder found the old faint glyph hard to
     spot. Calm at rest (sunken tint), lifts to the accent on hover. */
  background: var(--bg-sunken);
  color: var(--text-muted);
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.row-open-tasks:hover { background: var(--bg-selected); color: var(--text-accent); }
.row-open-tasks:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
}

/* First-run hint row inside an otherwise-empty body. */
.projects-empty-row td {
  padding: var(--space-5) var(--space-4);
  text-align: center;
  color: var(--text-muted);
  font: var(--t-body-sm)/1.4 var(--font-sans);
  background: var(--bg-surface);
}

/* -----------------------------------------------------------
   Column-aligned inline create row (table <tfoot>). Pinned to the
   bottom of the scroll region so it's always reachable; each field
   lines up under its column and rides the frozen Code/Name columns.
   ----------------------------------------------------------- */
.projects-table tfoot td {
  position: sticky;
  bottom: 0;
  z-index: 2;                       /* over scrolled body rows */
  background: var(--bg-sunken);
  border-top: 2px solid var(--border);
  padding: var(--space-1) var(--space-2);
}
.projects-table tfoot td:nth-child(-n+3) { z-index: 4; }  /* frozen + bottom corner */
.quick-create-row .qc-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  border-radius: var(--radius-sm);
  background: var(--bg-selected);
  color: var(--text-accent);
}
.quick-create-row input,
.quick-create-row select {
  width: 100%;
  height: 30px;
  min-width: 0;
  padding: 0 var(--space-1);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-strong);
  font: var(--t-body-sm)/1 var(--font-sans);
}
.quick-create-row input::placeholder { color: var(--text-subtle); }
.quick-create-row input:hover,
.quick-create-row select:hover { border-color: var(--border); background: var(--bg-surface); }
.quick-create-row input:focus,
.quick-create-row select:focus {
  outline: none;
  /* Calm focus, consistent with the global input rule (mixed border + halo). */
  border-color: color-mix(in srgb, var(--focus-ring) 55%, var(--border-strong));
  background: var(--bg-surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
.quick-create-row .qc-add { width: 100%; }
/* Actions cell holds the optional Activity picker + the Add button side by
   side (compact) rather than each full-width, so the create row stays one line.
   The cell widens to fit; the select is capped so it doesn't crowd the row. */
.quick-create-row .row-actions-cell { white-space: nowrap; }
.quick-create-row .qc-activity {
  width: auto; max-width: 128px; display: inline-block;
  vertical-align: middle; margin-right: var(--space-1);
}
.quick-create-row .row-actions-cell .qc-add {
  width: auto; display: inline-block; vertical-align: middle;
}

/* -----------------------------------------------------------
   Task List — column-aligned inline create row (table <tfoot>).
   The task table has no fixed-height scroll container, so (unlike
   .projects-table) the tfoot is NOT sticky — it just sits below the
   rows, each field aligned under its column. Field styling (inputs,
   selects, .qc-plus, .qc-add) is inherited from the generic
   .quick-create-row rules above; only the cell frame is task-scoped.
   ----------------------------------------------------------- */
.task-list-table tfoot td {
  background: var(--bg-sunken);
  border-top: 2px solid var(--border);
  padding: var(--space-1) var(--space-2);
}
/* A native multi-select would grow the row to several lines and strand
   the other cells at the top; cap its height so the create row stays a
   single, spreadsheet-like line (multi-select-mirror still collects the
   full selection into its hidden input). */
.task-list-table tfoot td select[multiple] {
  height: 30px;
  padding: 0 var(--space-1);
}
/* First-run hint row inside an otherwise-empty task body — mirrors
   .projects-empty-row. Removed on first inline insert. */
.task-empty-row td {
  padding: var(--space-5) var(--space-4);
  text-align: center;
  color: var(--text-muted);
  font: var(--t-body-sm)/1.4 var(--font-sans);
  background: var(--bg-surface);
}

/* -----------------------------------------------------------
   11b. Row density — global table preference (Compact default /
   Comfortable opt-out). Toggled by .density-toggle (list-density.js),
   persisted in localStorage('kit-density'), applied pre-paint via
   the _Layout head script to html[data-density]. Compact trims the
   vertical cell padding only; horizontal rhythm is untouched.
   ----------------------------------------------------------- */
[data-density="compact"] table thead th,
[data-density="compact"] table tbody td {
  /* Even denser than before (was 4px/16px → ~24px). 2px + 15px → ~19px,
     a deliberate high-density opt-in against the ~36px comfortable base. */
  padding-top: var(--space-0_5);
  padding-bottom: var(--space-0_5);
}
[data-density="compact"] table tbody td { line-height: 15px; }
/* The real row-height inflator on wide lists is text WRAPPING (long project
   names / text columns run to 2-3 lines), which cell padding can't fix. In
   compact, hold every row to a single line: no wrapping, and the name + text
   cells ellipsis instead (full value stays in the title tooltip). This is
   what makes compact read as genuinely dense. */
[data-density="compact"] table tbody td { white-space: nowrap; }
[data-density="compact"] .proj-name-cell { min-width: 0; }
[data-density="compact"] .proj-name-cell > span:not(.pill) {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
[data-density="compact"] table tbody td.col-text .inline-edit-cell {
  display: inline-block;
  max-width: 26ch;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}
/* Shrink the leading "open tasks" chip so it isn't the tallest thing in the row. */
[data-density="compact"] .row-open-tasks { width: 20px; height: 20px; }
[data-density="compact"] .row-open-tasks svg { width: 13px; height: 13px; }

/* Kanban honours the same preference. Compact (the default) restores the
   tighter card padding + meta gap, so the default board is byte-identical
   to before; Comfortable uses the roomier base defined on .kanban-card. */
[data-density="compact"] .kanban-card {
  padding: var(--space-2) var(--space-3);
}
[data-density="compact"] .kanban-card-meta { margin-top: 4px; }
[data-density="compact"] .kanban-column-tasks { gap: var(--space-2); }

.density-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex-shrink: 0;
}
.density-toggle__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 28px;
  padding: 0;
  background: var(--bg-surface);
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.density-toggle__btn + .density-toggle__btn { border-left: 1px solid var(--border); }
.density-toggle__btn:hover { background: var(--bg-hover); color: var(--text); }
.density-toggle__btn[aria-pressed="true"] { background: var(--bg-selected); color: var(--text-accent); }
.density-toggle__btn:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--focus-ring); }

/* -----------------------------------------------------------
   12. Status pills (HSL recipe)

   Usage:
     <span class="pill pill--open">Open</span>
     <span class="pill" style="--pill-h: 280;">Custom</span>
   ----------------------------------------------------------- */

.pill {
  --pill-h: 220;
  --pill-s: 30%;
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  height: 22px;
  padding: 0 var(--space-2);
  font: 600 var(--t-caption)/1 var(--font-sans);
  border-radius: var(--radius-full);
  /* Redesign 2026.1 — pills were near-grey (fixed 65% sat / 96% L) so
     statuses looked dull and "open" vs "in progress" barely differed.
     The tint now tracks each status's own saturation (--pill-s) at a
     deeper lightness, and the dot/text are richer, so colour-coded
     statuses read at a glance. Lightness stays high (light tint) in both
     themes by design — light pills pop on the dark surface too. */
  /* 2026.2 — deepened tint + bolder dot/text/border for vivid, ClickUp-
     grade status colour (bg 93→89 L, text 28→25, dot 48→44, border 80→74). */
  background: hsl(var(--pill-h) var(--pill-s) 89%);
  color: hsl(var(--pill-h) var(--pill-s) 25%);
  border: 1px solid hsl(var(--pill-h) var(--pill-s) 74%);
  white-space: nowrap;
}
.pill::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  background: hsl(var(--pill-h) var(--pill-s) 44%);
}

/* Hues spread far apart so the four core statuses are instantly
   distinguishable; saturations pushed (2026.2) for vivid, at-a-glance colour. */
.pill--open       { --pill-h: var(--status-open-h);     --pill-s: 92%; }
.pill--progress   { --pill-h: var(--status-progress-h); --pill-s: 96%; }
.pill--blocked    { --pill-h: var(--status-blocked-h);  --pill-s: 90%; }
.pill--done       { --pill-h: var(--status-done-h);     --pill-s: 72%; }
.pill--archived   { --pill-h: 220; --pill-s: 6%;  color: var(--text-muted); }
.pill--archived::before { background: var(--grey-400); }
/* Backlog is also the "open" type-group but a distinct status — give it
   a soft violet-grey so it doesn't read identically to "Open" (vivid blue).
   Low saturation keeps it calmer than the active statuses. */
.pill--backlog    { --pill-h: 260; --pill-s: 22%; }

/* Time-entry source pills (timesheet Source column). Every source used to
   render as .pill--archived, so Timer and Manual were the same grey chip and
   only the word distinguished them — unreadable when scanning a column to
   answer "which of these did I type in by hand?". Distinct hues, kept low-
   saturation because source is metadata, not a status: it must be scannable
   without competing with the status pills on the same row. */
.pill--src-timer    { --pill-h: 200; --pill-s: 55%; }  /* teal — machine-recorded */
.pill--src-manual   { --pill-h: 275; --pill-s: 45%; }  /* violet — hand-entered */
.pill--src-imported { --pill-h: 220; --pill-s: 6%; color: var(--text-muted); }
.pill--src-imported::before { background: var(--grey-400); }

/* Engine priority pills (PRD §8.1.2). Hue derives from the matching
   semantic token (red for error / critical, amber for warning / high,
   neutral grey for medium, muted for low / none) — keeps the colour
   story consistent with banners and toasts without baking new hex codes. */
.pill--priority-low      { --pill-h: 220; --pill-s: 8%;  color: var(--text-muted); }
.pill--priority-low::before { background: var(--grey-400); }
.pill--priority-med      { --pill-h: 220; --pill-s: 20%; }
.pill--priority-high     { --pill-h: 38;  --pill-s: 75%; }   /* warning amber */
.pill--priority-critical { --pill-h: 0;   --pill-s: 70%; }   /* error red */
.pill--priority-none     { --pill-h: 220; --pill-s: 4%;  color: var(--text-subtle); }
.pill--priority-none::before { background: var(--grey-300); }

/* Plain text-style pill (no dot, no border) — useful for tags. */
.tag {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 var(--space-2);
  background: var(--bg-sunken);
  color: var(--text-muted);
  font: 500 var(--t-caption)/1 var(--font-sans);
  border-radius: var(--radius-sm);
}

/* -----------------------------------------------------------
   13. Banners + toasts
   ----------------------------------------------------------- */

.banner {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-left: 3px solid var(--text-muted);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  font-size: var(--t-body-sm);
  margin: 0 0 var(--space-4);
  color: var(--text);
}
.banner strong { color: var(--text-strong); font-weight: 600; }
.banner-success { border-left-color: var(--success); background: var(--success-bg); }
.banner-warning { border-left-color: var(--warning); background: var(--warning-bg); }
.banner-error   { border-left-color: var(--error);   background: var(--error-bg); }
.banner-info    { border-left-color: var(--info);    background: var(--info-bg); }

/* Next-login auto-pause review banner (timer-autopause-review.js). A warning
   banner whose body stacks a heading over one row per auto-paused timer, each
   with Keep / Adjust / Discard. Reuses .banner-warning for the frame. */
.autopause-review { align-items: stretch; }
.autopause-review__body { display: flex; flex-direction: column; gap: var(--space-3); width: 100%; }
.autopause-review__head { display: flex; flex-direction: column; gap: var(--space-0_5); }
.autopause-review__lede { color: var(--text-muted); }
.autopause-review__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.autopause-review__item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.autopause-review__info { display: flex; flex-direction: column; gap: var(--space-0_5); min-width: 0; }
.autopause-review__task { color: var(--text-strong); font-weight: 600; }
.autopause-review__meta { color: var(--text-muted); font-size: var(--t-caption); }
.autopause-review__actions { display: flex; gap: var(--space-1); flex: none; }
.autopause-review__discard:hover { color: var(--error); }
@media (max-width: 640px) {
  .autopause-review__item { flex-direction: column; align-items: flex-start; }
  .autopause-review__actions { width: 100%; }
}

/* Toast container — fixed position; JS module will append children.
   The CSS is ready; the JS wiring lands when the API surface exists. */
.toast-stack {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  z-index: 80;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  min-width: 280px;
  max-width: 360px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3) var(--space-4);
  font-size: var(--t-body-sm);
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  animation: toast-in var(--duration-default) var(--ease-decelerate);
}
.toast--success { border-left: 3px solid var(--success); }
.toast--error   { border-left: 3px solid var(--error); }
.toast--info    { border-left: 3px solid var(--info); }
.toast__content { flex: 1; min-width: 0; }
.toast-title { font-weight: 600; color: var(--text-strong); }
.toast-body  { color: var(--text-muted); margin-top: 2px; }
/* Manual-dismiss button (toast.js). Sits at the toast's trailing edge. */
.toast__close {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin: -2px calc(var(--space-1) * -1) 0 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-subtle);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.toast__close:hover { background: var(--bg-hover); color: var(--text); }
.toast__close:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }

/* Action button (toast.js) — e.g. "Undo". Sits before the close button. */
.toast__action {
  flex-shrink: 0;
  align-self: center;
  margin-left: var(--space-1);
  padding: var(--space-0_5) var(--space-2);
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
  white-space: nowrap;
}
.toast__action:hover { background: var(--bg-hover); }
.toast__action:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }

@keyframes toast-in {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

/* -----------------------------------------------------------
   14. Skeleton loaders
   ----------------------------------------------------------- */

.skeleton {
  background: linear-gradient(100deg,
    var(--grey-100) 30%,
    var(--grey-50)  50%,
    var(--grey-100) 70%);
  background-size: 200% 100%;
  border-radius: var(--radius-sm);
  animation: skeleton-shimmer 1.4s linear infinite;
  display: inline-block;
}
.skeleton--text  { height: 12px; width: 80%; border-radius: 4px; }
.skeleton--title { height: 18px; width: 50%; }
.skeleton--row   { height: 36px; width: 100%; }
.skeleton--card  { height: 80px; width: 100%; border-radius: var(--radius-md); }
.skeleton--circle { border-radius: 50%; width: 24px; height: 24px; }

/* Dark mode — the grey ramp above is the fixed light slate, so without
   this override the shimmer renders as bright bars on dark surfaces.
   Re-point to the dark end of the same ramp (base 800, highlight 700);
   background-size from the base rule still applies. */
[data-theme="dark"] .skeleton {
  background-image: linear-gradient(100deg,
    var(--grey-800) 30%,
    var(--grey-700) 50%,
    var(--grey-800) 70%);
}

@keyframes skeleton-shimmer {
  from { background-position:  200% 0; }
  to   { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; background: var(--bg-hover); }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* -----------------------------------------------------------
   15. Empty state
   ----------------------------------------------------------- */

.empty {
  max-width: 480px;
  margin: var(--space-20) auto;
  text-align: center;
  color: var(--text-muted);
}
.empty-icon {
  width: 48px; height: 48px;
  margin: 0 auto var(--space-3);
  color: var(--text-accent);
}
.empty-title {
  font-size: var(--t-heading-sm);
  font-weight: 600;
  color: var(--text-strong);
  margin: 0 0 var(--space-1);
}
.empty-body { font-size: var(--t-body-sm); line-height: 20px; margin: 0 0 var(--space-4); }

/* -----------------------------------------------------------
   16. Avatar
   ----------------------------------------------------------- */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--bg-selected);
  color: var(--text-accent);
  font: 600 11px/1 var(--font-sans);
  letter-spacing: 0;
  flex-shrink: 0;
  text-transform: uppercase;
}
/* sm: fits inside the 32px .icon-btn without overflow */
.avatar--sm { width: 26px; height: 26px; font-size: 11px; }
.avatar--md { width: 24px; height: 24px; font-size: 12px; }
.avatar--lg { width: 32px; height: 32px; font-size: 13px; }

/* Multi-assignee step 3 — avatar stack. Overlapping circles with a thin
   surface-coloured ring so adjacent faces stay legible; the "+N" overflow
   chip reads as a muted face. Used by the Kanban card + the List cell's
   extra-assignee avatars. */
.avatar-stack {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
}
.avatar-stack .avatar:not(:first-child),
.avatar.avatar--stacked {
  margin-left: -6px;
  box-shadow: 0 0 0 2px var(--bg-surface);
}
.avatar--more {
  background: var(--bg-sunken);
  color: var(--text-muted);
  font-size: 10px;
}

/* Icon button variant that hosts an initials avatar instead of an SVG.
   Remove the default muted colour tint so the navy token on the avatar reads. */
.icon-btn--avatar { color: inherit; }

/* -----------------------------------------------------------
   17. Kanban
   ----------------------------------------------------------- */

.kanban-board {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  padding: var(--space-1) 0 var(--space-4);
  align-items: flex-start;
}

.kanban-column {
  flex: 0 0 280px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 240px);
}

.kanban-column-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  font: 600 var(--t-caption)/1 var(--font-sans);
  color: var(--text);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.kanban-column-name { display: inline-flex; align-items: center; gap: 6px; }
.kanban-column-name::before {
  content: '';
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

.kanban-column-count {
  background: var(--bg-surface);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  padding: 0 8px;
  font: 600 11px/18px var(--font-sans);
  font-variant-numeric: tabular-nums;
  min-width: 22px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.kanban-column-tasks {
  list-style: none;
  margin: 0;
  padding: var(--space-2);
  flex: 1;
  overflow-y: auto;
  min-height: 80px;
  display: flex;
  flex-direction: column;
  position: relative;
  /* Comfortable (base) gap; compact restores 8px in §11b. */
  gap: var(--space-3);
}

/* Empty-column drop hint — a dashed drop zone so an empty column reads
   as a valid drag target (ClickUp shows the same). Pure CSS, no JS.
   Uses :not(:has(> li)) rather than :empty because the Razor-rendered
   <ul> contains whitespace text nodes (so :empty never matches); this
   matches "no card <li> children" regardless of whitespace. */
.kanban-column-tasks:not(:has(> li)) {
  align-items: center;
  justify-content: center;
}
.kanban-column-tasks:not(:has(> li))::after {
  content: 'Drop here';
  display: block;
  width: 100%;
  padding: var(--space-4) var(--space-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  text-align: center;
  color: var(--text-subtle);
  font: 500 var(--t-caption)/1 var(--font-sans);
}

.kanban-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  /* Comfortable (base) padding. Compact — the global default — restores the
     tighter 8px/12px in the density block (§11b) so the default look is
     unchanged; choosing Comfortable lets the boards breathe. */
  padding: var(--space-3) var(--space-3);
  cursor: grab;
  box-shadow: var(--shadow-xs);
  transition: box-shadow var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.kanban-card:hover {
  box-shadow: var(--shadow-sm);
  border-color: var(--border-strong);
}
.kanban-card:active { cursor: grabbing; }
/* Cards are keyboard-focusable (tabindex=0) for the keyboard-move flow
   in kanban.js. Give them the standard focus ring. */
.kanban-card:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
/* "Picked up" state — a card grabbed via the keyboard. Lifted + ringed
   so sighted keyboard users see what they're moving; the aria-live
   region narrates it for screen readers. */
.kanban-card--grabbed {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  box-shadow: var(--shadow-lg);
  border-color: var(--focus-ring);
}

.kanban-card-code {
  font: 500 11px/1 var(--font-mono);
  color: var(--text-muted);
  letter-spacing: 0.02em;
  margin-bottom: 4px;
}

.kanban-card-title {
  font-size: var(--t-body-sm);
  font-weight: 500;
  color: var(--text-strong);
  line-height: 18px;
}

.kanban-card-meta {
  font-size: var(--t-caption);
  color: var(--text-muted);
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Phase 30 — task-level custom values rendered as chips beneath the
   card title. Per-column hue dispatch (priority red etc.) is a
   follow-up; for now they share a neutral chip vocabulary. */
.kanban-card-values {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: 6px;
}

.kanban-card-chip {
  font-size: var(--t-caption);
  font-weight: 500;
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  /* Role tokens so the chip auto-switches in dark mode. */
  background: var(--bg-sunken);
  color: var(--text);
  border: 1px solid var(--border);
  line-height: 1.4;
}

.kanban-card-footer {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--divider);
}
.kanban-card-billable {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border-radius: var(--radius-full);
  background: var(--success-bg);
  color: var(--success);
  font: 600 11px/1 var(--font-sans);
  flex-shrink: 0;
}
.kanban-card-footer .kanban-card-meta { margin-left: auto; }

/* SortableJS drop placeholder. */
.kanban-card-ghost {
  opacity: 0.5;
  background: var(--bg-selected);
  border: 1px dashed var(--accent);
  box-shadow: none;
}

/* -----------------------------------------------------------
   18. Gantt toolbar
   ----------------------------------------------------------- */

.gantt-toolbar {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.gantt-toolbar button {
  height: 28px;
  padding: 0 var(--space-3);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  background: transparent;
  color: var(--text-muted);
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-standard);
}
.gantt-toolbar button:hover { color: var(--text-strong); background: transparent; }
.gantt-toolbar button.active {
  background: var(--bg-surface);
  color: var(--text-strong);
  box-shadow: var(--shadow-xs);
}

/* Audit #2 — restore horizontal scroll. Frappe sizes the chart SVG to
   width="1680" (its computed timeline width) but its bundled CSS forces
   svg.gantt to width:100% of the container, so the element shrinks to
   ~container width while the bars/labels are drawn far to the right —
   beyond the box, with no scroll to reach them (scrollWidth == clientWidth).
   Let the SVG keep its intrinsic width so .gantt-container (overflow-x:auto)
   gets a real horizontal scrollbar; min-width keeps small charts full-bleed.
   !important is required: Frappe sets style="width:100%" INLINE on the svg
   (and re-applies it on every Day/Week/Month re-render), which a normal
   stylesheet rule can't beat. */
.gantt-container svg.gantt {
  width: auto !important;
  min-width: 100%;
  max-width: none;
}

/* Phase 57 — interactive dependency popup. Frappe Gantt's default
   popup-wrapper is a dark 80%-transparent panel; override it so the
   tooltip + dependency picker look like a native KIT popover. */
.gantt-container .popup-wrapper {
  background: var(--bg-raised);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 0;
  font-family: var(--font-sans);
  font-size: var(--t-body-sm);
  z-index: 60;
  max-width: 320px;
}
.gantt-container .popup-wrapper .pointer {
  border-top-color: var(--bg-raised) !important;
}

.gantt-popup {
  padding: var(--space-3) var(--space-4);
  min-width: 240px;
}
.gantt-popup__title {
  font-weight: 600;
  font-size: var(--t-body-md);
  color: var(--text-strong);
  letter-spacing: -0.01em;
  margin-bottom: 2px;
}
.gantt-popup__dates {
  font-size: var(--t-caption);
  margin-bottom: var(--space-2);
}
.gantt-popup__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--t-body-sm);
  padding: 2px 0;
}
.gantt-popup__label { color: var(--text-muted); }
.gantt-popup__value { color: var(--text); font-weight: 500; }

.gantt-popup__section {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
}
.gantt-popup__heading {
  font-size: var(--t-caption);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}
.gantt-popup__empty {
  font-size: var(--t-body-sm);
  font-style: italic;
}

.gantt-popup__deps {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.gantt-popup__dep-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: var(--bg-sunken);
  border-radius: var(--radius-sm);
  font-size: var(--t-body-sm);
}
.gantt-popup__dep-label { color: var(--text); }
.gantt-popup__remove {
  width: 22px; height: 22px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  line-height: 1;
  color: var(--text-muted);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-standard);
}
.gantt-popup__remove:hover {
  background: var(--error-bg);
  color: var(--error);
}
.gantt-popup__remove:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.gantt-popup__filter {
  width: 100%;
  height: 28px;
  padding: 0 var(--space-2);
  font: 400 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  outline: none;
  margin-bottom: var(--space-2);
  box-sizing: border-box;
}
.gantt-popup__filter:focus {
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 2px var(--focus-ring);
}

.gantt-popup__candidates {
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 180px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.gantt-popup__candidate {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.gantt-popup__candidate:hover { background: var(--bg-hover); }
.gantt-popup__candidate-code {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--t-caption);
  color: var(--text-muted);
}
.gantt-popup__candidate-title {
  font-size: var(--t-body-sm);
  color: var(--text);
}

/* Phase 58 — critical-path + milestone + drag-to-create dependency
   visual treatments. Per CLAUDE.md §10: all values driven by tokens.

   The critical-path color uses --warning (amber) — "needs attention"
   tone, the conventional CPM colour, and contrasts cleanly with the
   default navy bar. Selectors are scoped to .bar-wrapper.gantt-bar--critical
   to win over Frappe's `.bar { fill: ... }` base style without !important. */

.bar-wrapper.gantt-bar--critical .bar {
  fill: var(--warning);
}
.bar-wrapper.gantt-bar--critical .bar-progress {
  fill: var(--warning-bg);
}
.bar-wrapper.gantt-bar--critical .bar-label {
  fill: var(--text-strong);
  font-weight: 600;
}
.bar-wrapper.gantt-bar--critical .gantt-milestone {
  fill: var(--warning);
}

/* "ON CRITICAL PATH" banner inside the popup. Caption-sized, amber,
   uppercase — quiet but immediately legible. */
.gantt-popup__critical {
  display: inline-block;
  margin-bottom: var(--space-2);
  padding: 2px var(--space-2);
  background: var(--warning-bg);
  color: var(--warning);
  font-size: var(--t-caption);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--radius-sm);
}

/* Milestone diamond — the same visual weight as a bar but pointed.
   The fill inherits the bar palette (default navy / critical amber)
   so milestone-on-critical-path reads correctly. */
.gantt-milestone {
  fill: var(--accent);
  stroke: var(--accent-active);
  stroke-width: 1;
  cursor: pointer;
}
.gantt-milestone:hover {
  fill: var(--accent-hover);
}

/* Drag-to-create dependency: the link-dot handle sits past the bar's
   right edge. Hidden until hover so it doesn't clutter the chart. */
.gantt-link-dot {
  fill: var(--accent);
  stroke: var(--bg-surface);
  stroke-width: 1.5;
  cursor: crosshair;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-standard);
}
.bar-wrapper:hover .gantt-link-dot {
  opacity: 1;
}
.gantt-link-dot:hover {
  fill: var(--accent-hover);
  opacity: 1;
}

/* Preview line follows the cursor while the user is dragging a new
   dependency arrow. Dashed amber for "in-flight" feedback; the final
   solid arrow is drawn by Frappe once the POST succeeds. */
.gantt-link-preview {
  stroke: var(--warning);
  stroke-width: 2;
  stroke-dasharray: 4 3;
  fill: none;
  pointer-events: none;
}

/* -----------------------------------------------------------
   Third-party widget dark-mode parity (Gantt + AG Grid)
   -----------------------------------------------------------
   Frappe Gantt ships its own SCSS with hardcoded light values
   (#fff grid rows, #333 axis text, #e0e0e0 ticks). Those never see
   our role tokens, so in dark mode the rendered SVG is a bright-white
   island with near-black axis text. We remap only the *chrome* under
   [data-theme="dark"] .gantt — grid rows, header, lines, ticks, axis
   text, today highlight, dependency arrows. The default `.bar` fill
   (grey #b8c2cc) reads fine on dark, so it's deliberately left alone
   to avoid changing light mode. The even-row stripe is overridden
   explicitly because Frappe's `.grid-row:nth-child(even)` (#f5f5f5)
   would otherwise band light grey through the dark chart. */
[data-theme="dark"] .gantt .grid-header {
  fill: var(--bg-raised);
  stroke: var(--border);
}
[data-theme="dark"] .gantt .grid-row {
  fill: var(--bg-surface);
}
[data-theme="dark"] .gantt .grid-row:nth-child(even) {
  fill: var(--bg-app);
}
[data-theme="dark"] .gantt .row-line {
  stroke: var(--divider);
}
[data-theme="dark"] .gantt .tick {
  stroke: var(--border);
}
[data-theme="dark"] .gantt .today-highlight {
  fill: var(--warning-bg);
}
[data-theme="dark"] .gantt .arrow {
  stroke: var(--text-muted);
}
[data-theme="dark"] .gantt .lower-text {
  fill: var(--text);
}
[data-theme="dark"] .gantt .upper-text {
  fill: var(--text-muted);
}

/* AG Grid (Table view). The quartz / quartz-dark theme class is swapped
   in table.js to follow the resolved theme. These overrides bridge a
   few AG variables to KIT tokens so selection / focus accents are navy
   and the grid uses Inter — applies in both themes (the quartz themes
   read these CSS custom properties at runtime). Background / text /
   border colours are left to the quartz themes themselves.

   The selector pins the theme class to the grid mount ([data-module="table"]).
   AG Grid's own stylesheet defines these vars on `.ag-theme-quartz`
   (specificity 0,1,0) and is injected by the JS bundle *after* site.css,
   so a bare `.ag-theme-quartz` rule here would lose the cascade. Pairing
   the attribute with the class (0,2,0) wins regardless of load order. */
[data-module="table"].ag-theme-quartz,
[data-module="table"].ag-theme-quartz-dark {
  --ag-active-color: var(--accent);
  --ag-font-family: var(--font-sans);
}

/* Inline "add subtask" / quick-add input rendered into an AG Grid cell
   (table.js row-actions). Lifted out of an inline style string so it
   picks up dark-mode surface/text tokens instead of a default white
   input on a dark cell. */
.inline-add-input {
  flex: 1;
  min-width: 0;
  height: 24px;
  padding: 2px var(--space-1_5, 6px);
  font: inherit;
  font-size: var(--t-body-sm);
  color: var(--text);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  outline: none;
}
.inline-add-input:focus-visible {
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 2px var(--focus-ring);
}

/* -----------------------------------------------------------
   19. Utility primitives
   -----------------------------------------------------------
   Base composition helpers (.stack, .row, .spacer) live here.
   View-specific layout helpers (.grid-3col, .list-row,
   .section-heading, .timer-clock-xl, etc.) are appended near
   the bottom of this file in the "View utility helpers"
   section — see the roster comment there for a full index.
   When extracting an inline `style=`, check that roster first;
   add a new utility next to its peers rather than ad-hoc.
   ----------------------------------------------------------- */

.stack { display: flex; flex-direction: column; gap: var(--space-3); }
.stack--tight { gap: var(--space-2); }
.stack--loose { gap: var(--space-5); }

.row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.row--between { justify-content: space-between; }
.row--end     { justify-content: flex-end; }
.row--wrap    { flex-wrap: wrap; }

.spacer { flex: 1; }

/* -----------------------------------------------------------
   20. Print
   ----------------------------------------------------------- */

@media print {
  .site-header, .site-rail, .site-side, .site-footer, .page-actions, .tabs { display: none !important; }
  .site-shell { display: block; }
  .site-main { padding: 0; }
  body { background: white; }
}

/* -----------------------------------------------------------
   N. Breadcrumbs (Task D)
   ----------------------------------------------------------- */
.breadcrumbs {
  margin-bottom: var(--space-2);
  font-size: var(--t-body-sm);
  color: var(--text-muted);
}
.breadcrumbs ol {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-1);
  align-items: center;
}
.breadcrumbs li { display: inline-flex; align-items: center; gap: var(--space-1); }
.breadcrumbs a { color: var(--text-muted); text-decoration: none; }
.breadcrumbs a:hover { color: var(--text-link); text-decoration: underline; }
.breadcrumbs [aria-current="page"] { color: var(--text); font-weight: 500; }
.breadcrumbs__sep { color: var(--text-subtle); }

/* -----------------------------------------------------------
   N. Help modal + user menu (Task C)
   ----------------------------------------------------------- */
.help-modal {
  position: fixed; inset: 0;
  background: rgb(0 0 0 / 0.32);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000;
  padding: var(--space-4);
}
.help-modal__panel {
  background: var(--bg-surface);
  color: var(--text);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  padding: var(--space-5);
  width: min(440px, 100%);
  outline: none;
}
.help-modal__title {
  font-size: var(--t-heading-md);
  font-weight: 600;
  margin-bottom: var(--space-3);
  letter-spacing: -0.01em;
}
.help-modal__list {
  list-style: none; padding: 0; margin: 0 0 var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.help-modal__list li {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--space-3);
  font-size: var(--t-body-sm);
}
.help-modal__keys { display: inline-flex; gap: 4px; }

.user-menu,
.create-menu {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  min-width: 200px;
  padding: var(--space-1);
  z-index: 900;
  font-size: var(--t-body-sm);
}
.user-menu__item,
.create-menu__item {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  text-align: left;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
}
.user-menu__item:hover,
.create-menu__item:hover { background: var(--bg-hover); }
.user-menu__item:focus-visible,
.create-menu__item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring), 0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}
.user-menu__item--danger { color: var(--error); }

/* Appearance section — label row + three-way segmented control
   (Light / Dark / System) inside the account-menu dropdown. */
.user-menu__appearance {
  padding: var(--space-2) var(--space-3) var(--space-1);
}
.user-menu__appearance-label {
  display: block;
  font-size: var(--t-caption);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: var(--space-1);
}
.user-menu__appearance-group {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.user-menu__appearance-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 26px;
  padding: 0 var(--space-2);
  font: inherit;
  font-size: var(--t-caption);
  font-weight: 500;
  color: var(--text-muted);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-standard);
  white-space: nowrap;
}
.user-menu__appearance-btn:hover {
  color: var(--text-strong);
}
.user-menu__appearance-btn[aria-checked="true"] {
  background: var(--bg-surface);
  color: var(--text-strong);
  box-shadow: var(--shadow-xs);
}
.user-menu__appearance-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring), 0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}

/* Accent swatch picker (~50 colours) — sets the brand --accent live. */
.user-menu__accent {
  padding: var(--space-2) var(--space-3) var(--space-2);
  border-top: 1px solid var(--divider);
}
.user-menu__accent-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-1);
}
.user-menu__accent-reset {
  font: inherit;
  font-size: var(--t-caption);
  font-weight: 500;
  color: var(--text-link);
  background: transparent;
  border: 0;
  border-radius: 6px;
  padding: 2px 6px;
  cursor: pointer;
}
.user-menu__accent-reset:hover { color: var(--text-link-hover); background: var(--bg-hover); }
.user-menu__accent-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
}
.user-menu__swatch {
  width: 100%;
  aspect-ratio: 1;
  padding: 0;
  border-radius: 6px;
  background: var(--sw);
  border: 1px solid color-mix(in srgb, var(--sw) 72%, #000);
  cursor: pointer;
  transition: transform var(--duration-fast) var(--ease-standard);
}
.user-menu__swatch:hover { transform: scale(1.14); }
.user-menu__swatch[aria-checked="true"] {
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--text-strong);
}
.user-menu__swatch:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--focus-ring);
}

/* Create-menu item is a two-line link: action + the place it lands. */
.create-menu__label { display: block; }
.create-menu__sec {
  display: block;
  margin-top: 1px;
  font-size: var(--t-caption);
  color: var(--text-muted);
}

/* -----------------------------------------------------------
   N2. Workspace switcher (ClickUp-style)
   Chip at the top of the light side panel + its dropdown.
   Dropdown reuses the .user-menu recipe (surface/border/shadow).
   ----------------------------------------------------------- */
.ws-switcher-wrap { position: relative; }

.ws-switcher {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-1) var(--space-2);
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-strong);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.ws-switcher:hover { background: var(--bg-hover); }
.ws-switcher:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring), 0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}
.ws-switcher__avatar {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
}
.ws-switcher__name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--t-body);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws-switcher__caret { flex: 0 0 auto; color: var(--text-subtle); }

.ws-switcher-menu {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  min-width: 240px;
  max-width: 320px;
  max-height: min(70vh, 520px);
  overflow-y: auto;
  padding: var(--space-1);
  z-index: 950;
  font-size: var(--t-body-sm);
}
.ws-switcher-menu[hidden] { display: none; }

.ws-switcher-menu__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
  padding: var(--space-2) var(--space-2) var(--space-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws-switcher-menu__divider {
  height: 1px;
  background: var(--divider);
  margin: var(--space-1) 0;
}
.ws-switcher-menu__empty {
  padding: var(--space-2);
  margin: 0;
  color: var(--text-muted);
}
.ws-switcher-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.ws-switcher-menu__item:hover {
  background: var(--bg-hover);
  color: var(--text-strong);
  text-decoration: none;
}
.ws-switcher-menu__item:focus-visible {
  outline: none;
  /* inset so the ring isn't clipped by the menu's overflow */
  box-shadow: inset 0 0 0 2px var(--focus-ring);
}
.ws-switcher-menu__item-name {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws-switcher-menu__icon { flex: 0 0 auto; color: var(--text-muted); }
.ws-switcher-menu__ws-avatar {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-sunken);
  color: var(--text-strong);
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
}
.ws-switcher-menu__ws.is-active {
  background: var(--bg-selected);
  color: var(--text-accent);
  font-weight: 600;
}
.ws-switcher-menu__ws.is-active .ws-switcher-menu__ws-avatar {
  background: var(--accent);
  color: var(--accent-contrast);
}
.ws-switcher-menu__check { flex: 0 0 auto; margin-left: auto; }

/* -----------------------------------------------------------
   N3. Workspace settings hub (ClickUp-style two-pane layout)
   Left nav + right panes on the Workspace Edit page. The grid only
   engages once workspace-edit-tabs.js adds .is-ready, so with no JS
   the page stays single-column with every pane stacked (original
   behaviour preserved).
   ----------------------------------------------------------- */
.settings-hub.is-ready {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.settings-hub__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  position: sticky;
  top: var(--space-4);
}
.settings-hub__nav-group { display: flex; flex-direction: column; gap: 1px; }
.settings-hub__nav-group + .settings-hub__nav-group { margin-top: var(--space-3); }
.settings-hub__nav-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
  padding: var(--space-2) var(--space-2) var(--space-1);
}
.settings-hub__nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  height: 32px;
  padding: 0 var(--space-2);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.settings-hub__nav-item:hover {
  background: var(--bg-hover);
  color: var(--text-strong);
  text-decoration: none;
}
.settings-hub__nav-item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring), 0 0 0 4px color-mix(in srgb, var(--focus-ring) 30%, transparent);
}
.settings-hub__nav-item.active {
  background: var(--bg-selected);
  color: var(--text-accent);
  font-weight: 600;
}
.settings-hub__nav-item--danger { color: var(--error); }
.settings-hub__nav-item--danger:hover,
.settings-hub__nav-item--danger.active { background: var(--error-bg); color: var(--error); }
.settings-hub__nav-item-name { flex: 1 1 auto; min-width: 0; }
.settings-hub__nav-extlink { flex: 0 0 auto; color: var(--text-subtle); }
.settings-hub__panes { min-width: 0; }

@media (max-width: 720px) {
  .settings-hub.is-ready { grid-template-columns: 1fr; gap: var(--space-4); }
  .settings-hub__nav { position: static; flex-direction: row; flex-wrap: wrap; }
  .settings-hub__nav-group { flex-direction: row; flex-wrap: wrap; align-items: center; }
  .settings-hub__nav-group + .settings-hub__nav-group { margin-top: 0; }
  .settings-hub__nav-label { width: 100%; }
}

/* -----------------------------------------------------------
   O. Cmd+K command palette
   Modal-style, 640px wide, ~120px from top per design brief §4.
   ----------------------------------------------------------- */
.cmdk-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0.32);
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding-top: 120px;
  padding-left: var(--space-4);
  padding-right: var(--space-4);
  z-index: 1100;
}
.cmdk-modal {
  background: var(--bg-surface);
  color: var(--text);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  width: min(640px, 100%);
  max-height: calc(100vh - 160px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
}
.cmdk-input {
  appearance: none;
  width: 100%;
  height: 52px;
  padding: 0 var(--space-4);
  border: 0;
  border-bottom: 1px solid var(--divider);
  background: transparent;
  color: var(--text-strong);
  font: 400 var(--t-body-md)/1 var(--font-sans);
  outline: none;
}
.cmdk-input::placeholder { color: var(--text-subtle); }
.cmdk-list {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-2);
}
.cmdk-section + .cmdk-section { margin-top: var(--space-2); }
.cmdk-section-heading {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font: 600 var(--t-caption)/1 var(--font-sans);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
}
.cmdk-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
  user-select: none;
}
.cmdk-item--selected {
  background: var(--bg-selected);
}
.cmdk-item__main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.cmdk-item__label {
  font: 500 var(--t-body-sm)/1.2 var(--font-sans);
  color: var(--text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cmdk-item__secondary {
  font: 400 var(--t-caption)/1.2 var(--font-sans);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cmdk-empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  font: 400 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text-muted);
}

/* -----------------------------------------------------------
   Q. Quick-add row — inline task creation below list views
   ----------------------------------------------------------- */

.quick-add-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  /* Match the data-row rhythm: same horizontal padding as table cells
     so the input sits column-aligned with the Title column above. */
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--divider);
  color: var(--text-subtle);
  cursor: text;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.quick-add-row:hover {
  background: var(--bg-hover);
  color: var(--text-muted);
}
.quick-add-row:focus-within {
  background: var(--bg-surface);
  color: var(--text);
}

.quick-add-row input {
  flex: 1;
  min-width: 0;
  border: 1px solid transparent;
  background: transparent;
  font: 500 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text);
  padding: var(--space-1) var(--space-1_5, var(--space-2));
  border-radius: var(--radius-sm);
}
.quick-add-row input::placeholder {
  /* The placeholder carries the "Add task…" affordance — it has to
     read as actionable copy, not a faint label. Muted, not subtle. */
  color: var(--text-muted);
  font-weight: 500;
}
.quick-add-row input:focus {
  outline: none;
  /* Soft translucent halo inside the row (the row already lifts on
     focus-within); avoid the loud solid navy ring. */
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--focus-ring) 22%, transparent);
  background: var(--bg-surface);
}
.quick-add-row input::placeholder { transition: color var(--duration-fast) var(--ease-standard); }
.quick-add-row:focus-within input::placeholder { color: var(--text-subtle); }

/* Leading "+" glyph — render as an inline-flex chip rather than a
   bare character so it sits on the same baseline as the input and
   gets its own hover/focus highlight from the row state. ClickUp's
   "+ Add Task" reads as a single tappable affordance; this matches. */
.quick-add-row::before {
  content: '+';
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: inherit;
  font: 600 16px/1 var(--font-sans);
  border-radius: var(--radius-sm);
  transition: color var(--duration-fast) var(--ease-standard),
              background var(--duration-fast) var(--ease-standard);
}
.quick-add-row:hover::before {
  color: var(--text);
  background: var(--bg-selected);
}

/* Kanban column variant — flush with column walls, no top border
   separator. The hover lift still applies (it's the same affordance
   pattern, just in a card instead of a table). */
.quick-add-row--card {
  border-top: none;
  padding: var(--space-2);
  border-radius: var(--radius-sm);
}

/* Kanban + multi-field — stack the controls vertically so they fit
   the narrow column width. The List variant is single-row; this
   compose lets the same form generate the right layout per surface. */
.quick-add-row--card.quick-add-row--full {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  padding: var(--space-2);
}
.quick-add-row--card.quick-add-row--full::before {
  /* The leading + glyph reads as a row affordance on the List view;
     inside a card it doubles up visually with the "Add" button, so
     drop it for the stacked variant. */
  display: none;
}
.quick-add-row--card.quick-add-row--full input,
.quick-add-row--card.quick-add-row--full select,
.quick-add-row--card.quick-add-row--full input[type="date"] {
  width: 100%;
  flex: none;
}

/* Kanban add-card trigger — collapsed affordance. The full composer
   (.quick-add-row--card.quick-add-row--full) is `hidden` until this
   trigger is clicked, so an empty column reads compact instead of
   reserving ~250px for an always-open form (audit #4). */
.kanban-add-trigger {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2);
  margin-top: var(--space-1);
  border: 1px dashed transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-subtle);
  font: 500 var(--t-body-sm)/1.4 var(--font-sans);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.kanban-add-trigger:hover {
  background: var(--bg-hover);
  color: var(--text);
  border-color: var(--border);
}
.kanban-add-trigger[aria-expanded="true"] { display: none; }
/* The composer is flex (.quick-add-row--card.quick-add-row--full), whose
   class specificity (0,2,0) outranks the [hidden] attribute's UA
   display:none — so hiding needs an equal-or-higher-specificity rule
   (0,3,0 here) or the collapsed form stays visible. */
.quick-add-row--card.quick-add-row--full[hidden] { display: none; }

/* Multi-field "inline-create" variant — ClickUp-style. Every built-in
   column gets a control so the user can ship a fully-populated row
   without leaving the table. Inputs/selects share the row hover/focus
   lift; widths are constrained so the row reads as a row of cells
   rather than one long input. */
.quick-add-row--full {
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
}
.quick-add-row--full input[type="text"],
.quick-add-row--full input[type="number"],
.quick-add-row--full select,
.quick-add-row--full input[type="date"] {
  border: 1px solid var(--border);
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  font: 500 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text);
  padding: var(--space-1) var(--space-2);
  height: 28px;
  box-sizing: border-box;
}
.quick-add-row--full input[name="title"],
.quick-add-row--full input[name="name"] {
  flex: 1 1 200px;
  min-width: 160px;
}
.quick-add-row--full input[name="code"]   { flex: 0 0 100px; }
.quick-add-row--full select               { flex: 0 0 140px; }
.quick-add-row--full input[type="date"]   { flex: 0 0 130px; }
/* Number / currency custom-column inputs: fixed basis so they read as a
   cell rather than stretching (the base `.quick-add-row input { flex: 1 }`
   would otherwise make them grow). */
.quick-add-row--full input[type="number"] { flex: 0 0 120px; }
.quick-add-row--full input[type="checkbox"] {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--accent);
}
.quick-add-row--full label {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-muted);
  cursor: pointer;
}
.quick-add-row--full button[type="submit"] {
  flex: 0 0 auto;
  padding: 0 var(--space-3);
  height: 28px;
  border-radius: var(--radius-sm);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  background: var(--accent);
  color: var(--accent-contrast);
  border: 1px solid var(--accent);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.quick-add-row--full button[type="submit"]:hover  { background: var(--accent-hover); }
.quick-add-row--full button[type="submit"]:disabled { opacity: 0.6; cursor: progress; }

/* Inline validation message — wraps onto its own full-width line below
   the controls (flex-basis 100%), so a duplicate-code / blank-name error
   reads right next to the row that produced it instead of only as a
   transient toast. Hidden until quick-add.js fills it. */
.quick-add-row__error {
  flex: 1 0 100%;
  margin: 0;
  padding: var(--space-0_5) var(--space-1);
  font: 500 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--error);
}
.quick-add-row__error[hidden] { display: none; }

.quick-add-row--full input:focus,
.quick-add-row--full select:focus {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 2px var(--focus-ring);
}

/* Table view inline-create — single non-wrapping toolbar of cells. The
   Table view can't column-align a form to AG Grid's virtualized columns,
   so instead of the default `--full` wrap (which folded the many built-in
   + custom-column fields into a messy 2-row block) the row stays on one
   line and scrolls horizontally — consistent with the grid below it,
   which also scrolls. The compound selector (0,2,0) is required to beat
   `.quick-add-row--full`'s flex-wrap (same trap as the [hidden] rules
   above — equal specificity would let source order decide). */
.quick-add-row--full.quick-add-row--table {
  flex-wrap: nowrap;
  overflow-x: auto;
  /* Unlike the List (form lives in a <tfoot>, cells associated via `form=`)
     and Kanban (form sits in a ~250px card) inline-creates, this composer
     is a full-width form directly in .site-main — so it must opt out of the
     global `form { max-width: 640px }` cap (site.css §forms) or it would be
     clipped to 640px and scroll long before using the available width. */
  max-width: none;
}

/* -----------------------------------------------------------
   Inline, name-first creator (.quick-create) — ClickUp / Planner
   style. A single name field + Add by default; "More" reveals the
   optional fields. Distinct from .quick-add-row (the dense all-column
   task adder) so each can evolve independently.
   ----------------------------------------------------------- */
.quick-create {
  margin-top: var(--space-3);
  padding: var(--space-1) var(--space-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
.quick-create:focus-within {
  border-style: solid;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
.quick-create__main {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.quick-create__plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
  background: var(--bg-selected);
  color: var(--text-accent);
}
.quick-create__name {
  flex: 1 1 auto;
  min-width: 0;
  height: 36px;
  border: none;
  background: transparent;
  color: var(--text-strong);
  font: 500 var(--t-body-md)/1 var(--font-sans);
}
.quick-create__name::placeholder { color: var(--text-subtle); font-weight: 400; }
.quick-create__name:focus { outline: none; }
.quick-create__more {
  flex-shrink: 0;
  height: 28px;
  padding: 0 var(--space-2);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
}
.quick-create__more:hover { background: var(--bg-hover); color: var(--text); }
.quick-create__extra {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) var(--space-1) var(--space-2);
  margin-top: var(--space-1);
  border-top: 1px solid var(--divider);
}
.quick-create__extra[hidden] { display: none; }
.quick-create__extra input,
.quick-create__extra select {
  height: 34px;
  width: 100%;
}
.quick-create__check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
}
.quick-create .quick-add-row__error { margin-top: var(--space-1); }

/* Compact empty state — first-run, paired with the inline creator
   right below it (so no big CTA pulling to a separate page). */
.empty--compact {
  margin: var(--space-6) auto var(--space-2);
  padding: 0;
}

/* Per-group quick-add (List view grouped mode) — sits inside a
   <details> below the group's table. Drop the border-top (the table
   already has its own bottom edge) so the row hugs the table without
   a double-line. */
.task-list-group .quick-add-row--group {
  border-top: 1px solid var(--divider);
  margin: 0;
}

/* Inline subtask-add — the form is wrapped in a full-width <td>
   injected by subtask-add.js (so the existing table layout is
   undisturbed). The form itself is the .quick-add-row; the indent
   lives on the host <td>. Reads as a child-row continuation, not a
   detached input. */
.task-list-subtask-add > td {
  padding: 0;
  background: var(--bg-sunken);
  border-left: 2px solid var(--text-accent);
}
.quick-add-row--subtask {
  border-top: none;
  padding: var(--space-1_5, var(--space-2)) var(--space-3);
}
.quick-add-row--subtask::before {
  content: '↳';
  font-weight: 500;
  /* The corner-arrow makes it unambiguous that this is a subtask,
     not a sibling — keyed off the parent row immediately above. */
}

/* ── Presence avatar stack (drawer header) ────────────────
   Renders inside the drawer header. Each <li> is a 24px round
   avatar with initials; overlap is 8px so a stack of 3 reads as
   one compact cluster. Hidden when empty so the header stays
   tight in single-user use. */
.drawer-presence {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  margin-left: auto;
  margin-right: var(--space-2);
}
.drawer-presence:empty { display: none; }
.drawer-presence > li {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--bg-selected);
  color: var(--text-accent);
  font: 600 11px/24px var(--font-sans);
  text-align: center;
  border: 2px solid var(--bg-surface);
  margin-left: -8px;
  flex-shrink: 0;
}
.drawer-presence > li:first-child { margin-left: 0; }
.drawer-presence > li.drawer-presence__more {
  background: var(--bg-hover);
  color: var(--text-muted);
  font-size: 10px;
}

/* -----------------------------------------------------------
   R. Detail drawer — entity-agnostic slide-in panel.
   Opened by any [data-open-detail][data-detail-base] trigger;
   wired by detail-drawer.js.
   ----------------------------------------------------------- */
/* Any cell wired as a drawer trigger gets the pointer cursor for
   free — no ad-hoc style="cursor:pointer" per row needed. */
[data-open-detail] { cursor: pointer; }
.drawer-scrim { position: fixed; inset: 0; background: rgba(0,0,0,.32); z-index: 40; }
.drawer {
  position: fixed; top: 0; right: 0; height: 100vh; width: min(440px, 92vw);
  background: var(--bg-surface); border-left: 1px solid var(--border);
  z-index: 41;
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform var(--duration-default) var(--ease-standard);
  /* No shadow while parked off-screen — `display:flex` overrides [hidden]'s
     display:none, so a closed drawer stays rendered and its navy --shadow-xl
     was bleeding ~48px back onto the page's right edge (a navy band in light
     mode, camouflaged in dark). Only cast the shadow once it's open. */
  box-shadow: none;
  border-left-color: transparent;
}
.drawer:not([hidden]) {
  transform: translateX(0);
  box-shadow: var(--shadow-xl);
  border-left-color: var(--border);
}
.drawer-header { display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--divider); }
.drawer-code { color: var(--text-muted); font-size: var(--t-caption); }
.drawer-tabs { padding: 0 var(--space-4); }
.drawer-body { padding: var(--space-4); overflow-y: auto; display: flex;
  flex-direction: column; gap: var(--space-3); }
/* Each tab's content is a separate .drawer-body pane; detail-drawer.js shows
   only the active one via the [hidden] attribute. The display:flex above
   outranks the UA [hidden]=display:none, so restate it (same fix as
   .bulk-bar). Without this ALL panes stack and the drawer
   becomes one long scroll instead of switching per tab. */
.drawer-body[hidden] { display: none; }
/* Property grid — label column + value column on one row (Linear-style),
   so fields read as a scannable key/value table rather than a tower of
   stacked inputs. The first field (title) opts out into a full-width
   borderless heading. */
.drawer-label {
  display: grid;
  grid-template-columns: 96px 1fr;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  font-size: var(--t-body-sm);
  color: var(--text-muted);
}
/* Each property row is its own hover-highlightable unit — a calm full-row
   --bg-hover tint (the "box" without the chrome) that also signals the row
   is editable. Full-bleed to the drawer-body padding edges so the highlight
   reads as a row, not an inset block. The title opts out. */
.drawer-label:not(.drawer-label--title) {
  padding-block: var(--space-1);
  padding-inline: var(--space-4);
  margin-inline: calc(-1 * var(--space-4));
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard);
}
.drawer-label:not(.drawer-label--title):hover { background: var(--bg-hover); }
/* Textarea fields (e.g. description) are tall — top-align the label. */
.drawer-label:has(textarea) { align-items: start; }
.drawer-label-text { font-size: var(--t-caption); color: var(--text-muted); }
/* Controls read as values at rest (no visible frame); the input chrome
   reveals on hover/focus so editing stays discoverable without the
   page-form look. Inline-save behaviour is unchanged. */
.drawer-label input, .drawer-label select, .drawer-label textarea {
  color: var(--text); background: transparent;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2); font: inherit;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard); }
.drawer-label input:hover, .drawer-label select:hover, .drawer-label textarea:hover {
  border-color: var(--border); background: var(--bg-surface); }
.drawer-label input:focus, .drawer-label select:focus, .drawer-label textarea:focus {
  border-color: var(--border-strong); background: var(--bg-surface);
  outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.drawer-label input:disabled, .drawer-label select:disabled, .drawer-label textarea:disabled {
  color: var(--text-muted); cursor: default;
  border-color: transparent; background: transparent; }
/* Title — full-width borderless heading spanning both columns. */
.drawer-label--title { display: block; margin-bottom: var(--space-1); }
.drawer-label--title input {
  width: 100%; font-size: var(--t-heading-md); font-weight: 600;
  letter-spacing: -0.01em; color: var(--text-strong); }
.drawer-label--title input:hover { background: var(--bg-hover); border-color: var(--border); }
/* Keep the title a strong heading even when read-only (no edit permission) —
   otherwise the generic :disabled rule above greys it to --text-muted. */
.drawer-label--title input:disabled { color: var(--text-strong); }
.drawer-label--title .drawer-label-text { display: none; }

/* Value column — a flex row so the control can carry a leading colour cue
   (status dot / priority tint / assignee avatar). The cue reads the value
   as data, not a bare form field; the control underneath is still the live
   editor. Values sit a touch stronger than labels so they carry the row. */
.drawer-value { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.drawer-value > input,
.drawer-value > select,
.drawer-value > textarea { flex: 1 1 auto; min-width: 0; font-weight: 500; color: var(--text-strong); }
/* Status dot — same hue math as the status pills' own dot (hsl at L44 with
   each status's hue/sat), so the colour matches the pill rendered everywhere
   else. A dedicated class (not the shared .pill) so no pill box leaks in. */
.drawer-value-dot {
  flex: 0 0 auto; width: 8px; height: 8px;
  border-radius: var(--radius-full);
  background: var(--grey-400);
}
.drawer-value-dot--open     { background: hsl(var(--status-open-h) 92% 44%); }
.drawer-value-dot--progress { background: hsl(var(--status-progress-h) 96% 44%); }
.drawer-value-dot--blocked  { background: hsl(var(--status-blocked-h) 90% 44%); }
.drawer-value-dot--done     { background: hsl(var(--status-done-h) 72% 44%); }
.drawer-value-dot--archived { background: var(--grey-400); }
/* Priority — tint the value to the same scale as the .prio-flag swatches. */
.drawer-value--prio[data-prio="critical"] > select { color: var(--error); font-weight: 600; }
.drawer-value--prio[data-prio="high"]     > select { color: var(--warning); font-weight: 600; }
.drawer-value--prio[data-prio="medium"]   > select { color: var(--text-muted); }
.drawer-value--prio[data-prio="low"]      > select { color: var(--text-subtle); }
.drawer-value--prio[data-prio="none"]     > select { color: var(--text-subtle); }
/* Assignee — a small initials avatar (matches the comment/history/presence
   avatar motif); a dashed ring when unassigned. */
.drawer-value-avatar {
  flex: 0 0 auto;
  width: 20px; height: 20px;
  border-radius: var(--radius-full);
  background: var(--bg-sunken); color: var(--text-muted);
  font: 600 9px/20px var(--font-sans); text-align: center;
}
.drawer-value-avatar--empty { background: transparent; border: 1px dashed var(--border-strong); }

/* Subtasks section in the detail drawer (tasks only). Header + count,
   the child list, then the inline add row (reuses .quick-add-row--subtask). */
.drawer-subtasks {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
}
.drawer-subtasks-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.drawer-subtasks-label {
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-strong);
}
.drawer-subtasks-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 var(--space-1);
  background: var(--bg-sunken);
  color: var(--text-muted);
  border-radius: var(--radius-full);
  font: 600 var(--t-caption)/1 var(--font-sans);
}
.drawer-subtask-list {
  list-style: none;
  margin: 0 0 var(--space-1);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-0_5);
}
.drawer-subtask {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard);
}
.drawer-subtask:hover { background: var(--bg-hover); }
.drawer-subtask-link {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1 1 auto;
  min-width: 0;
  text-decoration: none;
  color: var(--text);
}
.drawer-subtask-link:hover { text-decoration: none; color: var(--text-strong); }
.drawer-subtask-code {
  flex-shrink: 0;
  font-size: var(--t-caption);
  color: var(--text-muted);
}
.drawer-subtask-title {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--t-body-sm);
}

/* ── Dependencies section (task detail drawer, Details pane) ───────── */
.drawer-deps {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
}
.drawer-deps-head { margin-bottom: var(--space-2); }
.drawer-deps-label {
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-strong);
}
.drawer-deps-group { margin-bottom: var(--space-2); }
.drawer-deps-grouplabel {
  display: block;
  margin-bottom: var(--space-1);
  font: 600 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.drawer-deps-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-0_5);
}
.drawer-deps-empty {
  color: var(--text-subtle);
  font-size: var(--t-body-sm);
  padding: var(--space-0_5) var(--space-2);
}
.drawer-deps-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard);
}
.drawer-deps-item:hover { background: var(--bg-hover); }
.drawer-deps-marker { flex-shrink: 0; width: 12px; color: var(--warning); font-size: var(--t-caption); }
.drawer-deps-item--done .drawer-deps-marker { color: var(--success); }
.drawer-deps-code { flex-shrink: 0; font-size: var(--t-caption); color: var(--text-muted); }
.drawer-deps-title {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--t-body-sm);
}
.drawer-deps-pill { flex-shrink: 0; }
/* B3 — link-type + lag chip (FS / SS +2d …). Monospace, subtle; metadata. */
.drawer-deps-linkbadge {
  flex-shrink: 0;
  font: 500 var(--t-caption)/1 var(--font-mono);
  color: var(--text-muted);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-0_5) var(--space-1);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.drawer-deps-remove {
  flex-shrink: 0;
  padding: 0 var(--space-1);
  color: var(--text-muted);
  font-size: var(--t-body);
  line-height: 1;
}
.drawer-deps-remove:hover { color: var(--error); background: var(--bg-hover); }
.drawer-deps-add {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-2);
  align-items: center;
}
.drawer-deps-add .drawer-deps-direction { flex: 0 0 auto; }
.drawer-deps-add .drawer-deps-task { flex: 1 1 auto; min-width: 0; }

/* Changes = an activity timeline: an actor avatar on a vertical rail, the
   action sentence, then human-readable field diffs (label · from → to).
   The avatar matches the presence/comment avatar motif. */
.history-list { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: var(--space-4); }
.history-entry {
  display: grid;
  grid-template-columns: 24px 1fr;
  gap: var(--space-3);
  position: relative;
}
/* Connector rail between consecutive avatars (skip the last). */
.history-entry:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 11px; top: 28px; bottom: calc(-1 * var(--space-4));
  width: 1px; background: var(--divider);
}
.history-avatar {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--bg-sunken); color: var(--text-muted);
  font: 600 10px/24px var(--font-sans); text-align: center;
  flex-shrink: 0; z-index: 1;
}
.history-content { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.history-head {
  font-size: var(--t-body-sm); color: var(--text-muted);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-2);
}
.history-head strong { color: var(--text); font-weight: 600; }
.history-when { color: var(--text-subtle); font-size: var(--t-caption); margin-left: auto; }
.history-diff {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-0_5);
}
.history-diff-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2);
  font-size: var(--t-caption);
}
.history-key { color: var(--text-muted); min-width: 72px; }
.history-from { color: var(--text-subtle); text-decoration: line-through; }
.history-arrow { color: var(--text-subtle); }
.history-to { color: var(--text); font-weight: 500; }
.history-to--muted { color: var(--text-muted); font-weight: 400; }
.history-empty { color: var(--text-subtle); font-size: var(--t-body-sm); padding: var(--space-2) 0; }

/* ── Comments tab (task detail drawer) ───────────────────────────── */
/* Watch / follow toggle bar atop the comments pane (task surfaces only). */
.comment-watch-bar { display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-3); }
.comment-watch-count { margin-left: auto; }

.comment-list { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: var(--space-4); }
/* Avatar + content row (matches the Changes timeline + presence motif). */
.comment { display: grid; grid-template-columns: 28px 1fr; gap: var(--space-3); }
.comment-avatar {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--bg-sunken); color: var(--text-muted);
  font: 600 11px/28px var(--font-sans); text-align: center;
  flex-shrink: 0; }
.comment-content { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.comment-head { display: flex; align-items: baseline; gap: var(--space-2);
  font-size: var(--t-body-sm); }
.comment-author { font-weight: 600; color: var(--text); }
.comment-when { color: var(--text-muted); font-size: var(--t-caption); }
/* Delete is quiet — revealed on row hover/focus-within, not always-on. */
.comment-delete { margin-left: auto; padding: 0 var(--space-1);
  color: var(--text-muted); font-size: var(--t-body); line-height: 1;
  opacity: 0; transition: opacity var(--duration-fast) var(--ease-standard); }
.comment:hover .comment-delete, .comment:focus-within .comment-delete { opacity: 1; }
.comment-delete:hover { color: var(--error); background: var(--bg-hover); }
.comment-body { color: var(--text); font-size: var(--t-body-sm);
  white-space: pre-wrap; word-wrap: break-word; }
.comment-empty { color: var(--text-subtle); font-size: var(--t-body-sm);
  padding: var(--space-2) 0; list-style: none; }

/* Composer — textarea + actions share one framed container (Linear/GitHub
   style) so Post sits inside the field's visual frame, not floating below. */
.comment-composer { display: flex; flex-direction: column; gap: var(--space-2);
  margin-top: var(--space-4); padding-top: var(--space-3);
  border-top: 1px solid var(--divider); }
.comment-composer-wrap { position: relative;
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--bg-surface);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard); }
.comment-composer-wrap:focus-within { border-color: var(--focus-ring);
  box-shadow: 0 0 0 2px var(--focus-ring); }
.comment-composer-input { width: 100%; resize: vertical; min-height: 72px;
  font: inherit; color: var(--text); background: transparent;
  border: 0; border-radius: var(--radius-md);
  padding: var(--space-2); display: block; }
.comment-composer-input:focus { outline: none; }
.comment-composer-actions { display: flex; justify-content: flex-end; }

.comment-mention-suggest { position: absolute; left: var(--space-2);
  top: 100%; margin-top: var(--space-1); list-style: none;
  background: var(--bg-raised); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
  padding: var(--space-1) 0; min-width: 160px; max-width: 280px; z-index: 10; }
.comment-mention-option { padding: var(--space-1) var(--space-2);
  font-size: var(--t-body-sm); color: var(--text); cursor: pointer; }
.comment-mention-option:hover, .comment-mention-option:focus {
  background: var(--bg-hover); outline: none; }

/* ── Attachments tab (task detail drawer) ────────────────────────── */
.attachment-list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-2); }
.attachment { display: flex; align-items: center;
  gap: var(--space-3); padding: var(--space-2);
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); }
.attachment:hover { border-color: var(--border-strong); }
/* File-type tile — the uppercase extension reads clearly, needs no icon
   assets, and themes for free. Tinted by coarse category. */
.attachment-icon {
  flex: 0 0 auto;
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  background: var(--bg-sunken); color: var(--text-muted);
  font: 700 9px/1 var(--font-sans); letter-spacing: 0.02em;
  text-transform: uppercase; overflow: hidden; }
.attachment-icon--image   { background: var(--info-bg);    color: var(--info); }
.attachment-icon--pdf     { background: var(--error-bg);   color: var(--error); }
.attachment-icon--sheet   { background: var(--success-bg); color: var(--success); }
.attachment-icon--doc     { background: var(--info-bg);    color: var(--info); }
.attachment-icon--archive { background: var(--warning-bg); color: var(--warning); }
.attachment-main { flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: var(--space-0_5); }
.attachment-name { font-size: var(--t-body-sm); font-weight: 500;
  color: var(--text-link); text-decoration: none;
  word-break: break-all; }
.attachment-name:hover { text-decoration: underline;
  color: var(--text-link-hover); }
.attachment-name--nolink { color: var(--text); cursor: default; }
.attachment-name--nolink:hover { text-decoration: none; }
.attachment-meta { font-size: var(--t-caption);
  color: var(--text-muted); }
.attachment-delete { padding: 0 var(--space-1); flex: 0 0 auto;
  color: var(--text-muted); font-size: var(--t-body); line-height: 1;
  opacity: 0; transition: opacity var(--duration-fast) var(--ease-standard); }
.attachment:hover .attachment-delete, .attachment:focus-within .attachment-delete { opacity: 1; }
.attachment-delete:hover { color: var(--error);
  background: var(--bg-hover); }
.attachment-empty { color: var(--text-subtle);
  font-size: var(--t-body-sm); padding: var(--space-2) 0;
  list-style: none; }

.attachment-uploader { display: flex; flex-direction: column;
  gap: var(--space-2); margin-top: var(--space-4);
  padding-top: var(--space-3); border-top: 1px solid var(--divider); }
/* Dropzone-style picker — full-width dashed target the file dialog opens. */
.attachment-file-label { display: flex; align-items: center; justify-content: center;
  gap: var(--space-2); padding: var(--space-3) var(--space-2);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-md);
  background: var(--bg-sunken); color: var(--text-muted);
  font-size: var(--t-body-sm); cursor: pointer; text-align: center;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard); }
.attachment-file-label:hover { background: var(--bg-hover);
  color: var(--text); border-color: var(--text-subtle); }
.attachment-file-input { position: absolute; opacity: 0;
  pointer-events: none; width: 1px; height: 1px; }
.attachment-file-text { word-break: break-all; }
.attachment-uploader-actions { display: flex; align-items: center;
  justify-content: space-between; gap: var(--space-2); }
.attachment-progress { font-size: var(--t-caption);
  color: var(--text-muted); }

/* ── Drawer tab bar — scoped tightening so all four tabs fit the narrow
   panel without touching the shared global .tabs nav. ───────────────── */
.drawer-tabs .tab { padding-left: var(--space-2); padding-right: var(--space-2);
  font-size: var(--t-body-sm); }

/* Mobile — the drawer reads as a full sheet, and the two-column property
   grid collapses to stacked label-over-control so values aren't squeezed. */
@media (max-width: 540px) {
  .drawer { width: 100vw; }
  .drawer-label { grid-template-columns: 1fr; gap: var(--space-0_5); }
  .drawer-label:has(textarea) { align-items: stretch; }
  .drawer-label-text { padding-top: 0; }
}

/* ── Workspace status builder ─────────────────────────────────────── */
.status-list { display: flex; flex-direction: column; gap: var(--space-2); }
.status-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.status-row__handle {
  cursor: grab;
  color: var(--text-subtle);
  font-size: var(--t-body-md);
  line-height: 1;
  user-select: none;
}
.status-row__handle:active { cursor: grabbing; }
.status-row .pill { flex-shrink: 0; }
.status-row__name {
  flex: 1 1 auto;
  min-width: 0;
  height: 32px;
}
.status-row__type { flex: 0 0 auto; height: 32px; }
.status-row__delete {
  flex-shrink: 0;
  padding: 0 var(--space-2);
  color: var(--text-muted);
}
.status-row__delete:hover { color: var(--error); }
.status-add {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
}
.status-add .status-row__name { flex: 1 1 auto; }
/* SortableJS drag affordances */
.status-row.sortable-ghost { opacity: 0.4; }
.status-row.sortable-chosen { box-shadow: var(--shadow-sm); }

/* Custom-column builder — mirrors the status builder, with more
   per-row controls (key, type, options, lookup, required). */
.column-list { display: flex; flex-direction: column; gap: var(--space-2); }
.column-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.column-row__handle {
  cursor: grab;
  color: var(--text-subtle);
  font-size: var(--t-body-md);
  line-height: 1;
  user-select: none;
}
.column-row__handle:active { cursor: grabbing; }
/* The global input/select rule forces width:100% + max-width:480px, which
   inside these flex rows makes each select balloon to 480px (flex:0 0 auto
   can't shrink it) and squeezes name/key to nothing — overflowing the page.
   Reset to auto here so the flex bases below actually govern the widths. */
.column-row > input,
.column-row > select,
.column-add > input,
.column-add > select { width: auto; max-width: none; min-width: 0; height: 32px; }
.column-row__name { flex: 1 1 10rem; min-width: 7rem; }
.column-row__key { flex: 0 1 6.5rem; min-width: 5rem; font-family: var(--font-mono, monospace); }
.column-row__type { flex: 0 1 11rem; }
.column-row__lookup { flex: 0 1 9.5rem; }
.column-row__dept { flex: 0 1 9.5rem; }
.column-row__permission { flex: 0 1 9.5rem; }
.column-row__options { flex: 1 1 10rem; min-width: 7rem; }
.column-row__required {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  font-size: var(--t-caption);
  color: var(--text-muted);
  white-space: nowrap;
}
.column-row__delete {
  flex-shrink: 0;
  padding: 0 var(--space-2);
  color: var(--text-muted);
}
.column-row__delete:hover { color: var(--error); }
.column-add {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding-top: var(--space-3);
  margin-top: var(--space-3);
  border-top: 1px solid var(--divider);
}
.column-add .column-row__name { flex: 1 1 10rem; }
.column-row.sortable-ghost { opacity: 0.4; }
.column-row.sortable-chosen { box-shadow: var(--shadow-sm); }

/* ── ClickUp-style "Add column" trigger on the List/Table view ─────
   The "+" affordance sits as the final <th> in the task header row
   (List view) or as a synthetic header cell (Table view). The partial
   renders it only for users that hold workspace.manage. */
.col-add-cell { width: 36px; padding: 0; text-align: center; }
.col-add-trigger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  background: transparent; border: 1px dashed transparent;
  color: var(--text-muted); cursor: pointer;
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.col-add-trigger:hover, .col-add-trigger:focus-visible {
  background: var(--bg-hover); border-color: var(--border-strong);
  color: var(--text-strong); outline: none;
}
.col-add-trigger:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
}

/* ── Field drawer (ClickUp-style column creator) ─────────────────────
   Uses the shipped .drawer / .drawer-scrim primitives. The body holds
   a search input, a tile grid of supported column types, and — once a
   tile is picked — a config form. Mounted once in _Layout via the
   _FieldDrawer partial; column-field-drawer.js owns the state. */
.field-drawer-search {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--bg-surface);
}
.field-drawer-search input {
  flex: 1; border: none; outline: none; background: transparent;
  color: var(--text); font: inherit;
}
.field-drawer-search svg { flex-shrink: 0; color: var(--text-muted); }
.field-drawer-tabs {
  display: flex; gap: var(--space-3); margin-top: var(--space-2);
  border-bottom: 1px solid var(--divider);
}
.field-drawer-tab {
  padding: var(--space-2) 0; background: none; border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-muted); font: 500 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.field-drawer-tab:hover:not([disabled]) { color: var(--text); }
.field-drawer-tab.active {
  color: var(--text-strong); border-bottom-color: var(--text-accent);
}
.field-drawer-tab[disabled] { color: var(--text-subtle); cursor: not-allowed; }
.field-drawer-section-title {
  margin: var(--space-3) 0 var(--space-1);
  font: 600 var(--t-caption)/1 var(--font-sans);
  color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em;
}
.field-tile-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-2);
}
.field-tile {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text); text-align: left; cursor: pointer; font: inherit;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard);
}
.field-tile:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.field-tile:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.field-tile:active { transform: translateY(1px); }
.field-tile-icon {
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  background: var(--bg-selected); color: var(--text-accent);
  border-radius: var(--radius-sm);
}
.field-tile-label { display: flex; flex-direction: column; min-width: 0; }
.field-tile-name {
  font: 500 var(--t-body-sm)/1.2 var(--font-sans); color: var(--text-strong);
}
.field-tile-desc {
  font: 400 var(--t-caption)/1.3 var(--font-sans); color: var(--text-muted);
  margin-top: 2px;
}
.field-drawer-empty {
  padding: var(--space-4) var(--space-2);
  text-align: center; color: var(--text-muted); font-size: var(--t-body-sm);
}

/* Config sub-panel — shown after a tile is clicked. */
.field-drawer-config { display: flex; flex-direction: column; gap: var(--space-3); }
.field-drawer-back {
  display: inline-flex; align-items: center; gap: var(--space-1);
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--text-muted); font: 500 var(--t-body-sm)/1 var(--font-sans);
  align-self: flex-start;
}
.field-drawer-back:hover { color: var(--text); }
.field-drawer-config-head { display: flex; align-items: center; gap: var(--space-2); }
.field-drawer-config-head .field-tile-icon { width: 32px; height: 32px; }
.field-drawer-config-head h3 {
  margin: 0; font: 600 var(--t-heading-sm)/1.2 var(--font-sans);
  color: var(--text-strong); letter-spacing: -0.01em;
}
.field-drawer-config-scope {
  padding: var(--space-2);
  background: var(--info-bg); color: var(--text);
  border-radius: var(--radius-sm);
  font-size: var(--t-caption); line-height: 1.4;
}
.field-drawer-config label {
  display: flex; flex-direction: column; gap: var(--space-1);
  font-size: var(--t-body-sm); color: var(--text-muted);
}
.field-drawer-config label > span:first-child {
  font-size: var(--t-caption); font-weight: 500;
}
.field-drawer-config input[type="text"],
.field-drawer-config input[type="number"],
.field-drawer-config input[type="date"],
.field-drawer-config select,
.field-drawer-config textarea {
  width: 100%;
  color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2); font: inherit;
}
.field-drawer-config input:focus-visible,
.field-drawer-config select:focus-visible,
.field-drawer-config textarea:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
}
.field-drawer-config-required {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--t-body-sm); color: var(--text);
}
.field-drawer-config-actions {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  margin-top: var(--space-2);
  padding-top: var(--space-3); border-top: 1px solid var(--divider);
}

/* SLA tier threshold builder — small row of (tier label, days input,
   "days" unit, reset button). Mirrors .status-row but lighter (no
   handle, no select). */
.sla-threshold-list { display: flex; flex-direction: column; gap: var(--space-2); }
.sla-threshold-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.sla-threshold-row__tier {
  flex: 0 0 6rem;
  font-weight: 600;
  color: var(--text);
}
.sla-threshold-row__input {
  flex: 0 0 6rem;
  height: 32px;
  text-align: right;
}
.sla-threshold-row__unit {
  flex: 0 0 auto;
  font-size: var(--t-body-sm);
  color: var(--text-muted);
}
.sla-threshold-row__reset {
  flex-shrink: 0;
  padding: 0 var(--space-2);
  color: var(--text-muted);
  margin-left: auto;
}
.sla-threshold-row__reset:not(:disabled):hover { color: var(--text); }

/* Per-user capacity input — narrow numeric cell inside the data-table.
   The table itself already carries the .data-table tokens; the input
   just needs to feel like a "tight numeric cell editor" rather than a
   free-floating form input. */
.capacity-row__input {
  width: 5.5rem;
  height: 32px;
  text-align: right;
}

/* -----------------------------------------------------------
   S. Task filter bar — shared chrome above the Task List and
   Kanban. Pill toggles for status / assignee / priority / due /
   billable / archived; search box; sort + group selects (List
   only). Sits between the page header and the view body.
   ----------------------------------------------------------- */
.task-filter-bar {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  /* Borderless toolbar (no card frame) — controls align flush with the
     page header + table edge, and the heavy bordered/shadowed card no
     longer eats vertical room. */
  padding: 0 0 var(--space-2);
  margin-bottom: var(--space-3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--divider);
  border-radius: 0;
  box-shadow: none;
}
.task-filter-bar__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
}
.task-filter-bar__row--secondary {
  border-top: 1px dashed var(--divider);
  padding-top: var(--space-2);
}
.task-filter-bar__group {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
}
.task-filter-bar__label {
  font: 500 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-right: var(--space-1);
  /* Keep "Group by" on one line — wrapping made the control taller than
     its neighbours and threw the whole row out of vertical alignment. */
  white-space: nowrap;
}

/* Each toggle pill. Default = ghost; aria-pressed=true = filled
   navy. Smaller than .pill (which is read-only) so a row of them
   doesn't dominate the viewport. */
.task-filter-pill {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 var(--space-2);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard);
}
.task-filter-pill:hover {
  background: var(--bg-hover);
}
.task-filter-pill[aria-pressed="true"] {
  background: var(--bg-selected);
  color: var(--text-accent);
  border-color: var(--navy-300, var(--border-strong));
}
.task-filter-pill:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--focus-ring);
}

.task-filter-bar__search {
  display: inline-flex;
  align-items: center;
  flex: 1 1 240px;
  min-width: 200px;
  max-width: 360px;
}
.task-filter-bar__search input[type="search"] {
  width: 100%;
  height: 30px;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: var(--t-body-sm)/1.2 var(--font-sans);
}
/* Focus handled by the global input:focus rule (calm mixed border + halo). */

.task-filter-bar__select {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.task-filter-bar__select select {
  height: 30px;
  padding: 0 var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: var(--t-body-sm)/1 var(--font-sans);
}

.task-filter-bar__clear {
  font: var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-link);
  text-decoration: none;
  margin-left: auto;
}
.task-filter-bar__clear:hover { text-decoration: underline; }

/* ── ClickUp-style "+ Filter" pattern ─────────────────────────────────
   Replaces the always-visible pill rows. The toolbar holds the trigger,
   Search, Sort, Group, and Clear. Active filters live below as
   editable rows. Each row carries a dimension chip + operator picker +
   value trigger (opens a popover) + remove ×. The popover is anchored
   to the value trigger and uses a checkbox list (+ typeahead for the
   big dimensions: members / tags). */
.filter-add {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 30px;
  padding: 0 var(--space-2);
  background: var(--bg-surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard);
}
.filter-add:hover { background: var(--bg-hover); border-color: var(--navy-500, var(--border-strong)); }
.filter-add:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--focus-ring);
}
.filter-add svg { color: var(--text-muted); }

/* Dimension picker popover — shown when "+ Filter" is clicked. Scrim-
   free (closes on outside-click via JS). Position: anchored absolutely
   under the trigger; the JS module owns placement. */
.filter-add-menu {
  position: fixed;
  z-index: 30;
  min-width: 220px;
  max-width: 280px;
  padding: var(--space-1) 0;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.filter-add-menu[hidden] { display: none; }
.filter-add-menu__search {
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--divider);
}
.filter-add-menu__search input {
  width: 100%;
  height: 26px;
  padding: 0 var(--space-1);
  border: none;
  background: transparent;
  color: var(--text);
  font: inherit;
  outline: none;
}
.filter-add-menu__list {
  list-style: none;
  margin: 0;
  padding: var(--space-0_5) 0;
  max-height: 280px;
  overflow-y: auto;
}
.filter-add-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-1) var(--space-3);
  background: transparent;
  border: none;
  text-align: left;
  color: var(--text);
  font: 500 var(--t-body-sm)/1.2 var(--font-sans);
  cursor: pointer;
}
.filter-add-menu__item:hover, .filter-add-menu__item:focus-visible {
  background: var(--bg-hover);
  outline: none;
}
.filter-add-menu__item[disabled] { color: var(--text-subtle); cursor: not-allowed; }
.filter-add-menu__item-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 22px; height: 22px;
  color: var(--text-muted);
}

/* Active filter rows. One per applied dimension. Compact, single-line. */
.filter-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.filter-row {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-0_5) var(--space-1);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: var(--t-body-sm)/1.2 var(--font-sans);
}
.filter-row__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  height: 24px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-strong);
}
.filter-row__op {
  width: auto;          /* override the global select { width:100% } */
  max-width: 160px;
  flex: 0 0 auto;
  height: 24px;
  padding: 0 var(--space-1);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: var(--t-body-sm)/1 var(--font-sans);
}
.filter-row__op:focus-visible {
  outline: none;
  border-color: color-mix(in srgb, var(--focus-ring) 55%, var(--border-strong));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
.filter-row__value {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 24px;
  padding: 0 var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
}
.filter-row__value:hover { background: var(--bg-hover); }
.filter-row__value--empty { color: var(--text-muted); font-style: italic; }
.filter-row__value-chips {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  flex-wrap: wrap;
  max-width: 360px;
}
.filter-row__value-chip {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 var(--space-1);
  background: var(--bg-selected);
  color: var(--text-accent);
  border-radius: var(--radius-sm);
  font: 500 var(--t-caption)/1 var(--font-sans);
}
.filter-row__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-left: auto;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
}
.filter-row__remove:hover { background: var(--bg-hover); color: var(--error); }

/* ── Phase 3: AND/OR + nested filter tree ──────────────────────
   Tree mode (when `?fq=` is present) wraps the leaf rows in groups.
   Each group has a connector (All/Any) and its own add actions; nested
   groups indent with a left rail. Leaf rows reuse .filter-row above. */
.filter-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-2);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.filter-group--nested {
  margin-left: var(--space-3);
  border-left: 2px solid var(--border-strong);
  background: var(--bg-surface);
}
.filter-group__header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.filter-group__lead {
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-strong);
}
.filter-group__match {
  font: var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-muted);
}
.filter-group__connector {
  width: auto;          /* override the global select { width:100% } */
  max-width: 160px;
  flex: 0 0 auto;
  height: 24px;
  padding: 0 var(--space-1);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
}
.filter-group__connector:focus-visible {
  outline: none;
  border-color: color-mix(in srgb, var(--focus-ring) 55%, var(--border-strong));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
.filter-group__remove { margin-left: auto; }
.filter-group__children {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.filter-group__children:empty { display: none; }
.filter-group__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
/* Small add buttons inside groups + the flat-mode entry bar. */
.filter-add--sm {
  height: 24px;
  padding: 0 var(--space-2);
  font: 500 var(--t-caption)/1 var(--font-sans);
}
.filter-add--sm svg { width: 13px; height: 13px; }
.filter-tree-entry {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-1);
}
.filter-tree-entry__lead {
  font: var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-muted);
}

/* Value popover — opens off the value trigger. Multi-checkbox list +
   optional typeahead. Apply + Cancel buttons batch the change so the
   user picks N values, hits Apply once, page reloads once. */
.filter-value-popover {
  position: fixed;
  z-index: 30;
  min-width: 240px;
  max-width: 320px;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
}
.filter-value-popover[hidden] { display: none; }
.filter-value-popover__search {
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--divider);
}
.filter-value-popover__search input {
  width: 100%;
  height: 26px;
  padding: 0 var(--space-1);
  background: transparent;
  border: none;
  color: var(--text);
  outline: none;
  font: inherit;
}
.filter-value-popover__list {
  list-style: none;
  margin: 0;
  padding: var(--space-0_5) 0;
  max-height: 240px;
  overflow-y: auto;
}
.filter-value-popover__option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  cursor: pointer;
  color: var(--text);
  font: var(--t-body-sm)/1.2 var(--font-sans);
}
.filter-value-popover__option:hover { background: var(--bg-hover); }
.filter-value-popover__option input[type="checkbox"] { flex-shrink: 0; }
.filter-value-popover__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-top: 1px solid var(--divider);
}
.filter-value-popover__empty {
  padding: var(--space-2);
  color: var(--text-muted);
  font: var(--t-body-sm)/1.2 var(--font-sans);
  text-align: center;
}
/* Inline date inputs for Due "between" — sits where the value popover
   would otherwise go; two date inputs side by side + Apply/Cancel. */
.filter-value-popover__range {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2);
}
.filter-value-popover__range input[type="date"] {
  height: 28px;
  padding: 0 var(--space-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* -----------------------------------------------------------
   T. List group-by (server-rendered <details> blocks) — used
   when ?group= is set on the Task List. <details>/<summary>
   gives us collapsible groups without any JS state.
   ----------------------------------------------------------- */
.task-list-group {
  margin-bottom: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  overflow: hidden;
}
.task-list-group > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--divider);
  cursor: pointer;
  font: 600 var(--t-body)/1.2 var(--font-sans);
  color: var(--text-strong);
}
.task-list-group > summary::-webkit-details-marker { display: none; }
.task-list-group > summary::marker { content: ''; }
/* Disclosure chevron — a CSS triangle that rotates open. Replaces the
   native marker (hidden above) so the group header reads like ClickUp's
   collapsible status groups. Sits first in the flex summary; the
   summary's gap provides spacing. */
.task-list-group > summary::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 4px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  color: var(--text-muted);
  transition: transform var(--duration-fast) var(--ease-standard);
}
.task-list-group[open] > summary::before { transform: rotate(90deg); }
.task-list-group__count {
  margin-left: auto;
  font: 500 var(--t-caption)/1 var(--font-sans);
  color: var(--text-muted);
}
.task-list-group table { margin: 0; }

/* Flat-list pager + grouped cap notice (Tasks/Index). */
.list-pager {
  margin-top: var(--space-4);
  align-items: center;
}
.list-cap-notice {
  margin-top: var(--space-3);
}

/* -----------------------------------------------------------
   T2. Task list subtask hierarchy — chevron toggle, indent,
   orphan chip. The depth padding itself is applied inline by the
   view (computed per row from --space-3 * depth); these classes
   provide the chrome that the depth interacts with. Hidden
   descendants get a [hidden] attribute set by the JS collapse
   module — that's standard HTML, no CSS needed for hide.
   ----------------------------------------------------------- */
.task-list-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-right: var(--space-1);
  padding: 0;
  background: transparent;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  vertical-align: middle;
  transition: transform var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.task-list-toggle:hover { color: var(--text-strong); }
.task-list-toggle[aria-expanded="false"] { transform: rotate(0deg); }
.task-list-toggle[aria-expanded="true"] { transform: rotate(90deg); }
.task-list-toggle--placeholder {
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-right: var(--space-1);
  cursor: default;
}

.task-list-orphan-chip {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-2);
  margin-right: var(--space-2);
  background: var(--bg-sunken);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--t-caption)/1.4 var(--font-sans);
}

/* Wave 4 — "Blocked by a dependency" badge on List rows. Warning-tinted
   (not error/red — the task isn't broken, it's waiting) and deliberately
   distinct from the status .pill--blocked. */
.task-blocked-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0_5);
  margin-left: var(--space-1);
  padding: 0 var(--space-1);
  background: var(--warning-bg);
  color: var(--warning);
  border-radius: var(--radius-sm);
  font: 600 var(--t-caption)/1.5 var(--font-sans);
  white-space: nowrap;
  vertical-align: middle;
}
.task-blocked-badge svg { flex-shrink: 0; }

.task-list-row--child > td:first-child {
  /* Subtle marker so the child rows are scannable as nested
     visually, even at the leftmost (Code) column where the
     depth padding lives in the Title column instead. */
  border-left: 2px solid var(--border);
}

/* -----------------------------------------------------------
   K2. Kanban subtask hierarchy — child cards keep a left-border
   accent + indent, with a "Parent: TaskCode" chip for cards
   whose parent is in a different column (or hidden by filter).
   Cards remain siblings in the DOM under .kanban-column-tasks
   so SortableJS still treats them as one flat list — nesting is
   strictly visual, never DOM nesting.
   ----------------------------------------------------------- */
.kanban-card--child {
  margin-left: var(--space-3);
  border-left: 2px solid var(--text-accent);
}
.kanban-card--orphan { /* parent in another column — render at column root */
  /* No indent (depth 0) — the chip below carries the relationship. */
}
.kanban-card-parent-chip {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-2);
  margin-bottom: 4px;
  background: var(--bg-sunken);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--t-caption)/1.4 var(--font-sans);
}

.kanban-column-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-right: var(--space-1);
  padding: 0;
  background: transparent;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: transform var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.kanban-column-toggle:hover { color: var(--text-strong); background: var(--bg-hover); }
.kanban-column-toggle[aria-expanded="true"] { transform: rotate(90deg); }

/* -----------------------------------------------------------
   Z. Notifications bell + inbox dropdown
   Mounts via wwwroot/js/modules/notifications-bell.js. Sized to
   sit next to the existing user-menu without disrupting the
   topbar cluster. All colour + spacing values pull from the
   design tokens — never hardcode hex here.
   ----------------------------------------------------------- */
.icon-btn--bell { position: relative; }
.bell-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  background: var(--error);
  color: var(--text-inverse);
  border-radius: var(--radius-full);
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--bg-surface);
}
.icon-btn--bell[data-has-unread] { color: var(--text-strong); }

.notif-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  width: 360px;
  max-height: 520px;
  display: flex;
  flex-direction: column;
  z-index: 900;
  font-size: var(--t-body-sm);
  overflow: hidden;
}
.notif-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3);
  border-bottom: 1px solid var(--divider);
}
.notif-panel__title {
  font-weight: 600;
  color: var(--text-strong);
  font-size: var(--t-body-md);
}
.notif-panel__mark-all {
  background: transparent;
  border: 0;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  font: inherit;
  color: var(--text-link);
  cursor: pointer;
}
.notif-panel__mark-all:hover { background: var(--bg-hover); }
.notif-panel__body {
  overflow-y: auto;
  flex: 1 1 auto;
}
.notif-empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--t-body-sm);
}
.notif-empty--error { color: var(--error); }

.notif-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: 1px solid var(--divider);
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.notif-row:last-child { border-bottom: 0; }
.notif-row:hover { background: var(--bg-hover); }
.notif-row[data-unread] { background: var(--bg-selected); }
/* Informational row with no navigable target — clickable to mark read,
   but not presented as a link. */
.notif-row--inert { cursor: default; }
.notif-row[data-unread]:hover { background: var(--bg-hover); }
.notif-row__indicator {
  flex: 0 0 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 16px;
  margin-top: 2px;
}
.notif-row__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--accent);
}
.notif-row__main {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
  flex: 1 1 auto;
}
.notif-row__headline {
  color: var(--text-strong);
  font-weight: 500;
  font-size: var(--t-body-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.notif-row__body {
  color: var(--text-muted);
  font-size: var(--t-body-sm);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.notif-row__meta {
  color: var(--text-subtle);
  font-size: var(--t-caption);
}

/* -----------------------------------------------------------
   Reports module (PRD §8.7) — Index cards grid + the run-page
   chrome. Reuses .task-filter-bar tokens / pills so the
   reports surface and the task surfaces look like one family.
   ----------------------------------------------------------- */
.reports-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-3);
}
.report-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  text-decoration: none;
  color: inherit;
  transition: box-shadow var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.report-card:hover {
  box-shadow: var(--shadow-sm);
  border-color: var(--border-strong);
}
.report-card__title {
  font: 600 var(--t-heading-sm)/1.2 var(--font-sans);
  color: var(--text-strong);
}
.report-card__body {
  margin: 0;
  font: var(--t-body-sm)/1.5 var(--font-sans);
  color: var(--text-muted);
  flex: 1 1 auto;
}
.report-card__cta {
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-link);
}

/* Project filter bar custom-column <select multiple>. The shared height pin
   would clip a multi-select to a single un-scrollable row — undo that so the native
   fallback shows a few rows and stays usable without JS. Lookup columns
   get the lookup-select combobox layered on top (the native control is
   then visually hidden), so this only affects the select / user / boolean
   fallbacks. Capped height keeps the filter bar tidy. */
select.project-filter-bar__custom[multiple] {
  height: auto;
  min-height: 28px;
  max-height: 96px;
  padding: var(--space-0_5) var(--space-1);
  min-width: 140px;
  max-width: 200px;
}

/* When a lookup custom-column select is progressive-enhanced by
   lookup-select.js, the module inserts a `.lookup-select` wrapper that
   is display:block / width:100% (correct for full-width form fields).
   Inside the filter bar's inline-flex row that 100% width forces each
   combobox onto its own full-width line, stacking them vertically and
   breaking the layout. Bound it to the same inline footprint as the
   native multi-select fallback so lookup filters flow inline with the
   pills and date pickers. Scoped to the filter-bar context so the
   form-field combobox keeps its full width elsewhere. */
.task-filter-bar .lookup-select {
  display: inline-flex;
  flex-direction: column;
  width: auto;
  min-width: 150px;
  max-width: 220px;
  vertical-align: top;
}

/* Label + control stay together as one wrapping unit so a narrow row
   never orphans a column label from its select/combobox. */
.project-filter-bar__field {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

/* ── Report filter bar → a sectioned control panel ──────────────────
   The bar is one <form> shared by all 16 run-reports; fields appear per
   ReportFilterVisibility (1–10 of them). It's recast as a bordered panel
   split into labelled bands — Time window → Narrow by → Shape output →
   actions — so it reads in a deliberate order instead of one flat row.
   Every rule is scoped to .report-filter-bar so the task board's shared
   .task-filter-bar / .task-filter-pill classes are untouched. Preserves
   three documented fixes: date width pinned (above), lookup-select filling
   its cell (below), and the global form>div margin neutralised. */
.report-filter-bar {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  box-shadow: var(--shadow-xs);
}

/* A band: a quiet caption above its fields. margin:0 kills the global
   `form > div { margin-bottom: 16px }` that would otherwise stack. */
.report-filter__band {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
}
.report-filter__band-label {
  font: 600 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
}

/* Time window — the Redmine-style operator control (.ts-datefilter, shared
   with /timesheet) is the band's only child; it owns its own inline layout. */

/* Field grid for Narrow by / Shape output — tiles selects into equal
   columns that fill the width with no stranded track. Safe here because
   the full-width date range lives in its own band, not mixed in.
   align-items:start (not end) so the labels line up across the row even
   when a lookup-select grows taller with selected chips — bottom-aligning
   variable-height cells left the labels jagged. */
.report-filter__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3) var(--space-4);
  align-items: start;
}

/* Selected-value chips in a report combobox: keep each chip to one line so a
   long project name truncates with an ellipsis instead of ballooning the cell
   to three lines (the full name still shows in the dropdown + the active-filter
   strip below). Scoped to the report bar — the task-board toolbar wraps freely. */
.report-filter-bar .lookup-select__chip { max-width: 100%; }
.report-filter-bar .lookup-select__chip-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Each field: label stacked above a control that fills its cell. */
.report-filter-bar .task-filter-bar__select {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-0_5);
  min-width: 0;
}
.report-filter-bar .task-filter-bar__label { margin-right: 0; }
.report-filter-bar .task-filter-bar__select select {
  width: 100%;
  min-width: 0;
  max-width: none;
  height: 34px;
}

/* lookup-select combobox fills its grid cell in the report panel. The
   shared .task-filter-bar rule caps it at 220px for the inline task-board
   toolbar; here we want it flush with the other fields. */
.report-filter-bar .task-filter-bar__select .lookup-select {
  width: 100%;
  min-width: 0;
  max-width: none;
}

/* Column-picker checkbox groups span the full grid width. */
.report-filter-bar .report-filter-bar__columns-picker {
  grid-column: 1 / -1;
  margin: 0;
}
.report-filter-bar .report-filter-bar__column-checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-1);
}
.report-filter-bar .report-filter-bar__col-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
}
.report-filter-bar .report-filter-bar__col-check input { accent-color: var(--accent); }

/* Actions → a footer on a divider. Run report (the one primary) leads,
   the export pair follows, Reset trails right. */
.report-filter__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  border-top: 1px solid var(--divider);
  padding-top: var(--space-4);
}
.report-filter__reset { margin-left: auto; }

/* Workspace Members — bulk-change-role bar. Surfaces above the
   table when at least one per-grant checkbox is ticked. Visually
   parallel to the .task-bulk-bar family without sharing CSS — the
   members surface uses a form-post round-trip (no AJAX/toast)
   so the controls are minimal. */
.members-bulk-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  background: var(--bg-selected);
  border: 1px solid var(--navy-300, var(--border-strong));
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
.members-bulk-bar[hidden] { display: none; }
.members-bulk-bar__count {
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-accent);
  min-width: 110px;
}
.members-bulk-bar__select {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.members-bulk-bar__select select {
  height: 30px;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: var(--t-body-sm)/1 var(--font-sans);
}
.members-bulk-checkbox {
  margin-right: var(--space-1);
  accent-color: var(--accent);
}

/* Projects list — bulk-archive checkbox column. Slim, left-
   aligned to keep the existing table density close to the
   original. Reuses the .members-bulk-bar surface above the table
   for the toolbar — both Projects and Members are workspace-scoped
   admin surfaces with the same form-post pattern. */
.projects-bulk-th { width: 28px; }
.projects-bulk-cell { width: 28px; padding-right: 0; }
.projects-bulk-checkbox { accent-color: var(--accent); }

/* Active-filter chip strip — sits between the filter bar and the
   result table. One chip per non-default narrowing filter, each with
   a plain <a> ✕ that drops just that param (no JS, no-JS-safe).
   Visual contract: subtle pill with a key/value split + ghost remove
   icon that fills on hover. Reads as derived from .pill (read-only)
   rather than .task-filter-pill (interactive toggle). */
.report-active-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: calc(var(--space-3) * -1) 0 var(--space-3);
}
.report-active-chips__label {
  font: 500 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.report-active-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 26px;
  padding: 0 var(--space-1) 0 var(--space-2);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-accent);
  background: var(--bg-selected);
  border: 1px solid var(--navy-300, var(--border-strong));
  border-radius: var(--radius-full);
}
.report-active-chip__key {
  color: var(--text-muted);
  font-weight: 500;
}
.report-active-chip__key::after {
  content: ":";
  margin-right: var(--space-0_5, 2px);
}
.report-active-chip__value {
  color: var(--text-accent);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.report-active-chip__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: var(--space-0_5, 2px);
  color: var(--text-muted);
  border-radius: var(--radius-full);
  text-decoration: none;
  transition:
    background var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
}
.report-active-chip__remove:hover {
  background: var(--navy-700);
  color: var(--text-inverse);
}
.report-active-chip__remove:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--focus-ring);
}
.report-active-chips__clear {
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-link);
  text-decoration: none;
  margin-left: var(--space-1);
}
.report-active-chips__clear:hover {
  text-decoration: underline;
}

/* Result-count line — "N rows · 2026-05-01 – 2026-05-30" above the
   table. Always shown when a report has run (Result not null), even
   for 0-row results so the user can see what they filtered. The date
   range is conditional on the report having a DateRange picker
   (TaskStatus/SLA are point-in-time and would mislead with one). */
.report-result-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  font: var(--t-body-sm)/1.2 var(--font-sans);
  color: var(--text-muted);
}
.report-result-summary__count {
  color: var(--text);
  font-weight: 600;
}
.report-result-summary__sep {
  color: var(--text-subtle);
}
.report-result-summary__range {
  font-variant-numeric: tabular-nums;
}

/* Save-this-view details/summary disclosure. The native marker
   muddles the button styling — hide it. */
.report-save-details > summary {
  list-style: none;
}
.report-save-details > summary::-webkit-details-marker {
  display: none;
}

/* ------------------------------------------------------------------
   AA. Saved views dropdown (Table toolbar)

   Anchored panel that pops under the .saved-views-trigger button on
   the Table page. Visual contract matches the notifications inbox
   (token-driven surface + border + shadow + radius) so the two
   dropdowns read as the same affordance family.
   ------------------------------------------------------------------ */
.saved-views-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  width: 320px;
  max-height: 480px;
  display: flex;
  flex-direction: column;
  z-index: 900;
  font-size: var(--t-body-sm);
  overflow: hidden;
}
.saved-views-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3);
  border-bottom: 1px solid var(--divider);
}
.saved-views-panel__title {
  font-weight: 600;
  color: var(--text-strong);
  font-size: var(--t-body-md);
}
.saved-views-panel__form {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: 1px solid var(--divider);
}
.saved-views-panel__name {
  flex: 1 1 auto;
  height: 28px;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: var(--t-body-sm)/1 var(--font-sans);
}
.saved-views-panel__name:focus-visible {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent);
}
.saved-views-panel__body {
  overflow-y: auto;
  flex: 1 1 auto;
}
.saved-views-empty {
  padding: var(--space-5) var(--space-4);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--t-body-sm);
}
.saved-views-empty--error { color: var(--error); }

.saved-views-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-2);
  border-bottom: 1px solid var(--divider);
  transition: background var(--duration-fast) var(--ease-standard);
}
.saved-views-row:last-child { border-bottom: 0; }
.saved-views-row:hover { background: var(--bg-hover); }
.saved-views-row__restore {
  flex: 1 1 auto;
  text-align: left;
  background: transparent;
  border: 0;
  padding: var(--space-3) var(--space-2);
  font: inherit;
  color: var(--text);
  cursor: pointer;
}
.saved-views-row__restore:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}
.saved-views-row__delete {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
}
.saved-views-row__delete:hover {
  background: var(--bg-hover);
  color: var(--error);
}
.saved-views-row__delete:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}

/* -----------------------------------------------------------
   T. Keyboard row navigation (keyboard-shortcuts.js)
   The active row is highlighted with a tinted background and a
   2px inset-left accent. Using box-shadow for the left bar avoids
   adding a layout-affecting border; the token colours flip in
   dark mode automatically via [data-theme="dark"].
   ----------------------------------------------------------- */
[data-keyboard-active] {
  background-color: var(--bg-selected) !important;
  box-shadow: inset 2px 0 0 var(--text-accent);
  outline: none;
}

/* -----------------------------------------------------------
   U. Calendar view (Calendar/Index.cshtml)

   Monthly grid — 7 columns × 5–6 rows. Tokens-only: surface bg,
   border, navy ramp for today, muted text for other-month days.
   Chips reuse the existing .pill / .pill--<status> system so a
   calendar chip and a list-row pill stay visually identical.
   ----------------------------------------------------------- */

.calendar-nav {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.calendar-month-label {
  font: 600 var(--t-heading-sm)/1 var(--font-sans);
  color: var(--text-strong);
  margin-left: var(--space-2);
  letter-spacing: -0.01em;
}

.calendar-grid {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-surface);
}
.calendar-grid-head {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--border);
}
.calendar-grid-head-cell {
  padding: var(--space-2) var(--space-3);
  font: 600 var(--t-caption)/1 var(--font-sans);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.calendar-week {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border-top: 1px solid var(--divider);
}
.calendar-week:first-child {
  border-top: 0;
}

.calendar-day {
  min-height: 110px;
  padding: var(--space-2);
  border-right: 1px solid var(--divider);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  background: var(--bg-surface);
  overflow: hidden;
}
.calendar-day:last-child {
  border-right: 0;
}
.calendar-day--other-month {
  background: var(--bg-sunken, var(--grey-50));
}
.calendar-day--other-month .calendar-day-num {
  color: var(--text-subtle);
}
.calendar-day--today .calendar-day-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 var(--space-1);
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.calendar-day-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Hover-revealed per-day "+" quick-add (calendar-quick-add.js). Hidden at
   rest so the grid stays calm; appears on cell hover / keyboard focus. */
.calendar-day-add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-standard),
              background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.calendar-day:hover .calendar-day-add,
.calendar-day:focus-within .calendar-day-add { opacity: 1; }
.calendar-day-add:hover { background: var(--bg-selected); color: var(--text-accent); }
.calendar-day-add:focus-visible { outline: none; opacity: 1; box-shadow: 0 0 0 2px var(--focus-ring); }

/* Injected inline add form (wraps the shared .quick-add-row). Compact to
   fit a day cell; sits below the chips. */
.calendar-day-add-wrap { margin-top: auto; }
.quick-add-row--calendar {
  border-top: none;
  padding: var(--space-0_5) var(--space-1);
}
.quick-add-row--calendar::before { display: none; }
.quick-add-row--calendar input {
  font: 500 var(--t-caption)/1.3 var(--font-sans);
  padding: var(--space-0_5) var(--space-1);
}
.calendar-day-num {
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
}

.calendar-day-chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  overflow: hidden;
}
.calendar-day-chips > li { margin: 0; }

/* Overflow chips behind a "+N more" disclosure. Native <details>
   so the affordance survives without JS; the cell grows only when
   the user opts in. Collapsed state preserves grid uniformity. */
.calendar-day-more {
  margin: 0;
}
.calendar-day-more__summary {
  /* Strip the native disclosure marker so we control the chevron
     ourselves (or omit it — a compact "+N more" reads as a link). */
  list-style: none;
  cursor: pointer;
  display: inline-block;
  padding: var(--space-0_5) var(--space-1);
  border-radius: var(--radius-sm);
  font: 500 var(--t-caption)/1 var(--font-sans);
  color: var(--text-muted);
  user-select: none;
}
.calendar-day-more__summary::-webkit-details-marker { display: none; }
.calendar-day-more__summary:hover {
  background: var(--bg-hover);
  color: var(--text);
}
.calendar-day-more__summary:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}
.calendar-day-more[open] .calendar-day-more__summary {
  color: var(--text);
  font-weight: 600;
}
.calendar-day-chips--overflow {
  margin-top: var(--space-1);
}
/* The .calendar-day-chips rule sets display:flex which beats the UA
   stylesheet's `details > :not(summary) { display: none }` when the
   <details> is collapsed. Re-hide explicitly when the parent is not
   open so the overflow chips genuinely stay hidden. */
.calendar-day-more:not([open]) .calendar-day-chips--overflow {
  display: none;
}

/* The chip itself reuses .pill so colour tokens stay consistent.
   We add a calendar-specific layout (code + title in one row, with
   the title truncated) without disturbing the pill colour recipe. */
.calendar-chip {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  min-width: 0;
  height: auto;
  padding: var(--space-0_5) var(--space-2);
  text-decoration: none;
  cursor: move;
}
/* DnD: active drop-target highlight */
.calendar-day--drop-target {
  background: var(--bg-selected);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 0;
  transition: background var(--duration-fast) var(--ease-standard),
              outline var(--duration-fast) var(--ease-standard);
}
.calendar-chip:hover {
  filter: brightness(0.97);
}
.calendar-chip:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
}
.calendar-chip-code {
  flex: 0 0 auto;
  font: 600 var(--t-caption)/1 var(--font-sans);
  opacity: 0.85;
}
.calendar-chip-title {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 500;
}

/* -----------------------------------------------------------
   V. Workload view (Workload/Index.cshtml)

   Per-workspace weekly hours table. Reuses the base <table>
   styles + tokens; adds an intensity tint per (user, day) cell
   so heavy days stand out without obscuring the numeric value.
   ----------------------------------------------------------- */

.workload-table {
  width: 100%;
}
.workload-table .workload-day-dow {
  display: block;
  font: 600 var(--t-caption)/1 var(--font-sans);
  color: var(--text-muted);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.workload-table .workload-day-num {
  display: block;
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text);
  margin-top: 2px;
}
.workload-table th[scope="row"] {
  text-align: left;
  font-weight: 500;
  color: var(--text-strong);
}

/* Intensity bands — paint the cell background to flag heavy days.
   The numbers stay readable because we only tint the bg; the text
   colour is unchanged from the base table. Tokens flip in dark mode
   automatically via the role-token system. */
.cell-load-low {
  background: var(--navy-50);
}
.cell-load-med {
  background: color-mix(in srgb, var(--navy-300) 25%, var(--bg-surface));
}
.cell-load-high {
  background: var(--warning-bg);
  color: var(--warning);
}

/* -----------------------------------------------------------
   Free-form tags (PRD §8.1.2).

   Neutral chip — one colour. The PRD doesn't ask for per-tag
   colours; if a user needs taxonomy, a custom column is the
   right answer. Role tokens so the chip auto-flips in dark
   mode. Used:
   - in the detail drawer's Details pane (above the field grid)
   - as inline chips after task titles on List and Kanban
   - as filter pills in the shared filter bar
   ----------------------------------------------------------- */
.chip.chip--tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  height: 22px;
  background: var(--bg-sunken);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--t-caption)/1 var(--font-sans);
  white-space: nowrap;
}
.chip.chip--tag .chip-label::before {
  content: "#";
  margin-right: 1px;
  color: var(--text-subtle);
}
.chip.chip--tag .chip-remove {
  background: transparent;
  border: 0;
  padding: 0 2px;
  margin-left: var(--space-0_5);
  color: var(--text-subtle);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  border-radius: var(--radius-sm);
}
.chip.chip--tag .chip-remove:hover {
  color: var(--error);
  background: var(--bg-hover);
}

/* Option-value chip — the select/multi_select sibling of .chip--tag.
   Same layout language, but NO "#" prefix (it's a chosen value, not a
   free-form tag). Shares the chip--color-{token} variants below. */
.chip.chip--option {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  height: 22px;
  background: var(--bg-sunken);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--t-caption)/1 var(--font-sans);
  white-space: nowrap;
}

/* -----------------------------------------------------------
   Chip colour variants — shared by per-workspace tag colours
   (workspace_tag_colors) and per-option select-column colours
   (workspace_custom_columns.option_colors_json). The variants attach
   to the bare .chip so both .chip--tag and .chip--option pick them up;
   the only tag-specific bit (the "#" prefix) stays on .chip--tag.

   Eight engine-known tokens, each driving the chip's background +
   foreground via existing CSS hues. The six semantic / brand
   variants reach for the matching token directly (--navy-700,
   --success, etc.); the two HSL variants (mint = 165, purple =
   280) continue the established pill recipe so no new hex codes
   are introduced. All dark-mode flips through the existing role
   tokens.

   The default ("grey") variant is the bare .chip--tag rule above —
   no modifier class needed.
   ----------------------------------------------------------- */

/* Semantic / brand-token variants — bg uses the existing *-bg
   companion (light tint), border tightens, foreground uses the
   solid family token so the # prefix and text both read
   confidently against the tint. */
.chip.chip--color-navy {
  background: color-mix(in srgb, var(--navy-500) 10%, var(--bg-surface));
  border-color: color-mix(in srgb, var(--navy-500) 30%, var(--border));
  color: var(--text-accent);
}
.chip.chip--color-success {
  background: var(--success-bg);
  border-color: color-mix(in srgb, var(--success) 30%, var(--border));
  color: var(--success);
}
.chip.chip--color-warning {
  background: var(--warning-bg);
  border-color: color-mix(in srgb, var(--warning) 30%, var(--border));
  color: var(--warning);
}
.chip.chip--color-error {
  background: var(--error-bg);
  border-color: color-mix(in srgb, var(--error) 30%, var(--border));
  color: var(--error);
}
.chip.chip--color-info {
  background: var(--info-bg);
  border-color: color-mix(in srgb, var(--info) 30%, var(--border));
  color: var(--info);
}
/* "grey" is the default — render explicitly so the picker can
   round-trip the active state for the selected option without
   needing a special case. Same look as the bare .chip--tag. */
.chip.chip--color-grey {
  background: var(--bg-sunken);
  border-color: var(--border);
  color: var(--text-muted);
}

/* HSL-recipe variants — continue the .pill convention (e.g.
   --pill-h: 280 is the "Custom purple" precedent in §12). Hue
   literal lives only inside the variant class; nothing leaks. */
.chip.chip--color-mint {
  background: hsl(165 65% 95%);
  border-color: hsl(165 40% 80%);
  color: hsl(165 60% 28%);
}
.chip.chip--color-purple {
  background: hsl(280 65% 96%);
  border-color: hsl(280 40% 86%);
  color: hsl(280 50% 38%);
}

/* The # prefix and remove button inherit colour from .chip-label
   / .chip-remove rules above; the role-token foreground we just
   set propagates through automatically. */

/* -----------------------------------------------------------
   Workspace Edit → Tag colours picker.

   Two-column rows: chip preview on the left, native <select>
   on the right. The preview lets admins eyeball the choice
   without saving first; the select drives a `change` event
   that the JS module catches.
   ----------------------------------------------------------- */
.tag-color-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.tag-color-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.tag-color-row__chip {
  flex: 0 0 auto;
  min-width: 6em;
}
.tag-color-row__select {
  flex: 0 0 auto;
  min-width: 9em;
}
.tag-color-row__count {
  font: 500 var(--t-caption)/1 var(--font-sans);
  color: var(--text-subtle);
  margin-left: auto;
}

/* Drawer tags strip — sits at the top of the Details pane. */
.drawer-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) 0 var(--space-4);
  border-bottom: 1px solid var(--divider);
  margin-bottom: var(--space-4);
}
.drawer-tags-label {
  font: 600 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.drawer-tags-list {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  list-style: none;
  padding: 0;
  margin: 0;
}
.drawer-tags-add {
  display: inline-flex;
  margin-left: auto;
}
.drawer-tags-input {
  font: 400 var(--t-body-sm)/1 var(--font-sans);
  height: 24px;
  padding: 0 var(--space-2);
  background: transparent;
  color: var(--text);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  min-width: 0;
  transition: width var(--duration-fast) var(--ease-standard);
}
/* Compact by default — a quiet affordance beside the chips, not a giant empty
   box. Grows on focus to give room while typing. Scoped under .drawer-tags so
   it out-specifies the global input[type=text]{width:100%} rule. */
.drawer-tags .drawer-tags-input { width: 120px; }
.drawer-tags .drawer-tags-input:focus { width: 180px; }
.drawer-tags-input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-style: solid;
}

/* List + Kanban inline tag rendering — chips wrap after the title. */
.task-tags-inline {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-left: var(--space-2);
  vertical-align: middle;
}

/* Tag pill in the shared task filter bar — same look as other
   filter pills (.task-filter-pill) but rendered through the tag
   chip styling so the # prefix is visible. The pressed state
   inherits from .task-filter-pill[aria-pressed=true]. */
.task-filter-pill.task-filter-pill--tag::before {
  content: "#";
  margin-right: 2px;
  color: var(--text-subtle);
}

/* ============================================================
   Dashboard charts (audit #8 → 2026 density pass)
   ------------------------------------------------------------
   A 12-column span-aware grid for /dashboard. Cards declare a
   span via .dash-span-{4,6,12}; the hero trend is full width,
   the composition charts are halves, the breakdowns are thirds.
   Collapses progressively: thirds → halves at 1200px, halves →
   full at 900px (the shell breakpoint). */

.dashboard-charts-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-5);
  margin-top: var(--space-4);
}

.dash-span-4  { grid-column: span 4; }
.dash-span-6  { grid-column: span 6; }
.dash-span-12 { grid-column: span 12; }

/* Each chart fills its card to a consistent height so rows align; the
   hero (trend) is taller. The skeleton inside fills 100% via inline style. */
.dash-chart { width: 100%; height: 280px; }
.dash-chart--hero { height: 340px; }

@media (max-width: 1200px) {
  /* Thirds become halves; a trailing lone third (e.g. Billable split when
     the assignee chart pushes the row to three) stretches to full width so
     it never sits alone at half with dead space beside it. */
  .dashboard-charts-grid > .dash-span-4 { grid-column: span 6; }
  .dashboard-charts-grid > .dash-span-4:last-child { grid-column: 1 / -1; }
}

@media (max-width: 900px) {
  .dashboard-charts-grid { grid-template-columns: 1fr; gap: var(--space-4); }
  .dash-span-4,
  .dash-span-6,
  .dash-span-12 { grid-column: 1 / -1; }
  .dash-chart { height: 260px; }
  .dash-chart--hero { height: 300px; }
}

/* ============================================================
   Print / Save-as-PDF (audit #7 — zero-dependency report PDF)
   ------------------------------------------------------------
   The "Print / PDF" button on report pages calls window.print();
   these rules strip the app chrome and the interactive controls
   so the browser's print/Save-as-PDF output is just the report
   title, the active-filter context, and the results table — on a
   clean white page regardless of the on-screen (dark) theme.
   ============================================================ */
@media print {
  .site-header,
  .site-rail,
  .site-side,
  .page-actions,
  .report-filter-bar,
  .report-save-details,
  .report-active-chip__remove,
  .report-active-chips__clear,
  .toast-stack { display: none !important; }

  .site-shell { display: block !important; }
  .site-main {
    display: block !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    overflow: visible !important;
  }

  html, body, .site-shell, .site-main {
    background: #fff !important;
    color: #000 !important;
  }

  /* Results table: full width, visible borders, no row splits across pages. */
  table { width: 100% !important; border-collapse: collapse !important; }
  th, td { border: 1px solid #bbb !important; color: #000 !important; }
  thead { display: table-header-group; }          /* repeat header on each page */
  tr { break-inside: avoid; }

  * { box-shadow: none !important; }
  a { color: #000 !important; text-decoration: none !important; }
}

/* ============================================================
   View utility helpers (Phase 73 — inline-style debt paydown)
   ------------------------------------------------------------
   Named utilities extracted from one-off `style="…"` attributes
   on the Manager dashboard, Home, Timesheet, Timer, and
   Portfolio views. Spacing/colour come from tokens — never hex.

   When you find yourself reaching for an inline `style=` on a
   view, check this section first; if a utility doesn't cover
   the case, ADD one here rather than introducing a one-off.
   See the comment block at the bottom of this section for a
   roster of every helper.
   ============================================================ */

/* Layout helpers --------------------------------------------- */

/* Vertical stack at the 4 / 5 spacing step. The base .stack
   ships at space-3; these are explicit for views where the
   intent reads better than the size keyword. */
.stack-4 { display: flex; flex-direction: column; gap: var(--space-4); }
.stack-5 { display: flex; flex-direction: column; gap: var(--space-5); }

/* Row variants — the base .row gaps at space-2; these set
   explicit, common gaps so views don't need inline overrides. */
.row-3            { display: flex; align-items: center; gap: var(--space-3); }
.row-4            { display: flex; align-items: center; gap: var(--space-4); }
.row-baseline     { align-items: baseline; }
.row-end-align    { align-items: flex-end; }
.row-stretch      { align-items: stretch; }
.row-wrap         { flex-wrap: wrap; }
.row-tight        { gap: var(--space-1); }
.row-inline       { display: inline-flex; }
.row-shrink-0     { flex-shrink: 0; }
.row-min-0        { min-width: 0; }

/* Margin-left:auto inside a flex row — used to push trailing
   chips/links to the far edge without a dedicated spacer. */
.ml-auto { margin-left: auto; }

/* Three-column equal-width grid for dashboard columns. */
.grid-3col {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

/* Flex grow helpers. .grow-1 expands within a row; .min-w-220
   is the standard responsive break for filter dropdowns. */
.grow-1     { flex: 1; }
.min-w-220  { min-width: 220px; }
.min-w-280  { min-width: 280px; }

/* Spacing helpers — gap and offset around tokens, named by
   step so they read straight. Use sparingly; layouts should
   compose from .stack / .row / .card whenever possible. */
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-5 { margin-bottom: var(--space-5); }
.mb-6 { margin-bottom: var(--space-6); }
.ml-2 { margin-left: var(--space-2); }
.m-0  { margin: 0; }
.p-0  { padding: 0; }

/* Padded card body — used when a .card otherwise has zero
   padding (because it hosts a list/table) but needs a friendly
   inset for a single paragraph. */
.card-pad-3 { padding: var(--space-3); }
.card-pad-4 { padding: var(--space-4); }

/* Form-row item that should fill the remaining space with a
   sensible minimum so the row wraps on narrow screens. */
.form-flex {
  flex: 1;
  min-width: 220px;
}

/* Subtle bordered list-item — one row per item, hairline
   divider above. The Manager dashboard task list and the
   Timer parked list both used this pattern inline. */
.list-row {
  padding: var(--space-3);
  border-top: 1px solid var(--divider);
}
.list-row--padded-x {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--divider);
  border-top: 0;
}

/* Plain unstyled list — kills bullets, padding, margin. */
.list-plain {
  list-style: none;
  padding: 0;
  margin: 0;
}

/* Tight bulleted list in muted body text — for "coming soon" /
   "deferred items" rosters inside a card. Bullets stay, but the
   left padding is tightened to the token scale. */
.deferred-list {
  margin: 0;
  padding-left: var(--space-5);
  color: var(--text-muted);
}

/* Text helpers --------------------------------------------- */

/* Inline anchor that should not look like a link — used when
   wrapping a clickable area where the visible style is owned
   by the parent (a card header link, a list-row title block). */
.link-plain {
  text-decoration: none;
  color: inherit;
}
.link-plain:hover { text-decoration: none; }

.text-end       { text-align: right; }
.text-center    { text-align: center; }
/* Token-sized text helpers — for the muted-paragraph-inside-card
   pattern where the default body size is too large. Pair with .muted. */
.text-body-sm   { font-size: var(--t-body-sm); }
.text-caption   { font-size: var(--t-caption); }
.weight-500     { font-weight: 500; }
.weight-600     { font-weight: 600; }
.muted-meta {
  color: var(--text-muted);
  font-size: var(--t-caption);
}

/* Section heading inside a long page — heading-sm weight,
   navy strong, zero top margin. The Portfolio sections used
   this inline three times. */
.section-heading {
  font: 600 var(--t-heading-sm) / 1.2 var(--font-sans);
  color: var(--text-strong);
  margin: 0 0 var(--space-3);
}
.section-heading .muted { font-weight: 400; }

/* Numeric display blocks used by the Timer view. */
.timer-clock-xl {
  font: 600 60px / 1 var(--font-sans);
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
  margin: var(--space-3) 0;
}
.timer-clock-xl--muted { color: var(--text-subtle); }

/* Heading-md display for a task title shown next to a code. */
.task-title-md {
  font: 600 var(--t-heading-md) / 1.2 var(--font-sans);
  color: var(--text-strong);
}
.task-title-strong {
  font-weight: 600;
  color: var(--text-strong);
}

/* Heading-lg "elapsed" display used in the Home Now-timing
   card. Smaller than the dedicated Timer view clock. */
.elapsed-display {
  font-size: var(--t-heading-lg);
  font-weight: 600;
}

/* Form helpers --------------------------------------------- */

/* Inline form that sits alongside buttons inside a parent
   flex row — `display: inline` so the form doesn't break out
   of the row, `margin: 0` so it doesn't push surrounding
   buttons around, and `max-width: none` so the global
   form { max-width: 640px } cap doesn't clip it. */
.form-inline {
  display: inline;
  margin: 0;
  max-width: none;
}

/* Form that IS the row — flex-aligned items, no global form
   margin, no max-width cap. Used for the Timer running-card
   Stop form where the form hosts a checkbox + notes input +
   submit button all in one row. Layered after .form-inline so
   any combined `.form-row .form-inline` would still resolve
   to display:flex via the .form-row declaration order. */
.form-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  max-width: none;
}

/* Inline checkbox label — weight 400 so it doesn't compete
   with the surrounding labels. */
.label-checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  font-weight: 400;
}

/* Stop-and-save form sits centred-left inside the Now timing
   card; the cap keeps the notes field from sprawling. */
.stop-form {
  margin-top: var(--space-4);
  max-width: 480px;
}

/* Caption-styled centred footer link inside a list card. */
.list-footer-link {
  display: block;
  padding: var(--space-3);
  text-align: center;
  border-top: 1px solid var(--divider);
  text-decoration: none;
}

/* Empty state padded inside a card (the Manager dashboard
   columns use this so the message fills the column visually). */
.empty--in-card {
  padding: var(--space-6) var(--space-4);
}

/* ============================================================
   HOME DASHBOARD (Your day)
   ------------------------------------------------------------
   Premium visual layer for Views/Home/Index.cshtml ONLY. Every
   rule is scoped under .home-dashboard so nothing leaks to other
   surfaces. Tokens only — role tokens flip for dark mode + any
   background shade; --accent carries the per-user brand colour.
   The hero clock mirrors the timer-popover state semantics
   (running = --success, paused = --warning) rather than inventing
   new colour, and is enlarged via a scoped override of the shared
   .elapsed-display utility (specificity 0,2,0 > the 0,1,0 base
   rule at the top of this file — no edit to the shared rule).
   ============================================================ */

/* Full-bleed within .site-main's padding, matching every other top-level
   surface (My work, timesheet, dashboards) — no width cap, so "Your day"
   never reads as inset with empty margins next to them. The four list cards
   and the now-timing hero span the width; the My week card splits into chart
   + summary so its right half carries data, not emptiness. */
.home-dashboard { max-width: none; }

/* --- Day band (header) -------------------------------------- */
/* Replaces the 3-row eyebrow/title/subtitle stack with one row: the
   page title pairs with TODAY's date (the truthful anchor for a page
   called "Your day"), separated by a hairline rule, actions pushed
   right to fill what was empty space. The week range moved to the
   "My week" card subtitle, where it's actually true. */
.home-dashboard .day-band {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--divider);
}
.home-dashboard .day-band__title {
  font-size: var(--t-heading-md);
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.1;
  color: var(--text-strong);
  margin: 0;
}
.home-dashboard .day-band__date {
  margin: 0;
  /* Hairline rule encodes "title · when" — a real divider, not decoration. */
  padding-left: var(--space-3);
  border-left: 1px solid var(--border);
  font-size: var(--t-body);
  color: var(--text-muted);
  white-space: nowrap;
}
/* Weekday is the meaningful token on "your day" — give it the weight. */
.home-dashboard .day-band__weekday { color: var(--text); font-weight: 500; }
.home-dashboard .day-band__full { margin-left: var(--space-1); }
.home-dashboard .day-band__actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}
@media (max-width: 560px) {
  /* Date drops below the title; the hairline rule would read as a stray
     mark once wrapped, so trade it for top spacing. */
  .home-dashboard .day-band__date {
    border-left: 0;
    padding-left: 0;
    flex-basis: 100%;
    order: 3;
    white-space: normal;
  }
  .home-dashboard .day-band__actions { margin-left: 0; }
}

.home-dashboard .stack--loose { gap: var(--space-5); }

/* --- Now timing hero ---------------------------------------- */
/* The page's pulse: the one surface where we spend visual weight.
   Soft raised depth + a barely-there accent wash set it apart from
   the calm cards below — no left rule, no transform. */
:is(.home-dashboard, .timer-page) .now-timing {
  /* Neutralise the navy-tinted global shadow tokens on the hero only, so the
     card casts a plain grey edge (not a blue one) on a near-white page. Scoped
     override — the global --shadow-* stay navy app-wide by design. Both the
     base (--shadow-md) and idle (--shadow-sm) rules below resolve these. */
  --shadow-md: 0 4px 8px rgba(24, 27, 33, 0.07), 0 2px 4px rgba(24, 27, 33, 0.05);
  --shadow-sm: 0 1px 3px rgba(24, 27, 33, 0.07), 0 1px 2px rgba(24, 27, 33, 0.05);
  padding: var(--space-5);
  box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--accent) 16%, var(--border));
}
:is(.home-dashboard, .timer-page) .now-timing--running {
  background:
    linear-gradient(120deg,
      color-mix(in srgb, var(--accent) 5%, transparent),
      transparent 42%),
    var(--bg-surface);
}
:is(.home-dashboard, .timer-page) .now-timing--paused { background: var(--bg-surface); }

/* Compact stacked hero: three short rows (eyebrow+controls / clock+task /
   full-width stop form) instead of a tall 2-column grid. Keeps the card
   short and lets the notes field span the full width. */
:is(.home-dashboard, .timer-page) .now-timing--running,
:is(.home-dashboard, .timer-page) .now-timing--paused {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-block: var(--space-4);
}

:is(.home-dashboard, .timer-page) .now-timing__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
:is(.home-dashboard, .timer-page) .now-timing__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
:is(.home-dashboard, .timer-page) .now-timing__label {
  margin: 0;
  font-size: var(--t-caption);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Pause/Resume + Manage — sit at the top-right beside the eyebrow. */
:is(.home-dashboard, .timer-page) .now-timing__controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}
/* Both timer forms render; show only the one matching the live state
   (the card's status class is server-rendered, so this is correct with
   JS off too — home-timing-tick.js just flips the class on resume/pause). */
:is(.home-dashboard, .timer-page) .now-timing--running .now-timing__resume,
:is(.home-dashboard, .timer-page) .now-timing--paused  .now-timing__pause { display: none; }

/* Live status dot, sized up from the pill dot to read as a beacon.
   The running state gets one soft pulse ring — the page's single
   piece of motion, gated on reduced-motion. */
:is(.home-dashboard, .timer-page) .now-timing__dot {
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-radius: var(--radius-full);
  background: var(--grey-400);
}
:is(.home-dashboard, .timer-page) .now-timing__dot[data-status="running"] {
  background: var(--success);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 60%, transparent);
}
:is(.home-dashboard, .timer-page) .now-timing__dot[data-status="paused"] { background: var(--warning); }
@media (prefers-reduced-motion: no-preference) {
  :is(.home-dashboard, .timer-page) .now-timing__dot[data-status="running"] {
    animation: home-timing-pulse 2s var(--ease-standard) infinite;
  }
}
@keyframes home-timing-pulse {
  0%   { box-shadow: 0 0 0 0   color-mix(in srgb, var(--success) 55%, transparent); }
  70%  { box-shadow: 0 0 0 7px color-mix(in srgb, var(--success) 0%,  transparent); }
  100% { box-shadow: 0 0 0 0   color-mix(in srgb, var(--success) 0%,  transparent); }
}

/* Clock + task identity sit on one baseline-aligned row, the task
   block taking the remaining width so long names show in full. */
:is(.home-dashboard, .timer-page) .now-timing__lead {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
}
:is(.home-dashboard, .timer-page) .now-timing__lead .now-timing__task { flex: 1 1 auto; }
/* The focal point — scoped enlargement of the shared utility.
   tabular-nums keeps the ticking digits from reflowing. */
:is(.home-dashboard, .timer-page) .elapsed-display {
  font-size: clamp(2rem, 4.5vw, 2.75rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
  color: var(--text-strong);
}
:is(.home-dashboard, .timer-page) .elapsed-display[data-status="running"] { color: var(--success); }
:is(.home-dashboard, .timer-page) .elapsed-display[data-status="paused"]  { color: var(--warning); }

:is(.home-dashboard, .timer-page) .now-timing__task {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
  padding-bottom: var(--space-1);
}
:is(.home-dashboard, .timer-page) .now-timing__task-head {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
:is(.home-dashboard, .timer-page) .now-timing__task-title {
  font: 600 var(--t-body-md)/1.3 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: normal;
}
:is(.home-dashboard, .timer-page) .now-timing__task-meta {
  font: 400 var(--t-caption)/1.4 var(--font-sans);
  color: var(--text-muted);
}
:is(.home-dashboard, .timer-page) .now-timing__task-meta a { color: var(--text-link); text-decoration: none; }
:is(.home-dashboard, .timer-page) .now-timing__task-meta a:hover { text-decoration: underline; }

/* Stop form: a single full-width row under the clock — notes input
   stretches, Billable + Stop pin to the right. A top hairline separates
   it from the lead row. */
:is(.home-dashboard, .timer-page) .now-timing__stop {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
  /* Beat the global `form { max-width: 640px }` cap so the row spans the card. */
  max-width: none;
}
:is(.home-dashboard, .timer-page) .now-timing__notes {
  flex: 1 1 240px;
  min-width: 0;
  /* Beat the global input cap (width:100%; max-width:480px). */
  max-width: none;
}
/* Visible label is redundant with the placeholder in a one-line row;
   keep it for screen readers only. */
:is(.home-dashboard, .timer-page) .now-timing__stop-label {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
:is(.home-dashboard, .timer-page) .now-timing__stop .label-checkbox { flex: 0 0 auto; }
:is(.home-dashboard, .timer-page) .now-timing__stop .btn-primary { flex: 0 0 auto; }

/* Idle hero — keeps the same frame so the layout doesn't jump
   between states; the clock is a muted placeholder. */
:is(.home-dashboard, .timer-page) .now-timing--idle { box-shadow: var(--shadow-sm); border-color: var(--border); }
:is(.home-dashboard, .timer-page) .now-timing__idle-body {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}
:is(.home-dashboard, .timer-page) .elapsed-display--idle { color: var(--text-subtle); }
:is(.home-dashboard, .timer-page) .now-timing__idle-text {
  margin: 0;
  font: 600 var(--t-body-md)/1.3 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text);
}
:is(.home-dashboard, .timer-page) .now-timing__idle-sub {
  margin: var(--space-0_5) 0 0;
  font: 400 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text-muted);
}
:is(.home-dashboard, .timer-page) .now-timing__idle-body .btn { margin-left: auto; }

/* --- Task-list cards --------------------------------------- */
.home-dashboard .home-list { padding: var(--space-4); }
.home-dashboard .home-list .card-header { margin-bottom: var(--space-2); }
/* Show the full task name (override the shared single-line ellipsis).
   The status pill moves to the meta line so the title owns the full row
   width and wraps to its natural height. Code aligns to the title top.
   The whole title line opens the detail drawer (data-open-detail). */
.home-dashboard .home-list .task-row__main { align-items: flex-start; cursor: pointer; }
.home-dashboard .home-list .task-row__title {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  /* Opens the detail drawer (data-open-detail) — read as clickable, not
     an editable text field (the shared base sets cursor:text). */
  cursor: pointer;
}
.home-dashboard .home-list .task-row__title:hover { background: transparent; color: var(--text-link); }
/* Status pill right-aligned on the meta (project) line: the title keeps the
   full row width on line 1, and only the secondary project line shares space
   with the pill — which right-aligns to a clean edge while the timer button
   stays in its own aligned column. Project text truncates if the pill is wide
   (e.g. "Waiting on Customer"). minmax(0,1fr) clamps the content column so the
   nowrap meta text can't grow the row past the card (the list is an
   overflow-x:auto scroller, so a plain 1fr would overflow instead of truncate). */
.home-dashboard .home-list .task-row { grid-template-columns: minmax(0, 1fr) auto; }
.home-dashboard .home-list .task-row__meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  white-space: nowrap;
  overflow: visible;
  line-height: 1.5;
}
.home-dashboard .home-list .task-row__meta-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.home-dashboard .home-list .task-row__meta-pill {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Count badge — pill-shaped, reads as a tally not body text. Goes
   warning-toned only when Past due is non-empty (set in the view). */
.home-dashboard .count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  font: 600 var(--t-caption)/1 var(--font-sans);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.home-dashboard .count-badge--warning {
  background: var(--warning-bg);
  border-color: color-mix(in srgb, var(--warning) 35%, transparent);
  color: var(--warning);
}

/* Empty state — small line icon + tight copy, an "all clear"
   confirmation rather than a bare muted sentence. */
.home-dashboard .home-list__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-3);
  color: var(--text-subtle);
}
.home-dashboard .home-list__empty-icon {
  width: 28px; height: 28px;
  color: color-mix(in srgb, var(--success) 60%, var(--text-subtle));
}
.home-dashboard .home-list__empty-text {
  margin: 0;
  font: 400 var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text-muted);
  max-width: 22ch;
}

/* --- My week chart ----------------------------------------- */
.home-dashboard .my-week { padding: var(--space-5); }
.home-dashboard .my-week__head { margin-bottom: var(--space-4); }
/* Two-column card: the chart fills ~60% (wide enough to use the space,
   capped by the grid so its height stays calm), and a summary panel
   carries derived week stats in the rest — so the right side is data,
   not emptiness. Collapses to stacked below 768px (see media query). */
.home-dashboard .my-week__body {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(220px, 1fr);
  gap: var(--space-7);
  align-items: center;
}
.home-dashboard .my-week__chart { min-width: 0; }
.home-dashboard .my-week__chart svg { width: 100%; }
.home-dashboard .my-week__summary {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-4);
  padding-left: var(--space-7);
  border-left: 1px solid var(--divider);
}
.home-dashboard .my-week__summary-hero {
  display: flex;
  flex-direction: column;
}
/* 2×2 tiles so the panel fills its column with real stats rather than a
   few stretched rows. Each tile is a label over a value. */
.home-dashboard .my-week__stats {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3) var(--space-5);
}
.home-dashboard .my-week__stat-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-0_5);
  padding: var(--space-2) 0 var(--space-2) var(--space-3);
  border-left: 2px solid var(--border);
}
.home-dashboard .my-week__stat-row dt {
  font: 400 var(--t-caption)/1.3 var(--font-sans);
  color: var(--text-muted);
}
.home-dashboard .my-week__stat-row dd {
  margin: 0;
  font: 600 var(--t-heading-sm)/1.2 var(--font-sans);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.home-dashboard .my-week__stat-of { color: var(--text-subtle); font-weight: 400; font-size: var(--t-body-sm); }
/* Anchor the panel with a larger total. */
.home-dashboard .my-week__summary-hero .my-week__stat-value { font-size: var(--t-display); }
.home-dashboard .my-week__stat-value {
  font: 600 var(--t-heading-md)/1 var(--font-sans);
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
}
.home-dashboard .my-week__stat-unit {
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text-muted);
  margin-left: 1px;
}
.home-dashboard .my-week__stat-label {
  margin-top: var(--space-0_5);
  font: 400 var(--t-caption)/1.4 var(--font-sans);
  color: var(--text-muted);
}
.home-dashboard .my-week svg { display: block; }

@media (max-width: 768px) {
  /* Chart + summary stacked: one column, the summary's left rule (a
     vertical separator when side-by-side) flips to a top rule, and the
     stat rows go horizontal so the panel stays short below the chart. */
  .home-dashboard .my-week__body { grid-template-columns: 1fr; gap: var(--space-4); }
  .home-dashboard .my-week__summary {
    padding-left: 0;
    border-left: 0;
    padding-top: var(--space-4);
    border-top: 1px solid var(--divider);
  }
  .home-dashboard .my-week__stats { gap: var(--space-3) var(--space-4); }
}

/* Narrow widths: the four list cards already wrap via the row
   utility; relax the hero's wide-baseline row so the clock and
   task identity stack cleanly and the actions don't crowd. */
@media (max-width: 560px) {
  :is(.home-dashboard, .timer-page) .now-timing { padding: var(--space-4); }
  :is(.home-dashboard, .timer-page) .now-timing__lead { align-items: flex-start; gap: var(--space-3); }
  :is(.home-dashboard, .timer-page) .now-timing__idle-body .btn { margin-left: 0; }
}

/* Centred big-display empty state — used by Timer when there
   is no active or parked timer. */
.empty--center-display {
  text-align: center;
  padding: var(--space-12) var(--space-6);
}

/* Live-activity panel footer — muted caption with a top hairline
   tucked under the panel's table. Drops the inline style that lived
   on the partial and gives the pattern a name for reuse. */
.activity-footer {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--divider);
}

/* ============================================================
   Live-activity board ("Now timing") — _ActivityPanel partial,
   shared by /activity and the Manager dashboard.

   A live presence board, not a spreadsheet: a pulse header whose
   team-total ticks every second, then one roomy row per running
   timer — initials avatar, who + what, a focus meter sized against
   the longest current timer, and a large ticking clock. Rows past
   App:TimerIdleHours flip to a "running long" warning. All colour
   via role / semantic tokens so it tracks light + dark.
   ============================================================ */

/* ── Header band: live eyebrow + title on the left, the two live
   stats (team total, headcount) right-aligned. ── */
.activity-head { align-items: flex-start; }
.activity-head__lead {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.activity-live {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--success);
}
.activity-pulse {
  position: relative;
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--success);
}
/* Expanding ring — the page's heartbeat. Disabled under reduced motion. */
.activity-pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--success);
  animation: activity-pulse 2s var(--ease-standard) infinite;
}
@keyframes activity-pulse {
  0%   { transform: scale(1);   opacity: 0.5; }
  70%  { transform: scale(3.4); opacity: 0; }
  100% { transform: scale(3.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .activity-pulse::after { animation: none; }
}

.activity-head__stats {
  margin-left: auto;
  display: flex;
  gap: var(--space-6);
  text-align: right;
}
.activity-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.activity-stat__value {
  font: 600 var(--t-heading-md) / 1 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.activity-stat__label {
  font: 400 var(--t-caption) / 1.3 var(--font-sans);
  color: var(--text-muted);
}

/* ── Rows ── */
.activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Grid row: fixed tracks so status / priority / due / elapsed line up
   exactly across every row (a flex row jittered as the elapsed text width
   changed). The who track (1fr) fills; the rest are fixed-width columns.
   Two-line "who" keeps the row short so more people fit without scrolling. */
.activity-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) 150px 116px 96px 128px;
  align-items: center;
  column-gap: var(--space-4);
  padding: var(--space-1_5, 6px) var(--space-4);
  border-top: 1px solid var(--divider);
  transition: background var(--duration-fast) var(--ease-standard);
}
.activity-row:first-child { border-top: 0; }
.activity-row:hover { background: var(--bg-hover); }

.activity-row__avatar { flex: none; }

.activity-row__who {
  min-width: 0;        /* grid 1fr track; min-width:0 lets the title truncate */
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* Line 1: name + task on one row; name stays whole, task truncates. */
.activity-row__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}
.activity-row__name {
  flex: none;
  font: 600 var(--t-body-sm) / 1.3 var(--font-sans);
  color: var(--text-strong);
}
.activity-row__task {
  flex: 1 1 auto;
  min-width: 0;
  font: 500 var(--t-body-sm) / 1.35 var(--font-sans);
  color: var(--text);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.activity-row__task:hover { color: var(--text-link-hover); }
.activity-row__code {
  margin-right: var(--space-1);
  font: 600 var(--t-caption) / 1 var(--font-mono);
  color: var(--text-subtle);
}
.activity-row__ctx {
  font: 400 var(--t-caption) / 1.3 var(--font-sans);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.activity-row__ctx a {
  color: var(--text-muted);
  text-decoration: none;
}
.activity-row__ctx a:hover {
  color: var(--text);
  text-decoration: underline;
}
.activity-row__dot {
  margin: 0 var(--space-1);
  color: var(--text-subtle);
}

/* Focus meter — width proportional to the longest current timer; a 6%
   floor keeps a just-started timer visible. Fill is the user's accent;
   it turns amber when the row is running long. */
/* Task metadata columns — status / priority / due — fixed widths so they
   line up across rows. They replace the old filler meter with information
   you can read without opening the task. */
.activity-row__status { min-width: 0; display: flex; align-items: center; }
.activity-row__status .pill {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.activity-row__prio {
  min-width: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.activity-row__due {
  text-align: left;
  font: 500 var(--t-body-sm) / 1.2 var(--font-sans);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

/* Time column — the large ticking clock is the focal element. */
.activity-row__time {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  text-align: right;
  white-space: nowrap;
}
.activity-row .elapsed-cell {
  font: 600 var(--t-heading-sm) / 1.1 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.activity-row__since {
  font: 400 var(--t-caption) / 1.3 var(--font-sans);
  color: var(--text-subtle);
}
/* The "running long" flag is hidden until the row crosses the threshold;
   the started-at caption shows otherwise (one or the other, never both). */
.activity-row__flag { display: none; }

/* ── Overrun: a timer past App:TimerIdleHours someone likely forgot. ── */
.activity-row.is-overrun { background: var(--warning-bg); }
.activity-row.is-overrun:hover { background: var(--warning-bg); }
.activity-row.is-overrun .elapsed-cell { color: var(--warning); }
.activity-row.is-overrun .activity-row__started { display: none; }
.activity-row.is-overrun .activity-row__flag {
  display: inline-block;
  font: 600 var(--t-caption) / 1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--warning);
}

/* Progressive disclosure: drop due, then priority, then status as width
   tightens — the who block + clock always stay. Each breakpoint redefines
   the grid tracks so the hidden column leaves no empty gap. */
@media (max-width: 1100px) {
  .activity-row { grid-template-columns: auto minmax(0, 1fr) 150px 116px 128px; }
  .activity-row__due { display: none; }
}
@media (max-width: 920px) {
  .activity-row { grid-template-columns: auto minmax(0, 1fr) 150px 128px; }
  .activity-row__prio { display: none; }
}
@media (max-width: 640px) {
  .activity-head__stats { gap: var(--space-4); }
  .activity-row { grid-template-columns: auto minmax(0, 1fr) 128px; column-gap: var(--space-2); }
  .activity-row__status { display: none; }
}

/* ============================================================
   Time logged — per-assignee breakdown of logged time_entries over a
   chosen day window, below the live panel on /activity. Reuses the
   .activity-stat header treatment and the .avatar/.segmented primitives.
   ============================================================ */
.timelog-head { align-items: flex-start; }
.timelog-head__totals { margin-left: auto; text-align: right; }

/* Filter bar: range presets + custom dates + assignee, left-aligned on one
   wrapping row. Controls share a baseline; labels sit inline to the left of
   their field so the row stays tight (no tall stacked label/gap). */
.timelog-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--divider);
  border-bottom: 1px solid var(--divider);
  /* Beat the global `form { max-width: 640px }` cap so the controls
     sit on one row across the card instead of wrapping the assignee. */
  max-width: none;
  width: 100%;
}
.timelog-filters__custom {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.timelog-filters__field {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1_5, 6px);
}
.timelog-filters__label {
  font: 500 var(--t-caption) / 1 var(--font-sans);
  color: var(--text-muted);
  white-space: nowrap;
}
/* Compact, auto-width controls (the global rule makes inputs 100%/480px). */
.timelog-filters input[type="date"],
.timelog-filters select {
  width: auto;
  min-width: 132px;
  height: 30px;
  font-size: var(--t-body-sm);
}
/* Kill the global form margins (`form > div { margin-bottom }`, label
   margins) — with align-items:center those unequal margin-boxes push the
   assignee field ~8px below the rest. Zeroing them + a uniform 30px
   control height puts every control on one baseline. */
.timelog-filters > *,
.timelog-filters .timelog-filters__custom,
.timelog-filters .timelog-filters__field { margin: 0; }
.timelog-filters .btn { height: 30px; }
/* Segmented preset buttons sit flush inside the pill; reset the global
   button chrome so only the .segmented look shows. */
.timelog-filters .segmented button {
  height: 28px;
  background: transparent;
  border: 0;
  box-shadow: none;
  cursor: pointer;
}
.timelog-filters .segmented > .active {
  background: var(--bg-surface);
  color: var(--text-strong);
  box-shadow: var(--shadow-xs);
}

/* Activity mix — stacked share bar + legend, grouping the same logged time
   by activity (Support / Development / …). Segment/legend colour comes from
   a per-activity hue set inline (--seg-h), mirroring the .pill HSL pattern. */
.timelog-activity {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--divider);
}
.timelog-activity__label {
  font: 600 var(--t-caption) / 1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.timelog-activity__bar {
  display: flex;
  height: 10px;
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--bg-sunken);
}
.timelog-activity__seg {
  height: 100%;
  background: hsl(var(--seg-h) 52% 52%);
  box-shadow: inset -1.5px 0 0 var(--bg-surface); /* hairline split between slices */
}
.timelog-activity__seg:last-child { box-shadow: none; }
.timelog-activity__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.timelog-activity__legend li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1_5, 6px);
  font: 400 var(--t-body-sm) / 1.2 var(--font-sans);
  color: var(--text);
}
.timelog-activity__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: hsl(var(--seg-h) 52% 52%);
  flex-shrink: 0;
}
.timelog-activity__val {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* By-assignee table. */
.timelog-table { width: 100%; border-collapse: collapse; }
.timelog-table th {
  padding: var(--space-2) var(--space-4);
  font: 600 var(--t-caption) / 1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  text-align: left;
  background: var(--grey-50);
  border-bottom: 1px solid var(--border);
}
.timelog-table th.num { text-align: right; }
.timelog-table td {
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--divider);
  vertical-align: middle;
}
.timelog-table td.num { text-align: right; }
.timelog-table tbody tr:hover { background: var(--bg-hover); }
.timelog-table tbody tr:first-child td { border-top: 0; }
.timelog-num--strong { color: var(--text-strong); font-weight: 600; }

.timelog-assignee {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  color: inherit;
}
.timelog-assignee__name {
  font: 500 var(--t-body) / 1.3 var(--font-sans);
  color: var(--text-strong);
}
a.timelog-assignee:hover .timelog-assignee__name { color: var(--text-link); }
/* Drill-down chevron — hidden until the row is hovered/focused, so the
   table stays calm but the row reads as clickable on approach. */
.timelog-assignee__go {
  color: var(--text-subtle);
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-standard);
}
.timelog-table tbody tr:hover .timelog-assignee__go,
a.timelog-assignee:focus-visible .timelog-assignee__go { opacity: 1; }

/* Share bar — width proportional to the top logger in the window. */
.timelog-table__bar-col { width: 38%; }
.timelog-bar {
  display: block;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--bg-sunken);
  overflow: hidden;
}
.timelog-bar__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}

/* Footer total row. */
.timelog-table tfoot td {
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--border-strong);
  font-weight: 600;
  color: var(--text-strong);
}

@media (max-width: 640px) {
  .timelog-filters__assignee { margin-left: 0; }
  /* Drop the share bar on narrow screens; numbers carry the story. */
  .timelog-table__bar-col { display: none; }
}

/* ── Timesheet (/timesheet) — summary strip, filter bar, table chrome ──
   Reuses .activity-stat and .timelog-activity primitives; only the layout
   shells below are new. */
.timesheet-summary {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.timesheet-summary__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
}
/* The activity-mix bar carries its own padding + bottom border for the
   activity-page card; inside the summary card we want just a top divider. */
.timesheet-summary__mix.timelog-activity {
  padding: var(--space-4) 0 0;
  border-bottom: 0;
  border-top: 1px solid var(--divider);
}

.timesheet-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  /* Forms are width-capped for readability app-wide; this is a full-width
     filter toolbar, not a data-entry form, so opt out of the cap. */
  max-width: none;
  width: auto;
}
.timesheet-filters__presets {
  display: inline-flex;
  gap: var(--space-1);
  flex: 0 0 auto;
}
.timesheet-filters__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 0 0 auto;
  min-width: 0;
}
/* Inputs fill their field so the flex-basis below controls real width. */
.timesheet-filters__field .input { width: 100%; }
.timesheet-filters__label {
  font: 500 var(--t-caption) / 1 var(--font-sans);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
/* Sized so the row packs tight on a normal screen and Project + Search
   absorb any slack on a wide one — no dead space on the right. */
.timesheet-filters__field--date   { flex: 0 0 9rem; }
.timesheet-filters__field--select { flex: 0 1 9.5rem; }
.timesheet-filters__field--project { flex: 1 1 13rem; max-width: 24rem; }
/* Search grows to absorb the row's slack, so Apply/Clear land flush right
   with no dead gap — and when the bar wraps, the search fills its own row. */
.timesheet-filters__search { flex: 1 1 13rem; }
.timesheet-filters__actions {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  align-self: flex-end;
  flex: 0 0 auto;
}


/* Filter area: the dedicated date-window control stacked above the reused
   operator filter bar (.task-filter-bar). */
.timesheet-dates {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
}
.timesheet-filter-area > .task-filter-bar {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
}

/* ============================================================
   Timesheet filter surface — refined "scope bar" (frontend-design).
   A calm two-tier instrument: Period (segmented presets + range) on top,
   Refine (add-filter + chips + search + view options) below. Every rule
   is scoped to .timesheet-filter-area so the shared filter primitives on
   the other list/report pages stay exactly as they are.
   ============================================================ */
.timesheet-filter-area {
  padding: var(--space-3) var(--space-4);
}

/* "My time / Everyone" scope toggle sits above the date row. */
.ts-scope {
  margin-bottom: var(--space-3);
}

/* — Tier 1: Redmine-style date operator control. "Date" label, an operator
   dropdown, and value inputs that swap by operator (hidden ones use [hidden]). */
.ts-datefilter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.ts-datefilter__lead {
  font: 500 var(--t-caption) / 1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.ts-datefilter__op {
  width: auto;            /* override global select { width: 100% } */
  min-width: 11rem;
  height: 36px;
  margin: 0;
}
.ts-datefilter__values {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.ts-datefilter__date { width: 9.5rem; height: 36px; margin: 0; }
.ts-datefilter__num  { width: 5rem;  height: 36px; margin: 0; }
.ts-datefilter__sep, .ts-datefilter__unit {
  font: var(--t-body-sm) / 1 var(--font-sans);
  color: var(--text-muted);
}
/* JS hides the value group (relative ops) or individual pieces by operator. */
.ts-datefilter [hidden] { display: none; }

/* — Tier 2: "Add filter" leads as the one solid affordance — */
.timesheet-filter-area .filter-add {
  border-style: solid;
  border-color: var(--border-strong);
  background: var(--bg-surface);
}
.timesheet-filter-area [data-filter-add] {
  color: var(--text-strong);
  font-weight: 600;
  box-shadow: var(--shadow-xs);
}
.timesheet-filter-area [data-filter-add] svg { color: var(--navy-500, var(--text-muted)); }

/* Saved / Save query are secondary utilities — quiet ghost buttons,
   grouped tight so they read as a pair, not two more primary actions. */
.timesheet-filter-area .saved-filters {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.timesheet-filter-area .saved-filters .filter-add {
  border-color: transparent;
  background: transparent;
  color: var(--text-muted);
  box-shadow: none;
}
.timesheet-filter-area .saved-filters .filter-add:hover {
  background: var(--bg-hover);
  color: var(--text);
  border-color: transparent;
}

/* Search: inset magnifier so the empty field reads as "search", not a gap. */
.timesheet-filter-area .task-filter-bar__search input[type="search"] {
  padding-left: 30px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%239aa1b2' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><path d='M21 21l-4.3-4.3'/></svg>");
  background-repeat: no-repeat;
  background-position: left 9px center;
}

/* View options (Sort / Group) — set apart from search by a hairline rail. */
.timesheet-filter-area .task-filter-bar__search + .task-filter-bar__select {
  margin-left: var(--space-2);
  padding-left: var(--space-4);
  border-left: 1px solid var(--divider);
}

/* Group-by section headers + per-group subtotal, and the grand-total footer. */
.timesheet-table .ts-group-row td {
  background: var(--bg-sunken);
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--border);
}
.ts-group-row__label { font-weight: 600; color: var(--text-strong); }
.ts-group-row__sub {
  margin-left: var(--space-2);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.timesheet-table tfoot .ts-total-row td {
  background: var(--bg-sunken);
  font-weight: 600;
  color: var(--text-strong);
  text-align: right;
  border-top: 1px solid var(--border-strong);
  font-variant-numeric: tabular-nums;
}
/* Notes are free text and can be long — let the column wrap instead of
   stretching the table into a wide horizontal scroll. Capped width so a
   long note reads as a paragraph; overflow-wrap breaks unbroken tokens
   (file names / URLs) rather than forcing the column wider. */
.timesheet-table td.ts-notes {
  white-space: normal;
  overflow-wrap: anywhere;
  min-width: 220px;
  max-width: 380px;
}

/* Dual-list column chooser (Redmine-style Available ↔ Selected + reorder). */
.cols-chooser { position: relative; display: inline-flex; }
.cols-chooser__popover {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 50;
  width: 32rem;
  max-width: 92vw;
  padding: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.cols-chooser__popover[hidden] { display: none; }
.cols-chooser__title {
  margin: 0 0 var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--divider);
  color: var(--text-strong);
  font: 600 var(--t-heading-sm) / 1 var(--font-sans);
  text-transform: none;
  letter-spacing: 0;
}
.cols-chooser__pane-label {
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.cols-chooser__panes {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: var(--space-2);
  align-items: stretch;
}
.cols-chooser__pane { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.cols-chooser__pane-label { color: var(--text-muted); }
.cols-chooser__list {
  width: 100%;
  /* These are multi-select LISTBOXES (size=N), not dropdowns. The global
     `select` rule forces height:36px + appearance:none + a caret onto every
     <select>, which collapsed these to ~2 rows. Restore the native listbox:
     let the box be a fixed scrollable height with no dropdown chrome. */
  height: 230px;
  overflow-y: auto;
  appearance: auto;
  -webkit-appearance: listbox;
  background-image: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background-color: var(--bg-app);
  color: var(--text);
  font: 400 var(--t-body-sm) / 1.4 var(--font-sans);
  padding: var(--space-1);
}
.cols-chooser__list option { padding: 3px var(--space-1); border-radius: var(--radius-sm); }
.cols-chooser__move {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-1);
}
.cols-chooser__move-btn {
  width: 2rem;
  min-width: 2rem;
  height: 2rem;
  padding: 0;
  font-size: var(--t-body-md);
  line-height: 1;
}
.cols-chooser__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

/* Constrained text input for the inline Stop&save notes field
   on the Timer view, so it doesn't expand past the row. */
.input-notes-inline { max-width: 240px; }

/* Activity picker in the Timer Stop&save row — kept compact so the
   inline form stays on one line. */
.input-activity-inline { max-width: 200px; }

/* ------------------------------------------------------------
   Task picker (manual time-entry form, /timesheet/new).
   A search box + absolutely-positioned results dropdown. The
   root .form-group is positioned so the list anchors to it.
   ------------------------------------------------------------ */
[data-task-picker-root] { position: relative; }

.task-picker-results {
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  margin: var(--space-1) 0 0 0;
  padding: var(--space-1);
  list-style: none;
  max-height: 280px;
  overflow-y: auto;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

.task-picker-option {
  display: flex;
  flex-direction: column;
  gap: var(--space-0_5);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.task-picker-option:hover,
.task-picker-option.is-active {
  background: var(--bg-hover);
}

/* The type tokens already carry px (--t-caption: 12px), so `var(--t-caption)`
   is the correct form — `calc(var(--t-caption) * 1px)` evaluates to
   `calc(12px * 1px)`, which is invalid and silently drops the declaration.
   In a `font:` shorthand it drops the weight and family with it.
   The two rules below had that bug, so the secondary line was rendering at
   the inherited 14px instead of the intended 12px caption. The idiom is now
   gone from this file and from the Razor inline styles — keep it that way. */
.task-picker-option__label {
  font-size: var(--t-body);
  color: var(--text-strong);
}

.task-picker-option__secondary {
  font-size: var(--t-caption);
  color: var(--text-muted);
}

/* Group heading ("Recent" / "Assigned to you") in the suggestions mode.
   Presentational only — not selectable, and keyboard nav skips it. Styled
   like a table header so it reads as a divider rather than a dead option. */
.task-picker-group {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--t-caption);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

/* First heading sits flush against the list's own padding. */
.task-picker-group:first-child { padding-top: var(--space-1); }

/* "Showing 20 of 57 — keep typing to narrow." and the empty-result line.
   The old picker truncated silently, which made a partial list look
   complete; this is the affordance that admits it. */
.task-picker-hint {
  padding: var(--space-2) var(--space-3);
  font-size: var(--t-caption);
  color: var(--text-subtle);
  border-top: 1px solid var(--divider);
  margin-top: var(--space-1);
}

/* No divider when the hint IS the whole list (the "No matching tasks" case). */
.task-picker-hint:first-child { border-top: 0; margin-top: 0; }

/* ------------------------------------------------------------
   Utility roster (keep alphabetised, one line each)
   ------------------------------------------------------------
   .btn-ghost--danger           Ghost button with --error text.
   .card--flush                 Zero-padding card (hosts table/list).
   .card-pad-3 / .card-pad-4    Inset padding for cards that
                                otherwise host padding-zero lists.
   .card-subtitle               Muted body-sm beneath .card-title.
   .deferred-list               Tight muted bulleted list inside card.
   .elapsed-display             Heading-lg "Hh MMm SSs" block.
   .empty--center-display       Centred large-padding empty state.
   .empty--in-card              Empty state padded inside a card column.
   .form-flex                   Grow + min-width 220 form item.
   .form-inline                 inline display + margin 0 for inline forms.
   .form-row                    Flex form that hosts a row of fields.
   .grid-3col                   3 equal columns, space-4 gap.
   .grow-1                      flex:1 — grow within a row.
   .input-activity-inline       max-width 200 for the Timer activity select.
   .input-notes-inline          max-width 240 for inline-notes text input.
   .label-checkbox              Inline-flex checkbox label, weight 400.
   .link-plain                  Text-decoration:none + color:inherit.
   .list-footer-link            Centred caption link with top divider.
   .list-plain                  list-style:none, no padding/margin.
   .list-row                    Padded list item with top divider.
   .list-row--padded-x          Row item with x-padding + bottom divider.
   .m-0 / .p-0                  Reset margin / padding.
   .mb-1..6 / .mt-1..6 / .ml-2  Token-step margin helpers.
   .min-w-220 / .min-w-280      Responsive minimum widths.
   .ml-auto                     margin-left:auto for trailing chips.
   .muted-meta                  caption-size text in --text-muted.
   .row-3 / .row-4              Row with explicit space-3 / space-4 gap.
   .row-baseline                align-items:baseline on a row.
   .row-end-align               align-items:flex-end on a row.
   .row-inline                  inline-flex variant of .row.
   .row-min-0                   min-width:0 to allow flex children to shrink.
   .row-shrink-0                flex-shrink:0 for fixed-content row clusters.
   .row-stretch                 align-items:stretch on a row.
   .row-tight                   space-1 gap.
   .row-wrap                    flex-wrap:wrap.
   .section-heading             heading-sm strong section title (margin 0 0 3).
   .stack-4 / .stack-5          Vertical stack with space-4 / space-5 gap.
   .stop-form                   max-width 480 + margin-top 4 for stop forms.
   .task-title-md               t-heading-md task title beside a code.
   .task-title-strong           Plain weight-600 + --text-strong span.
   .text-body-sm / .text-caption  Token-sized type helpers.
   .text-center / .text-end     Text alignment helpers.
   .timer-clock-xl              60px clock display (Timer view).
   .timer-clock-xl--muted       Variant in --text-subtle for inactive.
   .weight-500 / .weight-600    Inline font-weight helpers.
   ------------------------------------------------------------ */

/* Saved filters — reuses .filter-add / .filter-add-menu. */
.saved-filters { display: inline-flex; align-items: center; gap: var(--space-1); }
.filter-add-menu__empty {
  padding: var(--space-2) var(--space-3);
  color: var(--text-muted);
  font: var(--t-body-sm)/1.3 var(--font-sans);
}
.saved-filters__row { display: flex; align-items: center; }
.saved-filters__apply { flex: 1 1 auto; }
.saved-filters__delete {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; margin-right: var(--space-1);
  border: 0; background: transparent; color: var(--text-muted);
  border-radius: var(--radius-sm); cursor: pointer;
}
.saved-filters__delete:hover { background: var(--bg-hover); color: var(--error); }
.saved-filters__save-form { padding: var(--space-2); display: flex; flex-direction: column; gap: var(--space-2); min-width: 220px; }
.saved-filters__name {
  height: 30px; padding: 0 var(--space-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--bg-surface); color: var(--text); font: var(--t-body-sm)/1 var(--font-sans);
}
.saved-filters__name:focus-visible { outline: none; border-color: color-mix(in srgb, var(--focus-ring) 55%, var(--border-strong)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 20%, transparent); }
.saved-filters__save-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }
.saved-filters__share {
  display: flex; align-items: center; gap: var(--space-1);
  color: var(--text); font: var(--t-body-sm)/1.2 var(--font-sans); cursor: pointer;
}
.saved-filters__share input[type="checkbox"] { flex-shrink: 0; }
/* "Shared"/"Team" badge on a workspace-shared preset row. */
.saved-filters__badge {
  margin-left: var(--space-1);
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--bg-selected);
  color: var(--text-accent);
  font: 500 var(--t-caption)/1.6 var(--font-sans);
}

/* ============================================================
   Standalone auth pages (Login chooser) — app-shell-free.
   Two-pane: a branded navy hero (desktop) beside the sign-in card;
   token-driven so it tracks light/dark without authoring two
   stylesheets. Used only by the Layout=null Login view
   (change-password uses the normal shell).
   ============================================================ */
.auth-body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg-app);
  color: var(--text);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

/* Two-pane layout: a branded navy hero (desktop) beside the sign-in
   panel. Collapses to a single column below 920px — the hero is hidden
   so the form owns the small-screen viewport. */
.auth-layout {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  grid-template-columns: 1fr;
}
@media (min-width: 920px) {
  .auth-layout { grid-template-columns: 1.1fr 1fr; }
}

/* ---- Logo mark (shared by hero + card) ---- */
.auth-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border-radius: var(--radius-md);
}
.auth-mark svg { width: 58%; height: 58%; }
.auth-mark--brand {
  width: 46px; height: 46px;
  background: linear-gradient(150deg, var(--navy-600), var(--navy-800));
  color: #fff;
  box-shadow: 0 6px 16px rgba(15, 20, 91, 0.28);
}
.auth-mark--light {
  width: 30px; height: 30px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
}

/* ---- Hero pane (always navy; copy is factual product features) ----
   Signature: a sky-blue (KIT's secondary brand) glow + accents lift the
   navy from generic-corporate. Grid + glow sit behind content via a
   local stacking context (isolation), so no z-index juggling on copy. */
.auth-hero {
  display: none;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-10);
  padding: var(--space-12) clamp(var(--space-10), 5vw, var(--space-16));
  color: #fff;
  background:
    radial-gradient(115% 80% at 100% 0%, rgba(116, 192, 235, 0.14), transparent 55%),
    linear-gradient(155deg, var(--navy-700) 0%, var(--navy-900) 100%);
}
@media (min-width: 920px) { .auth-hero { display: flex; } }
/* Dot-grid texture, faded toward the top corner — depth without noise. */
.auth-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: radial-gradient(rgba(255, 255, 255, 0.10) 1px, transparent 1.4px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(120% 120% at 90% 6%, #000 20%, transparent 70%);
          mask-image: radial-gradient(120% 120% at 90% 6%, #000 20%, transparent 70%);
  pointer-events: none;
}
/* Soft sky glow orb, off the top-right corner. */
.auth-hero__glow {
  position: absolute;
  z-index: -1;
  top: -16rem; right: -12rem;
  width: 42rem; height: 42rem;
  background: radial-gradient(circle, rgba(116, 192, 235, 0.22), transparent 62%);
  pointer-events: none;
}
.auth-hero__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font: 600 var(--t-heading-sm)/1 var(--font-sans);
  letter-spacing: -0.01em;
}
.auth-hero__inner { max-width: 32rem; }
.auth-hero__headline {
  margin: 0 0 var(--space-4);
  font: 600 var(--t-display)/1.1 var(--font-sans);
  letter-spacing: -0.03em;
  /* Explicit — the global heading colour rule would otherwise paint this
     near-black on the navy hero. */
  color: #fff;
  text-wrap: balance;
}
.auth-hero__accent { color: var(--sky-300); }
.auth-hero__tagline {
  margin: 0;
  max-width: 30rem;
  font: var(--t-body-md)/1.65 var(--font-sans);
  color: rgba(255, 255, 255, 0.72);
}
.auth-hero__features {
  list-style: none;
  margin: var(--space-8) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
/* Frosted capability tiles — the hero's premium signature element. */
.auth-feature {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.09);
}
.auth-feature__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  border-radius: var(--radius-sm);
  background: rgba(116, 192, 235, 0.15);
  color: var(--sky-300);
}
.auth-feature__icon svg { width: 18px; height: 18px; }
.auth-feature__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.auth-feature__title { font: 600 var(--t-body)/1.3 var(--font-sans); color: #fff; }
.auth-feature__desc { font: var(--t-body-sm)/1.45 var(--font-sans); color: rgba(255, 255, 255, 0.6); }
.auth-hero__footer {
  margin: 0;
  font: var(--t-caption)/1 var(--font-sans);
  color: rgba(255, 255, 255, 0.5);
}

/* ---- Sign-in pane ---- */
.auth-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-6);
}
/* Inner column keeps the card + helper line centered and capped, so the
   card never drifts on ultra-wide screens. */
.auth-panel__inner {
  width: 100%;
  max-width: 26rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.auth-card {
  width: 100%;
  padding: var(--space-10) clamp(var(--space-6), 4vw, var(--space-10));
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  /* Layered shadow (not a lone hairline) defines every edge — survives
     sub-pixel rounding at fractional display scaling, so no edge "cuts off". */
  box-shadow: var(--shadow-xl);
}
.auth-card__mark { margin-bottom: var(--space-5); }
.auth-card__title {
  margin: 0 0 var(--space-1);
  font: 600 var(--t-heading-lg)/1.2 var(--font-sans);
  letter-spacing: -0.02em;
  color: var(--text-strong);
}
.auth-card__subtitle {
  margin: 0 0 var(--space-7);
  font: var(--t-body-sm)/1.5 var(--font-sans);
  color: var(--text-muted);
}
.auth-form .form-group { margin-bottom: var(--space-4); }
/* Roomier, more tactile fields than the app default (36px hairline). */
.auth-card input[type="email"],
.auth-card input[type="password"],
.auth-card input[type="text"] {
  height: 44px;
  max-width: none;
  border-radius: var(--radius-md);
  font-size: var(--t-body-md);
}
.auth-form__submit {
  width: 100%;
  height: 46px;
  margin-top: var(--space-2);
  justify-content: center;
  border-radius: var(--radius-md);
  font-size: var(--t-body-md);
  box-shadow: 0 6px 16px rgba(15, 20, 91, 0.18);
}
.auth-form__sso {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 46px;
  border-radius: var(--radius-md);
  font-size: var(--t-body-md);
  box-shadow: none;
}
.ms-logo__r1 { fill: #F25022; }
.ms-logo__r2 { fill: #7FBA00; }
.ms-logo__r3 { fill: #00A4EF; }
.ms-logo__r4 { fill: #FFB900; }

/* Helper line under the card — direction for anyone who can't get in. */
.auth-help {
  margin: 0;
  text-align: center;
  font: var(--t-body-sm)/1.5 var(--font-sans);
  color: var(--text-muted);
}

/* Validation summary — renders only when it carries an error. */
.auth-form__errors:not(:empty) {
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--error-bg);
  color: var(--error);
  font: var(--t-body-sm)/1.4 var(--font-sans);
  border: 1px solid color-mix(in srgb, var(--error) 24%, transparent);
}
.auth-form__errors ul { margin: 0; padding-left: var(--space-4); }

/* "Keep me signed in" — sits between the password field and the submit
   button. The caveat line is deliberately part of the control rather than
   fine print elsewhere: a persistent cookie on a shared machine is the one
   way this option can hurt someone. */
.auth-remember {
  margin-bottom: var(--space-4);
}
.auth-remember .label-checkbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font: var(--t-body-sm)/1.4 var(--font-sans);
  color: var(--text);
  cursor: pointer;
}
.auth-remember .label-checkbox input {
  width: auto;
  margin: 0;
  cursor: pointer;
  /* --text-accent, not --navy-700: the raw navy is near-black (#0F145B) and a
     checked box all but disappears against the dark surface. The role token
     lightens in dark mode. */
  accent-color: var(--text-accent);
}
.auth-remember small {
  display: block;
  margin-top: var(--space-1);
  /* Aligns the hint with the label text, clearing the checkbox gutter. */
  padding-left: calc(13px + var(--space-2));
  font: var(--t-caption)/1.4 var(--font-sans);
  color: var(--text-subtle);
}

/* Password field with a trailing reveal button. */
.auth-input-affix { position: relative; display: flex; }
.auth-input-affix input { width: 100%; padding-right: 44px; }
.auth-reveal {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 42px;
  height: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--text-subtle);
  cursor: pointer;
  border-radius: var(--radius-md);
  transition: color var(--duration-fast) var(--ease-standard);
}
.auth-reveal:hover { color: var(--text); background: transparent; }
.auth-reveal:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}
.auth-reveal svg { width: 18px; height: 18px; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  color: var(--text-subtle);
  font-size: var(--t-caption);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--divider);
}

/* Respect reduced-motion: the standalone page has no heavy motion, but
   keep transitions off for users who ask. */
@media (prefers-reduced-motion: reduce) {
  .auth-reveal { transition: none; }
}

/* ============================================================
   My work — summary stat strip. A row of metric cards above the
   task buckets (Open / Overdue / Due today / High priority),
   computed in the view from the rows already loaded. Auto-fits
   and wraps on narrow screens.
   ============================================================ */
/* The My-work tiles and the generic grid stat strip share one rule set.
   `.stat-strip` / `.stat-chip*` are the reusable names (projects/task lists);
   `.mywork-stat*` is the original consumer and keeps working unchanged. */
.mywork-stats, .stat-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.mywork-stat, .stat-chip {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
  text-decoration: none;
  color: inherit;
  transition: box-shadow var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
/* The tile's colour lives in the icon chip so the card body stays calm. */
.mywork-stat__icon, .stat-chip__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  background: var(--stat-accent-bg, var(--bg-sunken));
  color: var(--stat-accent, var(--text-muted));
}
.mywork-stat__icon svg, .stat-chip__icon svg { width: 20px; height: 20px; }
.mywork-stat__body, .stat-chip__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mywork-stat__value, .stat-chip__value {
  font: 600 var(--t-heading-md)/1 var(--font-sans);
  letter-spacing: -0.02em;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.mywork-stat__label, .stat-chip__label {
  font: 500 var(--t-caption)/1.1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
/* Per-metric accent — drives the icon chip's tint + glyph colour. */
.mywork-stat--open    { --stat-accent: var(--info);          --stat-accent-bg: var(--info-bg); }
.mywork-stat--overdue { --stat-accent: var(--error);         --stat-accent-bg: var(--error-bg); }
.mywork-stat--today   { --stat-accent: var(--warning);       --stat-accent-bg: var(--warning-bg); }
.mywork-stat--high    { --stat-accent: var(--accent-violet); --stat-accent-bg: var(--accent-violet-bg); }
.mywork-stat--done    { --stat-accent: var(--success);       --stat-accent-bg: var(--success-bg); }
/* Generic semantic variants for the shared .stat-chip (grids, lists). */
.stat-chip--info    { --stat-accent: var(--info);          --stat-accent-bg: var(--info-bg); }
.stat-chip--warning { --stat-accent: var(--warning);       --stat-accent-bg: var(--warning-bg); }
.stat-chip--error   { --stat-accent: var(--error);         --stat-accent-bg: var(--error-bg); }
.stat-chip--success { --stat-accent: var(--success);       --stat-accent-bg: var(--success-bg); }
.stat-chip--violet  { --stat-accent: var(--accent-violet); --stat-accent-bg: var(--accent-violet-bg); }
/* Deep-link tiles (Overdue / Due-today when non-empty) lift on hover;
   inert tiles rendered as <a> with no href don't. */
a.mywork-stat--link, a.stat-chip--link { cursor: pointer; }
a.mywork-stat--link:hover, a.stat-chip--link:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
a.mywork-stat--link:focus-visible, a.stat-chip--link:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }

/* Offset for deep-linked buckets so the heading clears the sticky top bar. */
.mywork-bucket { scroll-margin-top: var(--space-6); }

/* ============================================================
   My work — workload overview (open views). Replaces the flat
   4-tile strip with a triage-first summary: a proportional urgency
   bar (WHEN my work is due) sitting above status/priority tags
   (WHAT KIND of work it is). Dedicated `.mywork-overview*` /
   `.mywork-bar*` / `.mywork-legend*` / `.mywork-tag*` classes — no
   overlap with the shared `.mywork-stat` / `.stat-chip` ruleset
   above, so every other page is untouched. All colour comes from
   semantic role tokens, so light + dark both resolve with no
   theme-specific selectors.
   ============================================================ */
.mywork-overview {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}
.mywork-overview__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.mywork-overview__title { display: inline-flex; align-items: baseline; gap: var(--space-2); }
.mywork-overview__eyebrow {
  font: 600 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
}
.mywork-overview__total { font: 500 var(--t-body)/1 var(--font-sans); color: var(--text-muted); }
.mywork-overview__total strong {
  margin-right: 2px;
  font-size: var(--t-heading-md);
  letter-spacing: -0.02em;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
/* Actionable callout — verb + count, colour-coded by urgency. A link
   when there's somewhere to jump to; a calm green pill when all clear. */
.mywork-overview__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  text-decoration: none;
  background: var(--cta-bg, var(--bg-sunken));
  color: var(--cta-fg, var(--text-muted));
  transition: filter var(--duration-fast) var(--ease-standard);
}
a.mywork-overview__cta:hover { filter: brightness(0.96); }
a.mywork-overview__cta:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.mywork-overview__cta svg { width: 14px; height: 14px; }
.mywork-overview__cta--overdue { --cta-bg: var(--error-bg);   --cta-fg: var(--error); }
.mywork-overview__cta--today   { --cta-bg: var(--warning-bg); --cta-fg: var(--warning); }
.mywork-overview__cta--clear   { --cta-bg: var(--success-bg); --cta-fg: var(--success); }

/* Proportional urgency bar — each segment's flex-grow is set inline to
   its share of the open set. Purely visual (role=img + aria-label);
   the legend below carries the accessible jump links. */
.mywork-bar {
  display: flex;
  height: 10px;
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--bg-sunken);
}
.mywork-seg { display: block; min-width: 6px; }
.mywork-seg + .mywork-seg { box-shadow: inset 1px 0 0 var(--bg-surface); }
.mywork-seg--overdue  { background: var(--error); }
.mywork-seg--today    { background: var(--warning); }
.mywork-seg--upcoming { background: var(--info); }
.mywork-seg--nodate   { background: var(--text-subtle); }

/* Legend doubles as the bucket jump links — coloured dot + label + count. */
.mywork-legend { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); }
.mywork-legend__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-muted);
  text-decoration: none;
}
a.mywork-legend__item:hover { color: var(--text-strong); }
a.mywork-legend__item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
  border-radius: var(--radius-sm);
}
.mywork-legend__dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--dot, var(--text-subtle));
}
.mywork-legend__item--overdue  { --dot: var(--error); }
.mywork-legend__item--today    { --dot: var(--warning); }
.mywork-legend__item--upcoming { --dot: var(--info); }
.mywork-legend__item--nodate   { --dot: var(--text-subtle); }
.mywork-legend__count { font-weight: 600; color: var(--text-strong); font-variant-numeric: tabular-nums; }

/* Status / priority tags — the orthogonal facts the due-bar can't show. */
.mywork-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--divider);
}
/* Workspace scope filter — a compact select that narrows to one workspace;
   combines (AND) with the triage tags. */
.mywork-scope {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-right: var(--space-1);
}
.mywork-scope__label {
  font: 600 var(--t-caption)/1.2 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
}
.mywork-scope__select {
  -webkit-appearance: none;
  appearance: none;
  padding: 3px var(--space-5) 3px var(--space-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: 500 var(--t-caption)/1.2 var(--font-sans);
  cursor: pointer;
  /* Caret drawn inline so it themes correctly in light + dark. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%23667' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right var(--space-2) center;
}
.mywork-scope__select:hover { border-color: var(--text-subtle); }
.mywork-scope__select:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.mywork-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 3px var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font: 600 var(--t-caption)/1.2 var(--font-sans);
  background: var(--tag-bg, var(--bg-sunken));
  color: var(--tag-fg, var(--text-muted));
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  transition: filter var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}
.mywork-tag svg { width: 11px; height: 11px; }
.mywork-tag__n { font-variant-numeric: tabular-nums; }
.mywork-tag:hover { filter: brightness(0.96); }
.mywork-tag:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
/* Pressed = this filter is the active one: outline it in its own accent. */
.mywork-tag[aria-pressed="true"] { border-color: currentColor; }
.mywork-tag--high     { --tag-bg: var(--accent-violet-bg); --tag-fg: var(--accent-violet); }
.mywork-tag--blocked  { --tag-bg: var(--error-bg); --tag-fg: var(--error); }
.mywork-tag--progress { --tag-bg: var(--info-bg);  --tag-fg: var(--info); }
/* Clear-filter link — only shown (via [hidden] toggle) while a filter is on. */
.mywork-tags__clear {
  margin-left: var(--space-1);
  padding: 3px var(--space-1);
  border: 0;
  background: none;
  cursor: pointer;
  font: 500 var(--t-caption)/1.2 var(--font-sans);
  color: var(--text-muted);
  text-decoration: underline;
}
.mywork-tags__clear:hover { color: var(--text-strong); }
.mywork-tags__clear:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); border-radius: var(--radius-sm); }
/* A global flex rule overrides the UA [hidden] style on this button, so
   pin it: only show the Clear control while a filter is actually on. */
.mywork-tags__clear[hidden] { display: none; }

/* ------------------------------------------------------------
   Bucket card (shared by My work `.mywork-bucket` and Team tasks
   `.team-bucket`). Each bucket is ONE clean card: the inner
   `.table-scroll` and `table` would otherwise each add their own
   border + radius + shadow + margin, stacking three boxes and leaving
   an empty band at the card's foot. Neutralise those so the table sits
   flush inside the single card. Scoped to the two bucket classes, so
   tables elsewhere keep their standalone framing. Stated once for both,
   so the surfaces can't drift (Team tasks used to lack this and showed
   the nested-box + dead-band bug).
   ------------------------------------------------------------ */
.mywork-bucket,
.team-bucket { margin: 0; }
/* Tighter gap between bucket sections than the default .stack--loose
   (space-5), so more rows fit on screen. Scoped via :has to a stack
   that holds bucket cards. */
.stack--loose:has(> .mywork-bucket),
.stack--loose:has(> .team-bucket) { gap: var(--space-3); }
.mywork-bucket .card-header,
.team-bucket .card-header {
  padding: var(--space-2) var(--space-4) 0;
  margin-bottom: var(--space-1);
}
.mywork-bucket .card-title,
.team-bucket .card-title { display: inline-flex; align-items: center; gap: var(--space-2); }
.mywork-bucket .table-scroll,
.team-bucket .table-scroll {
  border: none;
  border-radius: 0;
  box-shadow: none;
  margin: 0;
  max-height: none;
}
.mywork-bucket table,
.team-bucket table {
  border: none;
  border-radius: 0;
  margin: 0;
}
/* Cross-bucket alignment. Each bucket is its own <table>, so auto layout
   sizes columns independently and they don't line up between Overdue /
   Today / Upcoming. Pin a fixed layout + identical column widths on the
   open views (same 8-column markup) so every bucket aligns; the Task
   column takes the remaining width. Completed view (different columns)
   keeps auto layout — it's a single table, nothing to align to. */
.mywork-bucket table[data-sort-persist-key="mywork-open"] {
  table-layout: fixed;
  width: 100%;
  /* Floor so the Task column never collapses when the fixed columns add
     up to more than the viewport — the bucket scrolls instead. */
  min-width: 1180px;
}
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(1) { width: 44px; }   /* complete */
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(2) { width: 34px; }   /* select */
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(3) { width: 98px; }   /* code */
/* th:nth-child(4) = Task — no width, absorbs the remainder */
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(5) { width: 184px; }  /* status (pill + arrow) */
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(6) { width: 120px; }  /* priority */
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(7) { width: 280px; }  /* project */
.mywork-bucket table[data-sort-persist-key="mywork-open"] th:nth-child(8) { width: 116px; }  /* due */
/* Truncate the cells that can overflow a fixed width (full text stays in
   the link's title / the cell itself); Task + Status wrap/flow as needed. */
.mywork-bucket table[data-sort-persist-key="mywork-open"] td:nth-child(3),
.mywork-bucket table[data-sort-persist-key="mywork-open"] td:nth-child(7) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Count chip beside a bucket title; tints red on an overdue bucket.
   Shared by My work (.mywork-bucket__count / .mywork-bucket--overdue) and
   Team tasks (the feature-neutral .bucket-count / .bucket-overdue) — one
   rule, so neither feature owns the other's class name. */
.mywork-bucket__count,
.bucket-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-full);
  background: var(--bg-sunken);
  color: var(--text-muted);
  font: 600 var(--t-caption)/1 var(--font-sans);
  font-variant-numeric: tabular-nums;
}
.mywork-bucket--overdue .mywork-bucket__count,
.bucket-overdue .bucket-count { background: var(--error-bg); color: var(--error); }

/* ------------------------------------------------------------
   Team tasks — the manager roster. Builds on the shared bucket-card
   neutralisation above; adds the identity header (avatar + name) and a
   fixed, aligned table so long project names truncate instead of
   forcing a horizontal scrollbar.
   ------------------------------------------------------------ */
/* Header: avatar (roster view only) + name + count + sub, grouped on the
   left. Override the base .card-header space-between so the identity reads as
   one unit instead of splitting to the card's edges. */
.team-bucket__head { align-items: center; justify-content: flex-start; gap: var(--space-2); }
.team-bucket__name { font-weight: 600; color: var(--text-strong); }
/* Sub-label sits after the count as quiet detail; turns alert-red when the
   bucket carries overdue work (the name itself stays calm). */
.team-bucket__sub { font-weight: 400; font-size: var(--t-caption); color: var(--text-muted); }
.team-bucket__sub--alert { color: var(--error); }
/* On the roster view the count chip means "open", not "overdue", so keep it
   neutral even when the person has overdue work — the red sub-label carries
   that signal. Non-roster overdue buckets (e.g. the "Overdue" due bucket,
   where the count IS the overdue count) keep the red chip from the rule above. */
.team-buckets--roster .bucket-overdue .bucket-count {
  background: var(--bg-sunken);
  color: var(--text-muted);
}
/* Initial-avatar disc — the roster's signature. Navy-tinted so a manager can
   scan down the left edge and find a person by their mark. */
.team-avatar {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background: var(--navy-50);
  color: var(--navy-700);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  text-transform: uppercase;
  user-select: none;
}
.team-avatar--none { background: var(--bg-sunken); color: var(--text-subtle); }

/* Fixed, aligned layout: columns line up across buckets and long free-text
   cells (Task, Project) ellipsis-truncate instead of blowing the table wide.
   min-width floor lets it scroll only on genuinely narrow viewports. */
.team-bucket table {
  table-layout: fixed;
  width: 100%;
  /* Floor clears the widest column set (the 7-column due/status views plus
     the trailing timer action: code+assignee+status+prio+project+date+timer
     ≈ 796px) and still leaves the Task column ~160px, so Task never
     collapses; narrower viewports scroll. */
  min-width: 960px;
}
.team-bucket th.tt-col-code     { width: 84px; }
.team-bucket th.tt-col-assignee { width: 132px; }
.team-bucket th.tt-col-status   { width: 132px; }
.team-bucket th.tt-col-prio     { width: 96px; }
.team-bucket th.tt-col-date     { width: 108px; }
.team-bucket th.tt-col-timer    { width: 44px; }
/* Task takes a bounded share rather than being greedy: an unbounded Task
   column stretched to ~1200px on a wide monitor, leaving a big empty gap
   between a short title and the Project column. A percentage keeps it a
   comfortable reading width (long titles ellipsis-truncate with a hover
   tooltip) — a fixed-layout table honours %/px but IGNORES clamp() on a
   column, so don't reach for clamp here. Project has no width and absorbs the
   leftover space, so it sits right after Task and shows the full project ·
   workspace text instead of truncating at a fixed 200px. */
.team-bucket th.tt-col-task     { width: 34%; }
/* th.tt-col-project has no width — it now absorbs the remaining space. */
/* Timer action cell — centre the ▶/⏸ button in its narrow column. */
.team-bucket .tt-timer-cell { text-align: center; padding-left: 0; padding-right: var(--space-2); }

/* Filter bar: controls left, KPI strip right, spanning the card's full width
   (it's a <div>, so the global form max-width cap doesn't apply). Wraps to
   stack the KPIs under the controls on narrow screens. */
.team-filterbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
}
.team-filter-controls { flex: 1 1 460px; min-width: 0; }
/* Team-at-a-glance KPI strip — the right-hand summary that fills the bar. */
.team-kpis {
  display: flex;
  align-items: stretch;
  gap: var(--space-6);
  margin: 0;
  flex: 0 0 auto;
}
.team-kpi { display: flex; flex-direction: column; gap: 2px; text-align: right; }
.team-kpi__value {
  font: 600 var(--t-heading-md)/1.1 var(--font-sans);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.team-kpi__label {
  font: 600 var(--t-caption)/1 var(--font-sans);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Overdue tile turns alert-red when the team has any past-due work. */
.team-kpi--alert .team-kpi__value { color: var(--error); }

@media (max-width: 640px) {
  /* Stack the KPIs under the controls and left-align them on small screens. */
  .team-kpis { flex-basis: 100%; justify-content: flex-start; }
  .team-kpi { text-align: left; }
}
.team-bucket .task-title-cell,
.team-bucket .tt-project-cell {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* "Starts …" hint — marks a task whose start date is still in the future
   (not yet in flight). Sits after the title, quiet by design. */
.mywork-start-hint {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-2);
  padding: 1px var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  color: var(--text-subtle);
  font: 600 var(--t-caption)/1.3 var(--font-sans);
  white-space: nowrap;
  vertical-align: middle;
}

/* Floating bulk-action bar — appears (via [hidden] toggle) when rows are
   selected. Pinned bottom-center; actions fan out per task client-side. */
.mywork-bulkbar {
  position: fixed;
  left: 50%;
  bottom: var(--space-5);
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2);
  /* One line. Cap to the viewport and let the bar scroll horizontally on
     screens too narrow to hold every control, rather than wrapping. */
  max-width: calc(100vw - var(--space-5));
  overflow-x: auto;
  padding: var(--space-2) var(--space-3);
  /* Premium "selection bar": a deep, desaturated frosted surface (a hint
     of the accent mixed into near-black navy) rather than a loud flat
     accent fill. The accent itself is spent in ONE place — the primary
     Complete pill below. Frosted blur + a soft navy lift sell the depth. */
  background: color-mix(in srgb,
              color-mix(in srgb, var(--accent, var(--navy-700)) 16%, var(--navy-900)) 85%,
              transparent);
  -webkit-backdrop-filter: blur(12px) saturate(135%);
  backdrop-filter: blur(12px) saturate(135%);
  border: 1px solid color-mix(in srgb, var(--text-inverse) 12%, transparent);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px -12px color-mix(in srgb, var(--navy-900) 65%, transparent),
              0 2px 6px color-mix(in srgb, var(--navy-900) 35%, transparent);
  color: var(--text-inverse);
}
.mywork-bulkbar[hidden] { display: none; }
.mywork-bulkbar__count {
  padding: 0 var(--space-1);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--text-inverse) 72%, transparent);
  white-space: nowrap;
}
.mywork-bulkbar__sep { width: 1px; height: 18px; align-self: center; background: color-mix(in srgb, var(--text-inverse) 16%, transparent); }
.mywork-bulkbar__group { display: inline-flex; align-items: center; gap: var(--space-1); }
/* Secondary actions stay quiet — barely-there glass that lifts on hover. */
.mywork-bulkbar__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--text-inverse) 9%, transparent);
  color: color-mix(in srgb, var(--text-inverse) 92%, transparent);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.mywork-bulkbar__btn:hover { background: color-mix(in srgb, var(--text-inverse) 18%, transparent); }
.mywork-bulkbar__btn svg { color: currentColor; }
/* The one bold thing: Complete carries the accent as a solid pill. */
.mywork-bulkbar__btn[data-bulk-complete] {
  background: var(--accent, var(--navy-700));
  color: var(--text-inverse);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--navy-900) 45%, transparent);
}
.mywork-bulkbar__btn[data-bulk-complete]:hover { background: color-mix(in srgb, var(--accent, var(--navy-700)) 88%, white); }
.mywork-bulkbar__btn[data-bulk-complete] svg { color: var(--text-inverse); }
/* Inputs become dark glass (was bright white, which read as "busy"). */
.mywork-bulkbar__date,
.mywork-bulkbar__select {
  padding: var(--space-1) var(--space-2);
  border: 1px solid color-mix(in srgb, var(--text-inverse) 16%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--text-inverse) 8%, transparent);
  color: var(--text-inverse);
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  cursor: pointer;
}
.mywork-bulkbar__date { color-scheme: dark; }
.mywork-bulkbar__clear {
  padding: var(--space-1) var(--space-2);
  border: 0;
  background: none;
  font: 500 var(--t-body-sm)/1 var(--font-sans);
  color: color-mix(in srgb, var(--text-inverse) 85%, transparent);
  text-decoration: underline;
  cursor: pointer;
}
.mywork-bulkbar__clear:hover { color: var(--text-inverse); }
/* Keep every control its natural size on one line; the bar scrolls
   rather than shrinking controls when space is tight. */
.mywork-bulkbar > *,
.mywork-bulkbar__group { flex: 0 0 auto; }
/* Enhanced (lookup-select) status/priority controls stay compact in the bar. */
.mywork-bulkbar__statushost { display: inline-flex; }
.mywork-bulkbar .lookup-select { width: 150px; margin: 0; }
.mywork-bulkbar .lookup-select__field { min-height: 0; }
/* Dark-glass combobox to match the bar (was bright white). */
.mywork-bulkbar .lookup-select__input {
  background: color-mix(in srgb, var(--text-inverse) 8%, transparent);
  border-color: color-mix(in srgb, var(--text-inverse) 16%, transparent);
  color: var(--text-inverse);
}
.mywork-bulkbar .lookup-select__input::placeholder { color: color-mix(in srgb, var(--text-inverse) 55%, transparent); }
.mywork-bulkbar .lookup-select__clear { color: color-mix(in srgb, var(--text-inverse) 60%, transparent); }
.mywork-bulkbar .lookup-select__clear:hover { background: color-mix(in srgb, var(--text-inverse) 16%, transparent); color: var(--text-inverse); }
.mywork-bulkbar__btn:focus-visible,
.mywork-bulkbar__date:focus-visible,
.mywork-bulkbar__select:focus-visible,
.mywork-bulkbar__clear:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.mywork-bulkbar__btn:disabled,
.mywork-bulkbar__date:disabled,
.mywork-bulkbar__select:disabled,
.mywork-bulkbar__clear:disabled { opacity: 0.5; cursor: default; }
/* Selected-row highlight. */
.mywork-bucket tr.is-bulk-selected > td { background: var(--bg-selected); }

/* Status cell — pill + quick-advance arrow. A bare, always-visible arrow
   glyph (no box) sits after the pill so it's discoverable but quiet; it
   gains a soft circular hover + accent colour on interaction. Tooltip
   "Advance to next status". */
.mywork-status-cell { display: inline-flex; align-items: center; gap: var(--space-0_5); }
.mywork-advance {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-subtle);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.mywork-advance svg { flex: 0 0 auto; width: 16px; height: 16px; }
.mywork-bucket tr:hover .mywork-advance { color: var(--text-muted); }
.mywork-advance:hover { background: var(--bg-hover); color: var(--text-accent); }
.mywork-advance:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); color: var(--text-accent); }
.mywork-advance:disabled { opacity: 0.35; cursor: default; }

/* Confirm-completion dialog — shown before closing a task (opt-in via
   data-confirm-done) so an accidental checkbox click can't close work.
   Native <dialog>: focus-trap + Esc handling come for free. */
.confirm-done-dialog {
  width: calc(100vw - var(--space-8));
  max-width: 420px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: var(--shadow-xl);
}
.confirm-done-dialog::backdrop { background: color-mix(in srgb, var(--navy-900) 50%, transparent); }
.confirm-done { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); }
.confirm-done__title {
  margin: 0;
  font: 600 var(--t-heading-sm)/1.3 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-strong);
}
.confirm-done__body { margin: 0; font: 400 var(--t-body-sm)/1.5 var(--font-sans); color: var(--text-muted); }
.confirm-done__notes {
  width: 100%;
  min-height: 72px;
  resize: vertical;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
  font: 400 var(--t-body-sm)/1.5 var(--font-sans);
}
.confirm-done__notes:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); border-color: var(--focus-ring); }
.confirm-done__actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-1); }

/* Move-project dialog — mirrors .confirm-done-dialog framing so the card
   reads as a real modal (dim backdrop + defined edge + shadow), not a
   floating white box. Token-only; replaced the old inline-styled card
   that had no ::backdrop and collapsed to mostly-empty whitespace. */
.move-modal {
  width: calc(100vw - var(--space-8));
  max-width: 440px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: var(--shadow-xl);
}
.move-modal::backdrop { background: color-mix(in srgb, var(--navy-900) 50%, transparent); }
.move-modal__body { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-5); }
.move-modal__head { display: flex; align-items: flex-start; gap: var(--space-3); }
.move-modal__icon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent);
}
.move-modal__title {
  margin: 0;
  font: 600 var(--t-heading-sm)/1.3 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-strong);
}
.move-modal__sub { margin: var(--space-1) 0 0; font: 400 var(--t-body-sm)/1.5 var(--font-sans); color: var(--text-muted); }
.move-modal__field { display: flex; flex-direction: column; gap: var(--space-1); }
.move-modal__label {
  font: 500 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-muted);
}
.move-modal__preview {
  font: 400 var(--t-body-sm)/1.5 var(--font-sans);
  padding: var(--space-3);
  background: var(--bg-sunken);
  border-radius: var(--radius-sm);
}
.move-modal__error { margin: 0; font: 400 var(--t-caption)/1.4 var(--font-sans); color: var(--error); }
.move-modal__foot { display: flex; justify-content: flex-end; align-items: center; gap: var(--space-2); margin-top: var(--space-1); }
.move-modal__form { margin: 0; }

/* Sortable-header affordance — show a faint ↕ on every sortable column
   so the user knows it's clickable BEFORE clicking; it darkens on hover
   and resolves to ↑/↓ once that column is the active sort. Scoped to
   .mywork-bucket (the attribute-qualified selectors out-specify the
   global .th-sortable caret rules, so My Work wins cleanly). */
.mywork-bucket .th-sortable { position: relative; }
.mywork-bucket .th-sortable[aria-sort="none"]::after {
  content: " ↕";
  color: var(--text-subtle);
  opacity: 0.5;
  transition: opacity var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.mywork-bucket .th-sortable[aria-sort="none"]:hover::after { opacity: 1; color: var(--text-muted); }
.mywork-bucket .th-sortable[aria-sort="ascending"]::after  { content: " ↑"; color: var(--text); opacity: 1; }
.mywork-bucket .th-sortable[aria-sort="descending"]::after { content: " ↓"; color: var(--text); opacity: 1; }

/* ============================================================
   TIMER PAGE (Views/Timer/Status.cshtml). The running timer is the
   hero — it reuses the `.now-timing*` pattern (generalised above to
   `:is(.home-dashboard, .timer-page)`) so the page matches the Home
   "Now timing" card: clock + identity lead, full-width "What did you
   work on?" notes row. Around it: a held-total stat in the header, an
   overrun warning banner, and a scannable parked-timer switcher.
   Token-only colour → light + dark resolve with no theme selectors.
   ============================================================ */
.timer-page { display: flex; flex-direction: column; gap: var(--space-4); }

/* Held-total stat under the page subtitle (the ticking session figure
   the old bar used to show, demoted to a quiet line). */
.timer-headstat {
  margin: var(--space-2) 0 0;
  font: 400 var(--t-body-sm)/1.3 var(--font-sans);
  color: var(--text-muted);
}
.timer-headstat strong {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Overrun banner — the one genuinely useful at-a-glance alert: a timer
   left running/parked past App:TimerIdleHours. Warning tint + left rule
   + a Stop affordance. Shown only when such a timer exists. */
.timer-alert {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--warning-bg);
  border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent);
  border-left: 3px solid var(--warning);
  border-radius: var(--radius-md);
  color: var(--text);
}
.timer-alert > svg { flex: 0 0 auto; width: 20px; height: 20px; color: var(--warning); }
.timer-alert__text { flex: 1 1 auto; min-width: 0; font: 400 var(--t-body-sm)/1.4 var(--font-sans); }
.timer-alert__text strong { color: var(--text-strong); font-weight: 600; }
.timer-alert form { flex: 0 0 auto; }

/* Show the precise start time inline in the hero meta line. */
.now-timing__started { color: var(--text-subtle); }

/* Task code+title opens the detail drawer (data-open-detail) so the task can
   be updated without leaving the timer — read as clickable on hover/focus. */
.now-timing__task-head[data-open-detail]:hover .now-timing__task-title,
.timer-parked__head[data-open-detail]:hover .timer-parked__title {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.now-timing__task-head[data-open-detail]:focus-visible,
.timer-parked__head[data-open-detail]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* ---- Parked-timer switcher --------------------------------------
   A list of paused timers inside one flush card. Resume is the
   prominent action (it promotes the timer into the hero, where it can
   be annotated + stopped properly); Stop is a quiet ghost. */
.timer-parked { overflow: hidden; }
.timer-parked__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  transition: background var(--duration-fast) var(--ease-standard);
}
.timer-parked__row + .timer-parked__row { border-top: 1px solid var(--divider); }
.timer-parked__row:hover { background: var(--bg-hover); }
.timer-parked__row--alert { background: var(--warning-bg); }
.timer-parked__row--alert:hover { background: color-mix(in srgb, var(--warning) 14%, var(--bg-surface)); }
.timer-parked__dot {
  flex: 0 0 auto;
  width: 8px; height: 8px;
  border-radius: var(--radius-full);
  background: var(--grey-400);
}
.timer-parked__dot--alert { background: var(--warning); }
.timer-parked__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-0_5); }
.timer-parked__head { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
.timer-parked__title {
  font: 600 var(--t-body)/1.3 var(--font-sans);
  color: var(--text-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.timer-parked__meta { font: 400 var(--t-caption)/1.4 var(--font-sans); color: var(--text-muted); }
.timer-parked__time {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-size: var(--t-body-sm);
  color: var(--text-muted);
}

@media (max-width: 640px) {
  /* Parked rows: let the meta wrap under, keep Resume a full tap target,
     and allow the time to drop to its own line rather than squashing. */
  .timer-parked__row { flex-wrap: wrap; row-gap: var(--space-1); }
  .timer-parked__main { flex-basis: 60%; }
  .timer-alert { flex-wrap: wrap; }
}

/* ============================================================
   Manager / Team dashboard (/manager) — decision surface.
   Two heroes (risk triage + workload) above a project-health
   rollup, then the money/flow charts. Dedicated `.mgr-*` names
   so no shared rule is touched; all colour from role tokens, so
   light + dark resolve without theme-specific selectors. The risk
   segmented control is CSS-only (hidden radios + :has()).
   ============================================================ */
.mgr-card { padding: var(--space-4); }
.mgr-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.mgr-hero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
  margin-bottom: var(--space-5);
}
.mgr-health { margin-bottom: var(--space-5); }
/* KPI chips deep-link to these; clear the sticky top bar on jump. */
#mgr-risk, #mgr-health { scroll-margin-top: var(--space-6); }

/* ── Risk triage — CSS-only segmented queue ── */
.mgr-seg__radio { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.mgr-seg {
  display: flex;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
  padding: 3px;
  background: var(--bg-sunken);
  border-radius: var(--radius-md);
}
.mgr-seg__btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  font: 600 var(--t-body-sm)/1 var(--font-sans);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.mgr-seg__count {
  font-variant-numeric: tabular-nums;
  font-size: var(--t-caption);
  padding: 0 var(--space-1);
  border-radius: var(--radius-full);
  background: var(--bg-raised);
  color: var(--text-muted);
}
#mgr-risk:has(#mgr-seg-overdue:checked)    .mgr-seg__btn--overdue,
#mgr-risk:has(#mgr-seg-due-soon:checked)   .mgr-seg__btn--due-soon,
#mgr-risk:has(#mgr-seg-unassigned:checked) .mgr-seg__btn--unassigned {
  background: var(--bg-surface);
  color: var(--text-strong);
  box-shadow: var(--shadow-xs);
}
#mgr-risk:has(#mgr-seg-overdue:focus-visible)    .mgr-seg__btn--overdue,
#mgr-risk:has(#mgr-seg-due-soon:focus-visible)   .mgr-seg__btn--due-soon,
#mgr-risk:has(#mgr-seg-unassigned:focus-visible) .mgr-seg__btn--unassigned {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring);
}
.mgr-seg-panel { display: none; }
#mgr-risk:has(#mgr-seg-overdue:checked)    .mgr-seg-panel--overdue,
#mgr-risk:has(#mgr-seg-due-soon:checked)   .mgr-seg-panel--due-soon,
#mgr-risk:has(#mgr-seg-unassigned:checked) .mgr-seg-panel--unassigned { display: block; }

.mgr-risk-list { display: flex; flex-direction: column; }
.mgr-risk-row {
  display: grid;
  grid-template-columns: 3px 1fr auto;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-2) 0;
  border-top: 1px solid var(--divider);
}
.mgr-risk-row:first-child { border-top: 0; }
.mgr-risk-row__rail { align-self: stretch; border-radius: var(--radius-full); background: var(--grey-300); }
.mgr-risk-row--overdue    .mgr-risk-row__rail { background: var(--error); }
.mgr-risk-row--due-soon   .mgr-risk-row__rail { background: var(--warning); }
.mgr-risk-row--unassigned .mgr-risk-row__rail { background: var(--grey-400); }
.mgr-risk-row__main { min-width: 0; }
.mgr-risk-row__title { display: block; font-size: var(--t-body-sm); }
.mgr-risk-row__title:hover { color: var(--text-link); }
.mgr-risk-row__meta { margin-top: 1px; }
.mgr-due-badge {
  white-space: nowrap;
  font: 600 var(--t-caption)/1 var(--font-sans);
  padding: 3px var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  color: var(--text-muted);
}
.mgr-due-badge--over { background: var(--error-bg);   color: var(--error); }
.mgr-due-badge--soon { background: var(--warning-bg); color: var(--warning); }
.mgr-more { margin-top: var(--space-2); }

/* ── Team workload bars — logged (billable/non-billable) vs capacity ── */
.mgr-wl-list { display: flex; flex-direction: column; gap: var(--space-3); }
.mgr-wl-row__link { display: block; padding: var(--space-1) 0; }
.mgr-wl-row__head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-1); }
.mgr-wl-row__avatar { flex: 0 0 auto; width: 24px; height: 24px; font-size: var(--t-caption); }
.mgr-wl-row__name { font-weight: 500; font-size: var(--t-body-sm); color: var(--text); }
.mgr-wl-row__link:hover .mgr-wl-row__name { color: var(--text-link); }
.mgr-wl-row__util { margin-left: auto; font-weight: 600; font-size: var(--t-body-sm); color: var(--text); font-variant-numeric: tabular-nums; }
.mgr-wl-row.is-over .mgr-wl-row__util { color: var(--error); }
.mgr-wl-bar {
  position: relative;
  display: flex;
  align-items: stretch;
  height: 12px;
  border-radius: var(--radius-full);
  background: var(--bg-sunken);
  overflow: hidden;
}
.mgr-wl-bar__seg { height: 100%; }
.mgr-wl-bar__seg--bill    { width: var(--bill, 0); background: var(--info); }
.mgr-wl-bar__seg--nonbill { width: var(--nonbill, 0); background: var(--grey-300); }
.mgr-wl-bar__cap {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--cap, 100%);
  width: 2px;
  background: var(--text-strong);
  opacity: 0.5;
}
.mgr-wl-row.is-over .mgr-wl-bar__cap { background: var(--error); opacity: 0.9; }
.mgr-wl-row__foot { display: flex; margin-top: 2px; }
.mgr-wl-note { margin-top: var(--space-3); }

/* ── Project health rollup table ── */
.mgr-table-scroll { overflow-x: auto; }
.mgr-health-table { width: 100%; border-collapse: collapse; font-size: var(--t-body-sm); }
.mgr-health-table th {
  text-align: left;
  font: 600 var(--t-caption)/1.2 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: var(--space-2);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.mgr-health-table td {
  padding: var(--space-2);
  border-bottom: 1px solid var(--divider);
  vertical-align: middle;
}
.mgr-health-table tr:hover td { background: var(--bg-hover); }
.mgr-health-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.mgr-health-progress { min-width: 160px; }
.mgr-progress { height: 6px; border-radius: var(--radius-full); background: var(--bg-sunken); overflow: hidden; margin-bottom: 2px; }
.mgr-progress__fill { display: block; height: 100%; background: var(--success); }
.mgr-health-dot { display: inline-block; width: 8px; height: 8px; border-radius: var(--radius-full); margin-right: var(--space-1); vertical-align: middle; }
.mgr-health-dot--red   { background: var(--error); }
.mgr-health-dot--amber { background: var(--warning); }
.mgr-health-dot--green { background: var(--success); }

@media (max-width: 1000px) {
  .mgr-hero-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   Configuration hub (/config) + inside pages.
   A count-aware grouped directory on the hub; a consistent
   breadcrumb + polished table / forms / import on the inside
   pages. Dedicated `.config-*` names, role tokens only, so light
   + dark resolve without theme-specific selectors.
   ============================================================ */
.config-intro { margin-bottom: var(--space-5); }
.config-intro__lead {
  margin: var(--space-1) 0 0;
  max-width: 60ch;
  font-size: var(--t-body-md);
  color: var(--text-muted);
}

.config-group { margin-bottom: var(--space-6); }
.config-group__label {
  margin: 0 0 var(--space-3);
  font: 600 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
}
.config-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: var(--space-4);
}

.config-card {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  text-decoration: none;
  color: inherit;
  transition: box-shadow var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard);
}
.config-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.config-card:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.config-card__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: var(--stat-accent-bg, var(--bg-sunken));
  color: var(--stat-accent, var(--text-muted));
}
.config-card__icon svg { width: 22px; height: 22px; }
.config-card__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.config-card__title {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font: 600 var(--t-heading-sm)/1.2 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-strong);
}
.config-card__chevron {
  width: 16px;
  height: 16px;
  color: var(--text-subtle);
  opacity: 0;
  transform: translateX(-2px);
  transition: opacity var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard);
}
.config-card:hover .config-card__chevron { opacity: 1; transform: translateX(0); }
.config-card__desc { font-size: var(--t-body-sm); color: var(--text-muted); }
.config-card__count {
  margin-top: var(--space-1);
  font: 600 var(--t-caption)/1 var(--font-sans);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.config-card__count.is-empty { color: var(--text-subtle); font-weight: 500; }
.config-card--info    { --stat-accent: var(--info);          --stat-accent-bg: var(--info-bg); }
.config-card--violet  { --stat-accent: var(--accent-violet); --stat-accent-bg: var(--accent-violet-bg); }
.config-card--success { --stat-accent: var(--success);       --stat-accent-bg: var(--success-bg); }
.config-card--warning { --stat-accent: var(--warning);       --stat-accent-bg: var(--warning-bg); }

/* Inside-page list subtitle hint (sits under the breadcrumb toolbar). */
.config-list-hint {
  margin: calc(-1 * var(--space-1)) 0 var(--space-4);
  font-size: var(--t-body-sm);
  color: var(--text-muted);
}

/* Polished reference-list table. */
.config-table-wrap {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-surface);
}
/* The wrapper owns the border + rounded corners; reset the base `table`
   border/radius/margin so it sits flush inside (no double hairline). */
.config-table { width: 100%; border-collapse: collapse; margin: 0; border: 0; border-radius: 0; background: transparent; }
.config-table thead th {
  background: var(--bg-sunken);
  font: 600 var(--t-caption)/1.2 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
}
.config-table tbody td {
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--divider);
  vertical-align: middle;
}
.config-table tbody tr:first-child td { border-top: 0; }
.config-table tbody tr:hover td { background: var(--bg-hover); }
.config-table td code {
  font-size: var(--t-body-sm);
  color: var(--text-muted);
  background: var(--bg-sunken);
  padding: 1px var(--space-1);
  border-radius: var(--radius-sm);
}
.config-table .row-actions-cell { text-align: right; white-space: nowrap; }

/* Forms — two-column code/name on wide, calmer checkbox rows. */
.config-form { max-width: 640px; }
.config-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--space-4); }
.config-form .req { color: var(--error); font-weight: 600; }
.config-form .form-group > label:has(> input[type="checkbox"]) {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  font-weight: 500;
}
@media (max-width: 560px) {
  .config-form-grid { grid-template-columns: 1fr; }
}

/* Import — dropzone + summary tiles. */
.config-dropzone {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-sunken);
}
.config-dropzone__icon { flex: 0 0 auto; width: 28px; height: 28px; color: var(--text-subtle); }
.config-import-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--space-3);
  margin: var(--space-3) 0;
}
.config-import-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-left: 3px solid var(--stat-accent, var(--border-strong));
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}
.config-import-stat__n { font: 700 var(--t-heading-md)/1 var(--font-sans); color: var(--text-strong); }
.config-import-stat__l {
  font: 500 var(--t-caption)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.config-import-stat--in   { --stat-accent: var(--success); }
.config-import-stat--up   { --stat-accent: var(--info); }
.config-import-stat--skip { --stat-accent: var(--grey-400); }
.config-import-stat--err  { --stat-accent: var(--error); }
