/* ============================================================
   EMPIRE OS — App shell layout (layout.css)
   Rail · Topbar · Content · Palette · Modals · Toasts
   Stays 100% consistent with tokens / base / components.
   ============================================================ */

/* ---------------------------------------------------------------
   App grid
--------------------------------------------------------------- */
.app {
  position: relative; z-index: 1;
  display: grid;
  /* Two-column shell: rail + content. The persistent AI dock was removed;
     the assistant now lives entirely on the AI Chat route. */
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  grid-template-rows: var(--topbar-h) minmax(0, 1fr);
  grid-template-areas:
    "rail topbar"
    "rail content";
  height: 100vh; height: 100dvh;
  transition: grid-template-columns var(--t-med) var(--ease-out);
}
body.rail-collapsed .app { grid-template-columns: var(--rail-w-collapsed) minmax(0, 1fr); }

/* ---------------------------------------------------------------
   Shell icon-button safety net
   Some shell/view markup uses `.btn-icon` without the base `.btn`
   class. Without `.btn`, the box model (height/radius/flex-center)
   was missing, so hover painted a flat, square, un-rounded slab
   behind the glyph — the "random squares" bug. This guarantees every
   shell icon button is a properly sized, centered, rounded hit area
   in all themes, with or without `.btn`.
--------------------------------------------------------------- */
.rail .btn-icon,
.topbar .btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0;
  border-radius: var(--r-sm);
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.rail .btn-icon.btn-sm,
.topbar .btn-icon.btn-sm { width: 32px; height: 32px; border-radius: var(--r-xs); }
.rail .btn-icon svg,
.topbar .btn-icon svg { display: block; }
/* keyboard focus ring parity across the chrome */
.topbar .btn-icon:focus-visible,
.rail .btn-icon:focus-visible,
.global-search:focus-visible {
  outline: none; box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent-ring);
}

/* ===============================================================
   RAIL
=============================================================== */
.rail {
  grid-area: rail;
  display: flex; flex-direction: column;
  background: var(--surface-1);
  border-right: 1px solid var(--border);
  z-index: var(--z-rail);
  min-height: 0;
  position: relative;
  /* Inter optical refinements for the chrome: contextual alternates +
     disabled common ligatures keep nav labels crisp at small sizes. */
  font-feature-settings: "cv05" 1, "cv08" 1, "ss03" 1;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}
.rail::after { content: none; }

.rail-head {
  display: flex; align-items: center; justify-content: space-between;
  height: var(--topbar-h); padding: 0 14px 0 16px; flex-shrink: 0;
  border-bottom: 1px solid var(--border-soft);
}
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; text-decoration: none; }
.brand-mark {
  width: 23px; height: 23px; flex-shrink: 0;
  display: grid; place-items: center;
}
.brand-mark-img { width: 23px; height: 23px; object-fit: contain; grid-area: 1 / 1; }
/* A workspace's own icon replaces the Nova spark. It carries its own ink, so it
   opts out of the theme-swap pair below and shows in both themes. Rounded +
   clipped so any square logo sits correctly next to the wordmark. */
.brand-mark-custom { display: block; border-radius: var(--r-2, 6px); object-fit: cover; }
.brand-word-img { height: 17px; width: auto; object-fit: contain; }
/* Theme-aware Nova ink: white on dark, black on light.
   These rules OWN the visibility of every brand image (mark + wordmark)
   so the per-element sizing rules above never need a `display` value
   (which previously fought these and double-rendered the wordmark). */
.brand-img-light { display: none; }
.brand-img-dark { display: block; }
[data-theme="light"] .brand-img-dark { display: none; }
[data-theme="light"] .brand-img-light { display: block; }

/* mark + wordmark lockup with a small muted instance label */
.brand-lockup { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.brand-wordmark { display: block; height: 17px; line-height: 0; }
.brand-instance {
  font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-500); white-space: nowrap; line-height: 1;
}
.brand-logo-link, .brand-wordmark-link { display: block; line-height: 0; text-decoration: none; }
/* workspace switcher: the sub-label becomes a click target with a caret when the
   user belongs to more than one workspace (top-of-rail dropdown) */
.ws-switch {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: 0; padding: 2px 4px 2px 0; margin: 0;
  cursor: pointer; color: var(--text-500); border-radius: var(--radius-sm, 6px);
  max-width: 100%; transition: color .12s ease;
}
.ws-switch .brand-instance { overflow: hidden; text-overflow: ellipsis; }
.ws-switch svg { flex: none; opacity: .7; transition: opacity .12s ease, transform .12s ease; }
.ws-switch:hover { color: var(--text-300); }
.ws-switch:hover svg { opacity: 1; }
.ws-switch[aria-expanded="true"] svg { transform: rotate(180deg); }
.rail-collapse { color: var(--text-400); }

