@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&display=swap');

:root {
  /* ─── Sumi (墨) — dark theme ─────────────────────────────────────
     Warm-neutral ramp + one koi accent. Vermillion is reserved for
     "this is asking for you": overdue, active state, brand mark,
     primary action. Everything else lives in the neutral or energy
     ramps — no orange theatrics, no competing accents. */

  /* Surfaces. --bg is the page; --bg-elevated lifts cards / drawers /
     popovers; --bg-input sits a touch deeper than the page so inputs
     read as sunken slots rather than another raised card.
     v2 tuning: deepened the page and brightened the card so the lift
     reads — original 3% delta was too thin to perceive against the
     dark surround. */
  --bg: #070710;
  --bg-elevated: #1a1a22;
  --bg-input: #0e0e13;

  /* Text ramp. Targets on --bg #0a0a0e: text ~14:1 (AAA), secondary
     ~8.1:1 (AAA), muted ~5.2:1 (AA body), dim ~3.0:1 (AA-large only
     — eyebrows, counts, placeholders), strike below that for finished
     state where readability is intentionally fading. */
  --text: #e8e6e0;
  --text-secondary: #b5b2ab;
  --text-muted: #8e8b85;
  --text-dim: #6a6864;
  --strike: #3f3d38;

  --border: #1f1f27;
  --border-light: #15151b;

  /* Koi. Used sparingly — overdue stripe, active tab, focused filter,
     primary button fill, brand mark, edit-form edge flash. If you're
     reaching for accent in a fourth place, ask whether it earns it. */
  --accent: #c8392e;
  --accent-hover: #d94539;

  /* Energy ramp — sits inside the same earthy family. Used by the
     energy-square indicator and the energy chip; can also tint the
     effort badge in future. low = restorative, med = default, high =
     draining. */
  --energy-low: #6b7f49;
  --energy-med: #7d7a72;
  --energy-high: #a85543;

  /* Semantic re-exports so the rest of the file keeps composing
     against meaningful names. success / warning / danger map onto
     the palette: completed work → moss green, waiting/parked → amber,
     true "danger" → koi (we only surface one alert color). */
  --success: #6b7f49;
  --warning: #b58a3e;
  --danger:  #c8392e;

  --google-blue: #6ea8f5;       /* slight desat for the warmer surround */

  /* Delegation — amber family, distinct chrome so "ACTING AS …" never
     conflates with the active koi. */
  --delegate:        #b58a3e;
  --delegate-bg:     #1a1410;
  --delegate-border: #4a3a1c;

  /* Card decoration. Inset top highlight + soft drop = warm sumi card
     resting on the board. Single token so we don't repeat it.
     v2 tuning: the original values were tuned for the mockup's body
     bg (#060609) at a higher zoom — at the app's actual viewport the
     shadow was invisible. Pushed both the inset and the drop a step
     stronger so cards now read as objects on a surface. */
  --card-top:    rgba(255,255,255,0.04);
  --card-shadow: 0 1px 0 rgba(0,0,0,0.55), 0 4px 14px rgba(0,0,0,0.45);

  /* Japanese serif — column-header glosses (受信 / 予定 / 完了 / いつか)
     and any inline JP characters. Mincho stack for the brushed-ink
     feel; fallbacks if the Japanese fonts aren't installed. */
  --jp: "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif JP", "Hiragino Sans", system-ui, serif;
}

/* Light theme — warm off-white bg paired with a darker accent so the
   contrast inversion holds. Pattern: many rules use `color: var(--bg)`
   on `background: var(--accent)` (buttons, badges, ::selection). For
   that to stay readable, accent and bg must remain high-contrast
   opposites in BOTH modes. Keep that invariant if you tweak colors. */
[data-theme="light"] {
  /* Direction 1 — Editorial Sumi.
     Warm beige page sits flat across header/tabs/filters; per-column tonal
     fills do the band work instead of stepping the chrome. Koi vermillion
     is the only saturated accent — reserved for active tab, restore links,
     overdue stripes. Parchment cards lift via shadow against the column
     tone. Energy ramp is moss / wheat / rust. */
  --bg: #ede7d8;                /* cool warm beige — flat across all chrome */
  --bg-elevated: #faf6ec;       /* parchment — kanban cards, popovers, drawer */
  --bg-header: #ede7d8;
  --bg-tabs: #ede7d8;
  --bg-filters: #ede7d8;
  --bg-input: #fdfbf5;
  --text: #2b2823;
  --text-secondary: #4a463e;    /* ~8:1 on --bg */
  --text-muted: #8a8278;
  --text-dim: #b3aa9b;
  --strike: #b3aa9b;
  --border: #d9d0bd;
  --border-light: #e4dccb;
  --accent: #c8392e;            /* koi vermillion — same as dark theme for brand continuity */
  --accent-hover: #a72e25;
  --success: #6a8460;           /* moss */
  --warning: #b8945b;           /* wheat */
  --danger:  #a85543;           /* rust */
  --google-blue: #1a73e8;
  --delegate: #92400e;
  --delegate-bg: #fef3c7;
  --delegate-border: #d97706;
  --energy-low:  #6a8460;
  --energy-med:  #b8945b;
  --energy-high: #a85543;
  --card-top:    rgba(255,255,255,0.55);
  --card-shadow: 0 1px 0 rgba(40,32,18,0.04), 0 6px 18px rgba(40,32,18,0.08);
  --jp: "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif JP", "Hiragino Sans", system-ui, serif;
  --serif-display: 'Fraunces', 'EB Garamond', Georgia, serif;
  --serif-body:    'EB Garamond', Georgia, serif;
  /* Per-column tonal fills — Editorial Sumi.
     Stone / sage / slate / mauve / tan. Each carries the column's
     temperament; parchment cards sit on top via .kb-card. */
  --col-inbox:     #e2dbc8;
  --col-scheduled: #c8d5b8;
  --col-done:      #b6c1c9;
  --col-someday:   #cfc1cd;
  --col-trashed:   #d6c5a8;
}

/* Direction 2 — Modernist Stripe. Pale white surface, geometric sans
   typography, columns identified by a 4px top stripe in their accent
   colour. Azure is the action accent. Energy ramp uses the standard
   green/amber/red traffic-light trio. */
[data-theme="modernist"] {
  --bg: #f5f6f8;
  --bg-elevated: #ffffff;
  --bg-header: #ffffff;
  --bg-tabs: #ffffff;
  --bg-filters: #ffffff;
  --bg-input: #ffffff;
  --text: #0d1117;
  --text-secondary: #3d434c;
  --text-muted: #6b7280;
  --text-dim: #aab0bb;
  --strike: #aab0bb;
  --border: #e5e7eb;
  --border-light: #eef0f3;
  --accent: #2563eb;
  --accent-hover: #1d4ed8;
  --success: #16a34a;
  --warning: #f59e0b;
  --danger:  #ef4444;
  --google-blue: #1a73e8;
  --delegate: #92400e;
  --delegate-bg: #fef3c7;
  --delegate-border: #d97706;
  --energy-low:  #16a34a;
  --energy-med:  #f59e0b;
  --energy-high: #ef4444;
  --card-top:    rgba(255,255,255,1);
  --card-shadow: 0 1px 2px rgba(15,23,42,0.04), 0 1px 0 rgba(15,23,42,0.02);
  --jp: 'Noto Sans JP', 'Plus Jakarta Sans', sans-serif;
  --sans-display: 'Plus Jakarta Sans', system-ui, sans-serif;
  --sans-body:    'Plus Jakarta Sans', system-ui, sans-serif;
  /* Per-column accent + ultra-pale body tint (the column reads as white
     unless you look closely). The accent rides as a 4px top border. */
  --col-inbox:     #f8faff;
  --col-scheduled: #fffaf0;
  --col-done:      #f5fbf6;
  --col-someday:   #faf7ff;
  --col-trashed:   #f7f8fa;
  --col-inbox-accent:     #2563eb;
  --col-scheduled-accent: #f59e0b;
  --col-done-accent:      #16a34a;
  --col-someday-accent:   #8b5cf6;
  --col-trashed-accent:   #9aa3b2;
}

/* Direction 3 — Terminal Dark. Mono everywhere, near-black surfaces
   with hairline borders. Each column carries a small filled-square
   marker in the top-left corner — nothing else is decorated. Koi is
   lifted for contrast on the darker page. */
[data-theme="terminal"] {
  --bg: #0a0a0c;
  --bg-elevated: #14141a;
  --bg-header: #0a0a0c;
  --bg-tabs: #0a0a0c;
  --bg-filters: #0a0a0c;
  --bg-input: #14141a;
  --text: #e8e6e0;
  --text-secondary: #b0aea7;
  --text-muted: #6e6c66;
  --text-dim: #44423d;
  --strike: #3f3d38;
  --border: #2a2a32;
  --border-light: #1d1d24;
  --accent: #ff8866;
  --accent-hover: #ffa088;
  --success: #7fbf7f;
  --warning: #d4a85a;
  --danger:  #e07b5f;
  --google-blue: #4d8bff;
  --delegate: #d4a85a;
  --delegate-bg: rgba(212,168,90,0.08);
  --delegate-border: #d4a85a;
  --energy-low:  #7fbf7f;
  --energy-med:  #d4a85a;
  --energy-high: #e07b5f;
  --card-top:    rgba(255,255,255,0.04);
  --card-shadow: 0 1px 0 rgba(0,0,0,0.55), 0 4px 14px rgba(0,0,0,0.45);
  --jp: 'Noto Sans JP', 'JetBrains Mono', monospace;
  /* No per-column body fills — terminal columns share the slate panel
     bg; only the corner marker carries the column's hue. */
  --col-inbox-accent:     #e0a060;
  --col-scheduled-accent: #6090e0;
  --col-done-accent:      #7fbf7f;
  --col-someday-accent:   #b080e0;
  --col-trashed-accent:   #7a7a82;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--accent); color: var(--bg); }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border); }

input, textarea, select, button { font-family: inherit; }

/* Keyboard-focus ring for non-input controls. Inputs already get a
   border-color shift on :focus (see .input below), so we exclude them here
   to avoid the outline doubling up on top of the border change.
   Mouse-click :focus on the same controls is explicitly cleared — without
   this, some browsers render their default outline (often dark / near-
   black) on the just-clicked button, which can read as a stray border on
   the surrounding container. */
button:focus,
[role="button"]:focus,
select:focus,
a:focus,
[tabindex]:focus { outline: none; }
button:focus-visible,
[role="button"]:focus-visible,
select:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ─── LAYOUT ──────────────────────────────────────────────── */
/* Wide container so the kanban + projects views can breathe. Vertical-list
   tabs (Today, Inbox, Done, Settings) constrain their own inner content to
   a 680px reading width via .app-narrow so prose stays comfortable. */
.app { max-width: 1400px; margin: 0 auto; padding: 16px 16px 100px; }
/* Kanban + projects boards need more width than the reading views — five
   columns at a comfortable width want ~1800px. Other views stay at 1400px. */
.app:has(.kb-board) { max-width: 1800px; }
.app-narrow { max-width: 680px; margin: 0 auto; }

.header {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--bg-header, transparent);
  padding: 14px 16px;
  margin: -16px -16px 0;   /* full-bleed against .app's 16px padding */
}
.logo { font-size: 1.2em; font-weight: 700; cursor: pointer; }
.logo-accent { color: var(--accent); }
.header-actions { display: flex; gap: 16px; align-items: center; }
.header-link {
  color: var(--text-muted); cursor: pointer; font-size: 0.8em;
  letter-spacing: 1px; transition: color 0.2s; border: none; background: none;
}
.header-link:hover { color: var(--text-secondary); }

/* Theme picker — replaces the old binary light/dark toggle. The trigger
   matches every other .header-link in size and colour so the row reads
   evenly; the dropdown anchors to the trigger and lists each theme from
   the THEMES registry in app.js. Outside clicks close it (see
   _themeMenuOutsideClick). */
.theme-switcher { position: relative; display: inline-flex; }
.theme-toggle {
  display: inline-flex; align-items: center; gap: 4px;
}
.theme-caret { color: var(--text-dim); font-size: 0.9em; line-height: 1; }
.theme-menu {
  position: absolute; right: 0; top: calc(100% + 6px);
  min-width: 120px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 4px 0;
  z-index: 200;
  box-shadow: 0 4px 14px rgba(0,0,0,0.18);
}
.theme-menu[hidden] { display: none; }
.theme-menu-item {
  display: block; width: 100%; text-align: left;
  padding: 7px 14px;
  background: none; border: none;
  font: inherit;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 0.85em;
  letter-spacing: 1px;
}
.theme-menu-item:hover { background: var(--bg); color: var(--text); }
.theme-menu-item-active { color: var(--accent); }
.theme-menu-item-active::before { content: "● "; }

/* ─── TABS ────────────────────────────────────────────────── */
.tabs {
  display: flex;
  background: var(--bg-tabs, transparent);
  padding: 0 16px;
  margin: 0 -16px 0;       /* flush against header band above */
}
.tab {
  padding: 12px 18px; cursor: pointer; font-size: 0.85em;
  letter-spacing: 1px; color: var(--text-muted); border: none; background: none;
  border-bottom: 2px solid transparent; transition: all 0.2s;
}
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab:hover:not(.active) { color: var(--text-secondary); }

