/* ── Structural tokens ──
   Theme-invariant shape language: radii, shadows, motion, typography.
   These do NOT change between color schemes. */
:root {
  --radius: 12px;
  --radius-sm: 8px;
  --radius-xs: 6px;
  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.03);
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.03);
  --shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04);
  --shadow-modal: 0 24px 60px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.10);
  --shadow-drag: 0 16px 40px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.06);
  /* Easing tokens. The CSS default `ease` is symmetric and reads as
     "soft" - fine for ambient motion but mushy for interactive feedback.
     We standardise on three named curves:
       - smooth: balanced in-out, default for state changes
       - out:    decelerates into rest, for entrances and hover lifts
       - spring: gentle overshoot feel (no actual overshoot), for opens
     Durations bumped slightly so 100ms doesn't read as a snap. */
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.16, 1, 0.3, 1);
  --transition:      200ms var(--ease-smooth);
  --transition-fast: 140ms var(--ease-smooth);
  /* Lets `transition: height` animate to/from `auto` (e.g. native
     <details> opening). Modern browsers (Chrome 129+, Safari 17.4+,
     Firefox 129+) honour it; older ones ignore it and snap as before. */
  interpolate-size: allow-keywords;
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Roboto, "Helvetica Neue", sans-serif;
}

/* ── Color scheme: default ──
   Theme-mutable tokens. To add a new scheme:
     1. Duplicate this block and rename the data-attribute selector to
        e.g. `[data-color-scheme="midnight"]`.
     2. Override only the variables that should change.
     3. Apply by setting `data-color-scheme="midnight"` on either:
          <html>          - app-wide theme
          .screen-board   - per-project scheme (this is what the project
                            settings modal currently writes; only board
                            chrome inside .screen-board is themed).
   The selector list below covers both targets so the same block serves
   both. :root carries the app-wide defaults; the data-attribute variants
   pick up scoped overrides. */
:root,
:root[data-color-scheme="default"],
.screen-board[data-color-scheme="default"] {
  /* Surfaces & lines */
  --bg:              #eeedeb;
  /* Hover-state background. Slightly darker than --bg so an element on
     the page (e.g. a button) shows visible feedback under the cursor.
     Used everywhere a previous `background: var(--bg)` hover was
     silently invisible after we shifted --bg to #eeedeb. */
  --bg-hover:        #e3e1de;
  --surface:         #ffffff;
  --surface-raised:  #ffffff;
  --border:          #e0ded9;
  --border-light:    #ebe9e5;

  /* Text */
  --text:            #1a1816;
  --text-secondary:  #4a4642;
  --text-tertiary:   #87867f;

  /* Accent: neutral warm-dark. Primary CTAs and featured surfaces lean
     on the same colour as body text - restrained and timeless rather
     than chromatic. Slightly browned dark (#1a1816) instead of pure
     near-black so text blends with the warm cream bg and unifies with
     the sailboat favicon, which uses the same hex. The cool midnight +
     lavender Pro pill does the chromatic accent work elsewhere. */
  --accent:          #1a1816;
  --accent-hover:    #000000;
  --accent-light:    #e6e2d6;
  --accent-subtle:   #f0eee6;

  /* Semantic - danger / success */
  --danger:          #dc2626;
  --danger-hover:    #b91c1c;
  --danger-surface:  #fef2f2;
  --danger-border:   #fee2e2;
  --success:         #16a34a;
  --success-surface: #e6f6ea;
  --success-text:    #166534;

  /* Default column dots (To Do / In Progress / Done seed columns) */
  --todo-dot:        #e5a00d;
  --doing-dot:       #3b82f6;
  --done-dot:        #16a34a;

  /* Priority palette - mirrors PRIORITY_COLORS in views/board/card-modal.js.
     Update both when adding a scheme that changes priority hues. */
  --priority-low:    #94a3b8;
  --priority-medium: #3b82f6;
  --priority-high:   #f97316;
  --priority-urgent: #dc2626;
}

/* ── Color scheme: midnight ──
   Cool blue-violet palette for late-night work. Surfaces are deep navy
   with a slight purple tilt; accent picks up the same indigo as the Pro
   pill so the chromatic story stays consistent. */
.screen-board[data-color-scheme="midnight"] {
  --bg:              #15172b;
  --bg-hover:        #1e2140; /* slightly lighter than --bg on dark schemes */
  --surface:         #1e2140;
  --surface-raised:  #262a4a;
  --border:          #353a5d;
  --border-light:    #2a2e4d;
  --text:            #e8eaf3;
  --text-secondary:  #b4b8d4;
  --text-tertiary:   #7d83a8;
  --accent:          #8b88f7;
  --accent-hover:    #a5a3f9;
  --accent-light:    #353a5d;
  --accent-subtle:   #2a2e4d;
  --todo-dot:        #f59e0b;
  --doing-dot:       #60a5fa;
  --done-dot:        #34d399;
}

/* ── Color scheme: ocean ──
   Cool teal/cyan, calmer than midnight. Light scheme - keeps text-on-
   surface contrast comfortable for daytime board work. */
.screen-board[data-color-scheme="ocean"] {
  --bg:              #eef6f8;
  --bg-hover:        #dfedf0;
  --surface:         #ffffff;
  --surface-raised:  #ffffff;
  --border:          #c7dde2;
  --border-light:    #dde9ec;
  --text:            #0f2933;
  --text-secondary:  #325560;
  --text-tertiary:   #6f8c95;
  --accent:          #0e7490;
  --accent-hover:    #155e75;
  --accent-light:    #c7dde2;
  --accent-subtle:   #dde9ec;
  --todo-dot:        #f59e0b;
  --doing-dot:       #0ea5e9;
  --done-dot:        #16a34a;
}

/* ── Color scheme: forest ──
   Earthy greens with a warm cream surface. Restful, low-saturation -
   meant for long working sessions. */
.screen-board[data-color-scheme="forest"] {
  --bg:              #f3f1e9;
  --bg-hover:        #e7e3d5;
  --surface:         #fbfaf4;
  --surface-raised:  #ffffff;
  --border:          #d6cfba;
  --border-light:    #e6dfca;
  --text:            #1f2a1c;
  --text-secondary:  #46523f;
  --text-tertiary:   #7a8472;
  --accent:          #2f6b3b;
  --accent-hover:    #1f4d29;
  --accent-light:    #d6cfba;
  --accent-subtle:   #e6dfca;
  --todo-dot:        #c2853a;
  --doing-dot:       #4f8ad6;
  --done-dot:        #2f6b3b;
}

/* ── Color scheme: sunset ──
   Warm peach/terracotta. The bg picks up a soft apricot tint without
   tipping into orange; accent is a brick red that reads as "primary"
   against the warm field. */
.screen-board[data-color-scheme="sunset"] {
  --bg:              #fcf2eb;
  --bg-hover:        #f3e3d4;
  --surface:         #ffffff;
  --surface-raised:  #ffffff;
  --border:          #ead6c3;
  --border-light:    #f3e3d0;
  --text:            #2a1d15;
  --text-secondary:  #573e2f;
  --text-tertiary:   #8d7765;
  --accent:          #b8431f;
  --accent-hover:    #94341a;
  --accent-light:    #ead6c3;
  --accent-subtle:   #f3e3d0;
  --todo-dot:        #f59e0b;
  --doing-dot:       #d97706;
  --done-dot:        #15803d;
}

/* ── Color scheme: graphite ──
   Neutral cool greys for users who want a mono board with no warmth.
   Pairs with monospaced screenshots / code-heavy boards. */
.screen-board[data-color-scheme="graphite"] {
  --bg:              #f3f4f6;
  --bg-hover:        #e6e8eb;
  --surface:         #ffffff;
  --surface-raised:  #ffffff;
  --border:          #d1d5db;
  --border-light:    #e5e7eb;
  --text:            #111827;
  --text-secondary:  #374151;
  --text-tertiary:   #6b7280;
  --accent:          #1f2937;
  --accent-hover:    #111827;
  --accent-light:    #d1d5db;
  --accent-subtle:   #e5e7eb;
  --todo-dot:        #d97706;
  --doing-dot:       #2563eb;
  --done-dot:        #16a34a;
}

/* ── Per-project text color override ──
   Targeted at the few chrome elements that sit directly on the board
   surface (which is what gets recoloured by an uploaded background
   image). Cards keep their dark text on a white surface; modals,
   topbar, and sidebars are also unaffected.
   Covered elements:
     .column-title       column header label
     .column-count       per-column tally next to the kebab
     .column-menu-btn    kebab itself (icon strokes use currentColor)
     .add-card-btn       per-column "+ Add card" CTA
     .add-column-btn     trailing "+ Add" column at the right of the board
     .empty-state        "Nothing to do" placeholder when a column is empty
   Hover variants are listed alongside so the elements stay legible
   when the cursor is over them (the default rules switch to
   var(--text-secondary)/var(--text), both of which read black against
   a dark background image without this override). */
.screen-board[data-text-color="light"] .column-title,
.screen-board[data-text-color="light"] .column-title-edit:hover .column-title,
.screen-board[data-text-color="light"] .column-count,
.screen-board[data-text-color="light"] .column-menu-btn,
.screen-board[data-text-color="light"] .column-menu-btn:hover:not([disabled]),
.screen-board[data-text-color="light"] .add-card-btn,
.screen-board[data-text-color="light"] .add-card-btn:hover,
.screen-board[data-text-color="light"] .add-column-btn,
.screen-board[data-text-color="light"] .add-column-btn:hover,
.screen-board[data-text-color="light"] .empty-state {
  color: #f5f3ee;
}

/* ── Icons (Lucide via icons.js) ── */
.icon {
  display: inline-block;
  flex-shrink: 0;
  vertical-align: middle;
  /* Stroke colour comes from currentColor so each context can theme its
     icons by setting `color`. */
}

/* ── Reset ── */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  min-height: 100vh;
  min-height: 100dvh;
}

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

button, input, textarea, select {
  font: inherit;
  color: inherit;
}

button { background: none; border: none; cursor: pointer; }

a { color: inherit; text-decoration: none; }

/* ── Layout ── */
#app {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.screen {
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* ── Top bar ──
   Three-column grid (1fr/1fr/1fr) instead of flex+space-between so the
   centered search box anchors to the actual page midline regardless of
   the asymmetric left (wordmark + switcher) vs right (avatar) content. */
.topbar {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  align-items: center;
  padding: 18px 32px;
  border-bottom: 1px solid var(--border);
  /* Solid fallback for browsers without backdrop-filter. Keeps the bar
     legible if the frosted-glass effect isn't available. */
  background: var(--bg);
  position: sticky;
  top: 0;
  z-index: 10;
}

/* On the board view the topbar floats over the column surface without
   a hairline divider - the columns themselves provide enough visual
   separation, and the seam was reading as a banded section. Other
   pages (projects list, settings, etc.) keep the rule because their
   bodies have no equivalent visual structure to lean on. */
.screen-board .topbar,
.screen-projects .topbar { border-bottom: none; }

/* Translucent frosted-glass topbar so users can faintly see content
   scroll underneath. Gated on backdrop-filter support so older browsers
   don't render a half-transparent bar without the blur (which looks
   unintentionally washed out). The rgba value mirrors --bg (#eeedeb)
   at ~72% opacity. */
@supports ((backdrop-filter: blur(0)) or (-webkit-backdrop-filter: blur(0))) {
  .topbar {
    background: rgba(238, 237, 235, 0.72);
    -webkit-backdrop-filter: saturate(1.5) blur(14px);
    backdrop-filter: saturate(1.5) blur(14px);
  }
}

/* Topbar left cluster - wordmark plus optional context (project
   switcher on the board view). Grouped so they stay together as the
   topbar's right-side actions float to the far right. */
.topbar-left {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

/* Centered slot for the board-only search box. Empty on non-board
   pages but kept in the DOM so the right-side avatar pins to the same
   place across views. */
.topbar-center {
  display: flex;
  justify-content: center;
  position: relative;
  min-width: 0;
}

/* Search input + dropdown panel. position: relative on .search-box
   anchors the panel to the input bottom; the panel uses position:
   absolute to break out of the topbar without pushing other elements.
   The input sits on a raised surface so it reads as a distinct input
   field against the topbar background, not as part of it. */
.search-box {
  position: relative;
  width: 100%;
  max-width: 420px;
  display: flex;
  align-items: center;
}
.search-icon {
  position: absolute;
  left: 12px;
  display: inline-flex;
  color: var(--text-secondary);
  pointer-events: none;
}
.search-input {
  width: 100%;
  /* Right padding leaves room for the "/" keyboard hint kbd. */
  padding: 9px 36px 9px 34px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  box-shadow: var(--shadow-xs);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
}
.search-input::placeholder { color: var(--text-tertiary); }
.search-input:hover { border-color: var(--text-tertiary); }
.search-input:focus {
  outline: none;
  border-color: var(--text);
  box-shadow: 0 0 0 3px var(--border), var(--shadow-sm);
}
/* Strip the native "x" clear button on WebKit/Edge - we want the
   dropdown UI to drive interactions, not platform chrome. */
.search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* "/" keyboard hint inside the search box. Hidden once the user
   focuses the input or types anything - that keystroke is what the
   hint was promoting in the first place. */
.search-kbd {
  position: absolute;
  right: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-tertiary);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 500;
  pointer-events: none;
  user-select: none;
}
.search-input:focus ~ .search-kbd,
.search-input:not(:placeholder-shown) ~ .search-kbd { display: none; }

.search-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  max-height: 420px;
  overflow-y: auto;
  padding: 4px;
  z-index: 20;
  animation: searchIn 180ms var(--ease-out);
}
@keyframes searchIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.search-empty {
  padding: 14px 12px;
  font-size: 13px;
  color: var(--text-tertiary);
  text-align: center;
}

.search-result {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: var(--radius-xs);
  padding: 10px 12px;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
  transition: background var(--transition-fast);
}
.search-result:hover,
.search-result.active { background: var(--bg); }