/* nav */
.rail-nav { flex: 1; min-height: 0; padding: 14px 12px 12px; overflow-y: auto; overflow-x: hidden; }
.rail-group { margin-top: 14px; }
.rail-group:first-child { margin-top: 4px; }
.rail-group-label {
  font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-500); padding: 0 10px 7px; white-space: nowrap;
}
.rail-item {
  position: relative; display: flex; align-items: center; gap: 11px;
  height: 38px; min-height: 38px; flex-shrink: 0;       /* never compress the row */
  padding: 0 10px; border-radius: var(--r-sm); margin-bottom: 2px;
  color: var(--text-300); font-size: var(--fs-sm); font-weight: 500;
  letter-spacing: -0.006em;
  transition: color var(--t-fast), background var(--t-fast);
}
.rail-item:hover { background: var(--surface-2); color: var(--text-100); }
.rail-ico { display: flex; color: var(--text-400); flex-shrink: 0; transition: color var(--t-fast); }
.rail-ico .rail-emoji { font-size: 17px; line-height: 1; display: inline-flex; }
.rail-item:hover .rail-ico { color: var(--text-200); }
/* label takes the room, truncates with ellipsis — never wraps or compresses */
.rail-label { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* count badge sits flush-right, never crushing or being crushed by the label */
.rail-item .badge { flex-shrink: 0; margin-left: auto; }

.rail-item.active {
  color: var(--text-100); font-weight: 600;
  background: var(--surface-3);
}
.rail-item.active .rail-ico { color: var(--text-100); }
.rail-item.active::before {
  content: ""; position: absolute; left: -12px; top: 8px; bottom: 8px; width: 2px;
  border-radius: 0 2px 2px 0; background: var(--accent);
}

/* rail footer / user chip */
.rail-foot { padding: 10px 12px 14px; border-top: 1px solid var(--border-soft); flex-shrink: 0; }
.user-chip {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;
  border-radius: var(--r-md); transition: background var(--t-fast); text-align: left;
}
.user-chip:hover { background: var(--surface-2); }
.user-meta { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
.user-name { font-size: var(--fs-sm); font-weight: 600; letter-spacing: -0.008em; color: var(--text-100); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-role { font-size: var(--fs-2xs); color: var(--text-400); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-gear { color: var(--text-400); display: flex; flex-shrink: 0; }
.user-chip:hover .user-gear { color: var(--text-200); }

/* ===============================================================
   COLLAPSED RAIL  (--rail-w-collapsed = 64px)
   Goal: a calm, intentional icon strip. Every icon sits in a
   consistent 40px square hit-area, centered in the rail, with a
   clear filled active chip + accent. Group labels collapse to a
   hairline divider so vertical rhythm reads as grouped, not random.
=============================================================== */
body.rail-collapsed .brand-lockup,
body.rail-collapsed .rail-label,
body.rail-collapsed .rail-item .badge,
body.rail-collapsed .rail-group-label,
body.rail-collapsed .user-meta,
body.rail-collapsed .user-gear { display: none; }

/* head: brand mark centered, collapse toggle centered below it isn't
   possible in the same row — keep the toggle but center the mark. */
body.rail-collapsed .rail-head { padding: 0; justify-content: center; position: static; }
body.rail-collapsed .rail { position: relative; }
body.rail-collapsed .brand { justify-content: center; gap: 0; }
/* the collapse/expand toggle is a small round handle centered on the rail's
   right edge (mid-height), half-overlapping the divider — discoverable, not noisy */
body.rail-collapsed .rail-collapse {
  position: absolute; right: -13px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--surface-1); border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-md); z-index: 5; color: var(--text-300);
  opacity: 0; pointer-events: none; transition: opacity var(--t-fast) var(--ease-out);
}
/* reveal the expand handle only while interacting with the rail, so it never
   sits over the content column otherwise */
body.rail-collapsed .rail:hover .rail-collapse,
body.rail-collapsed .rail:focus-within .rail-collapse,
body.rail-collapsed .rail-collapse:focus-visible { opacity: 1; pointer-events: auto; }
body.rail-collapsed .rail-collapse:hover { background: var(--surface-2); border-color: var(--border-strong); color: var(--text-100); }
body.rail-collapsed .rail-collapse svg { transform: rotate(180deg); width: 14px; height: 14px; }

/* nav: tighten the horizontal padding so the 40px squares center cleanly */
body.rail-collapsed .rail-nav { padding: 12px 0; }
body.rail-collapsed .rail-group { display: flex; flex-direction: column; align-items: center; margin-top: 0; gap: 4px; }
/* hairline divider between groups (replaces hidden text labels) */
body.rail-collapsed .rail-group + .rail-group { margin-top: 10px; padding-top: 14px; position: relative; }
body.rail-collapsed .rail-group + .rail-group::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 24px; height: 1px; background: var(--border);
}

/* the square icon button */
body.rail-collapsed .rail-item {
  justify-content: center; padding: 0; gap: 0;
  width: 40px; height: 40px; min-height: 40px; margin: 0 auto;
  border-radius: var(--r-md);
}
body.rail-collapsed .rail-item .rail-ico { margin: 0; }
/* kill the expanded-state left accent bar; collapsed uses a filled chip */
body.rail-collapsed .rail-item.active::before { content: none; }
body.rail-collapsed .rail-item.active {
  background: var(--accent); color: var(--accent-fg);
}
body.rail-collapsed .rail-item.active .rail-ico { color: var(--accent-fg); }
body.rail-collapsed .rail-item:not(.active):hover { background: var(--surface-2); }

/* footer / user chip collapses to just a centered avatar */
body.rail-collapsed .rail-foot { padding: 10px 0 14px; }
body.rail-collapsed .user-chip { justify-content: center; padding: 0; width: 40px; height: 40px; margin: 0 auto; border-radius: var(--r-md); gap: 0; }

/* mobile rail toggle — hidden on desktop (override the .topbar .btn-icon
   inline-flex safety net, which has higher specificity than a bare class) */
.topbar .rail-toggle-m { display: none; }

/* ===============================================================
   COLLAPSED-RAIL FLYOUT TOOLTIP  (rendered in a body-level layer
   by app.js so it escapes the rail's overflow + the viewport edge)
   Appears to the RIGHT of the hovered icon, vertically centered.
=============================================================== */
.rail-tip-layer { position: fixed; inset: 0; pointer-events: none; z-index: var(--z-toast); }
.rail-flyout {
  position: fixed; left: 0; top: 0;
  display: inline-flex; align-items: center;
  padding: 6px 11px; max-width: 220px;
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.2; letter-spacing: -0.004em;
  color: var(--text-100); white-space: nowrap;
  background: var(--raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-lg);
  pointer-events: none;
  opacity: 0; transform: translateX(-4px);
  transition: opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.rail-flyout.in { opacity: 1; transform: translateX(0); }
/* a small pointer notch on the left edge, pointing back at the icon */
.rail-flyout::before {
  content: ""; position: absolute; left: -4px; top: 50%; margin-top: -4px;
  width: 8px; height: 8px; transform: rotate(45deg);
  background: var(--raised);
  border-left: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
}
.rail-flyout .rail-flyout-badge {
  margin-left: 8px; padding: 1px 6px; border-radius: var(--r-pill);
  font-size: var(--fs-2xs); font-weight: 600; line-height: 1.4;
  color: var(--text-300); background: var(--surface-2); border: 1px solid var(--border);
}
@media (prefers-reduced-motion: reduce) {
  .rail-flyout { transition: opacity var(--t-fast) linear; transform: none; }
  .rail-flyout.in { transform: none; }
}

/* ===============================================================
   TOPBAR
=============================================================== */
.topbar {
  grid-area: topbar;
  display: flex; align-items: center; gap: var(--sp-4);
  height: var(--topbar-h); padding: 0 var(--sp-5);
  background: var(--surface-glass);
  backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid var(--border);
  z-index: var(--z-topbar);
  font-feature-settings: "cv05" 1, "cv08" 1, "ss03" 1;
  -webkit-font-smoothing: antialiased;
}
.topbar-left { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; flex: 0 0 auto; }
.breadcrumb { display: flex; align-items: center; gap: 6px; min-width: 0; }
.crumb { display: inline-flex; align-items: center; gap: 7px; font-size: var(--fs-sm); font-weight: 500; letter-spacing: -0.006em; color: var(--text-200); white-space: nowrap; }
.crumb-root { color: var(--text-100); font-weight: 600; letter-spacing: -0.01em; }
.crumb-root svg { color: var(--text-300); }
.crumb-sep { display: inline-flex; color: var(--text-500); }
.crumb-sep svg { width: 14px; height: 14px; }

/* global search trigger */
.global-search {
  flex: 1; max-width: 560px; margin: 0 auto;
  display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 12px;
  border-radius: var(--r-pill); background: var(--surface-1); border: 1px solid var(--border);
  color: var(--text-400); transition: background var(--t-fast) var(--ease-smooth), border-color var(--t-fast) var(--ease-smooth), color var(--t-fast) var(--ease-smooth), box-shadow var(--t-fast) var(--ease-smooth), transform var(--t-fast) var(--ease-smooth);
}
.global-search:hover { background: var(--surface-2); border-color: var(--border-strong); color: var(--text-300); }
.global-search:active { transform: scale(0.995); }
.global-search svg { color: var(--text-400); flex-shrink: 0; }
.gs-placeholder { font-size: var(--fs-sm); font-weight: 500; letter-spacing: -0.006em; flex: 1; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gs-kbd { display: inline-flex; gap: 3px; flex-shrink: 0; }
kbd {
  font-family: var(--font-mono); font-size: 0.66rem; line-height: 1; color: var(--text-300);
  background: var(--surface-3); border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: 5px; padding: 3px 5px; min-width: 17px; text-align: center;
}

.topbar-right { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.topbar-right .btn-icon { color: var(--text-300); }
.topbar-right .btn-icon.active { color: var(--text-100); background: var(--surface-3); }
/* ---- World clock (topbar) ---- subtle stacked local + extra zones ---- */
.world-clock { display: inline-flex; flex-direction: column; justify-content: center; gap: 0; align-items: flex-end; margin-right: 6px; padding: 3px 9px; border: 1px solid transparent; border-radius: var(--r-md); background: none; cursor: pointer; font-family: inherit; line-height: 1.05; transition: background var(--t-fast), border-color var(--t-fast); }
.world-clock:hover { background: var(--surface-2); border-color: var(--border-soft); }
.world-clock .wc-row { display: inline-flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.world-clock .wc-label { font-size: 9.5px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--text-500); }
.world-clock .wc-time { font-size: var(--fs-xs); font-weight: 600; color: var(--text-300); font-variant-numeric: tabular-nums; min-width: 52px; text-align: right; }
.world-clock .wc-primary .wc-time { color: var(--text-100); font-weight: 650; }
.world-clock .wc-primary .wc-label { color: var(--text-400); }
.world-clock.wc-multi { padding: 2px 9px; }
.world-clock.wc-multi .wc-time { font-size: 10.5px; }
.world-clock.wc-multi .wc-primary .wc-time { font-size: var(--fs-xs); }
@media (max-width: 720px) { .world-clock { display: none; } }
@media (prefers-reduced-motion: reduce) { .world-clock { transition: none; } }

.notif-btn { position: relative; }
/* Bell badge: a real numeric unread COUNT (was a 7px heuristic dot). Same
   corner + accent as before, now sized to hold 1–2 digits ("99+" max). */
.notif-dot { position: absolute; top: 3px; right: 4px; min-width: 15px; height: 15px; padding: 0 4px; display: grid; place-items: center; box-sizing: border-box; border-radius: 999px; background: var(--accent); color: var(--accent-fg); font-size: 9px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; box-shadow: 0 0 0 2px var(--bg); pointer-events: none; }

/* ---- Topbar tooltips MUST open DOWNWARD (never clip above y=0) ----
   The global [data-tip] rule (components.css) opens upward, which clips above
   the viewport for topbar triggers. Override so topbar tips drop below the
   trigger; the right-most tip right-aligns so it never spills past the edge. */
.topbar [data-tip]:hover::after {
  bottom: auto; top: calc(100% + 7px);
}
.topbar-right > [data-tip]:last-child:hover::after {
  left: auto; right: 0; transform: none;
}

/* ===============================================================
   CONTENT
=============================================================== */
.content { grid-area: content; overflow: hidden; min-height: 0; position: relative; }
#view {
  height: 100%; overflow-y: auto; overflow-x: hidden;
  padding: var(--sp-6) var(--sp-7) var(--sp-10);
}
#view > .view, .view { max-width: 1320px; margin: 0 auto; }

/* ---------------------------------------------------------------
   ONE clean view transition (replaces the per-view cascade on nav)
   The first load keeps the rich .rise/.stagger reveal. After that,
   `body.nav-ready` neutralizes those per-element animations so tabs
   switch crisply — only #view does a fast 120ms opacity/translate
   fade. No cascade re-trigger, no flash, no layout jump.
--------------------------------------------------------------- */
@keyframes eos-view-enter { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
body.nav-ready #view.view-enter { animation: eos-view-enter 120ms var(--ease-out) both; }
/* Gate the per-element cascade to first load only — our CSS wins even
   if a view re-adds .rise/.stagger on every navigation. */
body.nav-ready #view .rise,
body.nav-ready #view .stagger > * {
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
}
@media (prefers-reduced-motion: reduce) {
  body.nav-ready #view.view-enter { animation: none; }
}

/* standard view header */
.view-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-5);
  margin-bottom: var(--sp-6); flex-wrap: wrap;
}
.view-head .view-title-wrap { min-width: 0; }
.view-head .eyebrow { margin-bottom: 7px; }
.view-head h1 { font-family: var(--font-display); font-size: var(--fs-display); font-weight: 600; letter-spacing: -0.022em; line-height: 1.1; }
.view-head .view-sub { color: var(--text-400); margin-top: 9px; font-size: var(--fs-body); max-width: 60ch; }
.view-head .view-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; flex-shrink: 0; }
.view-body { margin-top: var(--sp-6); }