/* ─── SECTION HEADERS ─────────────────────────────────────── */
.section-label {
  color: var(--accent); font-size: 0.75em; letter-spacing: 3px;
  margin-bottom: 10px; font-weight: 500;
}
.section-label-row {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 10px;
}

/* ─── INPUTS ──────────────────────────────────────────────── */
.input {
  width: 100%; background: var(--bg-input); border: 1px solid var(--border);
  color: var(--text); padding: 10px 14px; font-size: 0.95em; outline: none;
  transition: border-color 0.2s;
}
.input:focus { border-color: var(--accent); }
.input::placeholder { color: var(--text-dim); }
.input-success:focus { border-color: var(--success); }

/* Password input with show/hide eye toggle. The wrap is relative so the eye
   button can absolutely-position over the input's right edge; the input gets
   extra right padding so typed characters don't slide under the icon. */
.password-wrap { position: relative; }
.password-wrap > .input { padding-right: 38px; }
.password-eye {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  background: none; border: none; color: var(--text-muted);
  cursor: pointer; padding: 4px 6px; line-height: 0;
}
.password-eye:hover { color: var(--text); }
.password-eye:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.password-eye svg { width: 18px; height: 18px; display: block; }

.input-row { display: flex; gap: 8px; margin-top: 16px; }
.input-row .input { flex: 1; }

/* ─── BUTTONS ─────────────────────────────────────────────── */
.btn {
  background: var(--accent); color: var(--bg); border: none;
  padding: 12px 24px; font-weight: 700; cursor: pointer;
  letter-spacing: 1px; font-size: 0.95em; transition: all 0.2s;
}
.btn:hover { background: var(--accent-hover); transform: translateY(-1px); }
.btn-full { width: 100%; }
.btn-outline {
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); padding: 10px 16px; font-size: 0.85em;
}
.btn-outline:hover { border-color: var(--text-muted); color: var(--text); transform: none; }
.btn-google {
  background: transparent; border: 1px solid var(--google-blue);
  color: var(--google-blue); display: flex; align-items: center;
  justify-content: center; gap: 10px;
}
.btn-google:hover { background: rgba(66, 133, 244, 0.07); transform: none; }
.btn-small {
  padding: 4px 8px; font-size: 0.75em; letter-spacing: 0;
  border: 1px solid var(--border);
}
.btn-danger { color: var(--danger); }
.btn-success { color: var(--success); }

/* ─── TASK LIST ───────────────────────────────────────────── */
.task-item { margin-bottom: 10px; }
.task-row {
  display: flex; align-items: center; gap: 10px;
}
.task-check {
  color: var(--text-dim); cursor: pointer; font-size: 1.1em;
  transition: color 0.2s; background: none; border: none; padding: 0;
}
.task-check:hover { color: var(--success); }
.task-title { color: var(--text); }
.task-meta { color: var(--text-muted); font-size: 0.8em; margin-left: 26px; }
.task-initiative { color: var(--text-muted); font-size: 0.8em; }
.task-schedule-btn {
  color: var(--text-dim); cursor: pointer; font-size: 0.75em;
  margin-left: auto; background: none; border: none; transition: color 0.2s;
}
.task-schedule-btn:hover { color: var(--accent); }

.badge { font-size: 0.85em; opacity: 0.7; }
/* Effort indicator — vertical pipes scale 1–5 with task duration. Mono +
 * letter-spacing keeps each pipe visually distinct at small sizes. */
.badge-size {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 1.5px;
  font-weight: 600;
  opacity: 0.85;
}
/* Energy indicator — small filled square. Pure CSS so it renders identically
 * on every platform (the prior emoji approach was unreliable). */
.energy-sq {
  display: inline-block;
  width: 9px;
  height: 9px;
  vertical-align: middle;
  border-radius: 1px;
}
/* Energy ramp lives on its own tokens (--energy-low / --energy-med /
 * --energy-high) so the meaning stays clear: moss / warm neutral /
 * dusty red. Pointing these at semantic tokens (success/warning/danger)
 * would conflate "task energy" with "ambient alert level". */
.energy-sq-low    { background: var(--energy-low); }
.energy-sq-medium { background: var(--energy-med); }
.energy-sq-high   { background: var(--energy-high); }
.badge-energy-low    { color: var(--energy-low); }
.badge-energy-medium { color: var(--energy-med); }
.badge-energy-high   { color: var(--energy-high); }

/* ─── CALENDAR TIMELINE ───────────────────────────────────── */
.timeline { border-left: 2px solid var(--border-light); padding-left: 16px; margin-bottom: 24px; }
.timeline-item { margin-bottom: 8px; display: flex; gap: 12px; align-items: baseline; }
.timeline-time { color: var(--text-muted); font-size: 0.85em; min-width: 42px; }
.timeline-title { color: var(--text-secondary); }
.timeline-title-task { color: var(--accent); }
.timeline-duration { color: var(--text-dim); font-size: 0.8em; }
.timeline-free { color: var(--border); }

/* ─── DONE LOG ────────────────────────────────────────────── */
.done-item { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.done-check { color: var(--success); }
.done-title { color: var(--text-secondary); }
.done-time { color: var(--text-dim); font-size: 0.85em; min-width: 42px; }

/* ─── STATUS BAR ──────────────────────────────────────────── */
.status-bar {
  border-top: 1px solid var(--border-light); padding-top: 12px;
  display: flex; gap: 16px; color: var(--text-muted); font-size: 0.8em;
  margin-top: 20px;
}

/* ─── INBOX ITEM ──────────────────────────────────────────── */
.inbox-item {
  border: 1px solid var(--border-light); padding: 14px; margin-bottom: 8px;
  display: flex; justify-content: space-between; align-items: center;
}
.inbox-text { color: var(--text); margin-bottom: 4px; }
.inbox-time { color: var(--text-dim); font-size: 0.8em; }
.inbox-actions { display: flex; gap: 8px; }

/* ─── INITIATIVES ─────────────────────────────────────────── */
.initiative-card {
  border: 1px solid var(--border-light); padding: 14px; margin-bottom: 8px;
}
.initiative-header {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 6px;
}
.initiative-title { color: var(--text); font-weight: 600; }
.initiative-next { color: var(--text-secondary); font-size: 0.85em; }
.progress-bar { letter-spacing: 1px; color: var(--text-muted); font-size: 0.85em; }

/* ─── SCHEDULE MODAL ──────────────────────────────────────── */
.schedule-modal {
  margin-left: 26px; margin-top: 8px; border: 1px solid var(--border);
  padding: 12px; background: var(--bg-elevated);
}
.picker-row { display: flex; gap: 4px; margin-bottom: 10px; }
.picker-btn {
  padding: 4px 10px; border: 1px solid var(--border); color: var(--text-muted);
  cursor: pointer; font-size: 0.8em; background: none; transition: all 0.15s;
}
.picker-btn.active { border-color: var(--accent); color: var(--accent); }
.picker-btn-energy-low.active    { border-color: var(--energy-low);  color: var(--energy-low);  }
.picker-btn-energy-medium.active { border-color: var(--energy-med);  color: var(--energy-med);  }
.picker-btn-energy-high.active   { border-color: var(--energy-high); color: var(--energy-high); }
.schedule-actions { display: flex; gap: 8px; }
.schedule-actions .btn { flex: 1; padding: 8px 0; font-size: 0.8em; }

/* ─── CONTEXT GROUP ───────────────────────────────────────── */
.context-group { margin-bottom: 12px; }
.context-label { color: var(--text-muted); font-size: 0.8em; margin-bottom: 6px; }
.context-tasks { margin-left: 12px; }

/* ─── AUTH PAGES ──────────────────────────────────────────── */
.auth-container {
  min-height: 100vh; display: flex; align-items: center;
  justify-content: center; padding: 20px;
}
.auth-box { max-width: 400px; width: 100%; }
.auth-step { color: var(--text-muted); margin-bottom: 32px; font-size: 0.85em; }
.form-group { margin-bottom: 16px; }
.form-label {
  display: block; color: var(--text-muted); font-size: 0.75em;
  letter-spacing: 2px; margin-bottom: 6px;
}
/* Was a styled <span onclick>; now a real <button> so it's keyboard-focusable
   and announced as a button. These resets make a button render like plain
   underlined accent text. */
.auth-link {
  background: none; border: none; padding: 0; font: inherit;
  color: var(--accent); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.auth-footer { text-align: center; margin-top: 20px; color: var(--text-muted); font-size: 0.85em; }

/* ─── SETTINGS ────────────────────────────────────────────── */
.settings-section {
  border: 1px solid var(--border-light); padding: 20px; margin-bottom: 12px;
}
.settings-row {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 12px;
}
.settings-row:last-child { margin-bottom: 0; }
.settings-label { color: var(--text-secondary); }
.settings-sublabel { color: var(--text-muted); font-size: 0.8em; }
.settings-value {
  color: var(--text-secondary); font-size: 0.9em; padding: 4px 10px;
  border: 1px solid var(--border-light); cursor: pointer;
}
.settings-value-select {
  color: var(--text-secondary); font-size: 0.9em; padding: 4px 10px;
  border: 1px solid var(--border-light); cursor: pointer;
  background: var(--bg-input); font-family: inherit;
}
.settings-value-select:focus { outline: none; border-color: var(--accent); }

/* Toggle switch */
.toggle {
  width: 44px; height: 24px; border-radius: 12px; cursor: pointer;
  position: relative; transition: background 0.2s; border: none;
}
.toggle.on { background: var(--accent); }
.toggle.off { background: var(--border); }
.toggle-knob {
  width: 18px; height: 18px; border-radius: 50%; background: var(--text);
  position: absolute; top: 3px; transition: left 0.2s;
}
.toggle.on .toggle-knob { left: 23px; }
.toggle.off .toggle-knob { left: 3px; }

/* ─── LANDING PAGE ────────────────────────────────────────── */
.landing { min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 40px 20px; }
.landing-content { max-width: 640px; width: 100%; }
.landing-version { opacity: 0.4; font-size: 0.8em; letter-spacing: 4px; margin-bottom: 8px; }
.landing-topbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.landing-topbar .landing-version { margin-bottom: 0; }
.landing-signin { font-size: 0.8em; letter-spacing: 2px; text-decoration: none; }
.landing-signin:hover { text-decoration: underline; text-underline-offset: 3px; }
.landing-hero { font-size: clamp(2rem, 6vw, 3.5rem); font-weight: 700; line-height: 1.1; margin-bottom: 16px; }
.landing-subtitle { font-size: 1.15em; line-height: 1.7; color: var(--text-secondary); }
.landing-subtitle-dim { color: var(--text-muted); }

.terminal-demo {
  background: var(--bg-elevated); border: 1px solid var(--border);
  padding: 24px 28px; margin-bottom: 50px;
}
.terminal-dots { display: flex; gap: 6px; margin-bottom: 16px; }
.terminal-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.terminal-dot-red { background: var(--danger); }
.terminal-dot-yellow { background: var(--warning); }
.terminal-dot-green { background: var(--success); }
.terminal-cmd { margin-bottom: 12px; }
.terminal-prompt { color: var(--accent); }
.terminal-desc { color: var(--text-secondary); margin-left: 18px; margin-top: 2px; font-size: 0.9em; }
.terminal-stats { margin-top: 20px; }

.how-section { margin-bottom: 50px; }
.how-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; }
.how-card { border: 1px solid var(--border-light); padding: 16px; }
.how-num { color: var(--accent); font-size: 0.75em; margin-bottom: 6px; }
.how-title { color: var(--text); font-weight: 600; margin-bottom: 6px; }
.how-desc { color: var(--text-muted); font-size: 0.85em; line-height: 1.5; }

.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 50px; }
.price-card { border: 1px solid var(--border); padding: 20px; position: relative; }
.price-card-pro { border-color: var(--accent); }
.price-badge {
  position: absolute; top: -1px; right: -1px; background: var(--accent);
  color: var(--bg); font-size: 0.65em; font-weight: 700; padding: 3px 8px;
  letter-spacing: 1px;
}
.price-tier { font-size: 0.75em; letter-spacing: 2px; margin-bottom: 8px; }
.price-amount { font-size: 1.8em; margin-bottom: 4px; }
.price-period { color: var(--text-muted); font-size: 0.85em; margin-bottom: 16px; }
.price-features { font-size: 0.85em; line-height: 2; }

.landing-cta { text-align: center; margin-bottom: 40px; }
.landing-cta-note { color: var(--text-muted); font-size: 0.8em; margin-top: 12px; }
.landing-footer { margin-top: auto; padding-top: 60px; color: var(--border); font-size: 0.75em; letter-spacing: 2px; }

/* ─── EMPTY STATE ─────────────────────────────────────────── */
.empty { text-align: center; padding: 40px; color: var(--text-muted); }
.empty-icon { font-size: 2em; margin-bottom: 8px; }

/* ─── TOAST ───────────────────────────────────────────────── */
.toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  background: var(--bg-elevated); border: 1px solid var(--border);
  padding: 12px 20px; z-index: 1000; display: flex; gap: 12px; align-items: center;
  animation: slideUp 0.3s ease;
}
.toast-success { border-color: var(--success); }
.toast-error { border-color: var(--danger); }

/* ─── ANIMATIONS ──────────────────────────────────────────── */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity: 0; transform: translate(-50%, 20px); } to { opacity: 1; transform: translate(-50%, 0); } }
@keyframes blink { 50% { opacity: 0; } }
.fade-in { animation: fadeIn 0.3s ease; }

