/* ─────────────────────────────────────────────────────────────
   BePositive Design System — ported from the React app
   (frontend/src/styles/bp-tokens.css + bp-crm.css + dark overrides
    from globals.css). Server-rendered via AssetMapper, no build step.
   Light = :root, dark = html.dark (toggled by theme_controller.js).
   ───────────────────────────────────────────────────────────── */

@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');

:root {
  /* ── Brand colors ──────────────────────────────────────── */
  --bp-green:        #23D887;
  --bp-green-600:    #1FC079;
  --bp-green-700:    #19A468;
  --bp-green-50:     #E8FBF3;

  --bp-blue:         #3898EC;
  --bp-blue-600:     #2A85D6;
  --bp-blue-50:      #E6F2FD;

  --bp-amber:        #F5A623;
  --bp-amber-50:     #FEF3DF;

  --bp-red:          #E5484D;
  --bp-red-50:       #FDECEC;

  /* ── Neutral palette ───────────────────────────────────── */
  --bp-fg:           #151515;
  --bp-fg-2:         #2A2A2E;
  --bp-fg-muted:     #5D5F63;
  --bp-fg-subtle:    #8A8C92;

  --bp-bg:           #FAFAFD;
  --bp-surface:      #FFFFFF;
  --bp-muted:        #F6F6FB;
  --bp-muted-2:      #EFEFF6;

  --bp-border:       #CECDDD;
  --bp-border-soft:  #E5E5EE;

  /* ── Semantic aliases ──────────────────────────────────── */
  --fg-1:            var(--bp-fg);
  --fg-2:            var(--bp-fg-2);
  --fg-muted:        var(--bp-fg-muted);
  --link:            var(--bp-blue);
  --accent:          var(--bp-green);

  /* Status */
  --status-todo-fg:     #4A4C52;  --status-todo-bg:     #ECECF3;
  --status-progress-fg: #1A6FBD;  --status-progress-bg: #E6F2FD;
  --status-review-fg:   #8A5A0B;  --status-review-bg:   #FEF3DF;
  --status-done-fg:     #0E7A4A;  --status-done-bg:     #E8FBF3;
  --status-blocked-fg:  #B23035;  --status-blocked-bg:  #FDECEC;

  /* ── Typography ────────────────────────────────────────── */
  --font-display: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-body:    'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono:    ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --t-display-1: 700 40px/1.15 var(--font-display);
  --t-display-2: 700 32px/1.2  var(--font-display);
  --t-h1:        700 24px/1.25 var(--font-display);
  --t-h2:        700 20px/1.3  var(--font-display);
  --t-h3:        600 16px/1.35 var(--font-display);
  --t-body:      400 14px/1.5  var(--font-body);
  --t-body-strong: 500 14px/1.5 var(--font-body);
  --t-caption:   500 12px/1.4  var(--font-body);
  --t-overline:  600 11px/1.3  var(--font-body);
  --t-mono:      400 13px/1.45 var(--font-mono);

  --tracking-display: -0.02em;
  --tracking-body:    0em;
  --tracking-caption: 0.01em;
  --tracking-overline: 0.08em;

  /* ── Spacing ───────────────────────────────────────────── */
  --space-1: 4px;   --space-2: 8px;  --space-3: 12px;
  --space-4: 16px;  --space-5: 24px; --space-6: 32px;
  --space-7: 48px;  --space-8: 64px;

  /* ── Radii ─────────────────────────────────────────────── */
  --radius-input: 6px;   --radius-card: 8px;
  --radius-pill:  36px;  --radius-sm:   4px; --radius-lg: 12px;

  /* ── Shadows ───────────────────────────────────────────── */
  --shadow-1: 0 1px 2px rgba(21,21,21,0.04), 0 0 0 1px rgba(21,21,21,0.04);
  --shadow-2: 0 2px 6px rgba(21,21,21,0.06), 0 0 0 1px rgba(21,21,21,0.05);
  --shadow-3: 0 8px 24px rgba(21,21,21,0.08), 0 0 0 1px rgba(21,21,21,0.05);
  --shadow-focus: 0 0 0 3px rgba(35,216,135,0.28);

  /* ── Layout ────────────────────────────────────────────── */
  --appbar-h:   56px;
  --sidebar-w: 220px;
  --container-max: 1280px;

  /* ── Motion ────────────────────────────────────────────── */
  --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --dur-fast: 120ms;
  --dur-med:  200ms;

  --appbar-bg: rgba(255,255,255,0.88);
}

/* ── Dark mode overrides ─────────────────────────────────── */
html.dark {
  --bp-fg:          #ECECF0;
  --bp-fg-2:        #D4D4DC;
  --bp-fg-muted:    #868692;
  --bp-fg-subtle:   #5C5C68;
  --bp-bg:          #0E0E12;
  --bp-surface:     #161619;
  --bp-muted:       #1D1D23;
  --bp-muted-2:     #222228;
  --bp-border:      #3A3A46;
  --bp-border-soft: #2A2A34;

  --fg-1:           #ECECF0;
  --fg-2:           #D4D4DC;
  --fg-muted:       #868692;
  --bp-blue:        #5AABFF;
  --bp-blue-50:     #132040;
  --bp-green-50:    #0D2118;
  --bp-amber-50:    #291D0C;
  --bp-red-50:      #260C0D;

  /* Status (dark) */
  --status-todo-fg:     #9898A8;  --status-todo-bg:     #242430;
  --status-progress-fg: #5AABFF;  --status-progress-bg: #132040;
  --status-review-fg:   #F5A623;  --status-review-bg:   #291D0C;
  --status-done-fg:     #23D887;  --status-done-bg:     #0D2118;
  --status-blocked-fg:  #F26A6E;  --status-blocked-bg:  #260C0D;

  --appbar-bg: rgba(22,22,25,0.85);
}

/* ── Base ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
img { display: block; max-width: 100%; }
button { font-family: inherit; }
/* The [hidden] attribute must always win over author display rules (e.g. .btn's
   inline-flex), so JS toggles via `hidden` reliably hide elements. */
[hidden] { display: none !important; }

html { color-scheme: light; }
html.dark { color-scheme: dark; }

html, body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--fg-1);
  background: var(--bp-bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1 { font: var(--t-h1); letter-spacing: var(--tracking-display); margin: 0; }
h2 { font: var(--t-h2); letter-spacing: var(--tracking-display); margin: 0; }
h3 { font: var(--t-h3); letter-spacing: var(--tracking-display); margin: 0; }
p  { font: var(--t-body); margin: 0; color: var(--fg-1); }

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: var(--bp-green-50); color: var(--fg-1); }

/* ── App layout (CSS Grid) ─────────────────────────────────── */
.app {
  min-height: 100vh;
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-rows: var(--appbar-h) 1fr;
  grid-template-areas: "appbar appbar" "sidebar main";
}
.appbar {
  grid-area: appbar;
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 24px;
  background: var(--appbar-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--bp-border-soft);
}
.appbar-left  { display: flex; align-items: center; gap: 16px; }
.appbar-right { display: flex; align-items: center; gap: 12px; }
.brand { display: inline-flex; align-items: center; gap: 10px; font: 700 16px var(--font-display); letter-spacing: var(--tracking-display); color: var(--fg-1); }
.brand:hover { text-decoration: none; }
.brand-mark { width: 26px; height: 26px; border-radius: 7px; background: var(--bp-green); color: #0E2A1C; display: inline-flex; align-items: center; justify-content: center; font: 800 14px var(--font-display); }

.sidebar {
  grid-area: sidebar;
  position: sticky; top: var(--appbar-h);
  height: calc(100vh - var(--appbar-h));
  background: var(--bp-surface);
  border-right: 1px solid var(--bp-border-soft);
  padding: 14px 12px;
  display: flex; flex-direction: column; gap: 4px;
  overflow-y: auto;
}
.main {
  grid-area: main;
  padding: 24px 32px 48px;
  width: 100%;
  min-width: 0;
}

/* Caps the measure so short content doesn't strand in horizontal void on wide
   monitors. Opt-in per page: dense table views still want the full width. */
.page-container { max-width: var(--container-max); margin-inline: auto; }
/* Single-column settings and form pages. The page title must live inside the
   same column as the content, or the heading and the cards end up on different
   left edges and the page reads as misaligned rather than centred. */
.page-container.is-narrow { max-width: 640px; }

/* ── Skip-to-content link (accessibility) ────────────────────── */
.skip-link {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
  z-index: -1;
}
.skip-link:focus {
  position: fixed;
  top: 8px;
  left: 8px;
  width: auto;
  height: auto;
  overflow: visible;
  z-index: 9999;
  padding: 8px 16px;
  background: var(--bp-surface);
  color: var(--fg-1);
  border-radius: var(--radius-input);
  box-shadow: var(--shadow-3);
  font: var(--t-body-strong);
  text-decoration: none;
}

/* Hamburger (mobile nav trigger) + off-canvas backdrop — hidden on desktop. */
.nav-toggle { display: none; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 8px; border: 0; cursor: pointer; background: transparent; color: var(--fg-1); }
.nav-toggle svg { width: 22px; height: 22px; }
.nav-backdrop { display: none; position: fixed; inset: var(--appbar-h) 0 0 0; background: rgba(0,0,0,0.4); z-index: 25; }

/* ── Sidebar items ─────────────────────────────────────────── */
.nav-group {
  font: 600 11px var(--font-body);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fg-muted); padding: 10px 12px 4px;
}
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border-radius: 6px;
  color: var(--fg-2);
  font: 500 14px var(--font-body);
  cursor: pointer;
  background: transparent; border: 0; text-align: left; width: 100%;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out);
}
.nav-item:hover { background: var(--bp-muted); text-decoration: none; }
/* Selection is a positional fact, not a live signal — marked by tone, not by
   Signal Green (same doctrine as .chip.active). Keeping the green here spent
   the One Green budget on every screen, leaving none for the primary action. */