/* router fallback */
.coming-online {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 10px; min-height: 60vh; padding: var(--sp-8);
}
.coming-online-ring {
  width: 72px; height: 72px; border-radius: 50%; display: grid; place-items: center;
  color: var(--text-300); margin-bottom: var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  animation: eos-pulse 2.6s var(--ease-in-out) infinite;
}
.coming-online h2 { font-size: 2rem; }
.coming-online .eyebrow { margin-bottom: 2px; }

/* ===============================================================
   ASSISTANT MESSAGE PRIMITIVES
   The persistent right dock was removed — the assistant lives on the
   AI Chat route (ai-chat.js owns its own bubble layout). These message
   primitives remain only because shared helpers (action cards) and any
   legacy chat markup may still reference them.
=============================================================== */
.msg-stack { display: flex; flex-direction: column; gap: 8px; min-width: 0; max-width: 84%; }
.msg-avatar { width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center; flex-shrink: 0; margin-top: 2px; }
.msg-avatar.ai { color: var(--text-100); background: var(--surface-3); border: 1px solid var(--border); }
.msg-bubble {
  padding: 10px 13px; border-radius: var(--r-lg); font-size: var(--fs-sm); line-height: 1.5;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-200);
  max-width: 100%; white-space: pre-wrap; overflow-wrap: anywhere;
}
/* assistant suggestion chip — shared by the AI Chat composer */
.sugg-chip {
  font-size: var(--fs-xs); font-weight: 500; color: var(--text-300);
  padding: 6px 11px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--border);
  transition: background var(--t-fast) var(--ease-smooth), color var(--t-fast) var(--ease-smooth), border-color var(--t-fast) var(--ease-smooth), transform var(--t-fast) var(--ease-smooth); white-space: nowrap;
}
.sugg-chip:hover { background: var(--surface-3); color: var(--text-100); }
.sugg-chip:active { transform: scale(0.97); }
.sugg-chip:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }

/* ===============================================================
   SHARED DRAG-AND-DROP VISUAL SYSTEM  (SortableJS)
   Used by EVERY board/list across the OS — do NOT rename these classes.

   The canonical SortableJS option set now lives in code as HOUSE_DND
   in assets/js/dnd.js — that file is the single source of truth. Wire
   any board/list with makeSortable(el, { group, handle, filter, ... });
   it spreads HOUSE_DND (animation 180, easing cubic-bezier(.22,1,.36,1),
   ghostClass dnd-ghost / chosenClass dnd-chosen / dragClass dnd-drag,
   forceFallback, fallbackOnBody, fallbackTolerance 4, swapThreshold 0.65,
   emptyInsertThreshold 16, scroll + scrollSensitivity 80 / scrollSpeed 12)
   and adds/removes body.dnd-scrolling around the drag. The classes below
   are the shared look those options reference — keep them here, and do
   NOT re-declare per view.
=============================================================== */
/* placeholder gap left where the card will drop — a CLEAR drop zone so you can
   see exactly where the card lands (the old accent-ring @14% dashed line + 6%
   fill was nearly invisible in dark mode). Uses color-mix on --accent so it
   reads in BOTH themes (accent = near-white dark / near-black light). */
.dnd-ghost {
  position: relative;
  border: 2px dashed color-mix(in srgb, var(--accent) 55%, transparent) !important;
  background: color-mix(in srgb, var(--accent) 11%, transparent) !important;
  box-shadow: none !important;
  border-radius: var(--r-lg, 12px) !important;
  opacity: 1;
  min-height: 46px;
  /* CRITICAL (WS-21): min-height above replaces the card's default
     `min-height:auto`, which is what normally STOPS a flex item from
     shrinking below its content. Board lanes (.board-col-body …) are
     height-constrained flex columns, so without this the ghost gets
     flex-crushed to exactly 46px — and SortableJS measures that crushed
     element when it builds the floating clone, giving the clone an inline
     height of 46px with the card's content spilling out over neighboring
     cards (the "somehow double built" drag look). */
  flex-shrink: 0;
}
.dnd-ghost > * { visibility: hidden; }   /* hide contents, keep the shape/radius */

/* Highlight the drop target the card is currently over — the "show me the zone
   I'm dropping into" cue. Every sortable container is tagged .dnd-list by the
   shared makeSortable() helper, so this works IDENTICALLY on ALL boards (CRM,
   Tasks, Projects, Priorities, Content…) regardless of their own column class.
   On boards with a .board-col wrapper we lift the WHOLE column for a nicer look;
   otherwise the list container itself lights up. Only active during a drag. */
body.dnd-scrolling .dnd-list:has(.dnd-ghost) {
  outline: 2px solid color-mix(in srgb, var(--accent) 34%, transparent);
  outline-offset: -2px;
  border-radius: var(--r-lg, 12px);
  box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(--accent) 5%, transparent);
  transition: box-shadow .12s ease, outline-color .12s ease;
}
body.dnd-scrolling .board-col:has(.dnd-ghost) {
  outline: 2px solid color-mix(in srgb, var(--accent) 46%, transparent);
  outline-offset: -2px;
  border-radius: var(--r-lg, 12px);
  box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(--accent) 8%, transparent);
  transition: box-shadow .12s ease, outline-color .12s ease;
}
/* when the whole column lifts, don't also double-outline the inner list */
body.dnd-scrolling .board-col:has(.dnd-ghost) .dnd-list:has(.dnd-ghost) {
  outline: none; box-shadow: none;
}
/* WS-21: while a drag is live, empty-lane "Drop here" placeholders leave the
   stage ENTIRELY — one uniform change at drag start, never toggled per-lane
   mid-drag. Two reasons:
   (1) visual — a lane can never show two dashed drop cues stacked (placeholder
       + ghost slot), and placeholders can't squeeze/pump as the ghost hops
       lanes ("the drop records here feel like they change somehow");
   (2) mechanical — SortableJS treats a placeholder-only lane as NON-empty
       (the placeholder matches `draggable`), which routes empty-lane hovers
       through the swap dead-zone math: the pointer must travel
       ~height×swapThreshold (≈100px on a flex:1 placeholder) INSIDE the box
       before the ghost inserts — the "I'm over the lane and nothing happens"
       bug (verified live). display:none children are skipped by Sortable's
       lastChild(), so the lane takes the instant `empty list → append` path.
   Lanes keep their min-heights (plus the guard below), so nothing collapses;
   the .dnd-list/.board-col drop-target highlight above is the affordance
   while a drag is in progress. */