/* ─── KANBAN ──────────────────────────────────────────────── */
/* The kanban breaks out of the 680px app container — it needs the full
   viewport so columns aren't crushed. We override max-width via a wider
   container that the kanban tab renders inside. */

/* Kanban-only card treatment in light mode. Cards take a warm near-white
   fill and a stronger shadow so they read as raised against the page.
   We DON'T darken the page bg here — the chrome→content ramp puts the
   content surface as the lightest band, and cards lift via shadow + tone.
   --kb-card-bg flows through the .kb-card / .kb-capture-closed rules below;
   keeping it as a var (rather than a direct .kb-card override) means
   .kb-card-ghost's translucent koi fill still wins on drop placement. */
[data-theme="light"]:has(.kb-board-kanban) {
  --kb-card-bg: #faf6ec;     /* parchment — Direction 1 */
  --kb-card-border: var(--border);
  --card-shadow: 0 1px 0 rgba(40,32,18,0.04), 0 6px 18px rgba(40,32,18,0.08);
}
/* Per-column tonal fills. Each column gets its own backdrop tone so
   parchment cards lift via the temperature change rather than via
   chrome stepping. Light theme only — dark stays flat on slate. */
[data-theme="light"] .kb-col[data-col="inbox"]     { background: var(--col-inbox);     border-radius: 10px; padding: 18px 12px 24px; }
[data-theme="light"] .kb-col[data-col="scheduled"] { background: var(--col-scheduled); border-radius: 10px; padding: 18px 12px 24px; }
[data-theme="light"] .kb-col[data-col="done"]      { background: var(--col-done);      border-radius: 10px; padding: 18px 12px 24px; }
[data-theme="light"] .kb-col[data-col="someday"]   { background: var(--col-someday);   border-radius: 10px; padding: 18px 12px 24px; }
[data-theme="light"] .kb-col[data-col="trashed"]   { background: var(--col-trashed);   border-radius: 10px; padding: 18px 12px 24px; }
[data-theme="light"] .kb-col-count {
  background: rgba(255,255,255,0.4);
  border: 1px solid rgba(255,255,255,0.5);
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.95em;
}
[data-theme="light"] .kb-capture-closed {
  background: rgba(255,255,255,0.35);
  border-style: dashed;
  border-color: rgba(0,0,0,0.18);
}

/* ─── Direction 2: Modernist Stripe — kanban specifics ─────────
   Each column reads as a white panel with a 4px coloured top stripe
   in its accent. Cards are pure white with a hairline border, sans
   typography throughout. */
[data-theme="modernist"]:has(.kb-board-kanban) {
  --kb-card-bg: #ffffff;
  --kb-card-border: var(--border);
}
[data-theme="modernist"] body,
[data-theme="modernist"] input,
[data-theme="modernist"] textarea,
[data-theme="modernist"] select,
[data-theme="modernist"] button {
  font-family: var(--sans-body);
}
[data-theme="modernist"] .kb-col[data-col] {
  border-radius: 10px;
  border: 1px solid var(--border-light);
  border-top: 4px solid var(--col-accent, var(--accent));
  padding: 16px 14px 24px;
}
[data-theme="modernist"] .kb-col[data-col="inbox"]     { background: var(--col-inbox);     --col-accent: var(--col-inbox-accent); }
[data-theme="modernist"] .kb-col[data-col="scheduled"] { background: var(--col-scheduled); --col-accent: var(--col-scheduled-accent); }
[data-theme="modernist"] .kb-col[data-col="done"]      { background: var(--col-done);      --col-accent: var(--col-done-accent); }
[data-theme="modernist"] .kb-col[data-col="someday"]   { background: var(--col-someday);   --col-accent: var(--col-someday-accent); }
[data-theme="modernist"] .kb-col[data-col="trashed"]   { background: var(--col-trashed);   --col-accent: var(--col-trashed-accent); }
[data-theme="modernist"] .kb-col-header {
  flex-wrap: wrap;
  align-items: center;
  padding: 4px 4px 10px;
  border-bottom: none;
  font-size: 1em;
  letter-spacing: 0;
}
[data-theme="modernist"] .kb-col-header > span:first-child {
  font-family: var(--sans-display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  display: inline-flex; align-items: center; gap: 8px;
}
/* Leading colour dot before the column name. */
[data-theme="modernist"] .kb-col-header > span:first-child::before {
  content: "";
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--col-accent);
}
[data-theme="modernist"] .kb-col[data-col] .kb-col-header > span:first-child::after {
  font-size: 0.85em;
  color: var(--text-muted);
  margin-left: 4px;
}
[data-theme="modernist"] .kb-col-header::after {
  content: var(--col-sub, "");
  flex-basis: 100%;
  margin-top: 4px;
  font-family: var(--sans-body);
  font-weight: 400;
  font-size: 12.5px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
}
[data-theme="modernist"] .kb-col[data-col="inbox"]     { --col-sub: "Capture inbound"; }
[data-theme="modernist"] .kb-col[data-col="scheduled"] { --col-sub: "On the calendar"; }
[data-theme="modernist"] .kb-col[data-col="done"]      { --col-sub: "Shipped"; }
[data-theme="modernist"] .kb-col[data-col="someday"]   { --col-sub: "On the horizon"; }
[data-theme="modernist"] .kb-col[data-col="trashed"]   { --col-sub: "Recently removed"; }
[data-theme="modernist"] .kb-col-count {
  background: rgba(0,0,0,0.04);
  border: none;
  padding: 2px 7px;
  border-radius: 4px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.85em;
}
[data-theme="modernist"] .kb-card-text {
  font-family: var(--sans-display);
  font-weight: 600;
  font-size: 13.5px;
  letter-spacing: -0.005em;
  line-height: 1.35;
}
[data-theme="modernist"] .kb-card {
  border-radius: 8px;
}
[data-theme="modernist"] .kb-card:hover {
  box-shadow: 0 4px 12px rgba(15,23,42,0.08), 0 1px 2px rgba(15,23,42,0.04);
}
[data-theme="modernist"] .kb-capture-closed {
  border: 1.5px dashed var(--accent);
  background: rgba(37,99,235,0.04);
  color: var(--accent);
  border-radius: 8px;
}

/* ─── Direction 3: Terminal Dark — kanban specifics ──────────────
   Slate panels share a flat bg; each column carries a small filled
   square in its top-left corner as the only chromatic cue. Mono
   typography stays from the body default; subtitles get a // prefix
   in dim grey for the terminal-comment feel. */
[data-theme="terminal"]:has(.kb-board-kanban) {
  --kb-card-bg: var(--bg);
  --kb-card-border: var(--border);
}
[data-theme="terminal"] .kb-col[data-col] {
  position: relative;
  background: var(--bg-elevated);
  border: 1px solid var(--border-light);
  border-radius: 6px;
  padding: 14px 12px 24px;
}
[data-theme="terminal"] .kb-col[data-col]::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 14px; height: 14px;
  background: var(--col-accent, var(--accent));
  border-radius: 6px 0 6px 0;
}
[data-theme="terminal"] .kb-col[data-col="inbox"]     { --col-accent: var(--col-inbox-accent); }
[data-theme="terminal"] .kb-col[data-col="scheduled"] { --col-accent: var(--col-scheduled-accent); }
[data-theme="terminal"] .kb-col[data-col="done"]      { --col-accent: var(--col-done-accent); }
[data-theme="terminal"] .kb-col[data-col="someday"]   { --col-accent: var(--col-someday-accent); }
[data-theme="terminal"] .kb-col[data-col="trashed"]   { --col-accent: var(--col-trashed-accent); }
[data-theme="terminal"] .kb-col-header {
  flex-wrap: wrap;
  align-items: baseline;
  padding: 4px 4px 10px 22px;       /* 22px left clears the corner marker */
  border-bottom: none;
  font-size: 1em;
}
[data-theme="terminal"] .kb-col-header > span:first-child {
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
}
[data-theme="terminal"] .kb-col-header::after {
  content: var(--col-sub, "");
  flex-basis: 100%;
  margin-top: 3px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
}
[data-theme="terminal"] .kb-col[data-col="inbox"]     { --col-sub: "// needs a decision"; }
[data-theme="terminal"] .kb-col[data-col="scheduled"] { --col-sub: "// on the calendar"; }
[data-theme="terminal"] .kb-col[data-col="done"]      { --col-sub: "// this week"; }
[data-theme="terminal"] .kb-col[data-col="someday"]   { --col-sub: "// on the horizon"; }
[data-theme="terminal"] .kb-col[data-col="trashed"]   { --col-sub: "// 30-day window"; }
[data-theme="terminal"] .kb-card {
  border-radius: 4px;
}

.kb-filters {
  display: flex; flex-wrap: wrap; gap: 6px;
  background: var(--bg-filters, transparent);
  padding: 12px 16px;
  margin: 0 -16px 16px;    /* flush against tabs above, breathe before content */
}
.kb-chip {
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); padding: 6px 12px; font-size: 0.75em;
  font-family: inherit; letter-spacing: 1px; cursor: pointer;
  transition: all 0.15s;
}
.kb-chip:hover { color: var(--text); border-color: var(--text-muted); }
.kb-chip-active { color: var(--accent); border-color: var(--accent); }

/* Columns show/hide dropdown — sits at the right end of the filter bar.
   margin-left:auto pushes it past whatever chips are present. The popover
   anchors to the trigger; checkboxes drive kanbanToggleHidden. */
.kb-cols-wrap { position: relative; margin-left: auto; }
.kb-cols-toggle-open { color: var(--accent); border-color: var(--accent); }
.kb-cols-menu {
  position: absolute; right: 0; top: calc(100% + 4px);
  background: var(--bg-elevated); border: 1px solid var(--border);
  min-width: 160px; z-index: 100; padding: 4px 0;
}
.kb-cols-item {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 12px; cursor: pointer; color: var(--text);
  font-size: 0.85em; user-select: none;
}
.kb-cols-item:hover { background: var(--bg); }
.kb-cols-item input { cursor: pointer; accent-color: var(--accent); margin: 0; }

/* The board is a flex row of columns that share the available width.
   On wide viewports the five columns flow to fit the container; on the
   medium range (900px–1100px) min-width can push columns past the edge
   and the overflow-x: auto fallback lets the row scroll. Below 900px
   the layout switches entirely to the tabbed mode (.kb-tabs above the
   board) — see the @media block at the bottom of this file. */
.kb-board {
  display: flex; gap: 12px;
  align-items: flex-start;   /* columns size to their own content, not the tallest */
  overflow-x: auto; overflow-y: visible;
  padding-bottom: 12px;
  scrollbar-width: thin;
}
.kb-col {
  flex: 1 1 0; min-width: 240px;
  display: flex; flex-direction: column;
}

/* ── Week view ──────────────────────────────────────────────────
   The Week tab reuses .kb-board / .kb-col / .kb-card but its
   columns are days, not status buckets. The toggle strip sits
   above the filter row; the board layout is identical to the
   home kanban. */