/* The tinted background alone is only ~1.1:1 against the sidebar in both themes,
   so the marker rail carries the signal. Inset shadow, not a border: no reflow
   between states, and it follows the 6px corner. */
.nav-item.active { background: var(--bp-muted-2); color: var(--fg-1); font-weight: 600; box-shadow: inset 2px 0 0 0 var(--fg-1); }
.nav-item.active .nav-ic { color: var(--fg-1); }
.nav-ic { width: 16px; height: 16px; color: var(--fg-muted); flex: 0 0 auto; }

/* ── Page header ───────────────────────────────────────────── */
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.page-title { font: var(--t-h1); letter-spacing: var(--tracking-display); }
.crumbs { display: flex; gap: 6px; color: var(--fg-muted); font: 500 13px var(--font-body); align-items: center; }
.crumbs a { color: var(--fg-muted); }
.crumbs a:hover { color: var(--fg-1); text-decoration: none; }
.crumbs .sep { opacity: .6; }

/* ── Card ──────────────────────────────────────────────────── */
.card { background: var(--bp-surface); border-radius: var(--radius-card); box-shadow: 0 0 0 1px var(--bp-border-soft); overflow: hidden; }
.card-head { padding: 18px 20px; display: flex; align-items: center; justify-content: space-between; gap: 16px; border-bottom: 1px solid var(--bp-border-soft); }
.card-title { font: var(--t-h2); letter-spacing: var(--tracking-display); }
.card-sub   { color: var(--fg-muted); font: 500 13px var(--font-body); }
.card-body  { padding: 20px; }
.card-body.flush { padding: 0; }

/* ── Button ────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 36px; padding: 0 16px;
  border-radius: 6px; border: 0; cursor: pointer;
  font: 500 14px/1 var(--font-body);
  transition: background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast);
  white-space: nowrap; text-decoration: none;
}
.btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.btn-primary   { background: var(--bp-green); color: #0E2A1C; }
.btn-primary:hover { background: var(--bp-green-600); text-decoration: none; }
.btn-primary:active { background: var(--bp-green-700); }
.btn-secondary { background: var(--bp-surface); color: var(--fg-1); box-shadow: inset 0 0 0 1px var(--bp-border); }
.btn-secondary:hover { background: var(--bp-muted); text-decoration: none; }
.btn-ghost { background: transparent; color: var(--fg-1); }
.btn-ghost:hover { background: var(--bp-muted); text-decoration: none; }
.btn-danger { background: var(--bp-surface); color: var(--status-blocked-fg); box-shadow: inset 0 0 0 1px rgba(229,72,77,0.4); }
.btn-danger:hover { background: var(--bp-red-50); }
.btn-sm   { height: 28px; padding: 0 12px; font-size: 13px; }
.btn-icon { width: 36px; padding: 0; }
.btn-icon.btn-sm { width: 28px; }
.btn svg  { width: 16px; height: 16px; }
.btn-sm svg { width: 14px; height: 14px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Input ─────────────────────────────────────────────────── */
.input, .select, .textarea {
  width: 100%; height: 36px; padding: 0 12px;
  background: var(--bp-surface);
  border-radius: var(--radius-input);
  box-shadow: inset 0 0 0 1px var(--bp-border);
  font: 400 14px var(--font-body); color: var(--fg-1);
  border: 0; outline: none;
  transition: box-shadow var(--dur-fast);
}
.input::placeholder, .textarea::placeholder { color: var(--bp-fg-subtle); }
.input:focus, .select:focus, .textarea:focus { box-shadow: inset 0 0 0 1px var(--bp-green), 0 0 0 3px rgba(35,216,135,0.22); }
.textarea { height: auto; padding: 10px 12px; min-height: 96px; resize: vertical; line-height: 1.5; }
/* A collapsed composer. It is a button, but it reads as the field it is about
   to become — same height, same ring, same placeholder tone — so the thing you
   click and the thing you get are one shape. */
.composer-trigger {
  display: block; width: 100%; height: 36px; padding: 0 12px;
  background: var(--bp-surface);
  border: 0; border-radius: var(--radius-input);
  box-shadow: inset 0 0 0 1px var(--bp-border);
  font: 400 14px/36px var(--font-body); color: var(--bp-fg-subtle);
  text-align: left; cursor: text;
  transition: box-shadow var(--dur-fast);
}
.composer-trigger:hover { box-shadow: inset 0 0 0 1px var(--fg-muted); }
.composer-trigger:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--bp-green), 0 0 0 3px rgba(35,216,135,0.22); }

.field { display: flex; flex-direction: column; gap: 6px; }
.field-label { font: 500 12px var(--font-body); color: var(--fg-1); }
.field-help  { font: 500 11px var(--font-body); color: var(--fg-muted); }
.field-error { font: 500 12px var(--font-body); color: var(--status-blocked-fg); }
.field.has-error .input, .field.has-error .select, .field.has-error .textarea { box-shadow: inset 0 0 0 1px var(--bp-red); }
.form-actions { display: flex; gap: 12px; align-items: center; margin-top: 8px; }
.form-narrow { max-width: 520px; }
.input-icon { position: relative; }
.input-icon .input { padding-left: 36px; }
.input-icon svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--fg-muted); pointer-events: none; }