body.dnd-scrolling .dnd-list > :is(.crm-col-empty, .ct-col-empty, .pj-empty-col, .tk-empty, .tk-col-empty, .plan-empty, .v-pri-empty, .pl-tree-empty) {
  display: none;
}
/* …and no list may collapse to a zero-height (undroppable) strip while its
   placeholder is off-stage. Board lanes already carry big per-view
   min-heights; this floor is for small flat lists (tasks list groups, plan
   trays…). Applied once at drag start — stable for the whole drag.
   :where() keeps specificity at zero so EVERY real min-height (components.css
   .board-col-body 60px, the per-view 200-220px lane rules…) still wins —
   without it this floor of 48px would REPLACE the lanes' 220px and collapse
   every empty lane at drag start (verified live). */
:where(body.dnd-scrolling .dnd-list) { min-height: 48px; }
/* the original card while it's being dragged */
.dnd-chosen { cursor: grabbing; opacity: 0.5; }
/* …but once the chosen element becomes the in-list ghost slot, the slot must
   read at full strength (the .5 above is only for the pre-drag "picked up"
   beat). Without this, .dnd-chosen (same specificity, declared after
   .dnd-ghost) wins and the dashed drop slot renders half-faded. */
.dnd-ghost.dnd-chosen { opacity: 1; }

/* the floating clone that follows the cursor — lifted + tinted so the drag
   feels physical. The lift reads via shadow + accent border/outline (NOT a CSS
   transform): with forceFallback, Sortable repositions this clone by writing an
   inline `transform: translate3d(...)` on EVERY pointer move, so any CSS
   transform here is overwritten after one frame (the old scale/rotate only
   flashed once, then vanished — "sometimes I see the lift, sometimes I don't").
   Crucially, `transition: none !important` kills ANY transition on transform —
   both a local one and the one the cloned card inherits from its own base class
   (e.g. .kard has `transition: … transform …`). Without this the clone eases
   toward each new position over 150ms and visibly lags/wobbles behind the
   cursor (the "super slow, doesn't look good"). The clone must be glued to the
   pointer, so it gets no transition at all. */
.dnd-drag {
  cursor: grabbing;
  background: var(--surface-0);
  box-shadow: var(--shadow-elevated), 0 22px 40px -12px rgba(0,0,0,0.35) !important;
  border-color: var(--accent-ring) !important;
  outline: 1px solid var(--accent-ring); outline-offset: -1px;
  opacity: 1 !important;
  transition: none !important;
  /* CRITICAL: kill any inherited keyframe ANIMATION on the clone. Cards carry an
     entrance reveal (mo-enter → animation: mo-slide-up, which animates transform
     translateY(8px)→none). SortableJS's forceFallback positions the clone by
     writing an inline `transform: translate3d(...)` on every pointer move — but a
     running CSS animation OVERRIDES inline styles (per spec), so the reveal's
     transform wins and the clone stays pinned near its origin instead of tracking
     the cursor (the "dragging really sucks / card doesn't follow" bug). Cloning
     the card restarts that animation, so it must be forced off here. `transition:
     none` (above) alone was NOT enough — that only covers transitions. */
  animation: none !important;
  will-change: transform; /* composite the moving clone on the GPU for a smooth drag */
}
/* soft settle when a card lands — a spring overshoot that resolves to rest.
   Views add .dnd-settle to evt.item on drop (see settle() helpers). */
@keyframes dnd-settle-house {
  0%   { transform: scale(1.045); box-shadow: var(--shadow-elevated); }
  55%  { transform: scale(0.992); }
  100% { transform: scale(1); box-shadow: var(--shadow-card); }
}
.dnd-settle { animation: dnd-settle-house var(--t-slow) var(--ease-spring); }
/* while a drag is in progress: stop text selection + smooth the cursor */
body.dnd-scrolling { cursor: grabbing; user-select: none; }
body.dnd-scrolling * { cursor: grabbing !important; }
/* anything draggable should advertise it */
[data-draggable], .dnd-handle { cursor: grab; }

/* ===============================================================
   COMMAND PALETTE
=============================================================== */
.palette-backdrop {
  position: fixed; inset: 0; z-index: var(--z-palette);
  background: rgba(0,0,0,0.32); backdrop-filter: blur(4px);
  display: flex; align-items: flex-start; justify-content: center; padding-top: 13vh;
  opacity: 0; pointer-events: none; transition: opacity var(--t-fast);
}
body.palette-open .palette-backdrop { opacity: 1; pointer-events: auto; }
.palette {
  width: min(640px, 92vw); max-height: 64vh; display: flex; flex-direction: column;
  background: var(--raised);
  border: 1px solid var(--border-strong); border-radius: var(--r-xl);
  box-shadow: var(--shadow-xl); overflow: hidden;
  transform: translateY(-12px) scale(0.98); opacity: 0;
  transition: transform var(--t-med) var(--ease-out), opacity var(--t-med);
}
body.palette-open .palette { transform: none; opacity: 1; }
.palette-input-row { display: flex; align-items: center; gap: 11px; padding: 15px 17px; border-bottom: 1px solid var(--border); }
.palette-input-row > svg { color: var(--text-400); flex-shrink: 0; }
.palette-input { flex: 1; background: none; border: none; outline: none; font-size: 1.02rem; letter-spacing: -0.012em; color: var(--text-100); }
.palette-input::placeholder { color: var(--text-500); }
.palette-results { flex: 1; min-height: 0; overflow-y: auto; padding: 8px; }
.palette-group { font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-500); padding: 10px 12px 5px; }
.palette-row { display: flex; align-items: center; gap: 12px; width: 100%; padding: 10px 12px; border-radius: var(--r-sm); text-align: left; transition: background var(--t-fast); }
.palette-row.sel { background: var(--surface-3); }
.palette-row-ico { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; background: var(--surface-2); border: 1px solid var(--border); color: var(--text-300); flex-shrink: 0; }
.palette-row-ico.gold { color: var(--text-100); background: var(--accent-soft); border-color: var(--border-strong); }
.palette-row.sel .palette-row-ico { color: var(--text-100); }
.palette-row-label { flex: 1; font-size: var(--fs-body); color: var(--text-100); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-row-hint { font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-500); flex-shrink: 0; }
.palette-foot { display: flex; align-items: center; gap: var(--sp-4); padding: 10px 16px; border-top: 1px solid var(--border); font-size: var(--fs-2xs); color: var(--text-500); }
.palette-foot span { display: inline-flex; align-items: center; gap: 5px; }
.palette-foot kbd { font-size: 0.6rem; padding: 2px 4px; }