.week-nav {
  display: flex; align-items: center; gap: 12px;
  margin: 0 16px 12px;
  padding-top: 4px;
  flex-wrap: wrap;
}
.week-toggle-group {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
}
.week-toggle {
  padding: 6px 16px;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.85em;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 200ms cubic-bezier(.2,.8,.2,1), color 200ms cubic-bezier(.2,.8,.2,1);
}
.week-toggle:hover { color: var(--text); }
.week-toggle.active {
  background: var(--text);
  color: var(--bg);
}
.week-range {
  font-size: 0.85em;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.week-badge {
  margin-left: auto;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(200,57,46,0.08);
  color: var(--accent);
  font-size: 0.75em;
  letter-spacing: 0.1em;
  font-weight: 600;
}

/* ── Week calendar grid ──────────────────────────────────────
   Real calendar layout: hour rail on the left, seven day columns to the
   right, events positioned absolutely by start time, height proportional
   to duration. Overlapping events split horizontally into equal tracks.
   The Inbox column lives in a sidebar to the left of the grid so the
   drag gesture still flows left-to-right.

   Geometry constants (kept in lockstep with WEEK_CAL_* in app.js):
     start hour: 6 AM,  end hour: 24 (midnight),  hour height: 56px. */
.week-layout {
  display: flex;
  gap: 16px;
  align-items: stretch;
  /* Cap the whole region so the Inbox sidebar can't grow taller than the
     calendar. Both children scroll internally. The 200px subtracts an
     estimate of the page chrome above (header + tabs + filters + week nav
     + padding). */
  max-height: calc(100vh - 200px);
}
.week-inbox-side {
  flex: 0 0 280px;
  max-width: 280px;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* "Autoschedule all" — quiet text link at the top of the Week Inbox.
   Reads as a secondary action; the koi accent only surfaces on hover so
   the bar doesn't pull weight from the cards. Running state stays muted
   but pulses subtly so the user can see the iteration progressing. */
.week-bulk-autoschedule {
  display: block; width: 100%;
  margin: 6px 0 8px;
  padding: 2px 4px;
  background: transparent;
  border: 0;
  color: var(--text-muted);
  font: inherit; font-size: 0.8em; letter-spacing: 0.02em;
  text-align: left;
  cursor: pointer;
  transition: color 200ms cubic-bezier(.2,.8,.2,1);
}
.week-bulk-autoschedule:hover { color: var(--accent); }
.week-bulk-status {
  margin: 6px 0 8px;
  padding: 2px 4px;
  color: var(--text-muted);
  font-size: 0.8em;
  letter-spacing: 0.02em;
  text-align: left;
  font-style: italic;
  animation: kb-pulse-dim 1.4s ease-in-out infinite;
}

.week-inbox-side .kb-col {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  display: flex;
  flex-direction: column;
}
.week-inbox-side .kb-col-body {
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 0;
}
.week-cal-wrap {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex; flex-direction: column;
  border: 1px solid var(--border-light);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-elevated);
}
.week-cal-headers {
  display: grid;
  grid-template-columns: 56px repeat(7, 1fr);
  border-bottom: 1px solid var(--border-light);
}
.week-cal-corner {
  border-right: 1px solid var(--border-light);
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.7em;
  color: var(--text-dim);
  display: flex; align-items: center; justify-content: center;
  letter-spacing: 0.04em;
  padding: 8px 4px;
}
.week-cal-corner-label { color: var(--text-muted); }
.week-cal-head {
  padding: 10px 12px;
  border-right: 1px solid var(--border-light);
  text-align: left;
}
.week-cal-head:last-child { border-right: 0; }
.week-cal-head-day {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.65em;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  margin-bottom: 2px;
}
.week-cal-head-num {
  font-size: 1.6em;
  font-weight: 500;
  color: var(--text);
  line-height: 1;
}
.week-cal-head-today .week-cal-head-num { color: var(--accent); }
.week-cal-head-today {
  box-shadow: inset 0 -2px 0 var(--accent);
}
.week-cal-allday-row {
  display: grid;
  grid-template-columns: 56px repeat(7, 1fr);
  border-bottom: 1px solid var(--border-light);
  min-height: 32px;
}
.week-cal-allday-cell {
  border-right: 1px solid var(--border-light);
  padding: 4px 6px;
  display: flex; flex-direction: column; gap: 3px;
}
.week-cal-allday-cell:last-child { border-right: 0; }
.week-cal-allday {
  padding: 3px 8px;
  background: rgba(96,144,224,0.16);
  color: var(--text);
  font-size: 0.72em;
  border-radius: 3px;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.week-cal-allday-link:hover { background: rgba(96,144,224,0.28); }

.week-cal-body {
  display: grid;
  grid-template-columns: 56px repeat(7, 1fr);
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  position: relative;
}
.week-cal-rail {
  position: relative;
  border-right: 1px solid var(--border-light);
}
.week-cal-rail-label {
  position: absolute;
  right: 6px;
  margin-top: -7px;             /* center the label on the hour line */
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.65em;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.week-cal-col {
  position: relative;
  border-right: 1px solid var(--border-light);
  /* The drop guideline appears at --week-cal-hint-y while drag-over. */
  transition: background 120ms ease;
}
.week-cal-col:last-child { border-right: 0; }
.week-cal-col-today { background: rgba(200,57,46,0.025); }
/* Drag-preview ghost block. Sized to the source task's size (height comes
   from --week-cal-hint-h, set by weekCalDragOver). Sits at the snap-Y so
   the user previews exactly where + how big the task lands. The column
   itself doesn't get the inherited kanban dragover chrome (dashed
   outline + koi wash) — the ghost block alone is enough. */
.week-cal-col.kb-col-dragover {
  outline: none;
  background: transparent;
}
.week-cal-col-today.kb-col-dragover {
  background: rgba(200,57,46,0.025);   /* keep the faint today tint */
}
.week-cal-col.kb-col-dragover::before {
  content: '';
  position: absolute; left: 2px; right: 2px;
  top: var(--week-cal-hint-y, 0);
  height: var(--week-cal-hint-h, 28px);
  background: rgba(200,57,46,0.14);
  border-radius: 3px;
  pointer-events: none;
  transition: top 80ms ease, height 80ms ease;
}
.week-cal-hour-line {
  position: absolute; left: 0; right: 0;
  border-top: 1px solid var(--border-light);
  pointer-events: none;
}

/* Current-time indicator: a 2px accent line across today's column with a
   dot at its left edge. Theme-aware via --accent (our take, not Google's
   red). Sits above event/task blocks; never intercepts pointer/drag. */
.week-cal-now {
  position: absolute; left: 0; right: 0;
  border-top: 2px solid var(--accent);
  z-index: 5;
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .week-cal-now { transition: top 300ms ease; }
}
.week-cal-now-dot {
  position: absolute; left: -3px; top: -4px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

/* Event + task blocks: absolute-positioned, height = duration × hour-px.
   Inner layout is title + small time chip stacked, clipped at the bottom
   when a block is short. Cursor:grab on tasks signals they're draggable;
   events are pointer for the GCal link. */
.week-cal-event,
.week-cal-task {
  position: absolute;
  box-sizing: border-box;
  padding: 4px 6px;
  border-radius: 3px;
  font-size: 0.75em;
  line-height: 1.2;
  overflow: hidden;
  text-decoration: none;
  display: flex; flex-direction: column; gap: 2px;
}
.week-cal-event {
  background: rgba(96,144,224,0.85);
  color: #fff;
  border-left: 3px solid rgba(96,144,224,1);
  cursor: pointer;
}
.week-cal-event:hover { background: rgba(96,144,224,1); }
/* Editable events become drag sources; the cursor telegraphs that. The
   lock variant keeps the block looking like a calendar item but signals
   it can't be moved (declined invites, shared calendars without write
   access). */
.week-cal-event-editable[draggable="true"] { cursor: grab; }
.week-cal-event-editable[draggable="true"]:active { cursor: grabbing; }
.week-cal-event-locked { opacity: 0.7; cursor: pointer; }
.week-cal-event-lock {
  margin-right: 4px;
  font-size: 0.85em;
  opacity: 0.85;
}
.week-cal-task {
  background: var(--bg-elevated);
  border: 1px solid var(--accent);
  border-left: 3px solid var(--accent);
  color: var(--text);
  cursor: grab;
  box-shadow: 0 1px 0 rgba(40,32,18,0.08);
}
.week-cal-task:hover { box-shadow: 0 2px 8px rgba(40,32,18,0.12); }
.week-cal-task[draggable="true"]:active { cursor: grabbing; }
.week-cal-task.kb-card-overdue { border-color: var(--danger); border-left-color: var(--danger); }
.week-cal-block-title {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.week-cal-block-time {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.85em;
  opacity: 0.85;
  letter-spacing: 0.02em;
}

/* Theme variants for blocks. The default greens/blues above are tuned for
   dark; light theme softens them so they don't shout on parchment. */
[data-theme="light"] .week-cal-event {
  background: rgba(96,144,224,0.18);
  color: var(--text);
  border-left-color: #6090e0;
}
[data-theme="light"] .week-cal-event:hover { background: rgba(96,144,224,0.28); }
[data-theme="light"] .week-cal-allday {
  background: rgba(96,144,224,0.15);
  color: var(--text-secondary);
}
[data-theme="light"] .week-cal-task {
  background: #faf6ec;
}
[data-theme="terminal"] .week-cal-event {
  background: rgba(96,144,224,0.18);
  color: var(--text);
  border-left-color: #6090e0;
}
[data-theme="modernist"] .week-cal-event {
  background: rgba(37,99,235,0.15);
  color: var(--text);
  border-left-color: #2563eb;
}
[data-theme="modernist"] .week-cal-allday {
  background: rgba(37,99,235,0.12);
  color: var(--text-secondary);
}
[data-theme="modernist"] .week-cal-task {
  background: #ffffff;
  border-color: #2563eb;
  border-left-color: #2563eb;
}

/* Mobile column-switcher tabs. Pill bar sits above .kb-board-kanban and
   is hidden on desktop. Horizontally scrollable in case the column-name +
   count pills don't all fit on small screens. */
.kb-tabs {
  display: none; /* shown by the narrow-viewport media query */
  gap: 6px;
  margin-bottom: 12px;
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}
.kb-tab {
  flex: 0 0 auto;
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); padding: 10px 14px;
  font-family: inherit; font-size: 0.75em; letter-spacing: 2px;
  cursor: pointer; white-space: nowrap;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.kb-tab:hover { color: var(--text); border-color: var(--text-muted); }
.kb-tab-active {
  color: var(--accent); border-color: var(--accent);
  background: var(--bg-elevated);
}
.kb-tab-count {
  display: inline-block; margin-left: 8px;
  color: var(--text-muted); font-size: 0.95em; letter-spacing: 0;
}
.kb-tab-active .kb-tab-count { color: var(--accent); }
.kb-col-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  color: var(--text); font-size: 0.75em; letter-spacing: 3px;
  /* Title sits in --text now, not --accent. The koi was carrying every
     column header at full volume; under the Sumi rule it's reserved
     for active state / overdue / brand. The JP gloss below adds back
     the colour interest without competing for attention. */
}
.kb-col-count {
  color: var(--text-dim); font-size: 1em; letter-spacing: 0;
  /* Count is plain text now — pill chrome removed. */
}

/* Japanese column gloss. The English header inherits the column name
   from app.js (`INBOX`, `SCHEDULED`, etc.); we append the matching JP
   character in the mincho stack via ::after, keyed off the data-col
   attribute that _dropAttrs writes onto each .kb-col wrapper. Gloss
   sits dimmer + larger than the English so it reads as ornament. */
.kb-col[data-col="inbox"]     .kb-col-header > span:first-child::after { content: " 受信";   }
.kb-col[data-col="scheduled"] .kb-col-header > span:first-child::after { content: " 予定";   }
.kb-col[data-col="done"]      .kb-col-header > span:first-child::after { content: " 完了";   }
.kb-col[data-col="someday"]   .kb-col-header > span:first-child::after { content: " いつか"; }
.kb-col[data-col="trashed"]   .kb-col-header > span:first-child::after { content: " ゴミ箱"; }
.kb-col[data-col] .kb-col-header > span:first-child::after {
  font-family: var(--jp);
  font-size: 1.05em;            /* slightly larger than the tracking-out English */
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-left: 6px;
  font-weight: 400;
}

/* Direction 1 — Editorial Sumi column headers.
   Light theme only. The header becomes a wrap-friendly flex row that
   carries title + count on row 1, and a subtitle injected via ::after
   on row 2. Subtitle text comes from --col-sub per data-col. Title gets
   the Fraunces serif treatment; kanji gloss sits inline next to it.
   Dark theme keeps the tracked-caps mono header unchanged. */
[data-theme="light"] .kb-col-header {
  flex-wrap: wrap;
  align-items: baseline;
  padding: 4px 4px 10px;
  border-bottom: none;
  font-size: 1em;
  letter-spacing: 0;
}
[data-theme="light"] .kb-col-header > span:first-child {
  font-family: var(--serif-display);
  font-weight: 500;
  font-size: 22px;
  letter-spacing: -0.01em;
  color: var(--text);
  text-transform: none;
}
[data-theme="light"] .kb-col[data-col] .kb-col-header > span:first-child::after {
  font-size: 0.6em;
  color: var(--text-muted);
}
[data-theme="light"] .kb-col-header::after {
  content: var(--col-sub, "");
  flex-basis: 100%;
  margin-top: 4px;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
[data-theme="light"] .kb-col[data-col="inbox"]     { --col-sub: "Capture · capture. Decide later."; }
[data-theme="light"] .kb-col[data-col="scheduled"] { --col-sub: "Plan · on the calendar."; }
[data-theme="light"] .kb-col[data-col="done"]      { --col-sub: "Ship · finished this week."; }
[data-theme="light"] .kb-col[data-col="someday"]   { --col-sub: "Dream · maybe one day."; }
[data-theme="light"] .kb-col[data-col="trashed"]   { --col-sub: "Archive · restorable for 30 days."; }

/* Card title gets the serif in light theme. The mono stays everywhere
   else so meta, chips, and counts keep their technical precision. */
[data-theme="light"] .kb-card-text {
  font-family: var(--serif-display);
  font-weight: 500;
  font-size: 15px;
  letter-spacing: -0.005em;
  line-height: 1.3;
}
.kb-col-body {
  flex: 1; overflow-y: auto; padding: 8px;
  display: flex; flex-direction: column; gap: 8px;
}
.kb-empty {
  color: var(--text-dim); font-size: 0.85em; text-align: center;
  padding: 24px 8px;
}

/* Quiet capture row — sits at the top of the Inbox column. Closed state is a
   barely-there "+" that opens into a real input on click. The row eats far
   less vertical space than the old wide capture bar so cards get more room. */
.kb-capture-row {
  width: 100%; box-sizing: border-box;
  font: inherit; color: inherit;
  background: transparent; border: 0; outline: 0;
  padding: 0;
}
.kb-capture-closed {
  display: block; position: relative;
  background: transparent;          /* empty-slot — page bg shows through */
  border: 1px dashed var(--border); /* slightly more present than dotted at this scale */
  color: var(--text-dim); font-size: 1.1em; line-height: 1;
  padding: 10px 12px;
  text-align: center;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.kb-capture-closed:hover,
.kb-capture-closed:focus-visible {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--bg-elevated);   /* fills toward card-state so the click feels pre-committed */
}
.kb-capture-input {
  width: 100%; box-sizing: border-box;
  background: var(--bg-input);
  border: 1px solid var(--accent);
  color: var(--text);
  font-family: inherit; font-size: 0.9em;
  padding: 6px 10px;
  outline: none;
}
.kb-capture-input::placeholder { color: var(--text-dim); }

/* Cards. Sumi card chrome: solid 1px border in --border, a faint inset
   top highlight (--card-top) catching imaginary overhead light, and a
   soft drop (--card-shadow) so the card reads as resting on the board.
   Sits inside the same warm palette as everything else — no orange
   theatrics. The dotted-hairline + transparent-bg pattern of the old
   theme was the "lots of lines" complaint; this is the answer.
   v7 layout: two-column flex — .kb-card-body on the left holds title,
   assignee-at-rest, and the .kb-card-hover-content reveal layer; the
   .kb-card-boxes stack lives on the right, anchored top, fixed-width
   so it doesn't shift when the body expands on hover. */
.kb-card {
  border: 1px solid var(--kb-card-border, var(--border));
  background: var(--kb-card-bg, var(--bg-elevated));
  padding: 10px 12px;
  position: relative;
  box-shadow: inset 0 1px 0 var(--card-top), var(--card-shadow);
  transition: border-color 240ms cubic-bezier(.2,.8,.2,1), box-shadow 240ms cubic-bezier(.2,.8,.2,1);
  display: flex;
  gap: 12px;
  align-items: flex-start;
}
.kb-card:hover {
  border-color: var(--text-dim);   /* warm-grey lift; vermillion stays reserved */
}
.kb-card-body {
  flex: 1 1 0;
  min-width: 0;
  /* Hard cap so a long unbreakable title can't push past the body's flex
     share and overlap the energy box stack on the right. */
  max-width: 100%;
  overflow: hidden;
}

/* Vertical 5-box energy/effort stack on the right of each card.
   column-reverse packs filled boxes at the bottom (fuel-gauge feel —
   bigger effort = taller stack). The whole column wears one colour
   via data-energy; empty boxes share that hue as a border so the
   strip reads as one element. align-self: flex-start anchors the
   stack to the card's top so it doesn't move when the body expands. */
.kb-card-boxes {
  display: flex;
  flex-direction: column-reverse;
  gap: 3px;
  flex-shrink: 0;
  align-self: flex-start;
  padding-top: 2px;
  cursor: pointer;              /* clickable surface — clicks bubble to card → opens drawer */
}
.kb-card-boxes span {
  width: 7px; height: 7px;
  background: transparent;
  border: 1px solid var(--energy-med);
  border-radius: 1px;
}
.kb-card-boxes span.on { background: var(--energy-med); }
.kb-card-boxes[data-energy="high"] span    { border-color: var(--energy-high); }
.kb-card-boxes[data-energy="high"] span.on { background: var(--energy-high); }
.kb-card-boxes[data-energy="low"] span     { border-color: var(--energy-low); }
.kb-card-boxes[data-energy="low"] span.on  { background: var(--energy-low); }

/* Box-stack tooltip. Singleton element appended to <body> by the
 * mousemove handler in app.js — lives outside the card / column /
 * flex tree entirely so its layout is never affected by overflow,
 * transforms, or stacking-context tricks elsewhere.
 * Positioning: bubble's bottom-right corner sits a touch above-and-
 * past the cursor X, so the downward arrow on the right side of
 * the bubble lands directly above the cursor. Above-and-left of the
 * cursor as a whole. */
.kb-tip {
  position: fixed;
  left: 0; top: 0;
  transform: translate(calc(-100% + 14px), calc(-100% - 10px));
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-family: var(--mono, inherit);
  font-size: 11px;
  line-height: 1.3;
  letter-spacing: 0.02em;
  padding: 6px 10px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 100ms ease;
  box-shadow: inset 0 1px 0 var(--card-top), 0 4px 14px rgba(0,0,0,0.32);
  z-index: 2147483647;
}
.kb-tip.kb-tip-show { opacity: 1; }
.kb-tip strong { color: var(--text); font-weight: 500; }
.kb-tip-energy[data-energy="low"]    { color: var(--success); }
.kb-tip-energy[data-energy="medium"] { color: var(--warning); }
.kb-tip-energy[data-energy="high"]   { color: var(--danger); }

/* Speech-bubble tail — simple downward triangle on the right side
 * of the bubble, just inside the corner so it visually "hangs" from
 * the bottom edge pointing at the cursor below. Two pseudo elements:
 * the OUTER triangle in --border colour, the INNER offset 1px so
 * the border continues along the tail's slanted edges. */
.kb-tip::before,
.kb-tip::after {
  content: '';
  position: absolute;
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
}
.kb-tip::before {
  top: 100%;
  right: 8px;
  border-top: 7px solid var(--border);   /* outer — provides the slanted border */
}
.kb-tip::after {
  top: calc(100% - 1px);                  /* 1px up so the inner fills inside the outer */
  right: 9px;
  border-top: 6px solid var(--bg-elevated);
}

/* Hover reveal. Chips, waiting banner, and the actions row all live
   inside .kb-card-hover-content. Hidden at rest on desktop, slides
   open in 320ms (opacity delayed 60ms so the slide arrives first).
   Touch devices keep it open at all times — no hover to drive it.
   .kb-card-scheduling forces it open mid-action (inline scheduler);
   :focus-within keeps it open while a control inside has focus, so
   keyboard tab-into works and clicking inputs doesn't collapse the
   layer. */
.kb-card-hover-content {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition:
    max-height 420ms cubic-bezier(.2,.8,.2,1),
    opacity 320ms ease 80ms,
    margin 420ms cubic-bezier(.2,.8,.2,1);
  margin-top: 0;
}
@media (hover: hover) and (pointer: fine) {
  .kb-card:hover .kb-card-hover-content,
  .kb-card:focus-within .kb-card-hover-content {
    max-height: 600px;
    opacity: 1;
    margin-top: 8px;
  }
}
@media (hover: none), (pointer: coarse) {
  .kb-card-hover-content { max-height: 600px; opacity: 1; margin-top: 8px; }
}
.kb-card.kb-card-scheduling .kb-card-hover-content {
  max-height: 600px;
  opacity: 1;
  margin-top: 8px;
}
/* Week-view inbox cards: drop the accordion entirely so chips + actions are
   always visible. Avoids the slide-open vs card-fade-out animation collision
   when the user clicks an action mid-hover. Kanban-tab cards are unaffected. */
.kb-card-week .kb-card-hover-content {
  max-height: none;
  opacity: 1;
  margin-top: 8px;
  transition: none;
}
.kb-card-week .kb-card-text { cursor: pointer; }

/* Overdue: 2px koi left edge. Compensate padding so title baseline
   doesn't shift between overdue and on-time cards. */
.kb-card-overdue {
  border-left: 2px solid var(--accent);
  padding-left: 10px;   /* 12 default → 10 with the 2px stripe restoring the 12px effective gap */
}
/* ::before colored edges retired (kept the rule shells dormant in case any
   per-status hooks are still referenced elsewhere; no visible output). */
.kb-card-needs-horizon { border-color: var(--danger); }

.kb-card-text {
  color: var(--text); font-size: 0.9em; line-height: 1.4;
  margin-bottom: 4px;
  /* `anywhere` + `break-word` together force long tokens (emails, URLs) to
     wrap at character boundaries when no whitespace break is available,
     so the title can't push past the body's flex share and overlap the
     vertical energy box stack on the right. */
  overflow-wrap: anywhere;
  word-break: break-word;
  hyphens: auto;
  min-width: 0;
  max-width: 100%;
}
/* Size+energy badge group, anchored to the card's top-right. Sits above the
 * title; the title's right padding (set per-column where needed) keeps long
 * titles from running underneath. */
.kb-card-badges-tr {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 0.85em;
  line-height: 1;
  pointer-events: none;
}
/* v7: the scheduled-card right padding was reserving room for the old
   top-right badge slot (.kb-card-badges-tr). Boxes now live as a sibling
   flex column outside .kb-card-body, so no reserved space is needed. */
/* Done cards keep the card chrome but quiet down — drop the drop-shadow
 * (so they don't "float" like in-flight cards) and dim the text to muted.
 * Strikethrough stays reserved for trashed so the two states keep their
 * meaning distinct (done = completed work / trashed = removed).
 * v7: also hide the box stack, the assignee, and the hover content —
 * archival records don't need energy / ownership / actions. */
.kb-card-done {
  box-shadow: inset 0 1px 0 var(--card-top);  /* keep the inset, drop the float */
}
.kb-card-done .kb-card-text { color: var(--text-muted); }
.kb-card-done .kb-card-boxes,
.kb-card-done .kb-card-assignee,
.kb-card-done .kb-card-hover-content { display: none; }

.kb-card-trashed .kb-card-text { color: var(--text-muted); text-decoration: line-through; }
.kb-card-trashed .kb-card-boxes,
.kb-card-trashed .kb-card-assignee,
.kb-card-trashed .kb-card-hover-content { display: none; }
.kb-card-meta {
  color: var(--text-muted); font-size: 0.75em; line-height: 1.4;
  margin-bottom: 4px;
}

/* Assignee at rest — v7 pattern. Plain small text under the title,
 * not a bordered chip. The bullet (● / ○) lives in the markup and
 * inherits the text colour. Hidden on done / trashed cards (archival
 * records — ownership isn't useful any more). */
.kb-card-assignee {
  display: inline-block;
  margin: 4px 0 0;
  padding: 0;
  font-size: 0.7em; letter-spacing: 0;
  font-family: inherit;
  color: var(--text-muted);
  background: transparent;
  border: 0;
}
.kb-card-assignee-unassigned {
  color: var(--text-dim);
}

/* Inline assignee dropdown on raw inbox cards. Matches the .kb-action
 * button styling exactly so it reads as part of the card's action row
 * rather than a form field. Sits where the captured-at timestamp used
 * to live. */
.kb-inbox-assignee-select {
  display: inline-block;
  margin: 4px 0 6px;
  padding: 3px 7px;
  font-size: 0.7em; letter-spacing: 0;
  font-family: inherit;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
  max-width: 100%;
  transition: all 0.15s;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  /* small caret rendered as a background image so the select looks like
     the surrounding buttons but still hints at being a dropdown */
  padding-right: 18px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 9px) 55%, calc(100% - 5px) 55%;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
.kb-inbox-assignee-select:hover { color: var(--text); border-color: var(--text-muted); }
.kb-inbox-assignee-select:focus { outline: 1px solid var(--accent); outline-offset: 1px; }

.kb-waiting {
  color: var(--warning); font-size: 0.75em; margin: 6px 0;
  padding: 4px 8px; background: rgba(181, 138, 62, 0.08);  /* matches sumi --warning */
  border-left: 2px solid var(--warning);
}
.kb-waiting-edit {
  display: flex; gap: 4px; margin: 6px 0; flex-wrap: wrap;
}
.kb-waiting-edit .input { padding: 4px 8px; font-size: 0.8em; flex: 1 1 100%; }

/* Actions container is now a column of two rows — paired by intent.
 * Row 1 = in-flight ops (Schedule / Reschedule / Auto-reschedule /
 * Someday / Inbox). Row 2 = terminal ops (Done / Trash). The 10px
 * gap between rows reads as deliberate semantic separation, not
 * accidental stacking. */
.kb-card-actions {
  display: flex; flex-direction: column; gap: 10px; margin-top: 8px;
}
.kb-card-actions-row {
  display: flex; flex-wrap: wrap; gap: 14px;
}
/* Action buttons render as text links — no border, no padding. The orange
   on hover is reserved for here and the column wake-up; cards stay grey. */
.kb-action {
  background: transparent; border: none;
  color: var(--text-secondary); padding: 0; font-size: 0.7em;
  font-family: inherit; letter-spacing: 0; cursor: pointer;
  transition: color 0.15s;
}
.kb-action:hover { color: var(--accent); }
.kb-action:disabled, .kb-action[disabled] {
  opacity: 0.55; cursor: progress; pointer-events: none;
}
.kb-event-link {
  display: inline-block; margin-left: 6px; font-size: 0.85em;
  text-decoration: none; opacity: 0.65; transition: opacity 0.15s;
}
.kb-event-link:hover { opacity: 1; }
.kb-action-success:hover { color: var(--success); }
.kb-action-danger { color: var(--text-dim); padding: 0; }
.kb-action-danger:hover { color: var(--danger); }
.kb-action-quiet {
  color: var(--text-dim); padding: 0;
  font-size: 0.65em; opacity: 0.7;
}
.kb-action-quiet:hover {
  color: var(--warning); opacity: 1;
}

/* Hidden on desktop — these duplicate column-move actions that drag covers.
   Re-shown at the same breakpoint the kanban switches to single-column tabs. */
.kb-action-drag-only { display: none; }
@media (max-width: 900px) {
  .kb-action-drag-only { display: inline-block; }
}
.kb-card-actions-secondary { margin-top: 4px; }

/* Locked actions row — task is assigned to someone other than the current
 * user. Children are visually disabled and unclickable. The wrapper retains
 * pointer-events so its `title` tooltip surfaces on hover, explaining why.
 * Card click → edit form is intentionally still allowed (handled at the
 * card level) so the user can reassign as the unblock path. */
.kb-card-actions-locked .kb-action,
.kb-card-actions-locked .kb-action-success,
.kb-card-actions-locked .kb-action-danger,
.kb-card-actions-locked .kb-action-quiet {
  opacity: 0.4;
  pointer-events: none;
  cursor: not-allowed;
}

/* Drag-and-drop states */
.kb-card[draggable="true"] { cursor: grab; }
.kb-card[draggable="true"]:active { cursor: grabbing; }
.kb-card-dragging { opacity: 0.4; }
.kb-col { transition: background 0.15s, opacity 0.18s ease; }
.kb-col-dragover {
  background: rgba(200, 57, 46, 0.06);   /* koi-tinted drop wash */
  outline: 2px dashed var(--accent); outline-offset: -2px;
}

/* Desktop column dulling — at rest each column sits at 0.7 opacity, the column
   under the mouse restores to 1.0. Touch/mobile keeps everything full strength
   since hover is not a usable state. The (hover:hover)+(pointer:fine) guard
   excludes touch devices and pen input. Drag-over forces full opacity so the
   destination column stays clearly readable while you're aiming a drop. */
@media (hover: hover) and (pointer: fine) {
  .kb-col { opacity: 0.7; }
  .kb-col:hover,
  .kb-col-dragover { opacity: 1; }
}
/* Ghost card placeholder — shown in Scheduled while picking a time after drop. */
.kb-card-ghost {
  border-style: dashed !important;
  background: rgba(200, 57, 46, 0.04);   /* koi-tinted ghost — matches drop wash */
  box-shadow: none;                       /* ghost doesn't get the resting card lift */
  animation: ghost-fade-in 0.15s ease-out;
}
.kb-card-meta-ghost {
  color: var(--accent) !important; font-size: 0.7em; letter-spacing: 1.5px;
  margin-top: 2px;
}
@keyframes kb-card-arrive {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.kb-card-arriving { animation: kb-card-arrive 0.28s ease-out; }

@keyframes kb-card-leave {
  to { opacity: 0; transform: translateY(-4px); }
}
.kb-card-leaving {
  animation: kb-card-leave 0.16s ease-in forwards;
  pointer-events: none;
}

@keyframes kb-pulse-dim {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
.kb-action-pulsing { animation: kb-pulse-dim 1.1s ease-in-out infinite; }

@keyframes ghost-fade-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Inline scheduler panel — appears inside a card when scheduling */
.kb-scheduler {
  margin-top: 8px; padding: 10px; background: var(--bg-elevated);
  border: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px;
}
.kb-scheduler-label {
  color: var(--text-muted); font-size: 0.7em; letter-spacing: 2px;
}
.kb-scheduler-input {
  background: var(--bg-input); border: 1px solid var(--border);
  color: var(--text); padding: 6px 8px; font-family: inherit; font-size: 0.85em;
}
.kb-scheduler-input:focus { outline: none; border-color: var(--accent); }
.kb-scheduler-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.kb-chip-mini {
  background: transparent; border: 1px solid var(--border); color: var(--text-muted);
  padding: 3px 8px; font-size: 0.7em; font-family: inherit; cursor: pointer;
  transition: all 0.15s;
}
.kb-chip-mini:hover { color: var(--accent); border-color: var(--accent); }
.kb-scheduler-actions { display: flex; gap: 6px; }

/* Inline edit form — appears inside a card when the user clicks it */
.kb-card-editing { cursor: default !important; }
.kb-card-editing::before { background: var(--accent) !important; }
.kb-edit-form {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 4px; padding: 10px;
  background: var(--bg-elevated); border: 1px solid var(--border);
}

/* Open animation. The card itself is a fresh DOM node after re-render,
 * so we can't smoothly transition height — instead the form fades and
 * slides down, and rows cascade a few ms behind so it reads as the card
 * unfolding rather than blinking into place. */
@keyframes kb-edit-form-open {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.kb-edit-form {
  transform-origin: top center;
  animation: kb-edit-form-open 0.18s ease-out;
}
@keyframes kb-edit-row-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: translateY(0); }
}
.kb-edit-form > .kb-edit-row,
.kb-edit-form > .kb-edit-grid,
.kb-edit-form > .kb-edit-actions {
  animation: kb-edit-row-in 0.22s ease-out backwards;
}
.kb-edit-form > *:nth-child(1) { animation-delay: 0.06s; }
.kb-edit-form > *:nth-child(2) { animation-delay: 0.09s; }
.kb-edit-form > *:nth-child(3) { animation-delay: 0.12s; }
.kb-edit-form > *:nth-child(4) { animation-delay: 0.15s; }
.kb-edit-form > *:nth-child(5) { animation-delay: 0.18s; }
.kb-edit-form > *:nth-child(6) { animation-delay: 0.21s; }
.kb-edit-form > *:nth-child(7) { animation-delay: 0.24s; }
.kb-edit-form > *:nth-child(8) { animation-delay: 0.27s; }

/* Subtle one-shot brighten of the accent left-edge so the click feels
 * acknowledged the moment the form starts to open. */
@keyframes kb-edit-edge-flash {
  0%   { box-shadow: none; }
  35%  { box-shadow: 0 0 6px 0 var(--accent); }
  100% { box-shadow: none; }
}
.kb-card-editing::before {
  animation: kb-edit-edge-flash 0.32s ease-out;
}

/* Close animation. The form fades AND collapses its height/padding/margin
 * to zero so the column reflows smoothly during the close — without that,
 * the column would hold the form's full height until the moment of
 * re-render and then snap down in one frame. JS sets max-height to the
 * form's measured scrollHeight inline before adding this class, giving
 * the keyframe a real from-value to transition down from. `forwards`
 * keeps the form collapsed once the animation lands so there's no
 * flicker before the re-render swaps the DOM. */
@keyframes kb-edit-form-close {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-top: 0;
    border-top-width: 0;
    border-bottom-width: 0;
  }
}
.kb-edit-form-closing {
  overflow: hidden;
  animation: kb-edit-form-close 0.22s cubic-bezier(0.4, 0, 0.2, 1) forwards !important;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .kb-edit-form,
  .kb-edit-form > .kb-edit-row,
  .kb-edit-form > .kb-edit-grid,
  .kb-edit-form > .kb-edit-actions,
  .kb-edit-form-closing,
  .kb-card-editing::before { animation: none; }
}
.kb-edit-row { display: flex; flex-direction: column; gap: 3px; }
.kb-edit-label {
  color: var(--text-muted); font-size: 0.65em; letter-spacing: 1.5px;
}
.kb-edit-textarea { resize: vertical; min-height: 36px; font-family: inherit; }
.kb-edit-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.kb-edit-form .input {
  background: var(--bg-input); border: 1px solid var(--border);
  color: var(--text); padding: 5px 7px;
  font-family: inherit; font-size: 0.85em; width: 100%;
}
.kb-edit-form .input:focus { outline: none; border-color: var(--accent); }
.kb-edit-actions {
  display: flex; gap: 6px; margin-top: 4px;
  border-top: 1px solid var(--border); padding-top: 8px;
}

.kb-progress {
  color: var(--text-muted); font-size: 0.75em; letter-spacing: 1px;
  margin: 4px 0;
}
.kb-horizon-select {
  background: var(--bg-input); border: 1px solid var(--border);
  color: var(--text-muted); padding: 4px 6px; font-size: 0.7em;
  font-family: inherit; width: 100%;
}
.kb-horizon-select:focus { outline: none; border-color: var(--accent); }

.kb-card-edit-btn {
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); padding: 5px 10px; font-size: 0.85em;
  font-family: inherit; cursor: pointer; transition: all 0.15s;
  flex-shrink: 0;
}
.kb-card-edit-btn:hover { color: var(--accent); border-color: var(--accent); }
.kb-card-editing { border-color: var(--accent); }

/* Project chip + edit pencil pair on the kanban filter bar */
.kb-chip-pair { display: inline-flex; gap: 0; }
.kb-chip-pair .kb-chip { border-right: 0; }
.kb-chip-edit {
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); padding: 6px 10px; font-size: 0.75em;
  font-family: inherit; cursor: pointer; transition: all 0.15s;
}
.kb-chip-edit:hover { color: var(--accent); border-color: var(--accent); }

/* Project edit modal (kanban entry point) */
.project-edit-modal-wrap {
  position: fixed; inset: 0; background: rgba(0,0,0,0.7);
  display: flex; align-items: center; justify-content: center;
  z-index: 200; padding: 16px;
}
.project-edit-modal {
  background: var(--bg-elevated); border: 1px solid var(--border);
  padding: 24px; max-width: 480px; width: 100%;
}

.kb-orphans {
  border: 1px dashed var(--danger); padding: 12px;
  margin-bottom: 16px;
  display: flex; flex-direction: column; gap: 8px;
}
.kb-orphans-label {
  color: var(--danger); font-size: 0.75em; letter-spacing: 2px;
  margin-bottom: 4px;
}

/* ─── DELEGATION ──────────────────────────────────────────── */
/* Banner shown at the top of the app when the logged-in user is acting as
   someone else's delegate. The accent border on .app-delegating frames the
   whole view in amber so confusion about whose data is on screen is impossible. */

.delegate-banner {
  background: var(--delegate-bg);
  border-bottom: 1px solid var(--delegate-border);
  color: var(--delegate);
  padding: 8px 16px;
  display: flex; align-items: center; gap: 12px;
  font-size: 0.8em; letter-spacing: 1px;
  margin: -16px -16px 16px;  /* counter the .app padding so banner reaches edges */
}
.delegate-banner-label {
  color: var(--delegate); font-weight: 600; letter-spacing: 2px;
}
.delegate-banner-name { color: var(--text); font-weight: 500; flex: 1; }
.delegate-banner-exit {
  background: transparent; border: 1px solid var(--delegate-border);
  color: var(--delegate); padding: 4px 12px; font-size: 0.85em;
  font-family: inherit; cursor: pointer; letter-spacing: 1px;
}
.delegate-banner-exit:hover { background: var(--delegate); color: var(--bg); }

.app-delegating { box-shadow: inset 0 0 0 1px var(--delegate-border); }

/* Workspace switcher dropdown in the header */
.ws-switcher { position: relative; display: inline-flex; }
/* No box around the toggle — matches the rest of the header strip via
   the .header-link class on the markup. This rule only handles the
   icon-label-caret alignment; color/font/hover come from .header-link. */
.ws-switcher-toggle {
  display: inline-flex; align-items: center; gap: 4px;
  text-transform: uppercase; /* match the row's BOOKINGS/THEME/STATS casing */
}
.ws-menu {
  position: absolute; right: 0; top: calc(100% + 4px);
  background: var(--bg-elevated); border: 1px solid var(--border);
  min-width: 220px; z-index: 100; padding: 4px;
}
.ws-menu-item {
  display: block; width: 100%; text-align: left;
  background: transparent; border: none; color: var(--text);
  padding: 8px 12px; font-family: inherit; cursor: pointer;
}
.ws-menu-item:hover { background: var(--bg); }
.ws-menu-item-active { background: var(--bg); border-left: 2px solid var(--accent); }
.ws-menu-name { font-size: 0.9em; }
.ws-menu-role {
  color: var(--text-muted); font-size: 0.7em; letter-spacing: 1.5px;
  text-transform: uppercase; margin-top: 2px;
}
.ws-menu-create {
  border-top: 1px solid var(--border-light); margin-top: 4px; padding-top: 12px;
  color: var(--text-muted);
}
.ws-menu-create:hover { color: var(--text); }

/* Workspace badge on initiative cards in the unified view (active = personal,
   row belongs to a team / delegated workspace). Subtle inline tag so the
   primary content stays the title; badge whispers "this lives somewhere else". */
.ws-badge {
  display: inline-block; margin-left: 8px;
  font-size: 0.65em; letter-spacing: 1px;
  padding: 1px 6px; border: 1px solid var(--border);
  color: var(--text-muted);
  vertical-align: middle;
}
.ws-badge-team { color: var(--text-secondary); border-color: var(--border); }
.ws-badge-personal { color: var(--delegate); border-color: var(--delegate-border); }

/* Settings → Delegates section */
.invite-row { display: flex; gap: 8px; align-items: stretch; }
.invite-row .input { flex: 1; }
.delegate-row { padding: 10px 0; border-bottom: 1px solid var(--border-light); }
.delegate-row:last-child { border-bottom: none; }

/* Invite link modal */
.invite-modal-wrap {
  position: fixed; inset: 0; background: rgba(0,0,0,0.7);
  display: flex; align-items: center; justify-content: center;
  z-index: 200; padding: 16px;
}
.invite-modal {
  background: var(--bg-elevated); border: 1px solid var(--border);
  padding: 24px; max-width: 480px; width: 100%;
}
.invite-link-row { display: flex; gap: 6px; }
.invite-link-row .input { flex: 1; font-size: 0.8em; }

/* ─── TEAM MANAGEMENT PANEL ───────────────────────────────── */
/* Used on the People tab when the active workspace is a team. Compose with
   .settings-section (border + padding) for the outer container. */
.team-mgmt-row {
  display: flex; gap: 10px; align-items: center;
}
.team-mgmt-row .input { flex: 1; }
.team-stats {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
}
.team-stat {
  border: 1px solid var(--border-light); padding: 12px 14px;
}
.team-stat-value {
  font-family: var(--font-mono, monospace);
  font-size: 1.4em; color: var(--text-primary); line-height: 1.1;
}
.team-stat-label {
  color: var(--text-muted); font-size: 0.72em;
  letter-spacing: 1.5px; text-transform: uppercase; margin-top: 4px;
}
@media (max-width: 700px) {
  .team-stats { grid-template-columns: repeat(2, 1fr); }
  .team-mgmt-row { flex-wrap: wrap; }
  .team-mgmt-row .settings-label { min-width: 0 !important; flex-basis: 100%; }
}

/* ─── RESPONSIVE ──────────────────────────────────────────── */
/* Narrow-viewport kanban: switch to tabbed single-column mode. Scoped to
   .kb-board-kanban so the projects board (.kb-board-projects, no qualifier)
   keeps its desktop horizontal-scroll behaviour on mobile. */
@media (max-width: 900px) {
  .kb-tabs { display: flex; }
  .kb-board-kanban {
    display: block;
    overflow: visible;
    scroll-snap-type: none;
    margin-left: 0; margin-right: 0;
    padding-left: 0; padding-right: 0;
  }
  .kb-board-kanban > .kb-col { display: none; }
  .kb-board-kanban > .kb-col-active {
    display: flex;
    width: 100%;
    max-height: calc(100vh - 250px);
  }
}
@media (max-width: 480px) {
  html { font-size: 13px; }
  .app { padding: 12px 12px 100px; }
  .tab { padding: 8px 10px; font-size: 0.8em; }
  .input-row { flex-direction: column; }
  .kb-col { max-height: calc(100vh - 200px); }
  .kb-board { gap: 8px; }
  .header { margin: -12px -12px 0; padding: 12px; }
  .tabs { margin: 0 -12px 0; padding: 0 12px; }
  .kb-filters { margin: 0 -12px 12px; padding: 10px 12px; }
  .delegate-banner { margin: -12px -12px 12px; flex-wrap: wrap; }
  .delegate-banner-exit { font-size: 0.75em; }
  .invite-row { flex-direction: column; }
}

/* ─── ON-CARD CHIPS (TASK CARDS) ─────────────────────────────
   The chip row sits below the title and above the action buttons.
   Each chip is a click target that opens a popover for that field.
   Empty-state chips (no value yet) use dashed borders so they read as
   "set me" affordances rather than filled state. Pattern mirrors
   .kb-card-assignee + .kb-card-assignee-unassigned which already
   exist for the assignee slot. */
.kb-card-chips {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 6px 0 2px;
  align-items: center;
}
.kb-card-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 3px 7px;
  font-size: 0.7em;
  font-family: inherit;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  letter-spacing: 0;
}
.kb-card-chip:hover { color: var(--text); border-color: var(--text-muted); }
.kb-card-chip-empty { color: var(--text-dim); border-style: dashed; }
.kb-card-chip-empty:hover { color: var(--text-muted); border-color: var(--text-muted); }
/* Priority chip retired from cards in v7. Class names removed from
   taskCardChipRowHTML / rawInboxChipRowHTML; the priority popover
   handlers (kanbanChipPriority etc.) still exist so the drawer can
   surface them. */
.kb-card-chip-due-overdue { color: var(--danger); border-color: var(--danger); }
.kb-card-chip-due-today { color: var(--warning); border-color: var(--warning); }

/* Inline-editable card title. The display element looks identical to
   .kb-card-text; on click we swap it for the input below in place. */
.kb-card-title-editable { cursor: text; }
.kb-card-title-editable:hover { background: rgba(255,255,255,0.02); }
[data-theme="light"] .kb-card-title-editable:hover { background: rgba(0,0,0,0.03); }
.kb-card-title-input {
  width: 100%; box-sizing: border-box;
  background: var(--bg-input); border: 1px solid var(--accent);
  color: var(--text); font-family: inherit; font-size: 0.9em;
  line-height: 1.4; padding: 4px 6px; margin: -5px -7px 4px;
  outline: none;
}

/* ─── CHIP POPOVER (FIELD QUICK-EDIT) ────────────────────────
   A small floating menu anchored to the chip that opened it.
   Used for priority cycle, due-date set, project picker, assignee
   picker, size/energy pickers. JS positions it via inline style;
   we just style the box. The backdrop is invisible but covers the
   viewport so a click anywhere outside dismisses. */
.kb-chip-popover-backdrop {
  position: fixed; inset: 0; z-index: 250; background: transparent;
}
.kb-chip-popover {
  position: absolute; z-index: 251;
  background: var(--bg-elevated); border: 1px solid var(--border);
  padding: 6px; min-width: 160px; max-width: 280px;
  /* Sumi: lean on the border, not the shadow. Soft drop keeps the menu
     anchored above the card without the old theatrical wash. */
  box-shadow: inset 0 1px 0 var(--card-top), 0 6px 18px rgba(0,0,0,0.28);
  display: flex; flex-direction: column; gap: 2px;
  animation: kb-chip-popover-in 0.12s ease-out;
}
@keyframes kb-chip-popover-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.kb-chip-popover-item {
  background: transparent; border: none;
  color: var(--text); padding: 6px 10px;
  font-family: inherit; font-size: 0.85em;
  text-align: left; cursor: pointer;
  letter-spacing: 0;
}
.kb-chip-popover-item:hover { background: var(--bg); color: var(--accent); }
.kb-chip-popover-item-active { color: var(--accent); }
.kb-chip-popover-item-clear {
  color: var(--text-muted); border-top: 1px solid var(--border-light);
  margin-top: 4px; padding-top: 8px;
}
.kb-chip-popover-input {
  background: var(--bg-input); border: 1px solid var(--border);
  color: var(--text); font-family: inherit; font-size: 0.85em;
  padding: 6px 8px; outline: none;
}
.kb-chip-popover-input:focus { border-color: var(--accent); }
.kb-chip-popover-actions { display: flex; gap: 4px; margin-top: 4px; }
.kb-chip-popover-actions .kb-action { flex: 1; }

/* ─── TASK DETAIL DRAWER ─────────────────────────────────────
   Lives in #task-detail-panel outside the kanban grid so column
   re-renders don't blow it away. On desktop ≥900px it's a right-side
   drawer ~420px wide, board scrolls behind. On mobile <900px it's a
   full-height bottom sheet. URL-synced to #/<tab>?task=<id>. */
.kb-detail-backdrop {
  /* Subtle dim — enough to indicate the drawer is the active surface
     and to register clicks outside as "close", but light enough that
     the kanban behind stays clearly readable.
     v2 tuning: the 0.32 wash was tuned against the old --bg (#0a0a0a);
     on the deeper Sumi page (#070710) the same alpha disappears
     into the bg, so we lift it slightly. */
  position: fixed; inset: 0; z-index: 300;
  background: rgba(0,0,0,0.42);
  animation: kb-detail-backdrop-in 0.18s ease-out;
}
[data-theme="light"] .kb-detail-backdrop { background: rgba(0,0,0,0.18); }
/* Mobile: keep the heavier curtain since the sheet itself fills
   most of the screen and the backdrop's mostly there to mask the
   body scroll lock anyway. */
@media (max-width: 900px) {
  .kb-detail-backdrop { background: rgba(0,0,0,0.5); }
  [data-theme="light"] .kb-detail-backdrop { background: rgba(0,0,0,0.32); }
}
@keyframes kb-detail-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.kb-detail-panel {
  /* Vertically + horizontally centered slide-up card: floats in the
     middle of the viewport, constrained width so the kanban stays
     visible on either side. The card slides in from below the
     viewport into its centered resting position. */
  position: fixed;
  top: 50%;
  left: 50%;
  width: min(600px, calc(100vw - 32px));
  height: min(680px, 70vh); max-height: min(680px, 70vh);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  z-index: 301;
  display: flex; flex-direction: column;
  /* The static transform centers the card on both axes. Keyframes
     compose translate(-50%, -50%) with the slide so centering
     survives the animation. */
  transform: translate(-50%, -50%);
  animation: kb-detail-panel-in-desk 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
  /* Sumi: the drawer is the card writ large — same inset top + soft
     drop pattern as .kb-card, just scaled up. No 64px hero shadow. */
  box-shadow: inset 0 1px 0 var(--card-top), 0 16px 40px rgba(0,0,0,0.4);
  overflow: hidden;
}
[data-theme="light"] .kb-detail-panel {
  box-shadow: inset 0 1px 0 var(--card-top), 0 16px 40px rgba(0,0,0,0.14);
}
@keyframes kb-detail-panel-in-desk {
  from { transform: translate(-50%, -50%) translateY(calc(50vh + 50%)); }
  to   { transform: translate(-50%, -50%); }
}
@keyframes kb-detail-panel-out-desk {
  from { transform: translate(-50%, -50%); }
  to   { transform: translate(-50%, -50%) translateY(calc(50vh + 50%)); }
}
.kb-detail-panel-closing {
  animation: kb-detail-panel-out-desk 0.26s cubic-bezier(0.5, 0, 0.75, 0) forwards;
}
@keyframes kb-detail-backdrop-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}
.kb-detail-backdrop-closing {
  animation: kb-detail-backdrop-out 0.22s ease-in forwards;
}
/* Constrain form width on wide screens so labels and selects don't
   sprawl across an enormous monitor. The body still scrolls within
   the panel; the constraint just keeps line-length comfortable. */
.kb-detail-body { max-width: 760px; width: 100%; margin: 0 auto; }
.kb-detail-actions-inner {
  max-width: 760px; width: 100%; margin: 0 auto;
  display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
}
.kb-detail-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 16px 14px;
  border-bottom: 1px solid var(--border-light);
  flex-shrink: 0;
  position: relative;
}
/* Pull-handle affordance at the top, identical look at every size. */
.kb-detail-header::before {
  content: ''; position: absolute; top: 6px; left: 50%;
  transform: translateX(-50%);
  width: 36px; height: 3px; border-radius: 2px;
  background: var(--border);
}
.kb-detail-status-pill {
  font-size: 0.7em; letter-spacing: 2px;
  color: var(--text-muted); text-transform: uppercase;
}
.kb-detail-status-pill-inbox { color: var(--warning); }
.kb-detail-status-pill-scheduled { color: var(--accent); }
.kb-detail-status-pill-someday { color: var(--text-muted); }
.kb-detail-close {
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); padding: 4px 10px;
  font-family: inherit; font-size: 0.9em; cursor: pointer;
  transition: all 0.15s;
}
.kb-detail-close:hover { color: var(--text); border-color: var(--text-muted); }