/* ── Logged-out / auth pages (login, password reset) ───────── */
.auth-page {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-5);
  background: var(--bp-bg);
}
/* The auth card IS the page, not a layer above it — it keeps .card's hairline ring. */
.auth-card {
  width: 100%; max-width: 380px;
  padding: var(--space-6);
}
.auth-brand {
  display: flex; align-items: center; gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.auth-logo {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 10px;
  background: var(--bp-green); color: #0E2A1C;
  font: 800 22px/1 var(--font-display);
}
.auth-brand-name { font: var(--t-h3); color: var(--fg-1); }
.auth-title { font: var(--t-h2); letter-spacing: var(--tracking-display); margin: 0 0 var(--space-2); color: var(--fg-1); }
.auth-lead { font: var(--t-body); color: var(--fg-muted); margin: 0 0 var(--space-5); }
.auth-form { display: flex; flex-direction: column; gap: var(--space-4); }
.auth-form .btn { width: 100%; margin-top: var(--space-1); }
.auth-error {
  font: var(--t-body-strong); color: var(--status-blocked-fg);
  background: var(--bp-red-50); border-radius: var(--radius-input);
  padding: var(--space-3); margin: 0 0 var(--space-4);
}
.auth-links { margin-top: var(--space-5); text-align: center; font: var(--t-caption); }
.auth-links a { color: var(--link); }

/* ── Badge ─────────────────────────────────────────────────── */
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--radius-pill); font: 500 12px/1 var(--font-body); letter-spacing: 0.01em; white-space: nowrap; }
.badge .dot { width: 6px; height: 6px; border-radius: 999px; flex: 0 0 auto; }
.badge.todo     { background: var(--status-todo-bg);     color: var(--status-todo-fg); }
.badge.todo .dot     { background: #8A8C92; }
.badge.progress { background: var(--status-progress-bg); color: var(--status-progress-fg); }
.badge.progress .dot { background: var(--bp-blue); }
.badge.review   { background: var(--status-review-bg);   color: var(--status-review-fg); }
.badge.review .dot   { background: var(--bp-amber); }
.badge.done     { background: var(--status-done-bg);     color: var(--status-done-fg); }
.badge.done .dot     { background: var(--bp-green); }
.badge.blocked  { background: var(--status-blocked-bg);  color: var(--status-blocked-fg); }
.badge.blocked .dot  { background: var(--bp-red); }
.badge.active   { background: var(--status-done-bg);     color: var(--status-done-fg); }
.badge.active .dot   { background: var(--bp-green); }
.badge.inactive { background: var(--status-todo-bg);     color: var(--status-todo-fg); }
.badge.inactive .dot { background: #8A8C92; }
/* Triage priority badges: urgent=red, high=amber, normal=neutral, low=muted */
.badge.prio-urgent { background: var(--status-blocked-bg); color: var(--status-blocked-fg); }
.badge.prio-urgent .dot { background: var(--bp-red); }
.badge.prio-high   { background: var(--status-review-bg);  color: var(--status-review-fg); }
.badge.prio-high .dot   { background: var(--bp-amber); }
.badge.prio-normal { background: var(--status-todo-bg);    color: var(--status-todo-fg); }
.badge.prio-normal .dot { background: #8A8C92; }
.badge.prio-low    { background: var(--status-todo-bg);    color: var(--fg-muted); }
.badge.prio-low .dot    { background: #C2C4CA; }
/* Triage category badge: neutral outline-style chip */
.badge.cat { background: var(--bp-bg); color: var(--fg-muted); border: 1px solid var(--bp-border-soft); }

/* ── Table ─────────────────────────────────────────────────── */
.table { width: 100%; border-collapse: separate; border-spacing: 0; }
.table th, .table td { padding: 12px 16px; text-align: left; vertical-align: middle; }
.table thead th { font: 600 11px var(--font-body); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-muted); background: var(--bp-bg); border-bottom: 1px solid var(--bp-border-soft); }
.table tbody tr { transition: background var(--dur-fast); }
.table tbody tr + tr td { border-top: 1px solid var(--bp-border-soft); }
.table tbody tr:hover { background: var(--bp-muted); }
.table td { font: 400 14px var(--font-body); color: var(--fg-1); }
.table .muted  { color: var(--fg-muted); }
.table .link   { color: var(--bp-blue); font-weight: 500; }
.table .link:hover { text-decoration: underline; cursor: pointer; }
.table td.right { text-align: right; }
/* Durations and dates are single tokens: "5h 50m" broken across two lines reads
   as two numbers. They were hidden behind a field of dashes until the dashes
   went. */
.table td.c-time, .table td.c-due, .table td.c-created, .table td.c-last { white-space: nowrap; }
.table thead th.right { text-align: right; }
.table th.sortable { padding: 0; user-select: none; }
/* Stretch the control to fill the whole header cell so the entire cell is clickable.
   The control is a <button> (sorting is a Live Component action, not navigation);
   reset the native button chrome so it stays visually identical to the header text. */
.table th.sortable .sort-link { display: block; width: 100%; padding: 12px 16px; margin: 0; color: inherit; cursor: pointer; background: none; border: 0; font: inherit; text-align: inherit; }
.table thead th.sortable.right .sort-link { text-align: right; }
.table th.sortable:hover { color: var(--fg-1); }
.table th.sortable .sort-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Pagination + sort indicator (Live tables) ─────────────── */
.pagination-bar{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-top:1px solid var(--bp-border-soft);flex-wrap:wrap;gap:12px}
.page-btn,.page-size{display:inline-flex;align-items:center;justify-content:center;height:30px;min-width:30px;padding:0 8px;border:0;border-radius:6px;background:transparent;color:var(--fg-2);font-size:13px;font-weight:500;cursor:pointer;transition:background .12s,color .12s}
.page-btn:hover:not([disabled]),.page-size:hover{background:var(--bp-muted);color:var(--fg-1)}
/* Current page is a position, not an action — invert to graphite like .chip.active. */
.page-btn.active,.page-size.active{background:var(--fg-1);color:var(--bp-surface)}
.page-btn[disabled]{opacity:.4;pointer-events:none}
.page-size{height:24px;min-width:24px;font-size:12px}
.sort-ind{margin-left:4px;font-size:10px;opacity:.6}
.row-inactive{opacity:.5}

/* ── Sparkline ─────────────────────────────────────────────── */
.sparkline{display:block;width:80px;height:20px}
.sparkline .spark-area{fill:var(--accent);opacity:.1}
.sparkline .spark-line{stroke:var(--accent);opacity:.75;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round}

/* ── Avatar ────────────────────────────────────────────────── */
.avatar { display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; font: 600 12px var(--font-body); flex: 0 0 auto; }
.avatar-sm { width: 24px; height: 24px; font-size: 11px; }
.avatar-md { width: 32px; height: 32px; font-size: 12px; }
.avatar-lg { width: 40px; height: 40px; font-size: 14px; }
.avatar-stack { display: inline-flex; }
.avatar-stack .avatar { box-shadow: 0 0 0 2px var(--bp-surface); }
.avatar-stack .avatar + .avatar { margin-left: -8px; }

/* ── Gray avatar stack (Tasks table assignees) ──────────────── */
.avatar-stack-gray{display:flex}
.avatar-stack-gray .avatar-gray{margin-left:-6px}
.avatar-stack-gray .avatar-gray:first-child{margin-left:0}
.avatar-gray{width:24px;height:24px;border-radius:50%;background:var(--bp-muted-2);border:2px solid var(--bp-surface);color:var(--fg-muted);font-size:10px;font-weight:600;display:inline-flex;align-items:center;justify-content:center}

/* ── Tasks table checkbox column ───────────────────────────── */
th.check-col,td.check-col{width:36px;padding-inline:0;text-align:center}

/* ── Chips ─────────────────────────────────────────────────── */
.chips { display: inline-flex; gap: 6px; flex-wrap: wrap; }
/* Two chip groups side by side read as one long list, and when both use the word
   "aktivní" for different things the row becomes unreadable. A caption per group
   restores which question each one answers. */
.filter-group { display: inline-flex; align-items: center; gap: 8px; }
.filter-group > .filter-label { font: 500 12px var(--font-body); color: var(--fg-muted); white-space: nowrap; }
.chip { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; background: var(--bp-muted); color: var(--fg-2); border-radius: var(--radius-pill); border: 0; font: 500 13px/1 var(--font-body); cursor: pointer; transition: background var(--dur-fast); }
.chip:hover { background: var(--bp-muted-2); }
.chip.active { background: var(--fg-1); color: var(--bp-surface); }
.chip .count { color: var(--fg-muted); font-weight: 500; }
.chip.active .count { color: var(--bp-fg-subtle); }

/* ── Stat bars (dashboard breakdowns) ──────────────────────────
   Label + proportional bar + count, so the largest value reads first.
   --c is the per-row status/priority colour, shared by dot and fill. */
.statbars { display: flex; flex-direction: column; gap: 8px; }
.statbar { --c: #8A8C92; display: grid; grid-template-columns: minmax(88px, auto) 1fr auto; align-items: center; gap: 10px; text-decoration: none; color: var(--fg-1); font: 500 13px/1 var(--font-body); }
.statbar:hover { text-decoration: none; }
.statbar:hover .statbar-track { background: var(--bp-muted-2); }
.statbar-label { display: inline-flex; align-items: center; gap: 7px; color: var(--fg-2); white-space: nowrap; }
.statbar-label .dot { width: 7px; height: 7px; border-radius: 999px; flex: 0 0 auto; background: var(--c); }
.statbar-track { position: relative; height: 22px; background: var(--bp-muted); border-radius: var(--radius-pill); overflow: hidden; }
.statbar-fill { position: absolute; inset: 0 auto 0 0; border-radius: var(--radius-pill); background: var(--c); transition: width var(--dur-fast) var(--ease-out); }
.statbar-count { color: var(--fg-1); font-weight: 600; font-variant-numeric: tabular-nums; min-width: 2.5ch; text-align: right; }
/* A breakdown with nowhere to drill to is a reading, not a control — drop the
   hover affordance so it doesn't promise navigation it can't deliver. */
.statbar.is-static { cursor: default; }
.statbar.is-static:hover .statbar-track { background: var(--bp-muted); }
.statbar.c-progress { --c: var(--bp-blue); }
.statbar.c-review, .statbar.c-high { --c: var(--bp-amber); }
.statbar.c-done { --c: var(--bp-green); }
.statbar.c-blocked, .statbar.c-urgent { --c: var(--bp-red); }
.statbar.c-low { --c: #C2C4CA; }

/* ── Tabs (pill) ───────────────────────────────────────────── */
.tabs { display: inline-flex; padding: 4px; background: var(--bp-muted); border-radius: 8px; gap: 2px; }
.tab { display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 14px; border-radius: 6px; border: 0; background: transparent; font: 500 13px var(--font-body); color: var(--fg-muted); cursor: pointer; transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
.tab svg { width: 14px; height: 14px; }
.tab:hover { color: var(--fg-1); text-decoration: none; }
/* Panel-on-recess already lifts the active tab; a hairline defines its edge.
   A shadow here would claim it floats above the page, which it does not. */
.tab.active { background: var(--bp-surface); color: var(--fg-1); box-shadow: 0 0 0 1px var(--bp-border-soft); }
.tab:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

/* ── Tabs (underline) ──────────────────────────────────────── */
.tabs-underline { display: inline-flex; gap: 24px; border-bottom: 1px solid var(--bp-border-soft); width: 100%; }
.tab-underline { border: 0; background: transparent; cursor: pointer; padding: 12px 0; margin-bottom: -1px; font: 500 14px var(--font-body); color: var(--fg-muted); border-bottom: 2px solid transparent; transition: color var(--dur-fast), border-color var(--dur-fast); }
.tab-underline:hover { color: var(--fg-1); }
.tab-underline.active { color: var(--fg-1); border-bottom-color: var(--fg-1); }
/* Inset, same as .sort-link: the tab sits flush on the card's bottom edge, and
   `.card { overflow: hidden }` would clip an outset ring. */
.tab-underline:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-sm); }
/* The panel is a tab stop only so its scrollable table can be scrolled; the ring
   marks that landing without implying the region itself is a control. */
.tab-panel:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Timer chip ────────────────────────────────────────────── */
.timer-chip { display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 12px 0 8px; border-radius: 36px; background: var(--bp-green-50); color: var(--status-done-fg); font: 500 13px var(--font-body); border: 0; cursor: pointer; }
.timer-chip .pulse { width: 8px; height: 8px; background: var(--bp-green); border-radius: 999px; animation: pulse-anim 1.6s var(--ease-out) infinite; }
@keyframes pulse-anim { 0%, 100% { box-shadow: 0 0 0 0 rgba(35,216,135,0.5); } 50% { box-shadow: 0 0 0 6px rgba(35,216,135,0); } }

/* ── Active Timer topbar pill (ActiveTimer live component) ─── */
.active-timer-pill {
  display: inline-flex;
  align-items: center;
  gap: 0;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--bp-green-50);
  border: 1px solid rgba(35,216,135,0.3);
  color: var(--status-done-fg);
  font: 500 13px var(--font-body);
  overflow: hidden;
}
.active-timer-title {
  padding: 0 6px 0 12px;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--status-done-fg);
  text-decoration: none;
  font-size: 13px;
  transition: color var(--dur-fast);
  flex-shrink: 1;
  min-width: 0;
}
.active-timer-title:hover { color: var(--bp-green-700); text-decoration: none; }
.active-timer-clock-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px 0 12px;
}
.active-timer-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--bp-green);
  flex-shrink: 0;
  animation: pulse-anim 1.6s var(--ease-out) infinite;
}
.active-timer-clock {
  font-variant-numeric: tabular-nums;
  min-width: 48px;
}
.active-timer-stop {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 0 10px;
  border: 0;
  border-left: 1px solid rgba(35,216,135,0.25);
  background: transparent;
  color: var(--status-done-fg);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}