/* ===============================================================
   POPOVERS (notifications, menus)
=============================================================== */
.popover {
  position: fixed; z-index: var(--z-modal);
  background: var(--ink-600); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-xl);
  opacity: 0; transform: translateY(-6px) scale(0.98);
  transition: opacity var(--t-fast), transform var(--t-fast) var(--ease-out);
}
.popover.in { opacity: 1; transform: none; }
.notif-pop { width: 360px; max-width: 92vw; padding: 6px; }
.popover-head { display: flex; align-items: center; justify-content: space-between; padding: 9px 10px 9px 12px; }
.popover-title { font-weight: 600; color: var(--text-100); font-size: var(--fs-sm); }
.notif-list { max-height: 60vh; overflow-y: auto; }
.notif-item { display: flex; gap: 11px; padding: 11px 10px; border-radius: var(--r-sm); transition: background var(--t-fast); cursor: default; }
.notif-item:hover { background: var(--surface-2); }
.notif-item.unread { background: var(--accent-soft); }
.notif-ico { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; flex-shrink: 0; background: var(--surface-3); color: var(--text-300); border: 1px solid var(--border); }
.notif-ico.gold { color: var(--text-100); background: var(--accent-soft); border-color: var(--border); }
.notif-ico.green { color: var(--green-400); background: var(--green-bg); }
.notif-ico.blue { color: var(--blue-400); background: var(--blue-bg); }
.notif-ico.red { color: var(--red-400); background: var(--red-bg); }
.notif-ico.violet { color: var(--violet-400); background: var(--violet-bg); }
.notif-body { min-width: 0; flex: 1; }
.notif-title { font-size: var(--fs-sm); font-weight: 600; color: var(--text-100); }
.notif-detail { font-size: var(--fs-xs); line-height: 1.45; margin-top: 2px; }
.notif-time { font-size: var(--fs-2xs); margin-top: 5px; display: block; }
.menu-item.active { color: var(--text-100); }
.menu-item.active svg { color: var(--text-100); }

/* ===============================================================
   MODALS
=============================================================== */
.modal-backdrop {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: rgba(0,0,0,0.4); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; padding: var(--sp-5);
  opacity: 0; transition: opacity var(--t-med);
}
.modal-backdrop.in { opacity: 1; }
.modal-backdrop.out { opacity: 0; }
.modal {
  width: min(520px, 94vw); max-height: 88vh; display: flex; flex-direction: column;
  background: var(--raised);
  border: 1px solid var(--border-strong); border-radius: var(--r-xl);
  box-shadow: var(--shadow-xl);
  transform: translateY(14px) scale(0.97); opacity: 0;
  transition: transform var(--t-med) var(--ease-out), opacity var(--t-med);
}
.modal-backdrop.in .modal { transform: none; opacity: 1; }
.modal-wide { width: min(820px, 95vw); }
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: 18px 18px 14px; border-bottom: 1px solid var(--border); }
.modal-ico { width: 32px; height: 32px; border-radius: var(--r-md); display: grid; place-items: center; color: var(--text-100); background: var(--surface-3); border: 1px solid var(--border); flex-shrink: 0; }
.modal-title { font-family: var(--font-display); font-size: 1.2rem; font-weight: 600; letter-spacing: -0.012em; color: var(--text-100); }
.modal-body { padding: 18px; overflow-y: auto; flex: 1; min-height: 0; }
.modal-foot { display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-2); padding: 14px 18px; border-top: 1px solid var(--border); }

/* guardrail variant */
.modal-guardrail { border-color: var(--amber-400); box-shadow: var(--shadow-xl); }
.modal-guardrail .modal-head { border-bottom-color: var(--border); }
.guardrail { text-align: center; padding: var(--sp-4) var(--sp-3) var(--sp-2); }
.guardrail-shield { width: 60px; height: 60px; border-radius: 50%; margin: 0 auto var(--sp-4); display: grid; place-items: center; color: var(--amber-400); background: var(--amber-bg); border: 1px solid var(--amber-400); }
.guardrail-action { font-family: var(--font-display); font-size: 1.15rem; color: var(--text-100); font-weight: 600; letter-spacing: -0.012em; }
.guardrail-detail { font-size: var(--fs-sm); line-height: 1.55; margin: 10px auto 0; max-width: 46ch; }

/* ---- workspace switcher ("Your workspaces" modal) ---- */
.wsx-hint { margin: 0 0 var(--sp-3); }
.wsx-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.wsx-row { display: flex; align-items: center; gap: 4px; border-radius: var(--r-3); }
.wsx-row:hover { background: var(--surface-2); }
.wsx-open {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--sp-3);
  padding: 10px; background: none; border: 0; cursor: pointer; text-align: left;
  border-radius: var(--r-3); color: inherit; font: inherit;
}
.wsx-open:disabled { cursor: default; }
.wsx-icon {
  width: 30px; height: 30px; flex-shrink: 0; border-radius: var(--r-2);
  object-fit: cover; display: grid; place-items: center;
}
/* No custom icon → the workspace's initial, so rows stay distinguishable. */
.wsx-icon-fallback {
  background: var(--surface-3); color: var(--text-300); border: 1px solid var(--border-soft);
  font-size: 12px; font-weight: 640;
}
.wsx-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.wsx-name { font-size: var(--fs-sm); font-weight: 560; color: var(--text-100); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wsx-sub { font-size: var(--fs-xs); text-transform: capitalize; }
.wsx-badges { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.wsx-badge {
  font-size: 10px; font-weight: 600; letter-spacing: 0.02em; padding: 2px 6px;
  border-radius: 999px; border: 1px solid var(--border); color: var(--text-400); white-space: nowrap;
}
.wsx-badge-role { text-transform: capitalize; }
.wsx-badge-current { border-color: var(--accent); color: var(--accent); }
.wsx-badge-susp { border-color: var(--amber-400); color: var(--amber-400); }
/* Dimmed, never hidden — a suspension you can't see is one you can't undo. */
.wsx-row.is-susp .wsx-icon, .wsx-row.is-susp .wsx-name { opacity: 0.55; }
.wsx-more { flex-shrink: 0; margin-right: 6px; }
.wsx-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--border-soft);
}
.wsx-new .field { margin-bottom: var(--sp-3); }
.wsx-new p { margin: 0; line-height: 1.55; }
.wsx-danger p { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); line-height: 1.55; }
.wsx-danger .field-label { display: block; margin-bottom: 6px; }