/* Read-only third-party calendar event modal — content inside the reused
   .kb-detail-panel / .kb-detail-body shell. */
.cal-evt-title { font-size: 1.05em; font-weight: 600; color: var(--text); }
.cal-evt-when {
  color: var(--accent); font-size: 0.85em; letter-spacing: 0.5px;
  margin-bottom: 18px;
}
.cal-evt-section { margin-bottom: 16px; }
.cal-evt-label {
  font-size: 0.7em; letter-spacing: 2px; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 5px;
}
.cal-evt-desc { white-space: normal; line-height: 1.55; color: var(--text-secondary); }
.cal-evt-attendee {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 4px 0; border-bottom: 1px solid var(--border-light);
}
.cal-evt-attendee:last-child { border-bottom: none; }
.cal-evt-resp { color: var(--text-muted); font-size: 0.85em; white-space: nowrap; }
.cal-evt-actions {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px;
}
.cal-evt-btn {
  display: inline-block; padding: 8px 14px; font-size: 0.85em;
  border: 1px solid var(--border); color: var(--text-secondary);
  text-decoration: none; transition: all 0.15s;
}
.cal-evt-btn:hover { color: var(--text); border-color: var(--text-muted); }
.cal-evt-btn-primary {
  background: var(--accent); color: var(--bg); border-color: var(--accent);
}
.cal-evt-btn-primary:hover { background: var(--accent-hover); color: var(--bg); border-color: var(--accent-hover); }
.kb-detail-body {
  flex: 1; overflow-y: auto;
  padding: 18px 18px 24px;
  display: flex; flex-direction: column; gap: 14px;
}
.kb-detail-row { display: flex; flex-direction: column; gap: 4px; }
.kb-detail-label {
  color: var(--text-muted); font-size: 0.7em;
  letter-spacing: 2px; text-transform: uppercase;
}
.kb-detail-row .input,
.kb-detail-row textarea.input,
.kb-detail-row select.input {
  width: 100%;
}
.kb-detail-row textarea.input {
  min-height: 80px; resize: vertical;
}
.kb-detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.kb-detail-meta {
  font-size: 0.7em; color: var(--text-muted);
  padding-top: 10px; border-top: 1px solid var(--border-light);
  display: flex; flex-direction: column; gap: 3px;
}
.kb-detail-meta a { color: var(--text-secondary); }
.kb-detail-actions {
  display: flex; gap: 6px; flex-wrap: wrap;
  padding: 12px 16px;
  border-top: 1px solid var(--border-light);
  flex-shrink: 0;
  background: var(--bg-elevated);
}
.kb-detail-actions .kb-action { flex: 0 1 auto; }
.kb-detail-actions-spacer { flex: 1; }