.search-result-title {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.search-result-archived {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  background: var(--border-light);
  border-radius: 999px;
  vertical-align: middle;
}
.search-result-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-tertiary);
}
.search-result-sep { opacity: 0.5; }
.search-result-snippet {
  margin-top: 4px;
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Project switcher - subtle button between the wordmark and avatar.
   Reads as breadcrumb / context, not a heavy nav element. */
.project-switcher {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 6px;
  background: none;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  font-family: inherit;
  transition: background var(--transition-fast);
  max-width: 320px;
  min-width: 0;
}

.project-switcher:hover { background: var(--border-light); }

.project-switcher-divider {
  color: var(--text-tertiary);
  font-weight: 400;
  margin-right: 2px;
}

.project-switcher-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.project-switcher-chevron { color: var(--text-tertiary); }

/* Share button in the topbar - sits next to the project switcher so the
   action is anchored to the project it acts on. Same low-key chrome as
   the switcher (no border, no fill until hover) so the topbar reads as
   one calm row of context, not a crowded toolbar. */
.topbar-share {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  margin-left: 4px;
  background: none;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.topbar-share:hover { background: var(--border-light); color: var(--text); }
.topbar-share .icon { color: var(--text-tertiary); }
.topbar-share:hover .icon { color: var(--text); }

/* Project-switcher dropdown - same surface treatment as the user menu
   but with a tiny eyebrow label up top, no identity header. */
.project-switcher-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  padding: 6px 12px 8px;
}

/* Wordmark - now an SVG image (web/static/craaft.svg). The .wordmark
   anchor stays for layout/click semantics; .wordmark-img sizes the
   actual mark. */
.wordmark {
  display: inline-flex;
  align-items: center;
  /* line-height:0 strips the inline-block baseline gap that would
     otherwise make the link box taller than the SVG. */
  line-height: 0;
  text-decoration: none;
}

.wordmark-img {
  /* Smaller, quieter wordmark - sits as identity rather than headline. */
  height: 12px;
  width: auto;
  display: block;
}

/* Icon-only mark for signed-in users. The favicon SVG has internal
   padding (the rounded square frame), so it can render a touch larger
   than the wordmark and still feel balanced in the topbar. */
.wordmark-img--icon { height: 26px; }

.topbar-nav {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.topbar-nav a {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  transition: color var(--transition-fast);
}

.topbar-nav a:hover { color: var(--text); }
.topbar-nav a.active { color: var(--text); }

/* Avatar trigger button - pill-shaped so the avatar + chevron read as a
   single menu affordance, mirroring the project-switcher on the left side
   of the topbar. */
.topbar-avatar {
  background: none;
  border: none;
  padding: 4px 10px 4px 4px;
  cursor: pointer;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-family: inherit;
  transition: background var(--transition-fast);
}

.topbar-avatar:hover { background: var(--border); }
.topbar-avatar:focus-visible { outline: none; background: var(--border); box-shadow: 0 0 0 2px var(--text); }

.topbar-avatar-chevron { color: var(--text-tertiary); transition: color var(--transition-fast); }
.topbar-avatar:hover .topbar-avatar-chevron,
.topbar-avatar:focus-visible .topbar-avatar-chevron { color: var(--text); }

/* Pro pill - sits in topbar-nav before the avatar trigger when the
   workspace is on a paid plan. Outline-only treatment (transparent
   fill, thin lavender border, lavender text) so it reads as a quiet
   status marker rather than a hard CTA chip - roughly half the
   visual weight of a filled badge while still distinct enough to
   register at a glance. */
/* PRO pill: filled violet→indigo gradient with a diagonal shine band
   that sweeps across every 6 seconds. Most of the cycle (5.4s) the
   band is parked off-screen; the actual sweep only takes ~600ms so
   the badge reads as "premium status indicator" without becoming
   peripheral-vision noise.

   Sweep is keyed to a single keyframe sequence and gated behind
   prefers-reduced-motion: users with motion preferences disabled
   keep the static gradient. The badge itself is the clipping mask
   for the shine pseudo-element — overflow: hidden + position:
   relative anchors the band. */
.topbar-pro-pill {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 10px;
  background: linear-gradient(135deg, #7c6ff0 0%, #5d4ee5 100%);
  color: #ffffff;
  border: 0;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  user-select: none;
}

.topbar-pro-pill::after {
  /* The shine band itself. Diagonal stripe, semi-translucent white,
     fading at the edges so it reads as a soft highlight rather than
     a hard rectangle. Positioned just off the left edge in its idle
     state; the keyframe animation walks it across. */
  content: "";
  position: absolute;
  top: -50%;
  left: -150%;
  width: 40%;
  height: 200%;
  background: linear-gradient(
    100deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.35) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  transform: skewX(-22deg);
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .topbar-pro-pill::after {
    /* 6s total cycle: idle from 0% → 80% (4.8s parked off-screen),
       sweep from 80% → 95% (~0.9s of motion), then back to idle.
       infinite + ease-in-out keeps the rest period unobtrusive. */
    animation: topbar-pro-shine 6s ease-in-out infinite;
  }
}

@keyframes topbar-pro-shine {
  0%, 80%, 100% { left: -150%; }
  95%           { left: 150%; }
}

.avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--text);
  color: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  user-select: none;
}

.avatar-md {
  width: 36px;
  height: 36px;
  font-size: 15px;
}

.avatar-img {
  display: block;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg);
}

/* ── User menu (avatar dropdown) ───────────────────────────────────── */
.user-menu {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.10), 0 2px 6px rgba(0, 0, 0, 0.06);
  padding: 8px;
  min-width: 240px;
  z-index: 100;
  /* Soft pop-in keeps it from feeling abrupt. */
  animation: user-menu-in 200ms var(--ease-spring);
  transform-origin: top right;
  /* When openMenuAt clips the menu to the viewport (long project lists),
     hide the scrollbar entirely while keeping wheel/touch/keyboard
     scrolling. Otherwise a chunky scrollbar gutter eats into the menu
     padding and breaks the rounded-corner silhouette. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.user-menu::-webkit-scrollbar { display: none; }

@keyframes user-menu-in {
  from { opacity: 0; transform: translateY(-8px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.user-menu-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px 8px;
}

.user-menu-identity { min-width: 0; }

.user-menu-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

.user-menu-email {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

.user-menu-divider {
  height: 1px;
  background: var(--border-light);
  margin: 6px 4px;
}

.user-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  font-family: inherit;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.user-menu-item:hover { background: var(--bg-hover); }

.user-menu-item.active {
  background: var(--bg);
  color: var(--text);
}

.user-menu-item.active::after {
  content: "";
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--text);
  margin-left: auto;
}

.user-menu-item.danger { color: var(--danger); }
.user-menu-item.danger:hover { background: var(--danger-surface); color: var(--danger); }

.user-menu-item .icon { flex-shrink: 0; color: var(--text-tertiary); }
.user-menu-item:hover .icon,
.user-menu-item.active .icon { color: var(--text); }
.user-menu-item.danger .icon { color: var(--danger); }

.settings-avatar {
  width: 64px;
  height: 64px;
  font-size: 28px;
}

.avatar-control {
  display: flex;
  align-items: center;
  gap: 16px;
}

.avatar-control-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 360px;
}

.avatar-control-buttons {
  display: flex;
  gap: 8px;
}

/* Avatar-as-button: the tile IS the file picker. Hover reveals a soft
   overlay with a pencil cue so the affordance is discoverable without
   demanding attention at rest. The button shape clips to a circle so
   the overlay doesn't bleed past the avatar's edge. */
.avatar-tile {
  position: relative;
  width: 64px;
  height: 64px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
  overflow: hidden;
  transition: transform 120ms var(--ease-out);
}
.avatar-tile:hover { transform: scale(1.03); }
.avatar-tile:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
.avatar-tile[disabled] {
  cursor: progress;
}
.avatar-tile .settings-avatar {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Pencil cue overlay - only shows on hover/focus. Translucent dark
   plate over the avatar with a centered icon. */
.avatar-tile-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(26, 24, 22, 0.55);
  color: #ffffff;
  opacity: 0;
  transition: opacity 120ms var(--ease-out);
  pointer-events: none;
}
.avatar-tile:hover .avatar-tile-overlay,
.avatar-tile:focus-visible .avatar-tile-overlay {
  opacity: 1;
}
.avatar-tile-busy .avatar-tile-overlay {
  opacity: 1;
  background: rgba(26, 24, 22, 0.4);
}

.avatar-control-helper {
  font-size: 12px;
  color: var(--text-tertiary);
  line-height: 1.45;
}

.avatar-control-helper a {
  color: var(--accent);
  text-decoration: underline;
}

/* ── Page header ── */
.page-body {
  padding: 36px 32px 64px;
  display: flex;
  justify-content: center;
  flex: 1;
}