.active-timer-stop:hover { background: rgba(35,216,135,0.15); }
@media (max-width: 520px) { .active-timer-title { display: none; } }

/* ── Misc ──────────────────────────────────────────────────── */
.separator { height: 1px; background: var(--bp-border-soft); border: 0; margin: 0; }
.empty { padding: 48px 24px; text-align: center; color: var(--fg-muted); display: flex; flex-direction: column; align-items: center; gap: 12px; }
/* An empty state has two jobs: name what is missing, and say what to do about
   it. One undifferentiated grey paragraph does neither well, so the naming line
   steps up to Ink and the h3 step while the explanation stays Slate and holds a
   reading measure. Still text-led — no illustration, no emoji. */
.empty-title { font: var(--t-h3); letter-spacing: var(--tracking-display); color: var(--fg-1); }
.empty-body  { font: var(--t-body); max-width: 52ch; }
.spinner { width: 20px; height: 20px; border: 2px solid var(--bp-border); border-top-color: var(--bp-green); border-radius: 50%; animation: spin 0.7s linear infinite; }
.spinner-lg { width: 36px; height: 36px; border-width: 3px; }
@keyframes spin { to { transform: rotate(360deg); } }

.alert-info    { padding: 12px 16px; border-radius: var(--radius-card); background: var(--bp-blue-50); color: var(--status-progress-fg); font: 400 14px var(--font-body); border: 1px solid rgba(56,152,236,0.2); margin-bottom: 12px; }
.alert-error   { padding: 12px 16px; border-radius: var(--radius-card); background: var(--bp-red-50); color: var(--status-blocked-fg); font: 400 14px var(--font-body); border: 1px solid rgba(229,72,77,0.2); margin-bottom: 12px; }
.alert-success { padding: 12px 16px; border-radius: var(--radius-card); background: var(--bp-green-50); color: var(--status-done-fg); font: 400 14px var(--font-body); border: 1px solid rgba(35,216,135,0.25); margin-bottom: 12px; }

/* ── Flash toasts ──────────────────────────────────────────── */
.toast-stack { position: fixed; top: 16px; right: 16px; z-index: 60; display: flex; flex-direction: column; gap: 8px; }
.toast { padding: 12px 16px; border-radius: var(--radius-card); box-shadow: var(--shadow-3); font: 500 14px var(--font-body); background: var(--bp-surface); color: var(--fg-1); min-width: 240px; }
.toast.success { border-left: 3px solid var(--bp-green); }
.toast.error   { border-left: 3px solid var(--bp-red); }

/* ── Stat tiles + readings ─────────────────────────────────────
   One typography for the app's single "reading with context" idea, wherever it
   appears: the tile on the dashboards and Reporting, and the lead reading on a
   project's Přehled, which needs the same scale without nesting a card inside a
   card. Three separate versions of it were the tell that the pattern had never
   been named. Order is always label → reading → context. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.tile { display: flex; flex-direction: column; min-height: 112px; background: var(--bp-surface); border-radius: var(--radius-card); box-shadow: 0 0 0 1px var(--bp-border-soft); padding: 18px 20px; }
.tile .k, .reading-k { font: 700 28px/1 var(--font-display); letter-spacing: var(--tracking-display); color: var(--fg-1); }
.tile .k { margin-top: 6px; }
.tile .l, .reading-l { font: 500 13px var(--font-body); color: var(--fg-muted); }
.reading-l { margin-top: 6px; }
.tile-sub, .reading-sub { font: 500 12.5px var(--font-body); color: var(--fg-muted); margin-top: 4px; }
.reading-sub { margin-top: 12px; }
.tile-sub b, .reading-sub b { color: var(--fg-1); font-weight: 600; font-variant-numeric: tabular-nums; }
/* A tile with nothing to add under the reading must not reserve the line. */
.tile-sub:empty { display: none; }
.tile-name { font-size: 20px; line-height: 1.15; }
/* Pinned to the bottom so a row of tiles shares one baseline for its series,
   however tall the readings above them turn out to be. */
.tile-spark { margin-top: auto; padding-top: 8px; width: 100%; height: 26px; }

/* Lead reading beside its distribution. Collapses before the bars get too
   narrow to compare by length — which is the whole point of a bar. */
.overview-split { display: grid; grid-template-columns: minmax(150px, 200px) minmax(0, 1fr); gap: 32px; align-items: start; }
@media (max-width: 720px) { .overview-split { grid-template-columns: minmax(0, 1fr); gap: 20px; } }

/* ── Disclosure trigger ────────────────────────────────────────
   Native <details> keeps its semantics and keyboard behaviour; the summary
   wears the secondary button's surface and ring so that at rest — before any
   hover, which a first-time reader has no reason to try — it reads as
   something you can operate rather than as a caption with a caret beside it.
   It stays 28px and borrows btn-sm's type, so a disclosure and a small button
   sitting in the same row are the same size. */
.disclosure-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 12px;
  border-radius: var(--radius-input);
  background: var(--bp-surface); color: var(--fg-1);
  box-shadow: inset 0 0 0 1px var(--bp-border);
  font: 500 13px/1 var(--font-body);
  cursor: pointer; list-style: none; width: fit-content;
  transition: background var(--dur-fast) var(--ease-out);
}
.disclosure-trigger::-webkit-details-marker { display: none; }
.disclosure-trigger::marker { content: ''; }
.disclosure-trigger:hover { background: var(--bp-muted); }
/* The focus ring is added to the border, not swapped for it — replacing the
   box-shadow would make the control lose its outline at the moment it is
   being pointed at. */
.disclosure-trigger:focus-visible { outline: none; box-shadow: var(--shadow-focus), inset 0 0 0 1px var(--bp-border); }
.disclosure-trigger svg { width: 14px; height: 14px; color: var(--fg-muted); transition: transform var(--dur-fast) var(--ease-out); }
details[open] > .disclosure-trigger svg { transform: rotate(90deg); }

/* ── Theme toggle ──────────────────────────────────────────── */
.theme-toggle { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 8px; border: 0; cursor: pointer; background: var(--bp-muted); color: var(--fg-1); font-size: 16px; line-height: 1; transition: background var(--dur-fast); }
.theme-toggle:hover { background: var(--bp-muted-2); }

/* ── Utilities ─────────────────────────────────────────────── */
.row   { display: flex; align-items: center; gap: 12px; }
.col   { display: flex; flex-direction: column; gap: 12px; }
.spacer { flex: 1; }
.muted  { color: var(--fg-muted); }
.body-strong { font: var(--t-body-strong); }
.overline { font: var(--t-overline); letter-spacing: var(--tracking-overline); text-transform: uppercase; color: var(--fg-muted); }
.tabular { font-variant-numeric: tabular-nums; }
/* align-items:start is load-bearing: without it the shorter column stretches to
   match the taller one and leaves a card hanging over empty space — the single
   biggest source of the "half-built" reading. */
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; align-items: start; }
.table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ── Tables as cards, narrow ───────────────────────────────────
   A nine-column table inside a horizontally scrolling wrapper is a desktop
   pattern wearing a phone's clothes: the status of a task — the single fact
   people come to this list for — sits three swipes off-screen. Below 760px the
   row stops being a row and becomes a card. The cells are the same cells in
   the same DOM order; only their arrangement changes. */