/* Calendar-event link — own drawer row promoted out of the meta
 * block. Read-only; clicking opens the event in Google Calendar. */
.kb-detail-cal-link {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.9em;
  padding: 6px 0;
  display: inline-block;
}
.kb-detail-cal-link:hover { color: var(--accent); text-decoration: underline; }

/* Notes accordion — wraps the existing .kb-notes block in a
 * <details>/<summary> so the thread is collapsed by default,
 * keeping the drawer compact for tasks where notes aren't in play.
 * Native disclosure marker is hidden; summary draws its own chevron
 * that rotates when open. */
.kb-detail-notes-acc {
  margin-top: 14px;
  border-top: 1px solid var(--border-light);
  padding-top: 14px;
}
.kb-detail-notes-acc > summary {
  list-style: none;            /* firefox */
  cursor: pointer;
  display: flex; align-items: center; gap: 8px;
  color: var(--text-muted); font-size: 0.7em;
  letter-spacing: 2px; text-transform: uppercase;
  padding: 2px 0;
  user-select: none;
}
.kb-detail-notes-acc > summary::-webkit-details-marker { display: none; }
.kb-detail-notes-acc > summary::before {
  content: '▸';
  font-size: 0.9em; color: var(--text-dim);
  transition: transform 120ms ease;
  display: inline-block;
  width: 8px;
}
.kb-detail-notes-acc[open] > summary::before { transform: rotate(90deg); }
.kb-detail-notes-acc > summary:hover { color: var(--text); }
/* The inner .kb-notes block sets its own border-top + padding-top;
 * the accordion wrapper already carries those, so collapse the
 * inner's to avoid doubling up. */