/* Mobile: every modal becomes a bottom sheet — CONSOLIDATED here so every surface
   behaves identically. Views must NOT redeclare this in their own media queries. */
@media (max-width: 680px) {
  .modal-backdrop { align-items: flex-end; padding: 0; }
  .modal { width: 100vw; max-height: 92vh; border-radius: var(--r-xl) var(--r-xl) 0 0; transform: translateY(100%); }
  .modal-backdrop.in .modal { transform: none; }
}

/* ===============================================================
   TOASTS
=============================================================== */
.toast-wrap { position: fixed; right: 20px; bottom: 88px; z-index: var(--z-toast); display: flex; flex-direction: column; gap: 10px; align-items: flex-end; pointer-events: none; }
.toast {
  display: flex; align-items: center; gap: 11px; padding: 12px 15px; min-width: 240px; max-width: 380px;
  border-radius: var(--r-md); font-size: var(--fs-sm); color: var(--text-100); pointer-events: auto; cursor: pointer;
  background: var(--raised);
  border: 1px solid var(--border-strong); box-shadow: var(--shadow-lg);
  transform: translateX(120%); opacity: 0; transition: transform var(--t-med) var(--ease-out), opacity var(--t-med);
}
.toast.in { transform: none; opacity: 1; }
.toast.out { transform: translateX(120%); opacity: 0; }
.toast-ico { width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; flex-shrink: 0; background: var(--surface-3); color: var(--text-300); }
.toast-msg { line-height: 1.4; }
.toast-success .toast-ico { color: var(--green-400); background: var(--green-bg); }
.toast-error .toast-ico { color: var(--red-400); background: var(--red-bg); }
.toast-ai .toast-ico { color: var(--text-100); background: var(--accent-soft); }
.toast-ai { border-color: var(--border-strong); }
.toast-warn .toast-ico { color: var(--amber-400); background: var(--amber-bg); }

/* ===============================================================
   ACTION CARDS (assistant tool results — AI Chat)
=============================================================== */
.action-cards { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.action-card {
  display: flex; align-items: flex-start; gap: 10px; width: 100%;
  padding: 11px 12px; border-radius: var(--r-md);
  background: var(--surface-1); border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  animation: eos-rise var(--t-med) var(--ease-out) both;
  transition: border-color var(--t-med) var(--ease-out), background var(--t-med) var(--ease-out);
}
/* an approval card is a STOP sign — amber, but a hairline of it, not a slab */
.action-card.ac-guard {
  flex-direction: column; gap: 11px; background: var(--amber-bg);
  border-color: color-mix(in srgb, var(--amber-400) 42%, transparent);
}
.ac-ico { width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; flex-shrink: 0; border: 1px solid var(--border); }
.ac-ico.ok { color: var(--green-400); background: var(--green-bg); border-color: color-mix(in srgb, var(--green-400) 30%, transparent); }
.ac-ico.err { color: var(--red-400); background: var(--red-bg); border-color: color-mix(in srgb, var(--red-400) 30%, transparent); }
.ac-ico.guard { color: var(--amber-400); background: color-mix(in srgb, var(--amber-400) 14%, transparent); border-color: color-mix(in srgb, var(--amber-400) 38%, transparent); }
.ac-body { min-width: 0; flex: 1; }
.ac-summary { font-size: var(--fs-sm); color: var(--text-100); line-height: 1.45; }
.ac-link {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 6px;
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-200);
  padding: 3px 8px; border-radius: var(--r-pill); background: var(--surface-3); border: 1px solid var(--border);
  transition: color var(--t-fast) var(--ease-smooth), border-color var(--t-fast) var(--ease-smooth), background var(--t-fast) var(--ease-smooth), transform var(--t-fast) var(--ease-smooth);
}
.ac-link:hover { color: var(--text-100); border-color: var(--border-strong); }
.ac-link:active { transform: scale(0.97); }
/* Undo affordance on an executed action card — quiet, monochrome, secondary. */
.ac-undo {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 6px; margin-left: 8px;
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-300);
  padding: 3px 8px; border-radius: var(--r-pill); background: transparent; border: 1px solid var(--border);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease-smooth), border-color var(--t-fast) var(--ease-smooth), background var(--t-fast) var(--ease-smooth), transform var(--t-fast) var(--ease-smooth);
}
.ac-undo:hover { color: var(--text-100); border-color: var(--border-strong); background: var(--surface-3); }
.ac-undo:active { transform: scale(0.97); }
.ac-undo:disabled { opacity: 0.6; cursor: default; }
.ac-undo svg { flex-shrink: 0; }
.ac-undone { display: inline-flex; align-items: center; gap: 4px; margin-top: 6px; margin-left: 8px; font-size: var(--fs-xs); font-weight: 600; color: var(--text-400); }
.ac-guard-top { display: flex; align-items: flex-start; gap: 10px; width: 100%; }
.ac-guard-label { font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--amber-400); display: inline-flex; align-items: center; gap: 5px; margin-bottom: 3px; }
.ac-guard-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; justify-content: flex-end; width: 100%; padding-top: 9px; border-top: 1px solid color-mix(in srgb, var(--amber-400) 24%, transparent); }
/* the dock is only ~340px wide — approval buttons share the row evenly there */
.ai-dock .ac-guard-foot .btn { flex: 1; justify-content: center; }
.ac-approving { font-size: var(--fs-xs); color: var(--text-300); display: inline-flex; align-items: center; gap: 6px; font-weight: 600; }
.ac-approving svg { animation: eos-spin 1s linear infinite; }
.ac-approving.err { color: var(--red-400); }
.ac-args { font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1.5; color: var(--text-200); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 10px 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-word; }
@keyframes eos-spin { to { transform: rotate(360deg); } }