.page-inner {
  width: 100%;
  max-width: 1180px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.page-inner.narrow { max-width: 720px; }
.page-inner.wide { max-width: 1376px; }

/* Empty-state centering: when a page has no chrome (header) and a single
   empty-state component, anchor it in the middle of the body so the eye
   lands on the call-to-action instead of starting at the top. */
.page-inner-empty {
  justify-content: center;
  align-items: center;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.page-title {
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.03em;
}

.page-sub {
  font-size: 14px;
  color: var(--text-tertiary);
  margin-top: 4px;
}

/* ── Buttons ──
   Pill-rounded: 999px collapses to whatever half-height the button
   ends up at, so ghost / primary / danger / outline variants all share
   the same fully-rounded silhouette without per-variant overrides. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 500;
  border: 1px solid transparent;
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.btn:hover { background: var(--bg-hover); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.btn-primary {
  background: var(--accent);
  color: var(--surface);
  border-color: var(--accent);
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

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

.btn-danger {
  background: var(--danger);
  color: var(--surface);
  border-color: var(--danger);
}
/* Override .btn:hover's grey background with a darker red. Without an
   explicit background here the generic .btn:hover rule wins (later in
   the cascade) and the button reads as "neutral" instead of "danger"
   on hover. */
.btn-danger:hover {
  background: var(--danger-hover);
  border-color: var(--danger-hover);
}

.btn-danger-outline {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger-border);
}
.btn-danger-outline:hover { background: var(--danger-surface); }

.btn .icon { width: 14px; height: 14px; }

/* Optical correction for icon+label buttons: an icon on the left adds
   visual weight that 16px right-padding doesn't have to balance, so the
   text reads as slightly closer to the left edge than the right. Trim
   4px off the leading padding when the first child is an icon to bring
   the geometric and optical centers back in line. */
.btn:has(> .icon:first-child) {
  padding-left: 12px;
}

/* ── Cards ── */
.card-surface {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.section-card {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.section-head {
  padding: 22px 28px 18px;
  border-bottom: 1px solid var(--border-light);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.section-head .section-title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.section-head .section-count {
  font-size: 13px;
  color: var(--text-tertiary);
  margin-left: 8px;
  font-weight: 500;
}

.section-head .section-sub {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 4px;
}

/* ── Inputs ── */
.field { display: flex; flex-direction: column; gap: 6px; }

.field-label {
  font-size: 13px;
  font-weight: 500;
}

.field-helper {
  font-size: 13px;
  color: var(--text-secondary);
}

/* Color picker - used by the edit-column modal. A row of round
   swatches, each a small button, with a ring on the selected one. The
   "default" swatch (no token, just a neutral grey) gets a thin border
   so it doesn't disappear into the modal background. */
.color-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.color-swatch {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
  position: relative;
  transition: transform var(--transition-fast);
}
.color-swatch:hover { transform: scale(1.08); }
.color-swatch.selected {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}
.color-swatch-default {
  background: var(--text-tertiary);
  border: 1px solid var(--border);
}

.input,
.textarea {
  width: 100%;
  padding: 11px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 14px;
  color: var(--text);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

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

.input:focus,
.textarea:focus {
  outline: none;
  border-color: var(--text);
  box-shadow: 0 0 0 3px rgba(26, 24, 22, 0.06);
}

.textarea {
  resize: vertical;
  min-height: 80px;
  line-height: 1.5;
}

/* ── Chips & dots ── */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--bg);
  color: var(--text);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
}

.chip-success {
  background: var(--success-surface);
  color: var(--success-text);
}

.chip .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.dot-todo { background: var(--todo-dot); }
.dot-doing { background: var(--doing-dot); }
.dot-done { background: var(--done-dot); }

/* ── Auth ── two-pane layout: focused form left, editorial canvas right */

.auth-page {
  flex: 1;
  display: grid;
  grid-template-columns: 480px 1fr;
  min-height: 100vh;
  min-height: 100dvh;
}

.auth-form-pane {
  background: var(--surface);
  padding: 64px 56px 48px;
  display: flex;
  flex-direction: column;
}

.auth-form-content {
  margin-top: 96px;
}

.auth-heading {
  font-size: 36px;
  line-height: 44px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
}

.auth-sub {
  margin-top: 8px;
  margin-bottom: 36px;
  font-size: 15px;
  line-height: 22px;
  color: var(--text-secondary);
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.auth-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.auth-field-label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.auth-field-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}

.auth-field-link {
  font-size: 12px;
  color: var(--text-tertiary);
  text-decoration: none;
}

.auth-field-link:hover { color: var(--text); }

.auth-cta {
  width: 100%;
  margin-top: 6px;
  padding: 14px 18px;
  font-size: 14px;
}

.auth-oauth {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 18px;
}

.auth-oauth-btn {
  width: 100%;
  justify-content: center;
  padding: 11px 16px;
  font-size: 14px;
  text-decoration: none;
}

.auth-oauth-divider {
  position: relative;
  text-align: center;
  margin: 4px 0 -4px;
  font-size: 12px;
  color: var(--text-secondary);
}

.auth-oauth-divider::before,
.auth-oauth-divider::after {
  content: "";
  position: absolute;
  top: 50%;
  width: calc(50% - 18px);
  height: 1px;
  background: var(--border-light);
}

.auth-oauth-divider::before { left: 0; }
.auth-oauth-divider::after  { right: 0; }

.auth-oauth-divider span { background: transparent; padding: 0 8px; }

.auth-error {
  display: none;
  font-size: 13px;
  color: var(--danger);
  text-align: center;
  margin-top: 4px;
}

.auth-error.visible { display: block; }

.auth-pwd-wrap {
  position: relative;
}

.auth-pwd-wrap .input-pwd {
  padding-right: 42px;
}

.auth-pwd-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color .15s ease, background .15s ease;
}

.auth-pwd-toggle:hover { color: var(--text); background: var(--bg-subtle, rgba(0,0,0,.04)); }
.auth-pwd-toggle:focus { outline: none; }
.auth-pwd-toggle:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

.auth-strength {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}

.auth-strength-bars {
  display: flex;
  gap: 4px;
  flex: 1;
}

.auth-strength-seg {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: var(--border-light, rgba(0,0,0,.08));
  transition: background .2s ease;
}

.auth-strength-label {
  font-size: 12px;
  color: var(--text-tertiary);
  min-width: 64px;
  text-align: right;
}

.auth-strength[data-level="1"] .auth-strength-seg.on { background: #c84a3b; }
.auth-strength[data-level="1"] .auth-strength-label { color: #c84a3b; }
.auth-strength[data-level="2"] .auth-strength-seg.on { background: #d98032; }
.auth-strength[data-level="2"] .auth-strength-label { color: #d98032; }
.auth-strength[data-level="3"] .auth-strength-seg.on { background: #c2853a; }
.auth-strength[data-level="3"] .auth-strength-label { color: #8a5a1f; }
.auth-strength[data-level="4"] .auth-strength-seg.on { background: #2f6b3b; }
.auth-strength[data-level="4"] .auth-strength-label { color: #2f6b3b; }

.auth-form-footer {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.auth-form-divider {
  height: 1px;
  background: var(--border-light);
}

.auth-footer-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-secondary);
}

.auth-footer-row a {
  color: var(--text);
  font-weight: 500;
  text-decoration: none;
}

.auth-footer-row a:hover { color: #000; }

/* ── Right canvas pane ───────────────────────────────────────────────── */

.auth-canvas-pane {
  position: relative;
  background: var(--bg);
  overflow: hidden;
}

.auth-canvas-headline {
  position: absolute;
  top: 124px;
  left: 80px;
  font-size: 56px;
  line-height: 60px;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--text);
  margin: 0;
}

/* Span from below the headline down to the canvas-pane edge so the
   troughs always reach the bottom of the viewport, regardless of how
   tall the browser is. The flex-stretching on .auth-col-hint takes
   care of pushing the troughs full-height. */
.auth-canvas-cols {
  position: absolute;
  top: 250px;
  bottom: 0;
  left: 80px;
  display: flex;
  gap: 0;
}

.auth-col-hint {
  width: 280px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* Stretch the column so the trough below can flex-fill the
     remaining vertical space. */
  align-self: stretch;
}

.auth-col-hint-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  letter-spacing: 0.13em;
  color: var(--text-tertiary);
}

.auth-col-hint-label { font-weight: 600; }

.auth-col-hint-count {
  font-weight: 500;
  color: var(--text-tertiary);
}

.auth-col-hint .dot {
  width: 7px;
  height: 7px;
}

.auth-col-hint-trough {
  width: 1px;
  flex: 1;
  background: #e2dfd9;
}

/* Card stack - vertically centered inside the trough region (which
   starts at y=290 below the column headers). The +145px offset puts
   the card centerline at the midpoint of (290, viewportBottom),
   keeping the top card from colliding with the column labels. */
.auth-canvas-cards {
  position: absolute;
  top: calc(50% + 145px);
  right: clamp(120px, 18%, 240px);
  width: 360px;
  height: 280px;
  transform: translateY(-50%);
}

.auth-fake-card {
  position: absolute;
  background: var(--surface);
  border: 1px solid var(--border-light);
  border-radius: 12px;
  padding: 16px 18px;
  width: 320px;
  box-shadow: 0 14px 36px -6px rgba(0, 0, 0, 0.10);
  display: flex;
  flex-direction: column;
  gap: 10px;
  transform-origin: center;
}

.auth-fake-card-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  line-height: 20px;
}

.auth-fake-card-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-tertiary);
}

.auth-fake-card-meta .dot {
  width: 8px;
  height: 8px;
}

/* Foreground card - gentle tilt, no offset */
.auth-fake-card-1 {
  top: 0;
  right: 0;
  transform: rotate(-0.6deg);
  z-index: 3;
}

/* Middle card - peeks behind, slightly left */
.auth-fake-card-2 {
  top: 68px;
  right: 84px;
  transform: rotate(-2.6deg);
  z-index: 2;
}

/* Back card - bottom, slightly right */
.auth-fake-card-3 {
  top: 148px;
  right: -8px;
  transform: rotate(1.8deg);
  z-index: 1;
}

.auth-canvas-signature {
  position: absolute;
  left: 80px;
  bottom: 64px;
}

.auth-signature-main {
  font-size: 13px;
  color: var(--text-tertiary);
}

.auth-signature-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--text-tertiary);
  opacity: 0.75;
}

/* Mobile - collapse to single column, hide canvas. The canvas is
   decorative; on small screens it would steal the form's airspace. */
@media (max-width: 800px) {
  .auth-page {
    grid-template-columns: 1fr;
  }
  .auth-canvas-pane {
    display: none;
  }
  .auth-form-pane {
    padding: 32px 24px;
  }
  .auth-form-content {
    margin-top: 48px;
  }
  .auth-heading {
    font-size: 28px;
    line-height: 36px;
  }
}

/* ── Projects page ──────────────────────────────────────────────────────── */

/* Wider gutter so the greeting + cards have room to breathe. */
.page-inner-projects { gap: 36px; }

/* Greeting / page header. */
.projects-greeting {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Adds breathing room between the greeting's stats line ("1 project ·
   1 card · ..." etc.) and the toolbar row beneath it. The parent
   .page-inner-projects flex container already supplies a 36px gap
   between every direct child; this margin pushes the toolbar an
   additional ~20px below the greeting specifically, without affecting
   the spacing between the toolbar and the sections that follow. */
.projects-greeting + .projects-toolbar { margin-top: 36px; }
.projects-greeting-title {
  font-size: 32px;
  line-height: 40px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0;
}
.projects-greeting-name { color: var(--text-secondary); }
.projects-greeting-sub {
  font-size: 14px;
  color: var(--text-tertiary);
  margin: 0;
}

/* Toolbar - filters left, search/sort/CTA right. */
.projects-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.projects-toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.filter-chip-row {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.filter-chip {
  appearance: none;
  background: none;
  border: none;
  cursor: pointer;
  border-radius: 999px;
  padding: 6px 12px;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.filter-chip:hover { background: var(--border-light); color: var(--text); }
.filter-chip.is-active {
  background: var(--text);
  color: var(--surface);
  font-weight: 600;
}

.projects-search {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.projects-search-icon {
  position: absolute;
  left: 12px;
  color: var(--text-secondary);
  pointer-events: none;
}
.projects-search-input {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  /* Vertical padding matches .btn (9px) so the search bar and the
     adjacent "+ Project" CTA align to the same 40px row height. */
  padding: 9px 14px 9px 32px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  width: 240px;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.projects-search-input::placeholder { color: var(--text-tertiary); }
.projects-search-input:focus {
  outline: none;
  border-color: var(--text);
  box-shadow: 0 0 0 3px var(--border);
}
.projects-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* Section eyebrows ("PINNED", "ALL PROJECTS"). */
.projects-section-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: -8px; /* tighten gap to the cards below */
}
.projects-section-icon {
  display: inline-flex;
  color: var(--todo-dot);
}
.projects-section-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--text-secondary);
}
.projects-section-count {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--border-light);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
}

/* Pinned section uses a single hero column - one wide card per row. */
.projects-pinned {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

/* Standard grid: 3 columns on wide screens, auto-fits down. */
.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(304px, 1fr));
  gap: 20px;
}

/* ── Project card ───────────────────────────────────────────────────────── */
.project-card {
  position: relative;
  background: var(--surface);
  border-radius: 14px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  cursor: pointer;
  /* Hover lift uses the spring curve so the card feels weighty - 140ms
     ease made the lift snap. Promoted to its own layer so the motion
     stays smooth. The resting + hover box-shadows were removed for a
     flatter aesthetic; the card now reads against the warm page bg
     via the surface-color contrast alone. */
  transform: translateZ(0);
  will-change: transform;
  transition:
    transform 220ms var(--ease-spring),
    border-color var(--transition-fast);
}
.project-card:hover {
  transform: translateY(-2px);
}
.project-card:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

.project-card-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px 20px 16px;
  flex: 1;
}

.project-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.project-card-titles {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.project-card-name {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.project-card-name > span:last-child {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-card-name-lg { font-size: 20px; letter-spacing: -0.02em; }

.project-card-sub {
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.project-favorite-star {
  display: inline-flex;
  align-items: center;
  color: var(--todo-dot);
  flex-shrink: 0;
}

.project-progress {
  height: 6px;
  background: var(--border-light);
  border-radius: 4px;
  overflow: hidden;
  display: flex;
}
.project-progress-segment { height: 100%; }
.project-progress-empty { background: var(--border-light); }

/* Count chips: dot + (label) + n. The dots line up vertically with
   the matching segments above for a clear read. */
.project-count-chips {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.project-count-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--text);
  font-weight: 600;
}
.project-count-chip.is-empty { color: var(--text-tertiary); font-weight: 500; }
.project-count-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}
.project-count-label {
  font-weight: 500;
  color: var(--text-secondary);
}

/* Member avatar stack, used at the bottom of each card. */
.member-stack {
  display: inline-flex;
  align-items: center;
}
.member-stack-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--surface);
  font-size: 10px;
  font-weight: 600;
  border: 2px solid var(--surface);
  user-select: none;
}
.member-stack-avatar + .member-stack-avatar { margin-left: -8px; }
.member-stack-more {
  background: var(--bg);
  color: var(--text-secondary);
  border-color: var(--surface);
}

.project-card-bottom {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.project-card-updated {
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* Featured (pinned) variant - taller, more padding, larger title. */
.project-card-featured .project-card-body { padding: 20px 28px 22px; }
.project-card-featured .project-progress { height: 8px; }
.project-card-featured .member-stack-avatar { width: 28px; height: 28px; font-size: 11px; }

/* Inline empty-result message inside the toolbar/grid block. */
.projects-empty-inline {
  padding: 48px 24px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 14px;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

/* ── Tasks view (Tasks pill) ────────────────────────────────────────────── */

.tasks-section {
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.tasks-loading {
  padding: 32px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 14px;
}

.tasks-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tasks-group-header {
  display: flex;
  align-items: center;
  gap: 8px;
}
.tasks-group-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--text-secondary);
  text-transform: uppercase;
}
.tasks-group-overdue .tasks-group-label { color: var(--danger); }
.tasks-group-today   .tasks-group-label { color: var(--warnText, #8a5a02); }

.tasks-group-count {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--border-light);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
}
.tasks-group-overdue .tasks-group-count {
  background: var(--danger-surface);
  color: var(--danger);
}

.tasks-group-body {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
}

/* Hygiene tray (Focus view, Section 3). Three count tiles in a row,
   informational only - hover surfaces the rule via title attribute. */
.hygiene-tray {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}
.hygiene-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.hygiene-tile:hover {
  border-color: var(--text-tertiary);
  background: var(--bg-hover);
}
.hygiene-tile.is-active {
  border-color: var(--text);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--text) inset;
}

.hygiene-close {
  margin-left: auto;
  appearance: none;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
}
.hygiene-close:hover { background: var(--bg-hover); color: var(--text); }
.hygiene-tile-n {
  font-size: 28px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
}
.hygiene-tile-label {
  font-size: 12px;
  color: var(--text-secondary);
}

.task-row {
  appearance: none;
  background: var(--surface);
  border: none;
  border-top: 1px solid var(--border-light);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 18px;
  font: inherit;
  text-align: left;
  color: var(--text);
  transition: background var(--transition-fast);
}
.task-row:first-child { border-top: none; }
.task-row:hover { background: var(--bg-hover); }
.task-row:focus-visible {
  outline: none;
  background: var(--bg);
  box-shadow: inset 2px 0 0 var(--accent);
}

.task-row-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.task-row-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.task-row-meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-tertiary);
}
.task-row-project { color: var(--text-secondary); font-weight: 500; }
.task-row-sep { opacity: 0.6; }

.task-row-side {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}
.task-row-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--surface);
  font-size: 10px;
  font-weight: 600;
}

.task-row-due {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background: var(--border-light);
  color: var(--text-secondary);
}
.task-row-due-overdue { background: var(--danger-surface); color: var(--danger); }
.task-row-due-today   { background: #fff5d8; color: #8a5a02; }
.task-row-due-week    { background: #eaf2ff; color: #1d4ed8; }
.task-row-due-later   { background: var(--border-light); color: var(--text-secondary); }

.tasks-empty {
  padding: 64px 24px;
  text-align: center;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: 14px;
}
.tasks-empty h2 {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 6px;
}
.tasks-empty p {
  font-size: 14px;
  color: var(--text-secondary);
  margin: 0;
}

@media (max-width: 720px) {
  .task-row { padding: 12px 14px; gap: 10px; }
  .task-row-title { font-size: 13px; }
}

/* ── Empty state (zero projects) ────────────────────────────────────────── */
.projects-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 16px;
  padding: 32px 24px 64px;
  max-width: 560px;
}
.projects-empty-illustration {
  position: relative;
  width: 360px;
  height: 220px;
  margin-bottom: 8px;
}
.projects-empty-halo {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center,
    rgba(59, 130, 246, 0.18) 0%,
    rgba(245, 201, 79, 0) 70%);
  pointer-events: none;
}
.projects-empty-card {
  position: absolute;
  width: 130px;
  height: 80px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
  overflow: hidden;
}
.projects-empty-card-stripe {
  height: 3px;
  width: 100%;
}
.projects-empty-card-line {
  height: 6px;
  width: 70%;
  background: var(--border-light);
  border-radius: 3px;
  margin: 14px 14px 0;
}
.projects-empty-card-line.short {
  width: 45%;
  margin-top: 8px;
}
.projects-empty-headline {
  font-size: 32px;
  line-height: 40px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0;
}
.projects-empty-sub {
  font-size: 16px;
  line-height: 24px;
  color: var(--text-secondary);
  margin: 0;
  max-width: 480px;
}
.projects-empty-ctas {
  display: flex;
  gap: 10px;
  margin-top: 8px;
}
.projects-empty-tip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 4px;
}
.kbd-inline {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 500;
}

@media (max-width: 720px) {
  .projects-greeting-title { font-size: 26px; line-height: 32px; }
  .projects-toolbar { gap: 12px; }
  .projects-search-input { width: 180px; }
  .project-grid { grid-template-columns: 1fr; }
  .projects-empty-illustration { transform: scale(0.85); }
  .projects-empty-headline { font-size: 24px; line-height: 32px; }
}