.kb-detail-notes-acc .kb-notes {
  border-top: 0;
  padding-top: 10px;
}

/* ── Notes thread (drawer) ─────────────────────────────────────
   A multi-author discussion attached to the task. Lives at the
   bottom of the drawer body, separated from the form fields and
   meta block by its own divider. Visual rhythm follows the same
   uppercase-label / muted-meta pattern as the rest of the drawer. */
.kb-notes {
  display: flex; flex-direction: column; gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--border-light);
}
.kb-notes-header {
  color: var(--text-muted); font-size: 0.7em;
  letter-spacing: 2px; text-transform: uppercase;
}
.kb-notes-thread { display: flex; flex-direction: column; gap: 10px; }
.kb-notes-empty {
  color: var(--text-dim); font-size: 0.85em; padding: 4px 0;
}
.kb-note {
  display: flex; flex-direction: column; gap: 4px;
  padding: 8px 10px;
  border: 1px solid var(--border-light);
  background: var(--bg-elevated);
}
.kb-note-editing { border-color: var(--border); }
.kb-note-meta {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 0.7em; color: var(--text-muted);
  letter-spacing: 1px; text-transform: uppercase;
}
.kb-note-author { color: var(--text-secondary); }
.kb-note-time { color: var(--text-muted); }
.kb-note-actions-inline { margin-left: auto; display: flex; gap: 8px; }
.kb-note-link {
  background: none; border: 0; padding: 0;
  font-family: inherit; font-size: inherit;
  color: var(--text-muted); cursor: pointer;
  text-transform: uppercase; letter-spacing: 1px;
}
.kb-note-link:hover { color: var(--text); }
.kb-note-link-danger:hover { color: var(--accent); }
.kb-note-body {
  color: var(--text); font-size: 0.95em; line-height: 1.5;
  word-wrap: break-word; overflow-wrap: anywhere;
}
.kb-note-body p { margin: 0 0 6px; }
.kb-note-body p:last-child { margin-bottom: 0; }
.kb-note-body a { color: var(--accent); }
.kb-note-body code {
  font-family: inherit; background: var(--bg-input);
  padding: 1px 4px; border: 1px solid var(--border-light);
}
.kb-note-body pre {
  background: var(--bg-input); border: 1px solid var(--border-light);
  padding: 8px 10px; overflow-x: auto;
}
.kb-note-body ul, .kb-note-body ol { margin: 0 0 6px 18px; padding: 0; }
.kb-note-body blockquote {
  margin: 0 0 6px; padding-left: 10px;
  border-left: 2px solid var(--border);
  color: var(--text-secondary);
}
.kb-note-fallback { white-space: pre-wrap; }
.kb-note-edit-input { min-height: 60px; resize: vertical; }
.kb-note-actions {
  display: flex; gap: 6px; align-items: center;
  margin-top: 4px;
}
.kb-notes-composer {
  display: flex; gap: 6px; align-items: stretch;
  margin-top: 2px;
}
.kb-notes-composer-input { flex: 1; min-height: 44px; resize: vertical; }
.kb-notes-post { flex: 0 0 auto; align-self: stretch; }