@media (max-width: 760px) {
    .table-cards, .table-cards tbody, .table-cards tr, .table-cards td { display: block; width: auto; }
    /* Column headers describe columns. Once there are none, they would be read
       out as a stray list of words, so they go — the cells carry their own
       meaning through badges, links and the labels below. */
    .table-cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

    .table-cards tbody tr { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; padding: 14px 16px; }
    .table-cards tbody tr + tr { border-top: 1px solid var(--bp-border-soft); }
    .table-cards tbody tr + tr td { border-top: 0; }
    .table-cards td { padding: 0; }
    /* The empty-result row is one wide cell, not a card. */
    .table-cards tbody tr > td[colspan] { flex: 1 1 100%; text-align: center; }

    /* Order is meaning, not DOM position: what the task is, then its state, then
       where it lives, then when. The columns are ordered for a wide table; a card
       is read top-down and needs its own sequence. */
    .table-cards .c-title { order: 1; flex: 1 1 100%; font-size: 15px; line-height: 1.35; }
    .table-cards .c-status { order: 2; }
    .table-cards .c-priority { order: 3; }
    .table-cards .c-category { order: 4; }
    .table-cards .c-status, .table-cards .c-priority, .table-cards .c-category { flex: 0 0 auto; }
    .table-cards .c-project { order: 5; flex: 0 1 auto; font-size: 13px; }
    .table-cards .c-customer { order: 6; font-size: 13px; }
    /* Separator trails the project rather than leading the customer: when the
       pair wraps, a dot left hanging at the end of a line reads as "continues",
       while a dot starting a line reads as a bullet. */
    .table-cards .c-project::after { content: ' ·'; color: var(--fg-muted); }
    /* Date and time read as bare figures once the header row is gone, so they
       name themselves. No value means no cell — an unset date should cost
       nothing, not print a dash. */
    .table-cards .c-due { order: 7; }
    .table-cards .c-time { order: 8; }
    .table-cards .c-assignees { order: 9; flex: 0 0 auto; }
    /* Why the task needs attention is the whole point of that list, so its
       reasons get their own line rather than trailing the meta run. */
    .table-cards .c-reasons { order: 10; flex: 1 1 100%; }
    .table-cards .c-due, .table-cards .c-time { flex: 0 0 auto; font-size: 12.5px; text-align: left; }
    .table-cards .c-due:not([data-label]), .table-cards .c-time:not([data-label]) { display: none; }
    .table-cards .c-due::before, .table-cards .c-time::before {
        content: attr(data-label) ' '; color: var(--fg-muted); font-weight: 500;
    }
    /* A cell whose entire content is the "—" placeholder is dropped, not just
       emptied: an empty flex item still spends a gap, and a card with three of
       them is the row of dashes this list is known for. One rule covers every
       such cell, including any added later. */
    .table-cards tbody td:has(> .muted:only-child),
    .table-cards tbody td:empty { display: none; }
    /* Whole-row separation is the card's job; per-cell rules would draw lines
       through the middle of it. */
    .table-cards tbody tr:hover { background: none; }

    /* ── Columns the other lists add ──────────────────────────
       Same vocabulary, so a table gains the card layout by tagging its cells
       rather than by growing its own set of rules. */
    /* Select-all and the task number ride on the title's line: a checkbox on a
       line of its own reads as a list item, and #123 is how people say the task
       out loud. The sibling selector only relaxes the title where a number
       actually precedes it, so the single-column lists are untouched. */
    .table-cards .c-check { order: 0; flex: 0 0 auto; }
    .table-cards .c-num { order: 1; flex: 0 0 auto; font-size: 13px; }
    /* The basis fills the line rather than merely being allowed to grow into
       it. With `flex: 1 1 auto` a short title left room for the status badge to
       join its line and then got stretched, so the badge landed at the right
       edge on short titles and at the left of the next line on long ones — the
       same chip in two places, and no two cards lining up. Filling the line
       makes the break happen at the same point whatever the title's length. */
    .table-cards .c-num + .c-title { flex: 1 1 calc(100% - 72px); }
    .table-cards .c-role { order: 2; flex: 0 0 auto; }
    .table-cards .c-spark { order: 5; flex: 0 0 auto; }
    .table-cards .c-email,
    .table-cards .c-position { order: 6; flex: 1 1 100%; font-size: 13px; }
    .table-cards .c-created,
    .table-cards .c-last { order: 7; }
    .table-cards .c-rate { order: 8; }
    .table-cards .c-created, .table-cards .c-last, .table-cards .c-rate {
        flex: 0 0 auto; font-size: 12.5px; text-align: left;
    }
    .table-cards .c-created:not([data-label]),
    .table-cards .c-last:not([data-label]) { display: none; }
    .table-cards .c-created::before, .table-cards .c-last::before, .table-cards .c-rate::before {
        content: attr(data-label) ' '; color: var(--fg-muted); font-weight: 500;
    }
    /* Who did it, written out. Distinct from c-assignees, which is a stack of
       avatars and hugs its content; a name is prose and belongs on the meta
       line with the project it was logged against. */
    .table-cards .c-person { order: 6; flex: 0 1 auto; font-size: 13px; }
    /* Any other small labelled figure — "Úkolů 12" — on the same line as the
       dates, so a list can add a count without adding a rule. */
    .table-cards .c-count { order: 7; flex: 0 0 auto; font-size: 12.5px; text-align: left; }
    .table-cards .c-count:not([data-label]) { display: none; }
    .table-cards .c-count::before { content: attr(data-label) ' '; color: var(--fg-muted); font-weight: 500; }
    /* A row that is editable in place puts its form on a line of its own and
       lets the fields fill the width. Inside a scrolling table the form is the
       one thing on the page you came to use and the last thing you can reach. */
    .table-cards .c-form { order: 12; flex: 1 1 100%; margin-top: 4px; }
    .table-cards .c-form .input { min-width: 0; }
    /* Buttons close the card on their own line, pushed to the far edge so a
       destructive action never sits flush against the text it belongs to. */
    .table-cards .c-actions { order: 11; flex: 0 0 auto; margin-left: auto; text-align: right; }
    /* A lone chevron wrapped onto its own line reads as a stray glyph, and
       letting it share the title's line pushes the badges around by title
       length, so no two cards line up. Taking it out of the flow pins it to the
       card's top-right and leaves every other cell laid out as if it weren't
       there. */
    .table-cards tbody tr { position: relative; }
    .table-cards .c-go { position: absolute; right: 12px; top: 10px; }
}

/* Task detail: main column + fixed 300px meta sidebar (ported from React). */
.task-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 20px; align-items: start; }
/* Facts that answer the same question sit together and share one rule between
   groups. A separator between every pair reads as eleven sections of one row
   each, and the rules alone added over a hundred pixels to the rail. */
.meta-group { display: flex; flex-direction: column; gap: 12px; }
.meta-row { display: flex; flex-direction: column; gap: 2px; }
/* A badge is as wide as its word. Stretched across the rail by the column's
   default alignment it reads as a filled-in text field. */
.meta-row > .badge { align-self: flex-start; }
.meta-label { font: 600 11px var(--font-body); letter-spacing: 0.06em; text-transform: uppercase; color: var(--fg-muted); }
.meta-value { font: 500 14px var(--font-body); color: var(--fg-1); }
.assignee-pill { display: inline-flex; align-items: center; gap: 6px; font: 500 12px var(--font-body); background: var(--bp-muted); color: var(--fg-2); border-radius: var(--radius-pill); padding: 3px 6px 3px 4px; }
.assignee-pill .x { border: 0; background: transparent; color: var(--fg-muted); cursor: pointer; font-size: 14px; line-height: 1; padding: 0 2px; }
.assignee-pill .x:hover { color: var(--bp-red); }
@media (max-width: 860px) { .task-grid { grid-template-columns: minmax(0, 1fr); } }

/* ── Responsive ────────────────────────────────────────────── */
@media (max-width: 860px) {
  .app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: var(--appbar-h) 1fr;
    grid-template-areas: "appbar" "main";
  }
  .nav-toggle { display: inline-flex; }
  .sidebar {
    position: fixed; top: var(--appbar-h); left: 0; bottom: 0;
    width: min(82vw, 280px); height: auto;
    transform: translateX(-100%);
    transition: transform var(--dur-med) var(--ease-out);
    z-index: 40; box-shadow: var(--shadow-3);
  }
  .app.nav-open .sidebar { transform: translateX(0); }
  .app.nav-open .nav-backdrop { display: block; }
  .appbar { padding: 0 12px; }
  .main { padding: 16px 16px 40px; }
  .page-head { flex-direction: column; align-items: flex-start; gap: 12px; }
  .appbar-right .user-email { display: none; }
}

@media (max-width: 520px) {
  .brand-text { display: none; }
  .main { padding: 14px 12px 36px; }
}

/* ── RoleBadge component ──────────────────────────────── */
.role-badge{display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:600;white-space:nowrap}

/* ── Dropzone upload zone (Task 10) ──────────────────── */
.dropzone {
  border: 1.5px dashed var(--bp-border);
  border-radius: var(--radius-lg);
  padding: 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  background: var(--bp-muted);
  text-align: center;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast);
  user-select: none;
}
.dropzone:hover { background: var(--bp-muted-2); }
.dropzone.dragover {
  background: var(--bp-blue-50);
  border-color: var(--bp-blue);
}
.dropzone-icon {
  width: 40px; height: 40px;
  border-radius: 999px;
  background: var(--bp-surface);
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--bp-border-soft);
  color: var(--fg-muted);
  flex: 0 0 auto;
}
.dropzone-title {
  font: var(--t-body-strong);
  color: var(--fg-1);
}
.dropzone-sub {
  font: 400 13px var(--font-body);
  color: var(--fg-muted);
}
.dropzone-link { color: var(--bp-blue); }

/* ── Timelog panel + sidebar timer widget (Task 11) ─── */

/* Panel wrapper */
.timelog-panel { display: flex; flex-direction: column; gap: 16px; padding: 20px; }

/* Status bar */
.timelog-status-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px; background: var(--bp-muted); border-radius: var(--radius-card);
}
.timelog-status-left  { display: flex; align-items: center; gap: 14px; }
.timelog-status-right { display: flex; align-items: center; gap: 8px; }

/* Big elapsed/total number */
.timelog-big-time {
  font-size: 28px; font-weight: 700; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.timelog-status-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--fg-muted);
}