.menu-dots {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border-radius: var(--radius-xs);
  cursor: pointer;
  color: var(--text-tertiary);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.menu-dots:hover { background: var(--bg-hover); color: var(--text-secondary); }

/* Project actions dropdown - portalled into <body> so it can float
   above the project grid without being clipped by card overflow.
   Position is computed inline by the JS based on the trigger's
   bounding rect; only the visual chrome lives in CSS. */
.project-menu {
  min-width: 160px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  padding: 4px;
  display: flex;
  flex-direction: column;
  z-index: 1000;
  animation: menuIn 180ms var(--ease-spring);
  transform-origin: top right;
}
@keyframes menuIn {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.project-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: transparent;
  border: none;
  border-radius: var(--radius-xs);
  font-family: inherit;
  font-size: 13px;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.project-menu-item:hover { background: var(--bg-hover); }
.project-menu-item .icon { color: var(--text-tertiary); flex-shrink: 0; }
.project-menu-item:hover .icon { color: var(--text-secondary); }

/* Destructive action sits at the bottom and shifts to red on hover so
   it reads as "different category" without being constantly alarming. */
.project-menu-item.danger:hover {
  background: var(--danger-surface, rgba(239, 68, 68, 0.08));
  color: var(--danger);
}
.project-menu-item.danger:hover .icon { color: var(--danger); }

/* ── Board (Craaft) ── */
.board-header {
  padding: 24px 32px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* When the board header has only a right-side group (project switcher
   moved to the topbar), push that group to the right so the board still
   reads as "header on top, columns below". */
.board-header-right-only {
  justify-content: flex-end;
}

.board-header-left {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}

.board-header-right {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
}

.board-header h1 {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.025em;
}

.board-header .task-count {
  font-size: 13px;
  font-weight: 400;
  color: var(--text-tertiary);
}

.board-back {
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 4px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.board-back:hover { color: var(--text); }

.board {
  display: flex;
  gap: 16px;
  /* Top padding gives the columns a small breath below the topbar now
     that the in-page board-header strip is gone on the owner view.
     Public boards still render a banner + header above this, so the
     extra 16px just widens that gap a touch (still acceptable rhythm). */
  padding: 16px 32px 32px;
  overflow: auto;
  align-items: stretch;
  scroll-snap-type: x proximity;
  /* scroll-padding mirrors the visual padding so scroll-snap targets
     (columns with scroll-snap-align: start) align to a position
     32px inside the scroll-port instead of flush at scroll-port:0.
     Without this the snap engine pulls scrollLeft to 32 on load,
     parking the first column's cards against the browser's left
     edge with no way to scroll back to the visual gutter. */
  scroll-padding: 0 32px;
  /* relative so the column-drop-indicator can position itself within
     the scroll container instead of the viewport. */
  position: relative;
}

/* Vertical drop indicator that follows the cursor while a column is
   being dragged. Pinned to the .board's coordinate system. */
.column-drop-indicator {
  position: absolute;
  top: 0;
  bottom: 32px;
  width: 3px;
  border-radius: 2px;
  background: var(--accent);
  pointer-events: none;
  transform: translateX(-1px);
}

/* Header is the drag handle. Show that on hover so users discover it. */
.column-header {
  cursor: grab;
}
.column-header:active { cursor: grabbing; }

/* The dragged column fades to give visual feedback that the row is
   moving. The browser also paints a drag image; this class styles the
   source. */
.column.dragging {
  opacity: 0.4;
}

/* Subtle but visible scrollbars on the board container. Hiding them
   completely (the previous default) made horizontal overflow invisible
   to users on a wide-column / many-column board. */
.board::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
.board::-webkit-scrollbar-track { background: transparent; }
.board::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 999px;
  border: 2px solid var(--bg);
}
.board::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }

/* Board view locks the layout to the viewport so the board area has
   its own scroll viewport instead of pushing the whole page tall when
   one column has many cards. Other views (projects, settings, ...)
   keep the default page-scroll model.

   Chain: .screen-board (full viewport) -> section (flex 1) -> .board
   (flex 1, overflow auto). board-header is fixed-height, columns
   stretch to the board's cross-axis. */
.screen-board {
  height: 100vh;
  display: flex;
  flex-direction: column;
}
.screen-board > section {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.screen-board .board-header { flex-shrink: 0; }
.screen-board .board {
  flex: 1;
  min-height: 0;
}

.column {
  /* Grow to fill horizontal space when few columns exist (so a 3-col
     project doesn't leave dead space on a wide viewport), but never
     shrink below 320px - that's the floor for readable card titles.
     max-width caps the upper end so columns don't get unwieldy. */
  flex: 1 0 320px;
  min-width: 320px;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  scroll-snap-align: start;
  /* Bounded height so the .card-list can scroll inside.
     min-height: 0 lets the flex child shrink below content. */
  min-height: 0;
  align-self: stretch;
  /* Faint warm wash so the column's full vertical extent reads as a
     contained channel rather than ending visually wherever the last
     card sits. Without this the page bg shows through the empty
     bottom of every short column, and the board feels half-finished.
     ~5% opacity is enough to register as a column at a glance
     without competing with the white card surfaces inside. */
  background: rgba(20, 24, 22, 0.05);
  border-radius: var(--radius-sm);
  padding: 12px 8px 8px;
}

.column-header {
  padding: 0 4px 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.column-header-left {
  display: flex;
  align-items: center;
  gap: 8px;
}

.column-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* Default dot tone for any column key the three named ones don't cover -
   user-created columns use generated keys like col_<hex> and need a
   neutral fallback so the header doesn't render an empty circle. */
.column-dot { background: var(--text-tertiary); }
.column[data-column="todo"] .column-dot { background: var(--todo-dot); }
.column[data-column="doing"] .column-dot { background: var(--doing-dot); }
.column[data-column="done"] .column-dot { background: var(--done-dot); }

.column-header-right {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Per-column "×" delete button. Hidden by default and revealed when the
   column header (or button) is hovered/focused - keeps the header calm
   most of the time, surfaces the action only when the user is mousing
   around it. Disabled state stays present but greyed; the title attribute
   explains why. */
.column-delete,
.column-menu-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  color: var(--text-tertiary);
  background: transparent;
  border: none;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--transition), color var(--transition), background var(--transition);
}
.column:hover .column-delete,
.column:hover .column-menu-btn,
.column-delete:focus-visible,
.column-menu-btn:focus-visible { opacity: 1; }
.column-delete:hover:not([disabled]),
.column-menu-btn:hover:not([disabled]) {
  color: var(--text);
  background: var(--surface-raised);
}
.column-delete[disabled] {
  cursor: not-allowed;
  opacity: 0;
}
.column:hover .column-delete[disabled] { opacity: 0.4; }

/* Disabled menu item (Delete when the column still has cards). Same
   surface as enabled items, just dimmed and non-interactive so users
   see *why* delete isn't available without the row vanishing. */
.project-menu-item[disabled] {
  cursor: not-allowed;
  opacity: 0.45;
}
.project-menu-item[disabled]:hover { background: transparent; }

.column-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}

/* The column title sits inside a button so the whole label is clickable.
   Strip the default button chrome - we want it to read as a heading,
   not an input. The pencil hint stays hidden until the column header is
   hovered, matching the column-delete affordance. */
.column-title-edit {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
}
.column-edit-icon {
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity var(--transition);
}
.column:hover .column-edit-icon,
.column-title-edit:focus-visible .column-edit-icon { opacity: 1; }
.column-title-edit:hover .column-title { color: var(--text); }

.column-count {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-tertiary);
  min-width: 20px;
  text-align: center;
}

/* Over-limit state: pulled into a danger pill so it reads as "too many"
   at-a-glance. Triggered when card count >= cardLimit (inclusive) - WIP
   limits are a stop-the-world signal, not a soft warning. */
.column-count.column-count-over {
  color: var(--danger);
  font-weight: 600;
}

.card-list {
  flex: 1 1 0;
  min-height: 48px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-radius: var(--radius);
  padding: 2px;
  /* Each column scrolls its cards independently so one tall column
     doesn't make the whole page tall. min-height: 0 lets the inner
     scroll area shrink below content size in the flex layout. */
  overflow-y: auto;
  min-height: 0;
  transition: background var(--transition), padding var(--transition);
  /* Hide the scrollbar entirely - no gutter, no thumb. Wheel/touch/
     keyboard scrolling all still work. Matches the .user-menu treatment. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.card-list::-webkit-scrollbar { display: none; }

.card-list.drag-over {
  background: var(--accent-subtle);
  padding: 6px;
}

.empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 80px;
  /* Border tone is intentionally darker than --border (#e8e6e3) - that
     value reads as "card edge on white" and disappears against the warm
     bg the columns sit on. This dashed tone gives the slot a clear
     "drop here" affordance without competing with real cards. */
  border: 1.5px dashed #c8c2b9;
  border-radius: var(--radius);
  color: var(--text-tertiary);
  font-size: 13px;
  font-weight: 400;
  padding: 20px;
  text-align: center;
  user-select: none;
}

.card-list.drag-over .empty-state {
  border-color: var(--accent);
  color: var(--accent);
  opacity: 0.7;
}

.craaft-card {
  background: var(--surface-raised);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  cursor: grab;
  position: relative;
  box-shadow: var(--shadow-sm);
  /* Composited layer + spring curve so hover/drag feedback feels
     weighty rather than snappy. */
  transform: translateZ(0);
  transition:
    box-shadow 220ms var(--ease-out),
    transform 220ms var(--ease-spring),
    opacity var(--transition);
  animation: cardIn 220ms var(--ease-out) both;
}

@keyframes cardIn {
  from { opacity: 0; transform: translateY(6px); }
}

.craaft-card:hover { box-shadow: var(--shadow); }
.craaft-card:active { cursor: grabbing; }

/* Priority stripe - left-edge accent on cards that need attention. Low
   and unset stay calm; medium/high/urgent surface with progressively
   thicker stripes in the priority palette. Drawn as a ::before so the
   stripe inherits the card's rounded corners (the inner card-radius
   would clip a real border) and doesn't shift the content's left edge.
   Padding-left is bumped on striped cards by the stripe width so the
   text edge stays visually consistent across cards. */
.craaft-card[data-priority]::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  pointer-events: none;
}
.craaft-card[data-priority]::before { width: 4px; }
.craaft-card[data-priority="medium"]::before { background: var(--priority-medium); }
.craaft-card[data-priority="high"]::before   { background: var(--priority-high); }
.craaft-card[data-priority="urgent"]::before { background: var(--priority-urgent); }
.craaft-card[data-priority="medium"],
.craaft-card[data-priority="high"],
.craaft-card[data-priority="urgent"] { padding-left: 18px; }

/* Placeholder shell for not-yet-hydrated cards on large boards. The
   IntersectionObserver in board.js swaps real content in once the
   card scrolls within range; until then the shell holds enough
   vertical space that scroll geometry stays stable and the column's
   drop-target math doesn't see a degenerate-height row. The
   `animation: none` kills the cardIn entrance jiggle so freshly
   hydrated cards just appear (the placeholder already played it). */
.craaft-card.placeholder {
  min-height: 56px;
  animation: none;
}

.craaft-card.dragging {
  opacity: 0.35;
  transform: scale(0.97);
  box-shadow: var(--shadow-xs);
}

.craaft-card.editing {
  box-shadow: 0 0 0 2px var(--accent), var(--shadow);
  cursor: default;
}

/* "Done" columns - per-column flag, not key-locked. Multiple columns
   can be marked done (Done, Won't Do, Cancelled) and they all get the
   same struck-through, dimmed treatment. data-done is set by the board
   view based on the column's is_done flag. */
.column[data-done="true"] .craaft-card { opacity: 0.7; }
.column[data-done="true"] .craaft-card:hover { opacity: 1; }
.column[data-done="true"] .card-content { text-decoration: line-through; text-decoration-color: var(--text-tertiary); }

.card-content {
  font-size: 14px;
  font-weight: 400;
  color: var(--text);
  word-break: break-word;
  outline: none;
  min-height: 20px;
  line-height: 1.5;
}

.card-content:empty::before {
  content: "Type something\2026";
  color: var(--text-tertiary);
  pointer-events: none;
}

.card-content[contenteditable="true"] { cursor: text; }

.card-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

.card-date {
  font-size: 11px;
  color: var(--text-tertiary);
}

/* Attachment indicator: paperclip + count, displayed when the card
   has at least one attachment. Sits between the date and the action
   buttons in the meta row. Quiet styling - tertiary text colour and
   small icon - so it reads as metadata, not a CTA. */
.card-attachments {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11px;
  color: var(--text-tertiary);
}
.card-attachments .icon { color: currentColor; }

/* Assignee avatar leads the meta row. Always rendered (real avatar
   when assigned, dashed-ring placeholder when not) so the layout is
   stable across cards. */
.card-assignee {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}

/* Empty-state placeholder when no one is assigned. Light dashed
   ring with the page bg inside - reads as "slot for an assignee"
   without demanding attention. */
.card-avatar-empty {
  display: inline-block;
  border-radius: 50%;
  background: transparent;
  border: 1.5px dashed var(--border);
  box-sizing: border-box;
}

.card-actions {
  display: flex;
  gap: 1px;
  margin-left: auto;
  opacity: 0;
  transition: opacity var(--transition-fast);
}

.craaft-card:hover .card-actions { opacity: 1; }

.card-btn {
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.card-btn:hover { background: var(--border-light); color: var(--text-secondary); }
.card-btn.delete:hover { background: var(--danger-surface); color: var(--danger); }

.add-card-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 4px;
  /* Lives inside .card-list now and inherits its 8px gap from the
     last card; no margin-top needed. flex-shrink: 0 prevents the
     button from being squeezed when many cards are present. */
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: var(--text-tertiary);
  font-size: 13px;
  font-weight: 500;
  width: 100%;
  text-align: left;
  border-radius: var(--radius-sm);
  font-family: inherit;
  cursor: pointer;
  transition: color var(--transition);
}

.add-card-btn:hover { color: var(--text-secondary); }

/* "Add column" trigger - intentionally subtle. Top-anchored, fixed
   height matching the empty-state placeholder, so its vertical center
   lines up with the "Nothing here yet" slot in adjacent columns and
   stays there as cards pile up below. No border, no background - just
   text + icon that lifts on hover. */
.add-column-btn {
  flex: 0 0 auto;
  align-self: flex-start;
  /* margin-top matches the height of .column-header (content + 10px
     bottom padding) so the button starts at the same baseline as the
     empty-state row. min-height matches .empty-state so the centered
     content sits on the same axis. */
  margin-top: 28px;
  min-height: 80px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 12px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: color var(--transition);
}
.add-column-btn:hover { color: var(--text-secondary); }

.drop-indicator {
  height: 3px;
  /* The indicator is a flex child of .card-list (display:flex,
     flex-direction:column). Without flex-shrink:0, when the column is
     scrollable (more cards than fit), the flex layout proportionally
     shrinks every child including the indicator - and a 3px-tall flex
     item with no min-height collapses to zero height first. The
     indicator stays in DOM but disappears. flex-shrink:0 keeps its
     declared 3px regardless of overflow pressure. */
  flex-shrink: 0;
  background: var(--accent);
  border-radius: 999px;
  margin: -2px 4px;
  opacity: 0;
  transition: opacity 140ms var(--ease-out);
}

.drop-indicator.active { opacity: 1; }

/* ── Modal ── */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  z-index: 100;
  animation: fadeIn 200ms var(--ease-out);
}

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

.modal {
  background: var(--surface);
  border-radius: 14px;
  box-shadow: var(--shadow-modal);
  width: 100%;
  max-height: calc(100vh - 64px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  animation: modalIn 240ms var(--ease-spring);
}

@keyframes modalIn {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-sm { max-width: 520px; }
.modal-md { max-width: 620px; }
.modal-lg { max-width: 720px; }
/* The card-detail variant runs wider so the two-column layout (main +
   metadata sidebar) breathes. Comments thread sits in the main column. */
.modal-xl { max-width: 960px; }

.modal-header {
  padding: 18px 16px 18px 24px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  border-bottom: 1px solid var(--border-light);
}

.modal-header.no-border { border-bottom: none; padding-bottom: 0; }

.modal-title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.modal-sub {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 3px;
}

.modal-close {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-xs);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.modal-close:hover { color: var(--text); }

.modal-close:hover { background: var(--bg-hover); }

.modal-body {
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  overflow-y: auto;
}

.modal-footer {
  padding: 14px 24px;
  border-top: 1px solid var(--border-light);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.modal-footer .footer-actions {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

/* ── Card detail modal (v2) ──
   Bigger, document-feeling layout. Header has the status pill +
   icon-button row (kebab + close). Body is a two-column grid with
   inline section labels and dividers between Description/Attachments/
   Comments. Sidebar drops the v1 grey container and uses chip-style
   value displays that swap to editors on click. */
.card-detail-header {
  padding: 16px 12px 16px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border: none;
}
.card-detail-header-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}
.card-detail-header-divider {
  display: none;
}

/* Pill that mirrors the inline column status - used in the header
   AND as the sidebar Status value chip. */
.value-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--accent-subtle);
  border: 1px solid var(--border);
  font-size: 12px;
  color: var(--text);
}
.value-chip.status-pill { font-weight: 500; }
.value-chip-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-tertiary);
  flex-shrink: 0;
}

/* Generic icon-shaped button used by the kebab + close in the header
   (avatar-trigger style - calm at rest, subtle hover). */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: transparent;
  border: none;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.icon-btn:hover { background: var(--bg-hover); color: var(--text); }
.kebab-glyph {
  font-size: 18px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: -1px;
}

/* Main column title - bigger, more confident than v1. Editable
   on click via contenteditable; padded so the hover background
   doesn't crowd the text. */
.card-detail-title {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.28;
  outline: none;
  border-radius: 6px;
  padding: 4px 6px;
  margin: -4px -6px;
  transition: background var(--transition-fast);
}
.card-detail-title:hover { background: var(--bg-hover); }
.card-detail-title:focus { background: var(--bg); }

/* Inline section label - bold sentence-case, replaces the all-caps
   eyebrow used in v1. Pairs with the bigger title for a less form-y
   read. */
.section-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

/* Section heading row (Comments + count chip). */
.section-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.count-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1px 7px;
  min-width: 22px;
  border-radius: 999px;
  background: var(--accent-subtle);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-tertiary);
  height: 18px;
}

.card-detail-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Subtle horizontal rule between major main-column sections. */
.card-detail-divider {
  height: 1px;
  background: var(--border-light);
  margin: 6px 0;
}

.card-detail-description {
  font-size: 14px;
  color: var(--text);
  line-height: 1.6;
  min-height: 24px;
  padding: 6px 8px;
  margin: -6px -8px;
  border-radius: var(--radius-sm);
  outline: none;
  transition: background var(--transition-fast);
}
.card-detail-description:hover,
.card-detail-description:focus { background: var(--bg); }
.card-detail-description:empty::before {
  content: "Add a description…";
  color: var(--text-tertiary);
  pointer-events: none;
}

/* ── Body: two-column with a wider gutter ── */
.card-detail-body {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 40px;
  padding: 28px 32px;
}
.card-detail-main {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

/* Sidebar drops the v1 box - now a flush column of fields. */
.card-meta-sidebar {
  display: flex;
  flex-direction: column;
  gap: 18px;
  align-self: flex-start;
}

.card-meta-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.card-meta-label {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--text-tertiary);
}

/* Click-to-edit value slot. Display state shows the chip / avatar
   row; .editing class hides the hover affordance and lets the
   underlying editor (select/input) fill the slot. */