/* Mobile: full-width bottom sheet (no width constraint, no centering
   transform). On a phone the contextual-card framing is unnecessary —
   tappable form controls need every pixel of width. Different keyframe
   names so the desktop translateX(-50%) doesn't apply here. */
@keyframes kb-detail-panel-in-mob {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@keyframes kb-detail-panel-out-mob {
  from { transform: translateY(0); }
  to   { transform: translateY(100%); }
}
@media (max-width: 900px) {
  .kb-detail-panel {
    /* Reset desktop's vertical-center anchoring; mobile is a
       bottom-anchored sheet meeting the screen edge. */
    top: auto;
    bottom: 0;
    left: 0; right: 0;
    width: 100%; max-width: 100%;
    height: 92vh; max-height: 92vh;
    border-left: none;
    border-right: none;
    border-bottom: none;
    border-radius: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    transform: none;
    animation-name: kb-detail-panel-in-mob;
  }
  .kb-detail-panel-closing {
    animation-name: kb-detail-panel-out-mob;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kb-detail-panel,
  .kb-detail-backdrop,
  .kb-chip-popover { animation: none; }
}

/* ─── CROSS-WORKSPACE PROJECTS ─────────────────────────────── */
/* Tokens for surfacing tasks/projects you have access to via direct project
   membership (not via the project's home workspace). Distinct steel-blue from
   the orange accent and amber delegate so signals don't conflict. Mirrored
   for the light theme. */
:root {
  --shared: #7aa6c2;
  --shared-bg: #0e1a22;
  --shared-border: #2a4a5e;
}
[data-theme="light"] {
  --shared: #1f5571;            /* darker steel-blue for cream-bg AA contrast */
  --shared-bg: #e8eef3;
  --shared-border: #5a8aa8;
}

/* Shared-project filter chip (mock variant of .kb-chip). Reuses .kb-chip
   structurally so layout, hover, and active states stay aligned. */
.kb-chip-shared { color: var(--shared); border-color: var(--shared); }
.kb-chip-shared:hover { color: var(--shared); border-color: var(--shared); }
.kb-chip-shared.kb-chip-active {
  background: var(--shared-bg); color: var(--shared); border-color: var(--shared);
}

/* Card-level badge naming the project that brought a task into the user's
   kanban via cross-workspace project membership. Worn by tasks whose project's
   home workspace is NOT one the user belongs to. */
.ws-badge-shared {
  display: inline-block; vertical-align: middle;
  border: 1px solid var(--shared-border); background: var(--shared-bg);
  color: var(--shared);
  padding: 1px 6px; margin-left: 6px;
  font-size: 0.7em; letter-spacing: 1px; border-radius: 2px;
  white-space: nowrap;
}

/* Project members section inside the project edit modal. */
.members-section {
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--border-light);
}
.members-section-label {
  display: block; color: var(--text-muted);
  font-size: 0.7em; letter-spacing: 2px; margin-bottom: 8px;
}
.members-list { display: flex; flex-direction: column; gap: 6px; }
.member-row {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 8px 10px; background: var(--bg-input);
  border: 1px solid var(--border-light);
  font-size: 0.84em; border-radius: 2px;
}
.member-row-pending { opacity: 0.7; }
.member-meta { color: var(--text-muted); font-size: 0.78em; }
.member-role-tag {
  color: var(--text-secondary); border: 1px solid var(--border);
  padding: 2px 8px; font-size: 0.72em; letter-spacing: 1px; border-radius: 2px;
}
.member-role-owner { color: var(--accent); border-color: var(--accent); }
.member-role-pending { color: var(--warning); border-color: var(--warning); }
.member-remove {
  background: none; border: none; color: var(--text-muted);
  font: inherit; font-size: 0.85em; cursor: pointer; padding: 2px 6px;
}
.member-remove:hover { color: var(--danger); }
.member-copy-link {
  background: none; border: none; color: var(--text-muted);
  font: inherit; font-size: 0.78em; letter-spacing: 0.5px;
  cursor: pointer; padding: 2px 6px;
}
.member-copy-link:hover { color: var(--accent); }
/* Stacked so the email field gets the full row even inside narrow project
   cards on the Projects tab. Role + button share a tighter second row. */
.invite-row { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.invite-row > .input { width: 100%; }
.invite-row-actions { display: flex; gap: 6px; }
.invite-row-actions select.input { flex: 0 0 110px; }
.invite-row-actions .btn { white-space: nowrap; }

/* ── Project page (#/w/<ws>/projects/<id>) and access-error page ── */
.project-page { max-width: 760px; margin: 0 auto; padding: 24px 20px 60px; }
.project-page-back { margin-bottom: 8px; }
.btn-link {
  background: none; border: none; color: var(--text-muted);
  font: inherit; cursor: pointer; padding: 4px 0;
}
.btn-link:hover { color: var(--text-primary); }
.project-page-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 12px;
}
.project-page-title { margin: 0; font-size: 1.6em; line-height: 1.2; }
.project-page-desc { color: var(--text-secondary); margin: 0 0 24px; }
.project-section { margin: 28px 0; }
.project-section-h {
  font-size: 0.78em; letter-spacing: 1px; color: var(--text-muted);
  border-bottom: 1px solid var(--border); padding-bottom: 6px; margin: 0 0 10px;
}
.project-section-empty { color: var(--text-muted); font-size: 0.9em; padding: 6px 0; }
.project-task-list { list-style: none; margin: 0; padding: 0; }
.project-task-row {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-bottom: 1px dashed var(--border);
}
.project-task-row:last-child { border-bottom: none; }
.project-task-title { color: var(--text-primary); }
.project-task-when { color: var(--text-muted); font-size: 0.85em; white-space: nowrap; }

.kb-card-link {
  color: inherit; text-decoration: none;
}
.kb-card-link:hover { color: var(--accent); }

.access-error-page {
  max-width: 520px; margin: 80px auto; padding: 0 20px; text-align: center;
}
.access-error-code {
  font-size: 3em; color: var(--text-muted); margin-bottom: 16px; letter-spacing: 2px;
}
.access-error-title { margin: 0 0 12px; font-size: 1.3em; }
.access-error-sub { color: var(--text-muted); margin: 0 0 24px; }

/* ── Booking links ───────────────────────────────────────────────────────── */
.btn-xs { padding: 6px 10px; font-size: 0.75em; }
.settings-empty-hint { color: var(--text-muted); font-size: 0.85em; }
.booking-link-row { align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.booking-link-meta { flex: 1; min-width: 180px; }
.booking-link-url { color: var(--text-muted); font-size: 0.76em; word-break: break-all; margin-top: 2px; }
.booking-link-dur { color: var(--text-muted); font-weight: 400; }
.booking-link-actions { display: flex; gap: 8px; align-items: center; }
.booking-link-create { gap: 8px; flex-wrap: wrap; }
.booking-link-create .input { flex: 1; min-width: 160px; }
.booking-link-create select.input { flex: 0 0 auto; width: auto; }

/* Public booking page (no-auth) */
.booking-page {
  min-height: 100vh; display: flex; align-items: flex-start; justify-content: center;
  padding: 48px 16px; background: var(--bg);
}
.booking-card {
  width: 100%; max-width: 520px; background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: 8px; padding: 28px;
}
.booking-loading, .booking-empty { color: var(--text-muted); font-size: 0.9em; }
.booking-title { font-size: 1.3em; font-weight: 700; color: var(--text); }
.booking-sub { color: var(--text-muted); font-size: 0.85em; margin-top: 4px; }
.booking-slotwrap { margin-top: 20px; max-height: 46vh; overflow-y: auto; }
.booking-day { margin-bottom: 16px; }
.booking-day-label {
  font-size: 0.72em; letter-spacing: 1.5px; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 8px;
}
.booking-slots { display: flex; flex-wrap: wrap; gap: 8px; }
.booking-slot {
  background: transparent; border: 1px solid var(--border); color: var(--text);
  padding: 8px 14px; min-height: 40px; border-radius: 6px; cursor: pointer;
  font-family: inherit; font-size: 0.85em; transition: border-color 0.15s, color 0.15s;
}
.booking-slot:hover { border-color: var(--accent); }
.booking-slot-selected { border-color: var(--accent); color: var(--accent); }
.booking-form-inner {
  margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 10px;
}
.booking-when { color: var(--text-secondary); font-size: 0.9em; }
.booking-form-actions { display: flex; gap: 8px; margin-top: 4px; }
.booking-confirmed { text-align: center; padding: 24px 0; }
.booking-confirmed-check {
  font-size: 2.4em; color: var(--success); line-height: 1; margin-bottom: 10px;
}

/* Collapsible settings sections (accordion) */
.settings-section > .section-label {
  cursor: pointer; user-select: none;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.settings-section > .section-label::after {
  content: '▾'; color: var(--text-muted); font-size: 0.85em;
  transition: transform 0.15s ease;
}
.settings-section.collapsed > .section-label::after { transform: rotate(-90deg); }
.settings-section.collapsed > :not(.section-label) { display: none; }
.settings-section > .section-label:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}

/* Inline nav icon set (Lucide-style) — monochrome via currentColor so each
   icon inherits its link's colour + hover state. */
.icon { width: 15px; height: 15px; vertical-align: -3px; flex: none; }
.header-link .icon { margin-right: 6px; }