/* Pulsing dot for active state */
.timelog-pulse {
  width: 8px; height: 8px; border-radius: 999px;
  background: var(--bp-green);
  display: inline-block;
  animation: pulse-anim 1.6s var(--ease-out) infinite;
}

/* Running row: comment + stop */
.timelog-running-row { display: flex; align-items: center; gap: 8px; }
.timelog-comment-wrap { position: relative; flex: 1; }
.timelog-comment-input { padding-right: 64px; }
.timelog-inline-form { display: inline-flex; flex-shrink: 0; }

/* "uloženo" pill */
.timelog-saved-pill {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-size: 11px; color: var(--status-done-fg); pointer-events: none; user-select: none;
  background: var(--bp-green-50); border-radius: var(--radius-pill);
  padding: 1px 8px;
}

/* Start comment input (no timer active) */
.timelog-start-comment-input { width: 100%; }

/* Manual form — display:flex is intentional but must yield to [hidden] */
.timelog-manual-form {
  border: 1px solid var(--bp-border-soft); border-radius: var(--radius-card);
  padding: 14px; display: flex; flex-direction: column; gap: 12px;
  background: color-mix(in srgb, var(--bp-muted) 40%, transparent);
}
.timelog-manual-form[hidden] { display: none; }
.timelog-manual-inner { display: flex; flex-direction: column; gap: 0; }
.timelog-manual-fields {
  display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap;
}
.timelog-dt-field { display: flex; flex-direction: column; gap: 4px; }
.timelog-dt-label { font-size: 11px; font-weight: 500; color: var(--fg-muted); }
.timelog-dt-input { width: 155px; height: 32px; font-size: 12px; }
.timelog-arrow { font-size: 14px; padding-bottom: 4px; }
.timelog-manual-comment { flex: 1; min-width: 120px; font-size: 13px; }
.timelog-manual-actions { display: flex; align-items: center; gap: 6px; padding-bottom: 2px; }

/* Table */
.timelog-table-wrap { border: 1px solid var(--bp-border-soft); border-radius: var(--radius-card); overflow: hidden; }

/* Totals row */
.timelog-totals-row { background: var(--bp-muted); }

/* Per-row delete icon button */
.btn-icon-delete {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: 0; border-radius: 4px;
  background: transparent; color: var(--fg-muted); cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast);
  padding: 0;
}
.btn-icon-delete:hover { color: var(--status-blocked-fg); background: var(--bp-red-50); }
.btn-icon-edit {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: 0; border-radius: 4px;
  background: transparent; color: var(--fg-muted); cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast);
  padding: 0;
}
.btn-icon-edit:hover { color: var(--accent-fg, #2563eb); background: var(--bp-blue-50, #eff6ff); }

/* ── Sidebar timer widget ─────── */
.timelog-widget { display: flex; flex-direction: column; gap: 12px; }
.timelog-widget-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.timelog-widget-time-block { display: flex; flex-direction: column; gap: 2px; }
.timelog-widget-big {
  font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1;
}
.timelog-widget-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--fg-muted); margin-top: 2px;
}
.timelog-widget-other-task { font-size: 11px; margin: 0; }

/* ── Rich-text comment editor (Tiptap / ProseMirror) ── */

/* Wrapper — the [data-rich-editor-target="editor"] div */
.rich-editor {
  border: 1px solid var(--bp-border);
  border-radius: var(--radius-card);
  background: var(--bp-surface);
  font-size: 13px;
  color: var(--fg-1);
  /* Bound the height so long content scrolls inside the editor instead of
     pushing the page; this is the scroll container the sticky toolbar pins to. */
  max-height: 50vh;
  overflow-y: auto;
}

/* Toolbar strip — stays anchored at the top of the editor while typing long
   content, so the formatting buttons are always reachable without scrolling up. */
.comment-editor-toolbar {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
  padding: 4px 8px;
  border-bottom: 1px solid var(--bp-border-soft);
  background: var(--bp-surface);
}
.comment-editor-toolbar button {
  height: 24px;
  min-width: 24px;
  padding: 0 6px;
  border: none;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  background: transparent;
  color: var(--fg-muted);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.comment-editor-toolbar button:hover {
  background: var(--bp-muted);
  color: var(--fg-1);
}
.comment-editor-toolbar button.is-active {
  background: var(--bp-muted);
  color: var(--fg-1);
}
.comment-editor-toolbar .toolbar-sep {
  display: inline-block;
  width: 1px;
  height: 16px;
  background: var(--bp-border-soft);
  margin: 0 4px;
  flex-shrink: 0;
}

/* ProseMirror content area */
.rich-editor .ProseMirror {
  min-height: 120px;
  padding: 10px 12px;
  color: var(--fg-1);
  font-size: 13px;
  line-height: 1.5;
  font-family: var(--font-body);
  outline: none;
  cursor: text;
}
.rich-editor .ProseMirror p { margin: 0 0 0.5em; }
.rich-editor .ProseMirror p:last-child { margin-bottom: 0; }
.rich-editor .ProseMirror ul,
.rich-editor .ProseMirror ol { padding-left: 1.4em; margin: 0.4em 0; }
.rich-editor .ProseMirror li { margin: 0.15em 0; }
.rich-editor .ProseMirror ul { list-style-type: disc; }
.rich-editor .ProseMirror ol { list-style-type: decimal; }
.rich-editor .ProseMirror blockquote {
  border-left: 3px solid var(--bp-border);
  margin: 0.5em 0;
  padding: 0.25em 0 0.25em 0.75em;
  color: var(--fg-muted);
  font-style: italic;
}
.rich-editor .ProseMirror code {
  background: var(--bp-muted);
  border-radius: 3px;
  font-size: 0.85em;
  padding: 0.1em 0.35em;
}
.rich-editor .ProseMirror strong { font-weight: 600; }
.rich-editor .ProseMirror em { font-style: italic; }
.rich-editor .ProseMirror s { text-decoration: line-through; }

/* Tiptap placeholder via Placeholder extension */
.rich-editor .ProseMirror p.is-editor-empty:first-child::before {
  content: attr(data-placeholder);
  color: var(--fg-muted);
  float: left;
  height: 0;
  pointer-events: none;
}

/* Text only a screen reader gets: kept in the accessibility tree (so not
   display:none) but taken out of the visual layout. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Tiptap rendered comment content ──────────────────── */
.tiptap-content {
  font-size: 13px;
  line-height: 1.6;
  color: var(--fg-1);
  outline: none;
}
.tiptap-content p { margin: 0 0 0.5em; }
.tiptap-content p:last-child { margin-bottom: 0; }
/* An empty paragraph is a blank line the author deliberately typed. With no
   content it generates no line box and collapses to zero height, so the saved
   text reflowed the instant it left the editor — where ProseMirror gives the
   same paragraph a full line via a trailing <br>. The empty inline box below
   restores that line box so preview and editor agree. */
.tiptap-content p:empty::before { content: ''; display: inline-block; }
.tiptap-content h1, .tiptap-content h2, .tiptap-content h3 {
  font-weight: 600; line-height: 1.3; margin: 0.8em 0 0.3em;
}
.tiptap-content h1 { font-size: 1.25em; }
.tiptap-content h2 { font-size: 1.1em; }
.tiptap-content h3 { font-size: 1em; }
.tiptap-content strong { font-weight: 600; }
.tiptap-content em { font-style: italic; }
.tiptap-content s { text-decoration: line-through; }
.tiptap-content ul, .tiptap-content ol { padding-left: 1.4em; margin: 0.4em 0; }
.tiptap-content li { margin: 0.15em 0; }
.tiptap-content ul { list-style-type: disc; }
.tiptap-content ol { list-style-type: decimal; }
.tiptap-content blockquote {
  border-left: 3px solid var(--bp-border);
  margin: 0.5em 0;
  padding: 0.25em 0 0.25em 0.75em;
  color: var(--fg-muted);
  font-style: italic;
}
.tiptap-content code {
  background: var(--bp-muted);
  border-radius: 3px;
  font-size: 0.85em;
  padding: 0.1em 0.35em;
}
.tiptap-content pre {
  background: var(--bp-muted);
  border-radius: var(--radius-card);
  font-size: 0.82em;
  margin: 0.5em 0;
  overflow-x: auto;
  padding: 0.75em 1em;
}
.tiptap-content pre code { background: none; padding: 0; }
.tiptap-content hr { border: none; border-top: 1px solid var(--bp-border); margin: 0.75em 0; }
.tiptap-content .ProseMirror { outline: none; }
.tiptap-content .mention,
.mention { color: var(--bp-blue); font-weight: 500; background: color-mix(in srgb, var(--bp-blue) 10%, transparent); border-radius: 4px; padding: 0 3px; }
.tiptap-content a { color: var(--bp-blue); }
.tiptap-content a:hover { text-decoration: underline; }
/* Images render at the same size in the read-only preview and in the editor. */
.tiptap-content img,
.rich-editor .ProseMirror img { display: block; max-width: min(360px, 100%); max-height: 240px; width: auto; height: auto; object-fit: contain; border-radius: var(--radius-card); margin: 0.5em 0; }
.tiptap-content img { cursor: zoom-in; }

/* Mention chip inside the editor */
.rich-editor .mention {
  color: var(--bp-blue);
  font-weight: 500;
  background: color-mix(in srgb, var(--bp-blue) 10%, transparent);
  border-radius: 4px;
  padding: 0 3px;
}

/* ── Inline file attachment pill ──────────────────────── */
.attachment-file-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  text-decoration: none !important;
  background: var(--bp-muted);
  border: 1px solid var(--bp-border-soft);
  border-radius: 4px;
  padding: 2px 6px 2px 5px;
  font-size: 0.78em;
  color: var(--fg-1);
  vertical-align: middle;
  max-width: 240px;
  transition: background var(--dur-fast);
  cursor: pointer;
}
.attachment-file-pill:hover { background: var(--bp-muted-2); }
.attachment-file-ext { font-weight: 700; color: var(--bp-blue); font-size: 0.85em; flex-shrink: 0; letter-spacing: 0.01em; }
.attachment-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Placeholder for an attachment whose binary is missing from storage. */
.attachment-missing {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--bp-muted);
  border: 1px dashed var(--bp-border-soft);
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 0.78em;
  color: var(--bp-fg-subtle);
  vertical-align: middle;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Mention dropdown (inline popup) */