.card-meta-value {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  margin: -4px -6px;
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: background var(--transition-fast);
  min-height: 28px;
}
.card-meta-value.editable:hover { background: var(--bg-hover); }
.card-meta-value.editing {
  cursor: default;
  background: transparent;
  padding: 0;
  margin: 0;
}
.card-meta-static {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  padding: 4px 6px;
  margin: -4px -6px;
}

/* The inline editor that replaces a chip on click. Slim padding so
   the layout doesn't jump. */
.card-meta-editor {
  width: 100%;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  padding: 5px 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  outline: none;
}
.card-meta-editor:focus { border-color: var(--text); }

/* Sidebar separator before the read-only metadata. */
.card-meta-divider {
  height: 1px;
  background: var(--border-light);
  margin: 2px 0;
}

/* Avatar + name row - used for assignee + creator displays. */
.user-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.muted { color: var(--text-tertiary); }

/* Initial-on-color avatar used in the sidebar (creator + assignee). */
.card-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: white;
  font-weight: 600;
  flex-shrink: 0;
}

/* Due-date display: formatted date + a small relative "in X days"
   pill. Tones (warn/danger/neutral) communicate urgency without
   leaning on color alone. */
.due-display {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.due-hint {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
}
.due-hint-neutral { background: var(--accent-subtle); color: var(--text-tertiary); }
.due-hint-warn    { background: #fef3c7; color: #92400e; }
.due-hint-danger  { background: var(--danger-surface); color: var(--danger); }

/* Inline empty state for attachments on free plans. Quieter than
   the dashed v1 dropzone - the upgrade link is the only ink that
   demands attention. */
.attach-empty-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-secondary);
}
.link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border);
}
.link-btn:hover { text-decoration-color: var(--text); }

/* Initial-on-color avatar used in the sidebar (creator + assignee). */
.card-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: white;
  font-weight: 600;
  flex-shrink: 0;
}

/* Image variant - uploaded user avatar in the same circular slot as
   the initials placeholder. object-fit:cover crops square so portrait
   uploads still feel right at small sizes. */
.card-avatar-img {
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

/* ── Comments thread ── */
.comments-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.comments-empty,
.comments-loading {
  font-size: 13px;
  color: var(--text-tertiary);
  padding: 8px 0;
}
.comment-row {
  /* Bigger avatar column (32) than v1 (28) so name+timestamp align
     comfortably and the body text gets a clearer left edge. */
  display: grid;
  grid-template-columns: 32px 1fr;
  gap: 12px;
  align-items: flex-start;
}
.comment-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--bg);
}
.comment-content { min-width: 0; }
.comment-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  margin-bottom: 2px;
}
.comment-author {
  font-weight: 600;
  color: var(--text);
}
.comment-time {
  color: var(--text-tertiary);
}
.comment-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
  opacity: 0;
  transition: opacity var(--transition-fast);
}
.comment-row:hover .comment-actions { opacity: 1; }
.comment-action {
  font-size: 12px;
  color: var(--text-tertiary);
  background: transparent;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
}
.comment-action:hover { color: var(--text); }
.comment-action.danger:hover { color: var(--danger); }

.comment-body {
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  word-wrap: break-word;
}