/* palette result rows with subtitles + spinner */
.palette-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.palette-row-main .palette-row-label { flex: none; }
.palette-row-sub { font-size: var(--fs-2xs); color: var(--text-400); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-spinner { display: inline-flex; color: var(--text-400); }
.palette-spinner svg { animation: eos-spin 1s linear infinite; }
.palette .empty { text-align: center; padding: var(--sp-7) var(--sp-4); color: var(--text-400); }
.palette .empty .ico { display: inline-flex; color: var(--text-500); margin-bottom: var(--sp-3); }

/* user-chip / generic menu */
.popover.menu { min-width: 210px; padding: 5px; }
.menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px; border-radius: var(--r-sm); font-size: var(--fs-sm); font-weight: 500; letter-spacing: -0.006em; color: var(--text-200); text-align: left; transition: background var(--t-fast), color var(--t-fast); }
.menu-item svg { color: var(--text-400); flex-shrink: 0; }
.menu-item:hover { background: var(--surface-2); color: var(--text-100); }
.menu-item:hover svg { color: var(--text-200); }
.menu-item.danger { color: var(--red-400); }
.menu-item.danger svg { color: var(--red-400); }
.menu-item.danger:hover { background: var(--red-bg); color: var(--red-400); }

/* notif empty */
.notif-empty { text-align: center; padding: var(--sp-7) var(--sp-4); }
.notif-empty .ico { display: inline-flex; color: var(--text-500); margin-bottom: var(--sp-2); }

/* shortcut cheat-sheet */
.shortcut-grid { display: flex; flex-direction: column; gap: 2px; }
.shortcut-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: 9px 6px; border-bottom: 1px solid var(--border-soft); }
.shortcut-row:last-child { border-bottom: none; }
.shortcut-keys { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; }
.shortcut-desc { font-size: var(--fs-sm); color: var(--text-200); text-align: right; }

/* mobile drawer backdrop (created lazily by app.js) */
.rail-backdrop {
  position: fixed; inset: 0; z-index: calc(var(--z-rail) - 1);
  background: rgba(0,0,0,0.42); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none; transition: opacity var(--t-med);
}
body.rail-open .rail-backdrop { opacity: 1; pointer-events: auto; }

/* ===============================================================
   RESPONSIVE
=============================================================== */
/* ---- TABLET (901–1100px): rail collapses to an icon strip in-grid ---- */
@media (max-width: 1100px) and (min-width: 901px) {
  .app { grid-template-columns: var(--rail-w-collapsed) minmax(0, 1fr); }
  .rail .brand-lockup, .rail-label, .rail-item .badge,
  .rail-group-label, .user-meta, .user-gear, .rail-collapse { display: none; }
  .rail-item { justify-content: center; padding: 0; }
  .user-chip { justify-content: center; }
  .rail-group { align-items: center; }
  .global-search { max-width: none; }
}

/* ---- MOBILE (≤900px): rail becomes a TRUE off-canvas drawer ----
   The grid collapses to a single content column so the rail can never
   overlay content. The rail is taken out of the grid, fixed at full
   width, and translated off-screen until body.rail-open. */
@media (max-width: 900px) {
  /* drop .app's own stacking context so the fixed rail can sit above the
     body-level backdrop (otherwise the rail is trapped at .app's z-index:1
     and the backdrop, a body child, paints over it). */
  .app { z-index: auto; }
  .app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "topbar"
      "content";
  }

  .rail {
    position: fixed; top: 0; bottom: 0; left: 0;
    width: min(86vw, var(--rail-w));
    transform: translateX(-100%);
    transition: transform var(--t-med) var(--ease-out);
    box-shadow: var(--shadow-xl);
    z-index: var(--z-modal);   /* above the backdrop (z-rail) at body level */
  }
  body.rail-open .rail { transform: none; }

  /* the drawer always shows the FULL labelled rail (never the icon strip) */
  body.rail-collapsed .rail .brand-lockup, body.rail-collapsed .rail-label,
  body.rail-collapsed .rail-item .badge, body.rail-collapsed .rail-group-label,
  body.rail-collapsed .user-meta, body.rail-collapsed .user-gear { display: flex; }
  .rail-collapse { display: none; }

  .topbar .rail-toggle-m { display: inline-flex; }
  .global-search { max-width: none; }

  /* content is full-width; no left offset, no overlap */
  .content { grid-area: content; }

  /* Tap targets ≥ 40px for in-body / sheet controls on mobile (CONTEXT
     mandate). Mobile-only (whole block is @media ≤900px) — desktop density is
     untouched. min-height overrides the fixed heights on .btn (36) / .btn-sm
     (30) / segmented + sub-tab buttons (~27-30) without changing their width,
     so no toolbar re-wraps. The rail hamburger (.rail-toggle-m, a
     btn-icon.btn-sm 30×30) is squared up to 40×40. */
  .btn,
  .btn-sm,
  .segmented button,
  .subtabs button { min-height: 40px; }
  .topbar .rail-toggle-m { min-width: 40px; min-height: 40px; }
}

/* ---- PHONE (≤760px): compress topbar + content padding ---- */
@media (max-width: 760px) {
  #view { padding: var(--sp-5) var(--sp-4) var(--sp-9); }
  .view-head h1 { font-size: 1.9rem; }
  .topbar { gap: var(--sp-2); padding: 0 var(--sp-3); }
  .global-search .gs-placeholder { display: none; }
  .global-search { flex: 0 0 auto; width: 38px; height: 38px; padding: 0; justify-content: center; margin: 0 auto 0 0; }
  .gs-kbd { display: none; }
  .breadcrumb .crumb:not(.crumb-root), .breadcrumb .crumb-sep { display: none; }

  /* tap targets ≥ 40px */
  .topbar-right .btn-icon, .rail-toggle-m, .global-search { min-width: 40px; min-height: 40px; }
  .rail-item { height: 44px; }
}