.mention-dropdown {
  background: var(--bp-surface);
  border: 1px solid var(--bp-border);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,.12);
  overflow: hidden;
  min-width: 160px;
  z-index: 9999;
}
.mention-dropdown button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 12px;
  font-size: 13px;
  text-align: left;
  border: none;
  cursor: pointer;
  background: transparent;
  color: var(--fg-1);
  transition: background var(--dur-fast);
}
.mention-dropdown button:hover,
.mention-dropdown button.is-selected { background: var(--bp-muted); }

/* ── Image lightbox overlay (Task 10) ────────────────── */
.lightbox-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.70);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  cursor: pointer;
}
.lightbox-inner {
  position: relative;
  max-width: 90vw;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  gap: 8px;
  cursor: default;
}
.lightbox-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 4px;
}
.lightbox-caption {
  font: 400 12px var(--font-body);
  color: rgba(255,255,255,0.7);
  max-width: 60vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lightbox-toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.lightbox-open-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font: 400 12px var(--font-body);
  color: rgba(255,255,255,0.8);
  background: rgba(255,255,255,0.1);
  border-radius: 4px;
  padding: 4px 8px;
  text-decoration: none;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.lightbox-open-link:hover {
  background: rgba(255,255,255,0.2);
  color: #fff;
  text-decoration: none;
}
.lightbox-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border: 0;
  border-radius: 4px;
  background: rgba(255,255,255,0.1);
  color: rgba(255,255,255,0.8);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.lightbox-close:hover { background: rgba(255,255,255,0.2); color: #fff; }
.lightbox-img {
  max-width: 90vw;
  max-height: 80vh;
  object-fit: contain;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-3);
  display: block;
}

/* ── Description card (view/edit + collapse) ── */
.description-collapsible.is-collapsed {
  max-height: 144px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
}
.description-empty {
  cursor: pointer;
  font-size: 13px;
  font-style: italic;
  color: var(--fg-muted);
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  width: 100%;
  transition: color .15s;
}
.description-empty:hover { color: var(--fg-1); }

/* ---- Kanban board ---------------------------------------------------- */
.board {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    align-items: flex-start;
    height: calc(100vh - 220px);
    padding-bottom: 8px;
    /* When the board does overflow, say so. Overlay scrollbars are invisible at
       rest on some platforms, which is how a whole column went missing without
       anything on screen suggesting there was more to the right. */
    scrollbar-width: thin;
    scrollbar-color: var(--bp-border) transparent;
}
.board::-webkit-scrollbar { height: 10px; }
.board::-webkit-scrollbar-track { background: transparent; }
.board::-webkit-scrollbar-thumb { background: var(--bp-border); border-radius: 999px; }
.board-col {
    display: flex;
    flex-direction: column;
    flex: 1 1 0;
    /* Five columns at 240 could not fit beside the 220px navigation on a 1440
       screen, so "Blokováno" was cut through the middle of its cards. At 210 the
       whole workflow fits, and the columns still hold a two-line title. */
    min-width: 210px;
    max-height: 100%;
    background: var(--bp-muted);
    border-radius: var(--radius-lg);
    padding: 12px;
    outline: 2px solid transparent;
    transition: background var(--dur-fast) var(--ease-out), outline-color var(--dur-fast) var(--ease-out);
}
.board-col.drag-over {
    background: var(--bp-muted-2);
    outline-color: var(--bp-green);
    outline-style: dashed;
}
.board-col-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    padding: 0 2px;
}
.board-col-count {
    color: var(--fg-muted);
    font-size: 12px;
    font-weight: 600;
}
.board-col-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    overflow-y: auto;
    flex: 1;
    padding-right: 2px;
    min-height: 40px;
}
.board-card {
    background: var(--bp-surface);
    border-radius: var(--radius-card);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* Flat at rest; the lift is feedback while held (.sortable-chosen). */
    box-shadow: 0 0 0 1px var(--bp-border-soft);
    cursor: grab;
}
.board-card.sortable-chosen { cursor: grabbing; box-shadow: var(--shadow-2); }
.board-card.sortable-ghost { opacity: .4; }
.board-card-title {
    font-size: 14px;
    font-weight: 500;
    line-height: 1.35;
    color: var(--fg-1);
    text-decoration: none;
}
.board-card-title:hover { color: var(--bp-green-700); text-decoration: none; }
.board-card-num { color: var(--fg-muted); font-weight: 400; margin-right: 4px; }
.board-card-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.board-card-meta-left { display: flex; align-items: center; gap: 8px; min-width: 0; }
.board-card-due, .board-card-time { font-size: 11px; color: var(--fg-muted); font-weight: 500; }
.board-col-empty {
    padding: 16px 0;
    text-align: center;
    font-size: 12px;
    font-weight: 500;
    color: var(--bp-fg-subtle, var(--fg-muted));
}

/* ── Calendar (/kalendar) ──────────────────────────────────── */
.cal-toolbar { display: flex; align-items: center; flex-wrap: wrap; gap: 12px 20px; margin-bottom: 20px; }
.cal-nav { display: flex; align-items: center; gap: 8px; }
.cal-nav-label { font: 600 1.1rem var(--font-body); min-width: 150px; text-align: center; color: var(--fg-1); }
.cal-hint { font: 500 12.5px var(--font-body); color: var(--fg-muted); }
.cal-placeholder { padding: 16px; margin-bottom: 16px; border-radius: var(--radius-input); background: var(--bp-muted); color: var(--fg-muted); border: 1px solid var(--bp-border-soft); font-size: 14px; }
.cal-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.cal-grid th { text-align: center; font: var(--t-overline); letter-spacing: var(--tracking-overline); text-transform: uppercase; padding: 0 6px 8px; color: var(--fg-muted); }
/* `height` on a table cell is read as a minimum — `min-height`, which was here
   before, is not applied to table-cell at all and was silently ignored. */
.cal-grid td { vertical-align: top; padding: 8px; border: 1px solid var(--bp-border-soft); height: 120px; width: 14.2857%; }
/* The work week is the figure; the weekend is the ground. One tinted band is
   what lets the eye find a date without counting across from Monday — the
   column headers stay at full contrast, since the tint already says it. */
/* Hairline colour used as a fill, not a tint from the surface ramp: the light
   neutrals sit so close together that Recess Deep reads at 1.10 against the
   page and is simply invisible. Filling with the rule colour makes the weekend
   read as denser ruling, which is the right metaphor for a calendar anyway. */
.cal-grid td.is-weekend { background: var(--bp-border-soft); }
.cal-day-num { font-size: 12px; font-weight: 600; margin-bottom: 6px; display: inline-block; color: var(--fg-1); }
/* Days spilling in from the neighbouring month still carry real chips, so they
   are content, not decoration — subtle reads at 3.2:1 and does not qualify. */