.comment-compose {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.comment-input {
  /* Soft document-feel textarea: no border, page-bg fill that
     darkens slightly on focus. Reads as "type into the document"
     instead of "fill in this form field". */
  width: 100%;
  padding: 12px 14px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  background: var(--border-light);
  border: 1px solid transparent;
  border-radius: 10px;
  resize: vertical;
  min-height: 76px;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
.comment-input::placeholder { color: var(--text-tertiary); }
.comment-input:focus {
  outline: none;
  background: var(--bg);
  border-color: var(--border);
}
.comment-compose-actions {
  display: flex;
  justify-content: flex-end;
}

.comment-editor {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.comment-editor-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

@media (max-width: 720px) {
  .card-detail-body { grid-template-columns: 1fr; }
}

/* ── Attachments ── */
.attach-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.attach-row {
  padding: 12px;
  display: flex;
  align-items: center;
  gap: 14px;
  border-bottom: 1px solid var(--border-light);
}

.attach-row:last-child { border-bottom: none; }

.attach-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  flex-shrink: 0;
}

.attach-icon.PDF { color: #dc2626; }
.attach-icon.PNG, .attach-icon.JPG { color: #16a34a; }
.attach-icon.DOC { color: #3b82f6; }
.attach-icon.TXT { color: var(--text-secondary); }
.attach-icon.ZIP { color: #7c3aed; }

.attach-info {
  flex: 1;
  min-width: 0;
}

.attach-name {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.attach-meta {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.attach-actions {
  display: flex;
  gap: 2px;
  align-items: center;
}

.attach-actions .btn-text {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  border-radius: var(--radius-xs);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  transition: background var(--transition-fast);
}

.attach-actions .btn-text:hover { background: var(--bg-hover); color: var(--text); }

.dropzone {
  border: 1.5px dashed var(--border);
  border-radius: 10px;
  padding: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  transition: border-color var(--transition-fast), background var(--transition-fast);
  cursor: pointer;
}

.dropzone:hover, .dropzone.drag {
  border-color: var(--text);
  background: var(--bg);
}

.dropzone-text {
  font-size: 13px;
  color: var(--text-secondary);
}

.dropzone-text strong {
  color: var(--text);
  font-weight: 500;
}

.dropzone-hint {
  font-size: 11px;
  color: var(--text-tertiary);
}

/* ── Settings rows / Members rows / Billing ── */
.list-row {
  padding: 14px 28px;
  display: flex;
  align-items: center;
  gap: 16px;
  border-bottom: 1px solid var(--border-light);
}

.list-row:last-child { border-bottom: none; }

.list-row .label-stack {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.list-row .label-stack .label {
  font-size: 14px;
  font-weight: 500;
}

.list-row .label-stack .helper {
  font-size: 13px;
  color: var(--text-secondary);
}

.list-row .control { margin-left: auto; }

/* Segmented */
.segmented {
  display: inline-flex;
  background: var(--bg);
  border-radius: var(--radius-sm);
  padding: 3px;
  gap: 0;
}

.segmented button {
  padding: 6px 12px;
  border-radius: var(--radius-xs);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.segmented button.active {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}

.segmented.full { display: grid; grid-template-columns: repeat(var(--cols, 4), 1fr); width: 100%; }

.segmented.full button {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  text-align: center;
  align-items: center;
}

.segmented.full button .seg-label {
  font-size: 13px;
  color: var(--text);
  font-weight: 500;
}

.segmented.full button .seg-desc {
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 400;
}

/* Toggle */
.toggle {
  position: relative;
  width: 36px;
  height: 20px;
  border-radius: 999px;
  background: var(--border);
  cursor: pointer;
  transition: background var(--transition-fast);
  border: none;
  padding: 0;
}

.toggle::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--surface);
  transition: transform var(--transition-fast);
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}

.toggle.on { background: var(--text); }
.toggle.on::after { transform: translateX(16px); }

/* Kbd */
.kbd-row {
  display: inline-flex;
  gap: 4px;
}

.kbd {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  font-size: 11px;
  font-weight: 500;
  font-family: var(--font);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-secondary);
}

/* Members */
.member-row {
  padding: 14px 16px 14px 28px;
  display: grid;
  grid-template-columns: minmax(220px, 1.8fr) 150px minmax(220px, 1.5fr) 110px 40px;
  align-items: center;
  gap: 16px;
  border-bottom: 1px solid var(--border-light);
}

.member-row:last-child { border-bottom: none; }

.member-identity {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.member-identity .avatar { width: 32px; height: 32px; font-size: 13px; }

.member-name-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.member-name {
  font-weight: 600;
  font-size: 14px;
}

.member-email {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.member-scope {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.role-select {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: background var(--transition-fast);
}

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

.role-select .caret {
  color: var(--text-tertiary);
}

.member-last {
  font-size: 13px;
  color: var(--text-tertiary);
}

.pending-row {
  padding: 14px 28px;
  display: flex;
  align-items: center;
  gap: 16px;
  border-bottom: 1px solid var(--border-light);
}

.pending-row:last-child { border-bottom: none; }

.pending-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1.5px dashed var(--border);
  flex-shrink: 0;
}

.pending-info { flex: 1; }
.pending-info .pending-email { font-weight: 500; font-size: 14px; }
.pending-info .pending-meta { font-size: 13px; color: var(--text-secondary); margin-top: 2px; }

.pending-actions { display: flex; gap: 8px; }

/* Billing plan picker */
.plans-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  padding: 24px;
}

.plan-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.plan-card.current {
  border: 1.5px solid var(--accent);
}

.plan-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.plan-name {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.plan-current-badge {
  background: var(--accent);
  color: var(--surface);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 999px;
}

.plan-price {
  display: flex;
  align-items: baseline;
  gap: 4px;
}

.plan-price-main {
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.03em;
}

.plan-price-unit {
  font-size: 13px;
  color: var(--text-tertiary);
}

.plan-tagline {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
}

.plan-features {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.plan-feature {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}

.plan-feature::before {
  content: "•";
  color: var(--text);
  font-weight: 600;
  font-size: 14px;
}

.summary-row {
  padding: 24px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.summary-left .summary-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.summary-left .summary-name {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.summary-left .summary-sub {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 6px;
}

.summary-actions {
  display: flex;
  gap: 8px;
}

.invoice-row {
  padding: 16px 28px;
  display: grid;
  grid-template-columns: 140px 1fr 100px 80px 100px;
  align-items: center;
  gap: 24px;
  border-bottom: 1px solid var(--border-light);
}

.invoice-row:last-child { border-bottom: none; }

.invoice-row .date { font-weight: 500; font-size: 13px; }
.invoice-row .desc { font-size: 13px; color: var(--text-secondary); }
.invoice-row .amount { font-weight: 500; font-size: 13px; text-align: right; }
.invoice-row .download { font-size: 12px; font-weight: 500; cursor: pointer; }

.payment-row {
  padding: 22px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.payment-info {
  display: flex;
  align-items: center;
  gap: 14px;
}

.card-brand {
  width: 40px;
  height: 28px;
  background: var(--text);
  color: var(--surface);
  border-radius: var(--radius-xs);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.05em;
}

.card-info-stack { display: flex; flex-direction: column; gap: 2px; }
.card-info-name { font-size: 14px; font-weight: 500; }
.card-info-meta { font-size: 12px; color: var(--text-secondary); }

/* Invite modal email chips */
.email-input-area {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 12px 8px 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  align-items: center;
  min-height: 44px;
}

.email-input-area:focus-within {
  border-color: var(--text);
  box-shadow: 0 0 0 3px rgba(26, 24, 22, 0.06);
}

.email-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 8px;
  background: var(--bg);
  border-radius: var(--radius-xs);
  font-size: 12px;
  font-weight: 500;
}

.email-chip-x {
  display: inline-flex;
  align-items: center;
  color: var(--text-secondary);
  cursor: pointer;
}

.email-chip-x:hover { color: var(--text); }

.email-input-bare {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  min-width: 120px;
  font-size: 13px;
  padding: 4px;
}

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

.project-picker {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.pp-row {
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  border-bottom: 1px solid var(--border-light);
}

.pp-row:last-child { border-bottom: none; }

.pp-row .pp-name { font-weight: 500; font-size: 13px; flex: 1; }

.checkbox {
  width: 16px;
  height: 16px;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--surface);
  font-size: 10px;
  font-weight: 600;
  flex-shrink: 0;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.checkbox.checked {
  background: var(--text);
  border-color: var(--text);
}

/* New project columns preview */
.cols-preview {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* ── Responsive ── */
@media (max-width: 820px) {
  .topbar { padding: 14px 20px; }
  .topbar-nav { gap: 16px; }
  .page-body { padding: 24px 16px 48px; }
  .board-header { padding: 18px 20px 12px; }
  .board {
    padding: 0 20px 20px;
    gap: 12px;
    scroll-snap-type: x mandatory;
    scroll-padding: 20px;
  }
  /* Mobile: each column is one screen-wide chunk so swipe paging
     between them feels natural. flex-basis matches min/max-width so
     the new desktop flex: 0 0 320px doesn't fight with the mobile
     sizing. */
  .column {
    flex: 0 0 86vw;
    min-width: 86vw;
    max-width: 86vw;
    scroll-snap-align: center;
  }
  .card-actions { opacity: 1; }
  .card-btn { width: 36px; height: 36px; }
  .plans-row { grid-template-columns: 1fr; }
  .member-row {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "identity menu"
      "role role"
      "scope scope"
      "last last";
    gap: 8px;
  }
  .member-identity { grid-area: identity; }
  .invoice-row {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "date amount"
      "desc desc"
      "status download";
    gap: 6px 16px;
  }
  .invoice-row .date { grid-area: date; }
  .invoice-row .desc { grid-area: desc; }
  .invoice-row .amount { grid-area: amount; text-align: right; }
  .invoice-row .download { grid-area: download; text-align: right; }
}

/* Honoured for both the OS-level pref and the user's in-app toggle
   (data.js applyPrefs() sets .reduce-motion on <html>). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0ms !important;
    transition-duration: 0ms !important;
  }
}
.reduce-motion *, .reduce-motion *::before, .reduce-motion *::after {
  animation-duration: 0ms !important;
  transition-duration: 0ms !important;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.card-content:focus-visible,
.card-detail-title:focus-visible,
.card-detail-description:focus-visible { outline: none; }

/* ── Share modal + public board ───────────────────────────────────────────── */

.share-explainer {
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.55;
  margin-bottom: 18px;
}

.share-link-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
  margin-bottom: 16px;
}

.share-link-input {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
}

.share-link-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.share-revoke-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border-light);
}

.share-preview-link {
  font-size: 13px;
  color: var(--text-secondary);
}

.share-preview-link:hover { color: var(--accent); }

/* Public read-only board */
.public-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 32px;
  border-bottom: 1px solid var(--border);
  background: var(--accent-subtle);
}

.public-banner-left {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  color: var(--text-secondary);
}

.public-banner-left .wordmark { font-size: 14px; }

.public-banner-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--accent-light);
  color: var(--accent);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.public-board-card {
  /* Public cards are always clickable - they open a read-only detail
     modal regardless of description state. */
  cursor: pointer;
}

.card-detail-empty {
  color: var(--text-tertiary);
  font-style: italic;
}

.public-board-empty {
  padding: 80px 24px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 14px;
}

/* ── Marketing landing (one-pager) ─────────────────────────────────── */

.landing {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  flex: 1;
  /* Anchors the page-wide meteor shower layer. */
  position: relative;
  overflow: hidden;
}

/* Top nav */
.landing-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 64px;
}

.landing-nav-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.landing-nav-link {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  padding: 8px 12px;
  border-radius: 8px;
  text-decoration: none;
  transition: background var(--transition-fast);
}

.landing-nav-link:hover { background: rgba(26, 24, 22, 0.05); }

.landing-nav-cta {
  font-size: 13px;
  font-weight: 600;
  color: var(--surface);
  background: var(--text);
  padding: 8px 16px;
  border-radius: 999px;
  text-decoration: none;
  transition: background var(--transition-fast);
}

.landing-nav-cta:hover { background: #000; }

/* Hero - left-aligned, vertically centered in the available space.
   The board preview is gone but the text keeps its editorial position. */
.landing-hero {
  flex: 1;
  display: flex;
  align-items: center;
  padding: 0 64px;
  position: relative;
  z-index: 1;
}

/* Foreground chrome sits above the meteor shower background layer. */
.landing-nav,
.landing-footer {
  position: relative;
  z-index: 1;
}

/* Meteor shower - decorative animated streaks pinned to the right half
   of the page so they fill the blank space beside the hero text but
   span the full page height (behind nav, hero, footer alike).
   pointer-events:none so it never interferes with clicks/selection. */
.meteor-shower {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 55%;
  pointer-events: none;
  z-index: 0;
}

.meteor {
  position: absolute;
  width: 0;
  height: 0;
  /* Each meteor is just a positioning anchor; the visible streak is the
     ::before pseudo-element. Wrapping this way means the diagonal motion
     (translate on .meteor) and the streak rotation (on ::before) compose
     cleanly without fighting each other. */
  animation-name: meteor-fall;
  animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}

.meteor::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: var(--meteor-length, 100px);
  height: var(--meteor-thickness, 1px);
  /* Streak gradient: SOLID at the left end (the meteor's anchor /
     leading edge of motion) fading to transparent on the right.
     Combined with the -45° rotation, the bright "head" lands at the
     anchor - i.e. at the front of the down-and-left fall - and the
     faint tail trails up-and-right behind it.

     Color + alpha are composed via color-mix so each meteor can carry
     its own --meteor-color (set per-instance in MeteorShower) and
     still respect the per-instance --meteor-opacity for fade depth.
     Falls back to the warm-dark brand color if the var is absent. */
  background: linear-gradient(
    to right,
    color-mix(
      in srgb,
      var(--meteor-color, #1a1816) calc(var(--meteor-opacity, 0.5) * 100%),
      transparent
    ),
    transparent
  );
  border-radius: 1px;
  /* -45° rotation about the top-left. The solid (left) end stays at
     the anchor; the transparent (right) end swings to upper-right. */
  transform-origin: 0 0;
  transform: rotate(-45deg);
  /* Faint bloom - only visible over the solid portion of the streak,
     so it naturally concentrates at the leading head. Blur radius
     scales with thickness so fatter streaks get a proportionally
     softer halo - otherwise a 2.5px meteor with a 6px blur reads as
     a hard rectangle instead of a glowing point. */
  box-shadow: 0 0 calc(var(--meteor-thickness, 1px) * 6)
    color-mix(in srgb, var(--meteor-color, #1a1816) 18%, transparent);
}

@keyframes meteor-fall {
  0% {
    transform: translate(0, 0);
    opacity: 0;
  }
  8% { opacity: 1; }
  92% { opacity: 1; }
  100% {
    /* Travel down-left across the page. Using viewport-height units so
       the distance scales with the page - even on tall screens, every
       meteor clears the bottom edge regardless of where it started.
       Equal X/Y components keep the path at 45° so it lines up with
       the rotated streak. */
    transform: translate(-130vh, 130vh);
    opacity: 0;
  }
}

/* Respect users who prefer reduced motion - pause animations cleanly so
   the page is still usable without flicker. Honoured for the OS pref and
   for the in-app Reduce Motion toggle. */
@media (prefers-reduced-motion: reduce) {
  .meteor { animation: none; opacity: 0; }
}
.reduce-motion .meteor { animation: none; opacity: 0; }

.landing-hero-text {
  max-width: 640px;
}

.landing-headline {
  font-size: 84px;
  line-height: 92px;
  font-weight: 600;
  letter-spacing: -0.04em;
  color: var(--text);
  margin: 0 0 28px;
  display: flex;
  flex-direction: column;
}

.landing-subhead {
  font-size: 18px;
  line-height: 28px;
  color: var(--text-secondary);
  max-width: 540px;
  margin: 0 0 32px;
}

.landing-ctas {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.landing-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 26px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 999px;
  text-decoration: none;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.landing-cta:hover { background: var(--bg-hover); }

.landing-cta-primary {
  background: var(--text);
  color: var(--surface);
  border-color: var(--text);
}

.landing-cta-primary:hover { background: #000; border-color: #000; }

.landing-trust {
  font-size: 13px;
  color: var(--text-tertiary);
  margin: 0;
}

/* Footer signature */
.landing-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 64px;
}

.landing-signature {
  font-size: 13px;
  color: var(--text-tertiary);
}

.landing-copy {
  font-size: 12px;
  color: #9a948c;
}

.landing-footer-links {
  display: flex;
  align-items: center;
  gap: 18px;
}
.landing-footer-links a {
  color: var(--text-tertiary);
  text-decoration: none;
  font-size: 13px;
  transition: color var(--transition-fast);
}
.landing-footer-links a:hover { color: var(--text); }

/* Mobile - shrink type, tighten padding. Layout stays single-column. */
@media (max-width: 900px) {
  .landing-nav { padding: 18px 24px; }
  .landing-hero { padding: 24px; }
  .landing-headline {
    font-size: 56px;
    line-height: 62px;
  }
  .landing-subhead {
    font-size: 16px;
    line-height: 24px;
  }
  .landing-footer { padding: 18px 24px; }
}

/* ── Pricing page ──────────────────────────────────────────────────── */

/* Pricing page reuses .landing for nav/footer chrome but its own
   middle section. Override the centered hero default - pricing has its
   own header block. */
.pricing-page .landing-hero { display: none; }

.pricing-header {
  text-align: center;
  padding: 72px 32px 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.pricing-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--text-tertiary);
}

.pricing-headline {
  font-size: 56px;
  line-height: 62px;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--text);
  margin: 0;
  display: flex;
  flex-direction: column;
}

.pricing-subhead {
  font-size: 17px;
  line-height: 26px;
  color: var(--text-secondary);
  max-width: 560px;
  margin: 4px 0 0;
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 32px 32px;
}

.pricing-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 32px 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  position: relative;
  transition: transform var(--transition), box-shadow var(--transition);
}

.pricing-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow);
}

.pricing-card-featured {
  border: 2px solid var(--text);
  /* The featured card lifts visually a touch so the eye lands here first. */
  box-shadow: var(--shadow);
}

.pricing-badge {
  position: absolute;
  top: -13px;
  left: 28px;
  background: var(--text);
  color: var(--surface);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: 999px;
}

.pricing-name {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
}

.pricing-price {
  display: flex;
  align-items: baseline;
  gap: 4px;
}

.pricing-price-main {
  font-size: 44px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.03em;
  line-height: 1;
}

.pricing-price-unit {
  font-size: 14px;
  color: var(--text-tertiary);
}

.pricing-tagline {
  font-size: 14px;
  line-height: 22px;
  color: var(--text-secondary);
  margin: 0;
}

.pricing-features {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Push the CTA to the bottom of the card so all three cards have
     CTAs at the same y, regardless of feature-list length. */
  flex: 1;
}

.pricing-feature {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 14px;
  line-height: 22px;
  color: var(--text);
}

.pricing-feature-icon {
  color: var(--text);
  margin-top: 4px;
  flex-shrink: 0;
}

.pricing-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 999px;
  text-decoration: none;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.pricing-cta:hover {
  background: var(--bg);
  border-color: var(--text);
}

.pricing-cta-primary {
  background: var(--text);
  color: var(--surface);
  border-color: var(--text);
}

.pricing-cta-primary:hover {
  background: #000;
  border-color: #000;
}

.pricing-trust {
  text-align: center;
  font-size: 13px;
  color: var(--text-tertiary);
  margin: 0 0 64px;
}

@media (max-width: 900px) {
  .pricing-header {
    padding: 48px 24px 32px;
  }
  .pricing-headline {
    font-size: 36px;
    line-height: 42px;
  }
  .pricing-subhead { font-size: 16px; }
  .pricing-grid {
    grid-template-columns: 1fr;
    padding: 0 24px 24px;
  }
  .pricing-trust { margin-bottom: 40px; padding: 0 24px; }
}

/* ── FAQ section (pricing page) ──────────────────────────────────────────── */
.faq-section {
  max-width: 760px;
  margin: 0 auto 96px;
  padding: 0 32px;
}

.faq-heading {
  font-size: 28px;
  line-height: 34px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-align: center;
  margin: 0 0 32px;
  color: var(--text);
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.faq-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.faq-item[open] {
  border-color: var(--text);
  box-shadow: var(--shadow-xs);
}

/* Smoothly animate the open/close of each <details>. The
   ::details-content pseudo-element is the modern (Chrome 131+,
   Safari 18.2+) way to address the disclosed content; combined with
   `interpolate-size: allow-keywords` on :root it lets us transition
   `block-size` to/from `auto`. Browsers without support fall back to
   the native instant snap. */
.faq-item::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 240ms var(--ease-out), content-visibility 240ms allow-discrete;
}
.faq-item[open]::details-content {
  block-size: auto;
}

/* Hide the native disclosure triangle in every engine. */
.faq-q {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 22px;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
  user-select: none;
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q:hover { background: var(--bg-hover); }
.faq-q:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}

.faq-chevron {
  color: var(--text-tertiary);
  flex-shrink: 0;
  transition: transform var(--transition-fast), color var(--transition-fast);
}
.faq-item[open] .faq-chevron {
  transform: rotate(180deg);
  color: var(--text);
}

.faq-a {
  margin: 0;
  padding: 0 22px 22px;
  color: var(--text-secondary);
  font-size: 15px;
  line-height: 24px;
}

@media (max-width: 900px) {
  .faq-section { padding: 0 24px; margin-bottom: 56px; }
  .faq-heading { font-size: 24px; line-height: 30px; margin-bottom: 24px; }
  .faq-q { font-size: 15px; padding: 16px 18px; }
  .faq-a { font-size: 14px; padding: 0 18px 18px; }
}

/* ── Legal pages (Terms, Privacy) ────────────────────────────────────────── */

.legal-page .landing-hero { display: none; }

.legal-article {
  max-width: 760px;
  margin: 0 auto;
  padding: 64px 32px 96px;
}

.legal-header {
  margin-bottom: 40px;
}

.legal-eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--text-tertiary);
  margin-bottom: 12px;
}

.legal-title {
  font-size: 44px;
  line-height: 50px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0 0 12px;
}

.legal-updated {
  font-size: 13px;
  color: var(--text-tertiary);
  margin: 0 0 24px;
}

.legal-intro {
  font-size: 17px;
  line-height: 28px;
  color: var(--text-secondary);
  margin: 0 0 32px;
}

.legal-body { color: var(--text-secondary); }

.legal-section {
  /* Anchored navigation should land below the sticky-ish reading
     position; the scroll-margin-top keeps the section heading clear of
     the top of the viewport when jumped to via TOC link. */
  scroll-margin-top: 24px;
  margin-top: 40px;
}
.legal-section:first-child { margin-top: 0; }

.legal-h2 {
  font-size: 22px;
  line-height: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 12px;
}

.legal-body p {
  font-size: 16px;
  line-height: 26px;
  margin: 0 0 14px;
}
.legal-body p:last-child { margin-bottom: 0; }

.legal-list {
  margin: 0 0 14px;
  padding: 0 0 0 22px;
  font-size: 16px;
  line-height: 26px;
}
.legal-list li { margin-bottom: 4px; }

.legal-body a,
.legal-contact a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.legal-body a:hover,
.legal-contact a:hover { color: var(--accent); }

.legal-contact {
  margin-top: 56px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
  font-size: 14px;
  color: var(--text-tertiary);
}

@media (max-width: 900px) {
  .legal-article { padding: 40px 24px 56px; }
  .legal-title { font-size: 32px; line-height: 38px; }
  .legal-intro { font-size: 16px; line-height: 26px; }
  .legal-h2 { font-size: 20px; line-height: 26px; }
  .legal-body p, .legal-list { font-size: 15px; line-height: 24px; }
}

/* ── Project settings modal ── */
.project-settings-body {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.project-settings-field {
  /* Flex column so the gap below actually applies between the label,
     control, and hint - otherwise gap on a default block box is silently
     ignored and only `margin-top` on .project-settings-hint shows up. */
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.project-settings-hint {
  font-size: 12px;
  color: var(--text-tertiary);
  line-height: 1.4;
}

.project-settings-status {
  font-size: 13px;
  color: var(--text-tertiary);
  min-height: 18px;
}
.project-settings-status[data-kind="error"] { color: var(--danger); }

/* Project ID field — value as a mono-font code block + a small copy
   button that flashes "Copied" briefly on click. */
.project-id-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
}
.project-id-value {
  flex: 1;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  user-select: all;
  /* Truncate cleanly on narrow viewports rather than forcing a wrap;
     the full id is always copyable via the button regardless. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: text;
  display: flex;
  align-items: center;
}
.project-id-copy {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 12px;
  /* Match the value box height so the row reads as a single unit. */
  min-height: 36px;
  transition: background 120ms ease, color 120ms ease;
}
.project-id-copy.copied {
  background: var(--text);
  color: #fff;
  border-color: var(--text);
}
.project-id-copy.copied .icon {
  color: #fff;
}

/* Background image: the preview *is* the picker. Click anywhere on it
   (except the corner ×) opens the file dialog. Banner-shaped so the
   user sees a recognisable bg crop, fixed-height so the modal layout
   doesn't reflow when they pick a tall portrait. */
.project-bg-preview {
  width: 100%;
  height: 140px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface);
  background-size: cover;
  background-position: center;
  cursor: pointer;
  position: relative;
  padding: 0;
  /* Override the global `button { cursor: pointer; background: none; }`
     normalisation cleanly without losing the inline-image background. */
  color: inherit;
  display: block;
  transition: border-color 120ms var(--ease-out), box-shadow 120ms var(--ease-out);
}
.project-bg-preview:hover { border-color: var(--accent); }
.project-bg-preview:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
.project-bg-preview-empty {
  background: var(--accent-subtle);
  border-style: dashed;
}
.project-bg-empty-cta {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-secondary);
}
/* Text-color segmented picker: a swatch + label per option. Selected
   option gets the accent ring used by the rest of the modal so the
   choice reads at a glance. */
.text-color-picker {
  display: flex;
  gap: 8px;
}
.text-color-option {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px 7px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  font-size: 13px;
  color: var(--text);
  transition: border-color 120ms var(--ease-out), background 120ms var(--ease-out);
}
.text-color-option:hover { border-color: var(--accent); }
.text-color-option.selected {
  border-color: var(--accent);
  background: var(--accent-subtle);
}
.text-color-swatch {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid var(--border);
  display: inline-block;
}
.text-color-swatch-dark  { background: #1a1816; }
.text-color-swatch-light { background: #f5f3ee; }

/* Small × button anchored top-right of the preview. Floats above the
   image with a subtle backdrop so it stays legible on busy photos. */
.project-bg-clear {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(26, 24, 22, 0.72);
  color: #ffffff;
  border: none;
  cursor: pointer;
  transition: background 120ms var(--ease-out), transform 120ms var(--ease-out);
}
.project-bg-clear:hover {
  background: rgba(26, 24, 22, 0.92);
  transform: scale(1.05);
}
.project-bg-clear:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}

/* Verify-email-pending screen. Reuses the auth-page split layout so
   the form pane on the left feels like its sibling marketing pages
   (sign-in, register). Pared-down content - mail icon, headline, body,
   one primary CTA + secondary text actions. */
.verify-pending-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: var(--border-light);
  color: var(--text);
  margin-bottom: 24px;
}
.verify-pending-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  margin: 8px 0 24px;
}
.verify-pending-link {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  padding: 4px 0;
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 4px;
  transition: color .15s ease, text-decoration-color .15s ease;
}
.verify-pending-link:hover {
  color: var(--text);
  text-decoration-color: var(--text-tertiary);
}
.verify-pending-link[disabled] { opacity: 0.5; cursor: progress; }
.verify-pending-link:focus { outline: none; }
.verify-pending-link:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
  border-radius: 4px;
}
.verify-pending-link.inline { padding: 0; }
.verify-pending-hint {
  font-size: 13px;
  color: var(--text-tertiary);
  margin: 0;
}
.verify-pending-error {
  font-size: 13px;
  color: var(--danger, #a3361f);
}

/* Gradient swatches under the upload preview. Eight presets in a single
   row (wraps to two rows on narrow modals). Each swatch is the gradient
   itself at thumbnail size; the active one gets an accent ring. */
.project-bg-stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.project-bg-gradients {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 8px;
}
@media (max-width: 600px) {
  .project-bg-gradients { grid-template-columns: repeat(4, 1fr); }
}
.project-bg-gradient {
  appearance: none;
  border: 1px solid var(--border-light, rgba(0,0,0,.08));
  border-radius: 10px;
  height: 44px;
  padding: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.project-bg-gradient:hover { transform: translateY(-1px); border-color: var(--accent); }
.project-bg-gradient:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
.project-bg-gradient.is-active {
  border-color: var(--text);
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--text);
}

/* Column edit modal - "Mark as a done column" toggle row. The checkbox
   sits inline with the label so a click anywhere on the row flips it. */
.column-done-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
}
.column-done-row .column-done-toggle {
  margin-top: 3px;
  flex-shrink: 0;
}
.column-done-row .field-helper {
  display: block;
  margin-top: 2px;
  color: var(--text-tertiary);
  font-size: 12px;
  line-height: 1.4;
  font-weight: 400;
}

/* ── API keys page ──
   Row layout mirrors the members table: identity column on the left
   (name + masked prefix stacked), then a small monospaced metadata
   column right-aligned, then a ghost icon button for delete. The
   delete button stays subdued until hover - a Kanban app deleting
   tokens is "destructive but not scary." */
.api-key-list {
  display: flex;
  flex-direction: column;
}
.api-key-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 16px 14px 28px;
  border-bottom: 1px solid var(--border-light);
}
.api-key-row:last-child { border-bottom: none; }

.api-key-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.api-key-row-name {
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
}
.api-key-mask {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  color: var(--text-secondary);
  background: var(--accent-subtle);
  padding: 3px 8px;
  border-radius: 6px;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  /* Width caps the dot tail so it never grows wider than the prefix
     deserves - keeps long-name rows from stretching the chip. */
  max-width: 220px;
}
.api-key-mask-dots {
  color: var(--text-tertiary);
  /* Spacing chosen so 10 dots fill the remaining chip width without
     wrapping. Negative margin trims the leading space character. */
  letter-spacing: 0.18em;
  margin-left: 6px;
}

.api-key-row-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  font-size: 12.5px;
  color: var(--text-secondary);
  white-space: nowrap;
  /* Keep the metadata column from shrinking awkwardly when the name
     wraps; a fixed minimum holds the right-edge alignment steady. */
  min-width: 200px;
}
.api-key-meta-label {
  color: var(--text-tertiary);
  font-size: 12px;
  margin-right: 4px;
}