.cal-day-num.muted { color: var(--fg-muted); font-weight: 500; }
.cal-day-num.today { background: var(--bp-green); color: #0E2A1C; border-radius: 50%; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; }
/* The status wash pair, not a hand-mixed one: --bp-blue on --bp-blue-50 measured
   2.7:1 in light mode, while the documented pair clears AA in both themes. */
/* The chip carries its task's status in the same two tokens the status badges
   use, so a colour means the same thing on the calendar as it does in a list.
   --chip-fg drives both the text and the hover ring, which is why it is a
   variable rather than two declarations per status. */
.cal-chip { display: block; font: 500 12px var(--font-body); padding: 3px 6px; border-radius: var(--radius-sm); margin-bottom: 2px; --chip-fg: var(--status-todo-fg); background: var(--status-todo-bg); color: var(--chip-fg); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-chip.is-progress { --chip-fg: var(--status-progress-fg); background: var(--status-progress-bg); }
.cal-chip.is-review   { --chip-fg: var(--status-review-fg);   background: var(--status-review-bg); }
.cal-chip.is-done     { --chip-fg: var(--status-done-fg);     background: var(--status-done-bg); }
.cal-chip.is-blocked  { --chip-fg: var(--status-blocked-fg);  background: var(--status-blocked-bg); }
/* Hover rings rather than inverts — inverting put white on Signal Blue at 3.1:1. */
.cal-chip:hover { box-shadow: inset 0 0 0 1px var(--chip-fg); text-decoration: none; }
.cal-hours { display: block; font: 600 12px var(--font-body); padding: 3px 6px; border-radius: var(--radius-sm); text-decoration: none; background: var(--status-done-bg); color: var(--status-done-fg); font-variant-numeric: tabular-nums; }
.cal-hours:hover { background: var(--bp-green); color: #0E2A1C; text-decoration: none; }

/* The overflow valve. Closed, it holds the cell to its height and keeps the
   month scannable; open, the day expands in place so nothing leaves the screen. */
.cal-more > summary { list-style: none; cursor: pointer; font: 500 11px var(--font-body); color: var(--fg-muted); padding: 2px 6px; border-radius: var(--radius-sm); }
.cal-more > summary::-webkit-details-marker { display: none; }
.cal-more > summary::marker { content: ''; }
.cal-more > summary:hover { color: var(--fg-1); background: var(--bp-muted-2); }
.cal-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal-more[open] > summary { color: var(--fg-1); margin-bottom: 2px; }

/* Narrow-layout affordances. The wide grid shows the month's shape on its own,
   so these stay out of its way. */
.cal-day-wd, .cal-day-none, .cal-empty-narrow { display: none; }

/* ── Calendar, narrow ──────────────────────────────────────────
   Seven columns stop working around 700px: a cell is under 60px and every
   task name truncates to "#16…". Below that the month stops being a grid and
   becomes a list of days, which is what a phone can actually show. Same DOM,
   restyled — nothing is rendered twice and nothing is dropped from the page. */
@media (max-width: 700px) {
    .cal-grid, .cal-grid tbody, .cal-grid tr, .cal-grid td { display: block; width: auto; }
    /* The weekday header belongs to a grid; in a list each day names itself. */
    .cal-grid thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

    .cal-grid tr { margin-bottom: 0; }
    .cal-grid td { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 12px; height: auto; min-height: 0;
                   padding: 10px 4px; border: 0; border-bottom: 1px solid var(--bp-border-soft); background: none; }
    .cal-grid td.is-weekend { background: none; }

    /* An empty day is a row that says nothing. Thirty of them would bury the
       five that matter, so they collapse — except today, which is the anchor
       you scroll to. */
    .cal-grid td.is-blank:not(.is-today) { display: none; }
    .cal-grid td.is-blank .cal-day-none { color: var(--fg-muted); font: 500 13px var(--font-body); }

    .cal-day-num { margin-bottom: 0; }
    .cal-day-wd { display: inline; font: 500 12px var(--font-body); color: var(--fg-muted); }
    /* Date column: fixed so the day contents line up down the list. */
    .cal-grid td > .cal-day-num { flex: 0 0 22px; text-align: right; font-size: 15px; }
    .cal-grid td > .cal-day-wd { flex: 0 0 24px; margin-left: -6px; }
    .cal-grid td.is-weekend > .cal-day-wd { color: var(--status-blocked-fg); }

    /* Everything after the date stacks in the remaining width, so a task name
       gets the whole screen instead of a 50px column. */
    .cal-grid td > .cal-chip, .cal-grid td > .cal-hours, .cal-grid td > .cal-more,
    .cal-grid td > .cal-day-none { flex: 1 1 100%; margin-left: 46px; }
    .cal-grid td > .cal-chip ~ .cal-chip, .cal-grid td > .cal-more { margin-top: 2px; }
    .cal-chip { white-space: normal; font-size: 13px; padding: 6px 8px; }
    .cal-more > summary { padding: 6px 8px; font-size: 12px; }

    .cal-empty-narrow { display: block; padding: 24px 4px; color: var(--fg-muted); font: 500 14px var(--font-body); }
}

/* ── Notifications: topbar bell + dropdown + full-page list ── */
.notif-bell { position: relative; }
.notif-bell-btn { position: relative; background: none; border: 0; color: var(--fg-1); cursor: pointer; padding: 6px; display: inline-flex; align-items: center; }
.notif-badge { position: absolute; top: 0; right: 0; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; background: var(--bp-red); color: #fff; font-size: 10px; line-height: 16px; text-align: center; }
.notif-dropdown { position: absolute; right: 0; top: 100%; width: 320px; max-height: 70vh; overflow: auto; background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: var(--radius-card, 12px); box-shadow: 0 8px 24px rgba(0,0,0,.12); z-index: 50; }
.notif-dropdown-head { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--bp-border-soft); font-weight: 600; color: var(--fg-1); }
.notif-list { display: flex; flex-direction: column; }
.notif-item { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; border-bottom: 1px solid var(--bp-border-soft); color: var(--fg-1); text-decoration: none; }
.notif-item:hover { background: var(--bp-bg); }
.notif-item.is-unread { background: var(--bp-green-50); }
.notif-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; background: var(--accent); flex: none; }
.notif-item.is-read .notif-dot { background: transparent; }
.notif-text { display: flex; flex-direction: column; gap: 2px; font-size: 13px; }
.notif-time { color: var(--fg-muted); font-size: 11px; }
.notif-empty { padding: 16px; text-align: center; color: var(--fg-muted); }
.notif-all { display: block; text-align: center; padding: 8px; font-size: 13px; }
.notif-page-list { display: flex; flex-direction: column; }

/* ── Modal (native <dialog> + Turbo Frame) — see modal_controller.js ─────── */
.modal {
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  background: var(--bp-surface);
  color: var(--bp-fg);
  box-shadow: var(--shadow-3);
  width: 100%;
  max-width: min(560px, calc(100vw - 32px));
  max-height: 90vh;
  overflow: hidden;
  /* Flex column so a tall body scrolls under a fixed header/footer. */
  display: flex;
  flex-direction: column;
}
/* Keep hidden when closed despite the display:flex above. */
.modal:not([open]) { display: none; }
/* Let the loaded .modal-panel become the flex child, not the frame wrapper. */
.modal > turbo-frame { display: contents; }
.modal::backdrop {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

/* Recommended chrome for content loaded into #modal-frame (all optional). */
.modal-panel { display: flex; flex-direction: column; min-height: 0; max-height: 90vh; }
.modal-header {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--bp-border);
}
.modal-title { margin: 0; font: 600 16px/1.3 var(--font-display); color: var(--bp-fg); }
.modal-body { padding: 20px; overflow: auto; }
.modal-footer {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
  padding: 12px 20px; border-top: 1px solid var(--bp-border);
}
.modal-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: 0 0 auto;
  border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--bp-fg-muted);
  font-size: 20px; line-height: 1; cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.modal-close:hover { background: var(--bp-muted); color: var(--bp-fg); }

/* ── Reporting module ─────────────────────────────────────────────── */
.reporting-filter { padding: 16px 18px; margin-bottom: 18px; display: flex; flex-direction: column; gap: 14px; }
.reporting-filter-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; }
.reporting-filter .field-grow { flex: 1 1 200px; min-width: 180px; }
.reporting-filter .multiselect { height: auto; padding: 4px; }
.reporting-filter .multiselect option { padding: 4px 8px; border-radius: var(--radius-sm); }

.segmented { display: inline-flex; border: 1px solid var(--bp-border); border-radius: var(--radius-input); overflow: hidden; height: 38px; }
.segmented .seg { appearance: none; border: 0; background: var(--bp-surface); color: var(--fg-muted); font: 600 13px var(--font-body); padding: 0 16px; cursor: pointer; border-right: 1px solid var(--bp-border-soft); }
.segmented .seg:last-child { border-right: 0; }
.segmented .seg.active { background: var(--fg-1); color: var(--bp-surface); }
.segmented .seg:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Four fixed columns rather than auto-fit: these four readings are one row of
   the same report and should stay one row, not reflow by label length. */
.reporting-tiles { grid-template-columns: repeat(4, 1fr); margin-bottom: 18px; }

.badge-retired { display: inline-block; font: 600 10.5px var(--font-body); color: var(--bp-red); background: color-mix(in srgb, var(--bp-red) 12%, transparent); border-radius: var(--radius-sm); padding: 1px 6px; vertical-align: middle; }

@media (max-width: 880px) {
    .reporting-tiles { grid-template-columns: repeat(2, 1fr); }
}

/* ── Reduced motion ────────────────────────────────────────────
   The system preference asks for less movement, not less feedback.
   Colour and shadow transitions stay: a cross-fade travels nowhere, and
   it is how a control says it heard you. What goes is travel — things
   that slide, grow, or rotate across the screen — and every loop that
   runs forever. A loop that carried meaning is replaced by a static form
   of the same meaning, never simply switched off.
   ───────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {

  /* The mobile drawer is the one full-size travelling object in the app:
     82vw sliding in from the edge. It arrives in place instead. */
  .sidebar { transition: none; }

  /* A bar states a quantity. Growing to it is decoration; the final
     length is the whole message. */
  .statbar-fill { transition: none; }

  /* The chevron still ends up rotated — only the tween goes. */
  .disclosure-trigger svg { transition: none; }

  /* "Time is being billed right now" — the pulse is how the app says it,
     so it becomes a steady ring rather than nothing at all. Same colour
     the pill already uses for its text, so it holds in both themes. */
  .timer-chip .pulse,
  .active-timer-dot,
  .timelog-pulse {
    animation: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--status-done-fg) 50%, transparent);
  }

  /* The spinner keeps turning. Its rotation is not decoration wrapped
     around the message, it *is* the message — a stopped ring reads as a
     broken one — and 20px of rotation triggers nothing. */
}