/* Ghost delete: subdued grey by default, red on hover. Without this
   the .btn-danger pill dominates the row visually. */
.api-key-delete {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: none;
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: background 120ms var(--ease-out), color 120ms var(--ease-out);
}
.api-key-delete:hover {
  background: var(--danger-surface);
  color: var(--danger);
}
.api-key-delete:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--danger);
}

.api-key-empty {
  padding: 28px 8px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 14px;
}

/* ── API docs panel (companion to the keys list) ───────────────────── */

.api-docs-section {
  display: flex;
  flex-direction: column;
  gap: 22px;
  /* SectionCard ships with zero padding; children own their own. The
     value matches .section-head's horizontal padding (28px) and gives
     a tighter top spacing because the card divider sits just above. */
  padding: 22px 28px 28px;
}

.api-docs-intro {
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.55;
  padding-bottom: 4px;
}
.api-docs-intro p { margin: 0; }
/* Inline `<code>` pills - outlined so they read as code at a glance
   even on the cream-on-white surface. Earlier they used --accent-subtle
   on white, ~5% luminance change; the border makes the pill explicit. */
.api-docs-intro code {
  font-family: ui-monospace, "JetBrains Mono", "SF Mono", "Menlo", monospace;
  font-size: 12.5px;
  padding: 1px 6px;
  border-radius: 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
}
.api-docs-rate { color: var(--text-tertiary); }

.api-docs-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 6px;
}
.api-docs-block-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.005em;
}
.api-docs-block-desc {
  font-size: 13px;
  color: var(--text-tertiary);
  line-height: 1.55;
}

/* Pre blocks - inverted to a dark "terminal" aesthetic so curl
   examples sit clearly distinct from the surrounding white card.
   Earlier they used the same near-cream background as the page,
   which made the whole panel read as one undifferentiated block. */
.api-docs-pre {
  position: relative;
  background: #1a1816;
  border: 1px solid #1a1816;
  border-radius: 12px;
  padding: 16px 18px;
  padding-right: 60px; /* room for the copy button */
}
.api-docs-pre pre {
  margin: 0;
  font-family: ui-monospace, "JetBrains Mono", "SF Mono", "Menlo", monospace;
  font-size: 12.5px;
  line-height: 1.65;
  color: #f5f3ee;
  white-space: pre;
  overflow-x: auto;
}
.api-docs-pre code { background: none; padding: 0; color: inherit; font: inherit; border: none; }
.api-docs-copy {
  position: absolute;
  top: 10px;
  right: 10px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 8px;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.75);
  cursor: pointer;
  transition: background 120ms var(--ease-out), color 120ms var(--ease-out);
}
.api-docs-copy:hover {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

/* Resources panel - links out to OpenAPI spec, SDK, Claude skill. */
.api-docs-resources {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 6px;
  padding-top: 22px;
  border-top: 1px solid var(--border-light);
}
.api-docs-resources-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 4px;
}
.api-docs-resource {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  transition: border-color 120ms var(--ease-out), background 120ms var(--ease-out);
}
.api-docs-resource:hover {
  border-color: var(--text-tertiary);
  background: var(--bg-hover);
}
.api-docs-resource .icon {
  flex-shrink: 0;
  color: var(--text-secondary);
}
.api-docs-resource-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.api-docs-resource-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}
.api-docs-resource-sub {
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Stack the row vertically on narrow viewports so nothing gets
   clipped. The metadata flips to left-aligned to follow the new
   reading order. */
@media (max-width: 640px) {
  .api-key-row {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: 14px 16px;
  }
  .api-key-row-meta {
    align-items: flex-start;
    min-width: 0;
  }
  .api-key-delete { align-self: flex-end; }
}

/* Reveal modal: the one-and-only chance the user has to grab the
   secret. Big monospace token + Copy button + a clear warning. */
.api-key-warning {
  background: var(--accent-subtle);
  color: var(--text-secondary);
  border-left: 3px solid var(--accent);
  padding: 12px 14px;
  border-radius: 6px;
  font-size: 14px;
  line-height: 1.5;
  margin: 0 0 16px;
}
.api-key-token-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.api-key-token {
  flex: 1;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  word-break: break-all;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  user-select: all;
  /* Pre-select inside the codeblock helps keyboard users hit Ctrl+A
     without grabbing the surrounding chrome. */
}

/* ─────────────────────────────────────────────────────────────────────
 * Card detail v3 - the swiss-army-knife modal
 *
 * Scoped under .card-detail-v3 so existing v1/v2 instances stay intact
 * during rollout. The v3 modal lives at width 1080 with a slim header,
 * an identity block (title + activity strip), a main column + 280px
 * sidebar split, and a sticky composer footer that anchors the
 * activity timeline below.
 *
 * v3 also introduces a unified Activity timeline that interleaves
 * comments with derived events (card created, file attached). Event
 * rows use the same 32px leading column as comments so the timeline
 * reads as one vertical rhythm.
 * ───────────────────────────────────────────────────────────────────── */

.modal.card-detail-v3 {
  width: 1080px;
  max-width: calc(100vw - 32px);
  padding: 0;
  /* Override the default flex centering so internal sticky positioning
     works against the modal's own viewport. */
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 64px);
  overflow: hidden;
  /* When openPreactModal focuses the container itself (the no-text-
     input path), don't paint a visible focus ring around the whole
     modal - we just need the focus for Escape / focus-trap, not the
     visual cue. */
  outline: none;
}
.modal.card-detail-v3:focus,
.modal.card-detail-v3:focus-visible {
  outline: none;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.10), 0 24px 60px rgba(0, 0, 0, 0.18);
}

/* Header - slim row, status pill left-aligned with the title below.    */
.card-detail-v3 .card-detail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 12px 16px 40px;
}
.card-detail-v3 .card-detail-header-actions {
  display: flex;
  gap: 4px;
  align-items: center;
}
/* Card-id chip in the header. Sits where the status pill used to live;
   monospace + muted treatment so it reads as a stable handle for the
   card without competing with the title below. Hover bumps the
   contrast a touch to hint that the kebab > "Copy ID" gives you the
   full UUID. */
.card-detail-v3 .card-detail-id-chip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-secondary, var(--muted));
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(26, 24, 22, 0.04);
  user-select: all;
}
/* Remove the default focus ring on the kebab + close buttons (per the
   v3.5 spec) and replace with a subtle warm-tint :focus-visible state
   that keeps keyboard nav intact without the chunky outline. */
.card-detail-v3 .modal-close,
.card-detail-v3 .kebab-btn,
.card-detail-v3 .icon-btn {
  outline: none;
  border: 0;
}
.card-detail-v3 .modal-close:focus-visible,
.card-detail-v3 .kebab-btn:focus-visible,
.card-detail-v3 .icon-btn:focus-visible {
  box-shadow: inset 0 0 0 1.5px rgba(26, 24, 22, 0.35);
}
.card-detail-v3 .card-detail-header-divider,
.card-detail-v3 .card-detail-identity-divider {
  height: 1px;
  background: #f0eeeb;
}

/* Identity block - title + an activity strip caption below it. */
.card-detail-v3 .card-detail-identity {
  padding: 28px 40px 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.card-detail-v3 .card-detail-title {
  font-size: 30px;
  line-height: 38px;
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--text);
  /* Cream background that reads as an editable surface. The block's
     left edge sits flush with the status pill above it (both at x=40
     from the modal edge), so the title and pill share a clean
     vertical line. The internal padding pushes the text a few px
     inside the chip - the activity strip + section headings below
     stay at x=40 in modal coords, so the visual layout still reads
     as left-aligned overall. */
  background: #f6f4f1;
  padding: 10px 14px;
  margin: 0;
  border-radius: 10px;
  outline: none;
  transition: background 80ms ease, box-shadow 80ms ease;
}
.card-detail-v3 .card-detail-title:hover {
  background: #efebe5;
}
.card-detail-v3 .card-detail-title:focus,
.card-detail-v3 .card-detail-title:focus-visible {
  background: #fff;
  box-shadow: inset 0 0 0 1.5px #d6cfc6;
}
.card-detail-v3 .card-detail-title[contenteditable="true"]:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
}
.card-detail-v3 .card-detail-activity-strip {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--muted);
}
.card-detail-v3 .card-detail-activity-strip .sep {
  color: #8a827a;
}

/* Body - main column + sidebar with a hairline divider between them.   */
.card-detail-v3 .card-detail-body {
  display: flex;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Reset the v2 .card-detail-body padding (28px 32px) + gap (40px)
     that would otherwise leak through and push the main column 32px
     inward, misaligning section headings with the title above. */
  padding: 0;
  gap: 0;
}
.card-detail-v3 .card-detail-main {
  flex: 1;
  min-width: 0;
  /* Restored 28px top padding now that the title lives inside this
     column instead of in a dedicated identity strip above the body. */
  padding: 28px 40px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.card-detail-v3 .card-detail-sidebar {
  width: 280px;
  flex-shrink: 0;
  border-left: 1px solid #f0eeeb;
  /* Match the top padding of the main column so the first sidebar
     row (STATUS label) sits at the same y as the title in the left
     pane. */
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 22px;
  /* Keep the sidebar visible while the main column scrolls. Without
     align-self: flex-start the sidebar stretches to the full main
     content height and position:sticky has nothing to pin against.
     The internal max-height + overflow-y let the sidebar scroll on
     its own when it ever grows past the visible body height. */
  position: sticky;
  top: 0;
  align-self: flex-start;
  max-height: 100%;
  overflow-y: auto;
}

/* Section heading - small caps not needed; use semibold 13. The
   reset on padding + border-bottom undoes a global .section-head rule
   defined elsewhere for project page headers (padding: 22px 28px 18px,
   border-bottom: 1px). Without this reset, every section heading in
   the card modal would be indented 28px past the title and have a
   stray line beneath it. */
.card-detail-v3 .section-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  padding: 0;
  border-bottom: 0;
  justify-content: flex-start;
}
.card-detail-v3 .count-chip {
  background: #f0eeeb;
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
  padding: 1px 7px;
  border-radius: 999px;
}

/* Description block. */
.card-detail-v3 .card-detail-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.card-detail-v3 .card-detail-description {
  font-size: 14px;
  line-height: 22px;
  color: var(--text);
  outline: none;
  min-height: 22px;
  /* The v1/v2 rule uses padding + negative margin to expand a hover
     background without affecting layout. We want the description body
     to share a left edge with the section heading above it, so reset
     both. The hover affordance is replaced by the click-to-edit
     pattern below. */
  padding: 0;
  margin: 0;
  background: transparent;
}
.card-detail-v3 .card-detail-description:hover,
.card-detail-v3 .card-detail-description:focus {
  background: transparent;
}
/* Description in textarea-edit mode. Takes the rendered view's place. */
.card-detail-v3 .card-detail-description.md-editor {
  width: 100%;
  border: 0;
  outline: none;
  background: #faf9f7;
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0 -12px;
  resize: vertical;
  font-family: inherit;
  font-size: 14px;
  line-height: 22px;
  color: var(--text);
  min-height: 80px;
  box-shadow: inset 0 0 0 1.5px #d6cfc6;
}
.card-detail-v3 .card-detail-description.md-editor:focus {
  background: #fff;
}

/* ── Rendered markdown elements ───────────────────────────────────
 * Scoped to .md-view so styles only land on rendered output, not on
 * the surrounding modal chrome.
 */
.card-detail-v3 .md-view {
  cursor: text;
}
.card-detail-v3 .md-view-empty {
  color: var(--muted);
  font-style: italic;
}
.card-detail-v3 .md-view p {
  margin: 0 0 8px;
}
.card-detail-v3 .md-view p:last-child { margin-bottom: 0; }
.card-detail-v3 .md-view h1,
.card-detail-v3 .md-view h2,
.card-detail-v3 .md-view h3,
.card-detail-v3 .md-view h4,
.card-detail-v3 .md-view h5,
.card-detail-v3 .md-view h6 {
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 14px 0 6px;
  line-height: 1.3;
}
.card-detail-v3 .md-view h1 { font-size: 20px; }
.card-detail-v3 .md-view h2 { font-size: 17px; }
.card-detail-v3 .md-view h3 { font-size: 15px; }
.card-detail-v3 .md-view h4,
.card-detail-v3 .md-view h5,
.card-detail-v3 .md-view h6 { font-size: 14px; }
.card-detail-v3 .md-view h1:first-child,
.card-detail-v3 .md-view h2:first-child,
.card-detail-v3 .md-view h3:first-child { margin-top: 0; }
.card-detail-v3 .md-view ul,
.card-detail-v3 .md-view ol {
  margin: 0 0 8px;
  padding-left: 22px;
}
.card-detail-v3 .md-view li { margin: 2px 0; }
.card-detail-v3 .md-view a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.card-detail-v3 .md-view a:hover { color: #000; }
.card-detail-v3 .md-view strong { font-weight: 600; }
.card-detail-v3 .md-view em { font-style: italic; }
.card-detail-v3 .md-view del { color: var(--muted); }
.card-detail-v3 .md-view code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  padding: 1px 5px;
  background: #f3efea;
  border-radius: 4px;
  color: #1a1816;
}
.card-detail-v3 .md-view pre {
  background: #f6f4f1;
  border: 1px solid #eae7e2;
  border-radius: 8px;
  padding: 10px 12px;
  overflow-x: auto;
  margin: 8px 0;
}
.card-detail-v3 .md-view pre code {
  background: transparent;
  padding: 0;
  border-radius: 0;
  font-size: 12.5px;
  line-height: 1.55;
}
.card-detail-v3 .md-view blockquote {
  border-left: 3px solid #d6cfc6;
  padding: 2px 0 2px 10px;
  margin: 6px 0;
  color: var(--muted);
}
.card-detail-v3 .md-view hr {
  border: 0;
  border-top: 1px solid #eae7e2;
  margin: 12px 0;
}
.card-detail-v3 .md-view .md-mention {
  display: inline-block;
  padding: 0 4px;
  border-radius: 4px;
  background: #f3efea;
  color: #1a1816;
  font-weight: 500;
}
.card-detail-v3 .card-detail-description:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
}

/* Files section. */
.card-detail-v3 .v3-file-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: 10px;
  background: #faf9f7;
  border: 1px solid #eae7e2;
  transition: background 80ms ease;
}
.card-detail-v3 .v3-file-row:hover {
  background: #f3efea;
}
.card-detail-v3 .v3-file-tile {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: #fff;
  border: 1px solid #eae7e2;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  flex-shrink: 0;
}
.card-detail-v3 .v3-file-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.card-detail-v3 .v3-file-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-decoration: none;
}
.card-detail-v3 .v3-file-name:hover {
  text-decoration: underline;
}
.card-detail-v3 .v3-file-sub {
  font-size: 11px;
  color: var(--muted);
}
.card-detail-v3 .v3-file-trail {
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0;
  transition: opacity 80ms ease;
}
.card-detail-v3 .v3-file-row:hover .v3-file-trail,
.card-detail-v3 .v3-file-row:focus-within .v3-file-trail {
  opacity: 1;
}
.card-detail-v3 .v3-file-action {
  width: 28px;
  height: 28px;
  border-radius: 999px;
  border: 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-detail-v3 .v3-file-action:hover {
  background: #ecebe6;
  color: var(--text);
}

/* Dropzone - slim dashed strip instead of a full card. */
.card-detail-v3 .v3-dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 16px;
  border: 1px dashed #eae7e2;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  cursor: pointer;
  transition: background 80ms ease, border-color 80ms ease;
}
.card-detail-v3 .v3-dropzone:hover,
.card-detail-v3 .v3-dropzone.over {
  background: #faf9f7;
  border-color: #d6cfc6;
}
.card-detail-v3 .v3-dropzone-link {
  color: var(--text);
  font-weight: 500;
}
.card-detail-v3 .v3-dropzone-hint {
  font-size: 12px;
}

/* Sidebar fields. */
.card-detail-v3 .card-meta-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  cursor: pointer;
}
.card-detail-v3 .card-meta-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.card-detail-v3 .card-meta-value {
  font-size: 13px;
  color: var(--text);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  min-height: 24px;
}

/* Created / Updated audit rows. Smaller, muted, with a thin separator
   between the timestamp and the "by <name>" half so they read as one
   coherent string rather than two competing values. */
.card-detail-v3 .card-meta-audit {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-secondary, var(--muted));
  font-size: 12px;
  line-height: 1.4;
}
.card-detail-v3 .card-meta-audit-sep {
  opacity: 0.5;
}
.card-detail-v3 .card-meta-editor {
  font-size: 13px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  color: var(--text);
  width: 100%;
  outline: none;
}
.card-detail-v3 .card-meta-editor:focus {
  border-color: var(--text);
}

/* Chip used inside sidebar values for status / priority / size etc.   */
.card-detail-v3 .v3-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 8px;
  border-radius: 999px;
  background: #f0eeeb;
  font-size: 13px;
  color: var(--text);
}
.card-detail-v3 .v3-chip .dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: currentColor;
}

/* Tags row. */
.card-detail-v3 .v3-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.card-detail-v3 .v3-tag-pill {
  background: #f3efea;
  border: 1px solid #e4ddd5;
  color: var(--text);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 8px 4px 10px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.card-detail-v3 .v3-tag-pill .x {
  cursor: pointer;
  color: var(--muted);
  background: transparent;
  border: 0;
  padding: 0;
  font-size: 12px;
  line-height: 1;
  /* Hidden by default to match the Figma rest state - the × only
     appears when the user hovers the pill or focuses inside it.
     Keeps the tag row visually compact + matches the design. */
  opacity: 0;
  pointer-events: none;
  margin-left: -4px;
  transition: opacity 80ms ease, margin-left 80ms ease;
}
.card-detail-v3 .v3-tag-pill:hover .x,
.card-detail-v3 .v3-tag-pill:focus-within .x {
  opacity: 1;
  pointer-events: auto;
  margin-left: 0;
}
.card-detail-v3 .v3-tag-pill .x:hover {
  color: var(--text);
}
.card-detail-v3 .v3-tag-add {
  border: 1px dashed #e4ddd5;
  background: transparent;
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.card-detail-v3 .v3-tag-add:hover {
  color: var(--text);
  border-color: #d6cfc6;
}
.card-detail-v3 .v3-tag-input {
  font-size: 11px;
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  outline: none;
  width: 100px;
}

/* Activity timeline. */
.card-detail-v3 .card-detail-timeline {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.card-detail-v3 .timeline-comment {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}
.card-detail-v3 .timeline-comment .comment-avatar {
  width: 32px;
  height: 32px;
  border-radius: 999px;
  flex-shrink: 0;
}
/* v3 comment avatars: solid brand color with a white initial inside.
   Replaces the gray Gravatar identicons used in v1/v2. Color comes
   from colorForId() so each author renders the same shade everywhere.  */
.card-detail-v3 .comment-avatar-color {
  width: 32px;
  height: 32px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  flex-shrink: 0;
  text-transform: uppercase;
}
.card-detail-v3 .timeline-comment .comment-content {
  flex: 1;
  min-width: 0;
}
.card-detail-v3 .timeline-event {
  display: flex;
  gap: 12px;
  align-items: center;
  font-size: 13px;
  color: var(--muted);
}
.card-detail-v3 .timeline-event-marker {
  width: 32px;
  display: flex;
  justify-content: center;
  flex-shrink: 0;
}
.card-detail-v3 .timeline-event-marker .dot {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: #d6cfc6;
}
.card-detail-v3 .timeline-event-msg {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.card-detail-v3 .timeline-event-actor {
  font-weight: 500;
  color: var(--text);
}
.card-detail-v3 .timeline-event-time {
  color: var(--muted);
  font-size: 12px;
}
.card-detail-v3 .timeline-event-sep {
  color: #8a827a;
}

/* Sticky composer at the modal foot. */
.card-detail-v3 .v3-composer {
  flex-shrink: 0;
  padding: 18px 24px 22px;
  background: #faf9f7;
  border-top: 1px solid #f0eeeb;
}
.card-detail-v3 .v3-composer-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 12px 10px 16px;
  border-radius: 12px;
  background: #fff;
  border: 1px solid #eae7e2;
  transition: border-color 80ms ease;
}
.card-detail-v3 .v3-composer-field:focus-within {
  border-color: #d6cfc6;
}
.card-detail-v3 .v3-composer-input {
  border: 0;
  outline: none;
  background: transparent;
  resize: none;
  font-size: 13px;
  line-height: 20px;
  font-family: inherit;
  color: var(--text);
  width: 100%;
  min-height: 40px;
}
.card-detail-v3 .v3-composer-input::placeholder {
  color: var(--muted);
}
.card-detail-v3 .v3-composer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
/* Hint + button live INSIDE the white field, so their colours stay
   the original light-on-light treatment. */
.card-detail-v3 .v3-composer-hint {
  font-size: 11px;
  color: var(--muted);
}
.card-detail-v3 .v3-send-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--text);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  border: 0;
  cursor: pointer;
  transition: opacity 80ms ease;
}
.card-detail-v3 .v3-send-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ════════════════════════════════════════════════════════════════════
   Mobile responsive overrides
   ════════════════════════════════════════════════════════════════════
   A single trailing block keeps the responsive surface easy to audit -
   each rule's selectors mirror the desktop counterpart up the file.
   Breakpoints:
     720px - tablet / small laptop in vertical orientation. Topbar
              collapses, button labels become icon-only, modals tighten.
     520px - phones. Search input hidden (keyboard shortcut still works
              on devices that have one), columns become viewport-wide,
              project switcher truncates harder.
*/

/* ── Topbar: collapse the 3-col grid to a flex bar with intrinsic
       sizing, drop button labels to icons, kill the floor-and-ceiling
       of fixed padding so the bar stops eating ~10% of the viewport. */
@media (max-width: 720px) {
  .topbar {
    grid-template-columns: auto 1fr auto;
    padding: 10px 14px;
    gap: 8px;
  }
  .topbar-left { gap: 2px; flex: 0 1 auto; min-width: 0; }
  .topbar-center {
    /* Search shrinks instead of holding its preferred 420px width,
       leaving room for the project switcher on the left. */
    flex: 1 1 auto;
  }
  .topbar-nav { gap: 4px; flex-shrink: 0; }
  .search-box { max-width: none; }

  /* Icon-only Share + Settings - their labels eat too much real
     estate and the icon alone communicates the action. We hide the
     last child (the text span) and tighten padding so the hit target
     stays a comfortable square. */
  .topbar-share { padding: 8px; gap: 0; }
  .topbar-share > span { display: none; }

  /* Wordmark shrinks a touch so the project name has more space. */
  .wordmark-img--icon { height: 22px; }
}

/* ── Phones: drop the search input from the topbar entirely. Keeping
       the slot empty (rather than removing it) preserves the spring
       between left and right clusters; users can still hit `/` on a
       BT keyboard. Future: a search trigger that expands the input
       inline. */
@media (max-width: 520px) {
  .topbar { padding: 8px 12px; }
  .topbar-center { display: none; }
  .topbar-pro-pill { display: none; } /* free up two more chars of width */
  .project-switcher-name {
    /* Truncate hard: any project name beyond what fits ellipses. */
    max-width: 45vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ── Board: horizontal-snap column carousel. Each column takes ~88vw
       so the next column peeks ~12vw - the standard "scroll-right"
       discoverability hint used by every mobile kanban. scroll-snap
       upgrades from `proximity` to `mandatory` so the column doesn't
       park mid-stride between snap points.

       The minimum 280px floor catches phones in landscape (where 88vw
       could be ~750+ and we want the columns to stay sensibly sized).
       The 360px ceiling caps tablets so the peek is preserved even on
       big phones. */
@media (max-width: 720px) {
  .board {
    padding: 12px 12px 24px;
    gap: 12px;
    scroll-padding: 0 12px;
    scroll-snap-type: x mandatory;
  }
  .column {
    flex: 0 0 min(88vw, 360px);
    min-width: 0;
    max-width: none;
    padding: 12px 10px 10px;
  }
  /* Column header tightens slightly so the title + count + kebab all
     fit at narrow widths without the kebab spilling out. */
  .column-header { padding: 0 2px 8px; }

  /* Kebab + edit pencil need to always be visible on touch (hover
     reveal is meaningless without a cursor). The base rule uses
     `.column:hover .column-menu-btn` (0,2,0) so we need at least the
     same specificity to override - hence the leading `.column`. */
  .column .column-menu-btn,
  .column .column-delete { opacity: 1; }
  .column .column-menu-btn { width: 28px; height: 28px; }
  .add-card-btn { min-height: 44px; }

  /* "Add column" trigger sits to the right of all columns; it works
     fine via scroll, but the original "+ Add" pill is tiny - widen
     for thumb-reach. */
  .add-column-btn { min-height: 44px; padding: 10px 14px; }

  /* ── Card-detail modal: stack the right sidebar below the main
        content so each section gets full width. The desktop layout
        is a flex row with main + 280px sidebar - on a 390px viewport
        the sidebar's fixed width forces the main column to ~80px,
        which renders one word per line. */
  .modal.card-detail-v3 {
    width: 100vw;
    max-width: 100vw;
    max-height: 100vh;
    /* Strip the rounded corners on phones - the modal now spans the
       whole viewport so the radii would only show on the bottom
       (cut off above by the topbar of the OS chrome anyway). */
    border-radius: 0;
  }
  .card-detail-v3 .card-detail-body {
    /* Single column scroll. The sidebar's `position:sticky` would
       have no useful effect here (it would just stick within its
       own scroll context) so we drop it implicitly by overriding
       its parent's flex direction. */
    flex-direction: column;
  }
  .card-detail-v3 .card-detail-header {
    padding: 14px 12px 14px 20px;
  }
  .card-detail-v3 .card-detail-main {
    padding: 0 20px 24px;
    gap: 22px;
  }
  .card-detail-v3 .card-detail-sidebar {
    width: auto;
    border-left: none;
    border-top: 1px solid #f0eeeb;
    padding: 20px;
    /* Take the sidebar out of sticky positioning and let it flow
       inline with the rest of the content. */
    position: static;
    align-self: stretch;
    max-height: none;
    overflow: visible;
  }

  /* ── Settings / Members / Billing list rows: stack the label + helper
        above the control instead of squeezing the helper into a 165px
        left column where every two-word phrase wraps to its own line.
        Tighter horizontal padding so the white card surface doesn't
        eat the whole viewport. */
  .list-row {
    flex-direction: column;
    align-items: stretch;
    padding: 16px 18px;
    gap: 10px;
  }
  .list-row .control { margin-left: 0; }
  .list-row .input,
  .list-row .input-with-action { width: 100%; }
}

/* ── Toasts ──
   Stacked notifications anchored bottom-right. Each toast is a flex row
   with a message and a dismiss button. Tone colours the left bar via a
   shadow inset to keep the surface itself neutral; that way error toasts
   don't blow up the visual hierarchy in a sea of dialog modals. */
#toast-root {
  position: fixed;
  inset: auto 1rem 1rem auto;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  pointer-events: none; /* let toasts themselves capture; root ignores */
  max-width: min(420px, calc(100vw - 2rem));
}
.craaft-toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--line, rgba(0,0,0,0.08));
  font-size: 0.9rem;
  line-height: 1.35;
  animation: craaft-toast-in 180ms var(--ease-out) both;
}
@keyframes craaft-toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.craaft-toast-message {
  flex: 1 1 auto;
  min-width: 0;
  word-wrap: break-word;
  white-space: pre-line;
}
.craaft-toast-close {
  appearance: none;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: inherit;
  opacity: 0.55;
  font-size: 1.2rem;
  line-height: 1;
  padding: 0 0.25rem;
  border-radius: var(--radius-xs);
}
.craaft-toast-close:hover { opacity: 1; }
.craaft-toast-close:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }

.craaft-toast-info    { box-shadow: var(--shadow-md), inset 4px 0 0 var(--text); }
.craaft-toast-success { box-shadow: var(--shadow-md), inset 4px 0 0 #16a34a; }
.craaft-toast-warning { box-shadow: var(--shadow-md), inset 4px 0 0 #d97706; }
.craaft-toast-error   { box-shadow: var(--shadow-md), inset 4px 0 0 var(--danger); }

/* Confirm modal inherits the standard .modal/.modal-sm shell so its
   sizing, padding, and centering match the rest of the modal stack.
   Only the per-confirm tweaks live here. */
.craaft-confirm-modal .modal-body p {
  margin: 0;
  color: var(--text);
  white-space: pre-line;
}


