/* SmartRoster app — warm-stone palette, sidebar shell, dashboard widgets.
   Palette matches marketing/style.css for visual continuity; authored
   independently so the two sites can diverge without a shared dep. */

/* tokens.css and motion.css are loaded via <link> in head.tsx (parallel
 * fetch, no waterfall). Don't @import them here.
 *
 * tokens.css owns the brand palette, accents, role colours, type, spacing,
 * and shape. The block below is app-only — currently just the six-shade
 * hash palette for staff with no assigned role. Keep new app-only tokens
 * here; new brand tokens belong in tokens.css. */

:root {
  --hash-1: oklch(0.62 0.10 30);
  --hash-2: oklch(0.62 0.10 90);
  --hash-3: oklch(0.62 0.10 150);
  --hash-4: oklch(0.58 0.10 210);
  --hash-5: oklch(0.58 0.10 280);
  --hash-6: oklch(0.58 0.10 340);
}

/* ── Reset ──
 * Wrapped in @layer base so cascade order works correctly on pages that
 * also load packages/ui-react's Tailwind v4 output (`globals-*.css`).
 *
 * Why this matters: an un-layered universal selector has higher cascade
 * priority than ANY layered rule, including @layer utilities. Tailwind
 * v4 emits all its utility classes inside @layer utilities, so an
 * un-layered `* { padding: 0 }` silently zeroed every Tailwind padding
 * + margin utility on every SR page (`.px-2.5`, `.py-1`, `.gap-3`,
 * `.mx-auto`, etc.) — buttons collapsed to text-width, the Drafts
 * top-bar action row clipped "✨ Talk to Roz" to "Talk to Ro", and the
 * gap between elements vanished. Putting this in @layer base means
 * Tailwind's @layer utilities continue to override it as designed,
 * while SR's specific class selectors (which are intentionally
 * un-layered) keep their precedence over Tailwind utilities. */
@layer base {
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
}
/* HTML's [hidden] is display:none by default, but class rules with
   display: inline-flex/block override it. Keep hidden honoured always. */
[hidden] { display: none !important; }
html, body { font-family: var(--font); color: var(--text); background: var(--bg); -webkit-font-smoothing: antialiased; }
/* 100dvh (dynamic viewport height), not 100vh (the LARGE viewport on iOS
   Safari) — matches the idiom already used at .sr-assistant-island below. */
body { font-size: 15px; line-height: 1.55; min-height: 100dvh; }
h1, h2, h3, h4 { font-family: var(--font-display); letter-spacing: -0.02em; font-weight: 700; }
a { color: inherit; text-decoration: none; }
code { font-family: var(--mono); font-size: 13px; background: var(--bg-2); padding: 1px 6px; border-radius: 3px; }
button { font-family: inherit; cursor: pointer; }
dl, dt, dd { margin: 0; }

/* ── App shell ──
   Top-bar navigation so pages get the full viewport width. The roster grid
   is the highest-value surface and was being crushed by a fixed sidebar. */
.app-layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.app-sidebar {
  position: sticky;
  top: 0;
  z-index: 20;
  /* min-height (not height) so the bar grows to contain the nav when its
     links wrap to a second row at narrower desktop widths. With a fixed
     height the wrapped tabs (Plans/Settings) overflowed and overlapped the
     page content below. */
  min-height: var(--topbar-h);
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding: 0 clamp(1rem, 2vw, 1.5rem);
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
}

.app-logo {
  padding: 0 12px 0 0;
  display: inline-flex;
  align-items: center;
}
.app-logo img { display: block; height: 28px; width: auto; }

.app-nav {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 2px;
  flex: 1 1 auto;
}

.app-nav-link {
  display: block;
  padding: 6px 12px;
  font-size: 14px;
  color: var(--text-2);
  border-radius: var(--r-sm);
  font-weight: 500;
  transition: background 0.12s, color 0.12s;
}
.app-nav-link:hover { background: var(--bg-3); color: var(--text); }
.app-nav-link.is-active { background: var(--bg); color: var(--text); font-weight: 600; box-shadow: 0 0 0 1px var(--border); }

.app-nav-link-ai { display: inline-flex; align-items: center; gap: 8px; }
.nav-ai-pill {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 999px;
  background: var(--indigo-dim);
  color: var(--indigo);
}

/* Approvals nav-item pending-request count. Hidden by default (server-
   rendered); session-hydrate.js shows it after /api/me resolves a non-zero
   count, and hides it again at zero — a manager must never see a "0" badge. */
.app-nav-badge {
  display: inline-block;
  margin-left: 6px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--amber-dim);
  color: var(--amber);
}

.app-sidebar-footer {
  margin-left: auto;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

.user-tag { font-size: 12px; color: var(--text-2); word-break: break-all; min-height: 16px; }

.app-main {
  padding: clamp(1.25rem, 2.5vw, 2rem) clamp(1rem, 2.5vw, 2rem);
  width: 100%;
  max-width: 1280px;
  /* On viewports wider than max-width, centre the content column. Without
     this, the page hugged the left edge with a large blank gutter on the
     right at any width > 1280px (Sites, Staff, Rules, etc.). */
  margin-inline: auto;
}
/* Drafts detail uses the roster grid — let it breathe across the viewport. */
.app-main[data-page-context="drafts"] { max-width: none; }

/* Assistant page is a full-bleed React island (four zones own their own
   chrome) — drop the centred 1280px column + padding so it fills the area,
   matching the old chat-layout's full-height behaviour.
   `flex: 1 1 auto` + `min-height: 0` (not a hardcoded `calc(100vh - 4rem)`)
   makes it take exactly the space .app-sidebar doesn't use — immune to the
   topbar growing taller than --topbar-h when its nav wraps to a second row
   at narrower widths (a fixed calc under-subtracted that case, so the
   island overflowed past the sidebar's real height and the window grew a
   scrollbar with dead whitespace below the app — 2026-07-11 prod incident). */
.app-main[data-page-context="assistant"] {
  max-width: none;
  padding: 0;
  flex: 1 1 auto;
  min-height: 0;
}
/* Base fallback: a computed height (not the flex-sized 100% below) so the
   island — and therefore .app-main/.app-layout, which size to its content
   here — has a real height even when the viewport-lock media query below
   doesn't apply. Without this, .app-layout (flex column, height: auto) has
   nothing to size against and the island collapses toward 0. Replaces the
   pre-B6 `height: calc(100vh - 4rem)` rule (a magic-number stand-in for the
   topbar) with the actual --topbar-h token. */
.sr-assistant-island { display: block; height: calc(100dvh - var(--topbar-h)); overflow: hidden; }

/* Lock the shell to the viewport ONLY on this page — the assistant island
   manages its own internal scrolling (rail / chat / activity log each carry
   their own overflow-y-auto), so the outer document must never scroll.
   Scoped via :has() so every other page keeps .app-layout's normal
   min-height: 100vh + natural document scroll.
   Gated to viewports >= 560px tall: below that, the locked + overflow:
   hidden shell put the composer under the fold with no way to scroll to it
   (unreachable below ~545px — 2026-07-11 prod feedback). Short windows fall
   back to the base calc() height above + normal document scroll instead —
   the flex-sized 100% override below only wins once there's room for it to
   actually help. */
@media (min-height: 560px) {
  .app-layout:has(.app-main[data-page-context="assistant"]) {
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
  }
  .sr-assistant-island { height: 100%; min-height: 480px; }
}

/* ── Page header ── */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  margin-bottom: 28px;
  flex-wrap: wrap;
}
.eyebrow {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--amber);
  background: var(--amber-dim);
  padding: 3px 8px;
  border-radius: 4px;
  margin-bottom: 10px;
}
.page-title { font-size: clamp(1.6rem, 3vw, 2rem); font-weight: 800; line-height: 1.15; }
.page-sub { color: var(--text-2); font-size: 14px; margin-top: 6px; max-width: 540px; }
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.page-back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--text-3);
  text-decoration: none;
  margin-bottom: 8px;
  transition: color 0.12s;
}
.page-back:hover { color: var(--text); }
.page-back-chevron { font-size: 14px; line-height: 1; }
.btn-ai-icon { color: var(--amber); flex-shrink: 0; }

/* Compact variant — used on the draft editor where vertical space
   is at a premium and we want findings + grid on one laptop screen.
   Inlines the title + meta on a single row and shrinks the title. */
.page-header-compact {
  margin-bottom: 12px;
  align-items: center;
  gap: 12px;
}
.page-header-compact .page-header-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 12px;
  min-width: 0;
}
.page-header-compact .page-back { margin-bottom: 0; }
.page-header-compact .page-title {
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  font-weight: 700;
}
.page-header-compact .page-sub {
  margin-top: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.btn-ai-review .review-btn-label { display: inline-flex; align-items: center; gap: 8px; }

/* ── Cards ── */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
}
.card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 20px;
}
.card-title { font-family: var(--font-display); font-size: 15px; font-weight: 700; margin-bottom: 12px; }

.kv { display: grid; grid-template-columns: 110px 1fr; gap: 6px 14px; font-size: 14px; }
.kv dt { color: var(--text-2); }
.kv dd { font-family: var(--mono); font-size: 13px; word-break: break-all; }
.kv .loading { font-family: var(--font); color: var(--text-3); font-style: italic; }
.kv .status-trialing { color: var(--amber); font-weight: 600; font-family: var(--font); }
.kv .status-active   { color: var(--sage);  font-weight: 600; font-family: var(--font); }
.kv .status-none,
.kv .status-past-due,
.kv .status-canceled { color: var(--rose);  font-weight: 600; font-family: var(--font); }

/* ── Trial status chip (item 25) — top-bar pill, hydrated by utils.js.
   Hidden until /api/me confirms a trialing workspace; escalates with days
   left. The class sets display, so re-assert display:none for [hidden]. ── */
.trial-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  padding: 5px 10px;
  border-radius: 99px;
  text-decoration: none;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: filter 0.12s;
}
.trial-chip[hidden] { display: none; }
.trial-chip:hover { filter: brightness(0.97); }
.trial-chip-led { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.trial-chip-ok    { background: var(--sage-dim);  color: var(--sage);  border-color: var(--sage); }
.trial-chip-ok    .trial-chip-led { background: var(--sage); }
.trial-chip-warn  { background: var(--amber-dim); color: var(--amber); border-color: var(--amber); }
.trial-chip-warn  .trial-chip-led { background: var(--amber); }
.trial-chip-ended { background: var(--rose-dim);  color: var(--rose);  border-color: var(--rose); }
.trial-chip-ended .trial-chip-led { background: var(--rose); }

/* ── Demo mode toggle (sidebar) + demo entity badge/outline. ──
   Demo data is seeded into every new workspace and coexists with real data;
   demo rows are dashed-outlined and badged so they're unmistakable. Turning
   the toggle off permanently deletes the demo data. */
.demo-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  padding: 5px 10px;
  border-radius: 99px;
  cursor: pointer;
  border: 1px dashed var(--amber);
  background: var(--amber-dim);
  color: var(--amber);
  white-space: nowrap;
  transition: filter 0.12s;
}
.demo-toggle[hidden] { display: none; }
.demo-toggle:hover { filter: brightness(0.97); }
.demo-toggle:disabled { opacity: 0.6; cursor: default; }
.demo-toggle-led { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--amber); }

/* Demo entity treatment — apply .sr-demo to any card/row/cell that renders a
   demo roster, staff member, site or rule. */
.sr-demo {
  position: relative;
  border: 1px dashed var(--amber) !important;
  background-image: repeating-linear-gradient(
    45deg,
    transparent,
    transparent 11px,
    var(--amber-dim) 11px,
    var(--amber-dim) 13px
  );
}
.sr-demo-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fff;
  background: var(--amber);
  border-radius: 4px;
  padding: 1px 5px;
  vertical-align: middle;
}

/* "Your roster · ready to publish" on the manager's first unpublished
 * roster (draftRowsFor in routes/htmx-drafts.tsx). */
.sr-ready-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  color: var(--sage);
  background: var(--sage-dim);
  border-radius: 4px;
  padding: 1px 6px;
  vertical-align: middle;
}

/* Manager badge on a team-member row (managers + staff share one list).
 * Slate, not indigo — indigo is reserved for Roz/AI surfaces only
 * (DESIGN.md §3); "Manager" is a neutral role fact, not an AI marker. */
.sr-role-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--slate);
  background: var(--slate-dim);
  border-radius: 4px;
  padding: 1px 5px;
  vertical-align: middle;
  margin-left: 4px;
}

/* Enrolment-state badges on a team-member row (item 14). Enrolled = the
 * person accepted their invite and can sign in (success → sage); Invited =
 * an RM sent the invite but it's still pending (neutral/cool → slate). */
.sr-enrolled-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fff;
  background: var(--sage);
  border-radius: 4px;
  padding: 1px 5px;
  vertical-align: middle;
  margin-left: 4px;
}
.sr-invited-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fff;
  background: var(--slate);
  border-radius: 4px;
  padding: 1px 5px;
  vertical-align: middle;
  margin-left: 4px;
}
/* Invite expired (never accepted, past INVITE_EXPIRY_DAYS) — muted warning,
 * not an error: clay reads "needs a resend" without alarming a manager. */
.sr-invite-expired-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fff;
  background: var(--clay);
  border-radius: 4px;
  padding: 1px 5px;
  vertical-align: middle;
  margin-left: 4px;
}
/* Pending Clerk ORG (manager) invite, live-fetched on the People pane's
 * full-page render only (see routes/pages.tsx). Slate matches
 * .sr-role-badge's Manager badge — same "manager" concept, pending state,
 * not an AI marker (DESIGN.md §3 reserves indigo for Roz/AI). */
.sr-manager-invite-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--slate);
  background: var(--slate-dim);
  border-radius: 4px;
  padding: 1px 5px;
  vertical-align: middle;
  margin-left: 4px;
}

/* ── Onboarding flow (Import → Roz → Setup). ── */
.sr-onboarding-shell {
  height: 100vh;
}
.sr-onboarding-shell > #onboarding-app-root {
  height: 100%;
}
.onboarding-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 16px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--amber);
  border-radius: 12px;
  background: var(--surface, #fff);
}
.onboarding-banner > div:first-child {
  display: flex;
  gap: 10px;
  align-items: flex-start;
}
.onboarding-step-dot {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--amber-dim);
  color: var(--amber);
  font-weight: 800;
  font-size: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 1px;
}
.onboarding-banner p {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--text-2);
}
.onboarding-banner-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

/* "Book a 30-min setup call" (components/setup-call-link.tsx, text variant). */
.setup-call {
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--text-2);
}
.setup-call a {
  color: var(--text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ── Trial banner (item 25) — top of every manager page (trial-banner.tsx),
   hydrated by session-hydrate.js. One slim row; wraps on narrow screens. ── */
.trial-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  position: relative;
  /* Room on the right for the corner close button. */
  padding: 10px 40px 10px 14px;
  border-radius: var(--r);
  margin-bottom: 20px;
  color: var(--text);
  background: var(--sage-dim);
  border: 1px solid var(--sage);
}
.trial-banner[hidden] { display: none; }
.trial-banner-spark {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--sage);
  color: #fff;
  font-weight: 700;
}
.trial-banner-copy { flex: 1 1 240px; display: flex; flex-direction: column; gap: 1px; }
.trial-banner-title { font-size: 14px; font-weight: 700; color: var(--text); }
.trial-banner-sub { font-size: 13px; color: var(--text-2); }
.trial-banner-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.trial-banner-cta {
  font-size: 13px;
  font-weight: 600;
  padding: 7px 14px;
  border-radius: var(--r-sm);
  background: var(--stone-900);
  color: var(--stone-50);
  text-decoration: none;
  white-space: nowrap;
}
.trial-banner-link {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.trial-banner-close {
  position: absolute;
  top: 6px;
  right: 6px;
  border: 0;
  background: none;
  color: var(--text-2);
  font-size: 18px;
  line-height: 1;
  padding: 4px 6px;
  cursor: pointer;
}
.trial-banner-close[hidden] { display: none; }
.trial-banner-warn { background: var(--amber-dim); border-color: var(--amber); }
.trial-banner-warn .trial-banner-spark,
.trial-banner-warn .trial-banner-cta { background: var(--amber); color: var(--stone-900); }
.trial-banner-ended { background: var(--rose-dim); border-color: var(--rose); }
.trial-banner-ended .trial-banner-spark,
.trial-banner-ended .trial-banner-cta { background: var(--rose); color: #fff; }
/* The Roz page is a full-height island locked to the viewport; a banner
   above it would push the composer off screen. The top-bar chip still
   shows the countdown there. */
.app-main[data-page-context="assistant"] > .trial-banner { display: none; }

/* ── Plans / upgrade page (item 25) — mirrors marketing pricing. ── */
.plans-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  align-items: stretch;
}
.plan-col {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 22px;
}
.plan-col-featured { border-color: var(--amber); box-shadow: 0 0 0 1px var(--amber); }
.plan-col-current,
.plan-col-picked { border-color: var(--sage); box-shadow: 0 0 0 1px var(--sage); }
.plan-col-badge,
.plan-col-tag {
  position: absolute;
  top: -10px;
  left: 22px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 3px 9px;
  border-radius: 99px;
}
.plan-col-badge { background: var(--amber); color: var(--stone-900); }
.plan-col-tag { right: 22px; left: auto; background: var(--sage); color: #fff; }
.plan-col-name { font-family: var(--font-display); font-size: 18px; font-weight: 700; }
.plan-col-price {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 8px 0 2px;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.plan-col-per { font-size: 13px; font-weight: 500; color: var(--text-2); letter-spacing: 0; font-family: var(--font); }
.plan-col-was { font-size: 18px; font-weight: 600; color: var(--text-2); text-decoration: line-through; text-decoration-thickness: 2px; }
.plan-col-promo {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--rose);
  background: var(--rose-dim);
  padding: 4px 10px;
  border-radius: 99px;
  align-self: flex-start;
  margin-top: 6px;
}
.plan-col-pitch { font-size: 13px; color: var(--text-2); margin: 10px 0 0; min-height: 2.4em; }
.plan-col-list { list-style: none; padding: 0; margin: 16px 0 20px; }
.plan-col-list li { font-size: 13px; color: var(--text); padding: 5px 0; display: flex; align-items: flex-start; gap: 8px; }
.plan-col-list li::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--sage); margin-top: 7px; flex: 0 0 auto; }
.plan-col-cta { margin-top: auto; justify-content: center; }
.plan-col-cta[aria-disabled="true"] { opacity: 0.6; pointer-events: none; }
.plan-col-custom { background: var(--stone-900); border-color: var(--stone-900); }
.plan-col-custom .plan-col-name,
.plan-col-custom .plan-col-price { color: var(--stone-50); }
.plan-col-custom .plan-col-pitch { color: var(--stone-300); }
.plan-col-custom .plan-col-list li { color: var(--stone-50); }
.plan-col-custom .plan-col-list li::before { background: var(--amber); }
.plan-col-custom .plan-col-cta { color: var(--stone-50); background: transparent; border-color: oklch(1 0 0 / 0.3); }
.plans-msg { font-size: 13px; margin-top: 16px; color: var(--text-2); min-height: 1.2em; }
.plans-msg[data-state="error"] { color: var(--rose); }
.plans-fine { font-size: 12px; color: var(--text-2); margin-top: 8px; line-height: 1.7; }

.muted { color: var(--text-3); font-size: 13px; }

/* ── Rule template groups (SR-UX-025) ──
   Collapsed <details> accordion, one per category, instead of 6 always-open
   rows of chips — same pattern as .rule-entity-group below. Header chrome
   (border, stone header, chevron) comes from `.accordion` (DESIGN.md §5,
   ── Accordions ──); this modifier only adds inter-group spacing and the
   chip-row body padding. */
.rule-template-group { margin-top: 12px; }
.rule-template-group > .btn-row { padding: 10px 12px 12px; }

/* ── Sidebar org switcher / user button ── */
.org-switcher-mount,
.user-button-mount {
  display: flex;
  align-items: center;
}
/* Compress Clerk's default min-widths so the components fit inside
   the 240px sidebar without overflowing. */
.org-switcher-mount .cl-organizationSwitcherTrigger,
.user-button-mount .cl-userButtonTrigger {
  width: 100%;
  justify-content: flex-start;
}

/* ── Publish confirm strip ── */
.draft-publish-confirm {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.draft-publish-confirm-msg { font-size: 13px; color: var(--text); }
/* htmx-indicator: htmx adds the class to the disabled element while
   the request is in flight. Pair these so a button can show two
   labels — one default, one in-flight — without JS. */
.htmx-indicator-only { display: none; }
.htmx-request .htmx-indicator-only { display: inline-flex; align-items: center; gap: 8px; }
.htmx-request .htmx-default-only { display: none; }

/* ── Settings / Staff-hub tabs (shared styling; separate behaviour hooks
      so utils.js's .settings-tabs controller never hijacks the staff nav) ── */
.settings-tabs,
.staff-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}
.settings-tab {
  background: transparent;
  border: none;
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-2);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.settings-tab:hover { color: var(--text); }
.settings-tab.is-active {
  color: var(--text);
  border-bottom-color: var(--stone-900);
}
.settings-pane[hidden] { display: none; }

/* ── Staff site assignment checkboxes ── */
.staff-site-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding: 8px 0;
}
.staff-site-check {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text);
}

/* ── Site form sections ── */
.site-section {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 16px;
  margin: 0 0 16px;
  background: #fff;
}
.site-section-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  font-weight: 700;
  padding: 0 6px;
}

/* ── Site form polish ── */
.closed-days-picker { display: flex; flex-direction: column; gap: 8px; }
.closed-days-add { display: inline-flex; gap: 8px; align-items: center; }
.closed-days-textarea { font-family: var(--mono); }
/* When a business-hours day row is checked Closed, dim and disable
   the time inputs so the user isn't asked to set times for a closed
   day (F-014). :has() handles state without needing JS. */
.bh-row:has(input[name="bhClosed"]:checked) input[type="time"] {
  opacity: 0.4;
  pointer-events: none;
  background: var(--bg-3);
}

/* ── Inline role edit ── */
.role-row-actions { display: inline-flex; gap: 4px; }
.role-row-edit { padding: 12px; }
.role-edit-form { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.role-edit-grid {
  display: grid;
  grid-template-columns: minmax(160px, 1fr) auto auto;
  gap: 12px;
  align-items: start;
}
.role-edit-actions { display: inline-flex; gap: 8px; align-self: end; }
@media (max-width: 720px) {
  .role-edit-grid { grid-template-columns: 1fr; }
  .role-edit-actions { justify-self: start; }
}

/* ── Staff filter bar ── */
.staff-filters {
  display: grid;
  grid-template-columns: minmax(180px, 2fr) minmax(140px, 1fr) minmax(140px, 1fr);
  gap: 8px;
  margin-bottom: 12px;
}
@media (max-width: 720px) {
  .staff-filters { grid-template-columns: 1fr; }
}

/* ── Color picker ── */
.cp-root { display: flex; flex-direction: column; gap: 8px; }
.cp-swatches { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cp-swatch {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
  background: transparent;
  transition: transform 0.1s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.cp-swatch:hover { transform: scale(1.06); }
.cp-swatch.is-active { border-color: var(--stone-900); box-shadow: 0 0 0 2px var(--stone-50); }
.cp-swatch-auto {
  width: auto;
  height: 28px;
  padding: 0 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  border: 1px solid var(--border-2);
  background: #fff;
}
.cp-swatch-auto.is-active { border-color: var(--stone-900); color: var(--text); }
.cp-custom { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-2); }
.cp-native { width: 32px; height: 28px; border: 1px solid var(--border-2); border-radius: 6px; padding: 0; cursor: pointer; }
.cp-legacy { color: var(--text-3); font-style: italic; }

/* ── Toasts ── */
.toast-slot {
  position: fixed;
  top: calc(var(--topbar-h) + 8px);
  right: max(16px, env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1000;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  background: var(--stone-900);
  color: var(--stone-50);
  border-radius: var(--r-sm);
  padding: 10px 14px;
  font-size: 14px;
  font-weight: 500;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  min-width: 220px;
  max-width: 360px;
  cursor: pointer;
  transition: opacity 0.2s ease, transform 0.2s ease;
  animation: toast-enter 0.18s ease both;
}
/* `.toast-slot` anchors to the LAYOUT viewport (`position: fixed` + `top`),
   which on iOS renders off-screen while the on-screen keyboard is up —
   exactly when a form submit (leave, swap) fires a toast. On coarse
   pointers, anchor to the bottom instead (below the keyboard) with safe-area
   padding, and cap `.toast`'s width so 360px + 16px doesn't overflow a
   375px-wide screen. */
@media (pointer: coarse) {
  .toast-slot {
    top: auto;
    bottom: max(16px, env(safe-area-inset-bottom));
    right: max(16px, env(safe-area-inset-right));
  }
  .toast {
    max-width: min(360px, calc(100vw - 32px));
  }
}
.toast-success { background: #1f4d3a; }
.toast-error { background: #6b2230; }
.toast-info { background: var(--stone-700); }
.toast-leave { opacity: 0; transform: translateX(8px); }
@keyframes toast-enter {
  from { opacity: 0; transform: translateX(8px); }
  to { opacity: 1; transform: translateX(0); }
}

/* ── Buttons ──
 * Canonical variants (DESIGN.md §5): primary / outline / ghost / destructive,
 * each in sm / default / lg. `btn-amber` and `btn-xs` were folded into
 * `btn-primary` / `btn-sm` (todo 104) — the whole `.btn*` rule set now
 * lives in this one section instead of being split across the file. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  padding: 9px 16px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text);
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  text-decoration: none;
}
.btn-sm { font-size: 12px; padding: 5px 10px; }
.btn-lg { font-size: 15px; padding: 12px 22px; }
.btn-primary { background: var(--stone-900); color: var(--stone-50); border-color: var(--stone-900); }
.btn-primary:hover { background: var(--stone-700); border-color: var(--stone-700); }
.btn-outline { border-color: var(--border-2); color: var(--text); background: #fff; }
.btn-outline:hover { border-color: var(--stone-500); }
.btn-ghost { color: var(--text-2); }
.btn-ghost:hover { color: var(--text); background: var(--bg-3); }
/* .btn-stop's real variant (todo 104) — stopping an in-flight AI action is
 * a destructive-toned action. Uses `filter: brightness()` on hover rather
 * than a second rose shade, matching the house pattern for darkening a
 * token color without a raw color literal (see `.trial-chip:hover`, this
 * file). */
.btn-destructive { background: var(--rose); color: var(--stone-50); border-color: var(--rose); }
.btn-destructive:hover { filter: brightness(0.92); }
.btn[aria-disabled="true"],
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* Accessibility — a visible keyboard-focus ring across interactive surfaces.
   Uses :focus-visible so it only shows for keyboard nav, not mouse clicks. */
.btn:focus-visible,
a:focus-visible,
button:focus-visible,
.settings-tab:focus-visible,
[role="tab"]:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
/* Lock the primary's hover styles so disabled state stays muted on hover. */
.btn-primary:disabled,
.btn-primary[aria-disabled="true"],
.btn-primary:disabled:hover,
.btn-primary[aria-disabled="true"]:hover {
  background: var(--stone-900);
  border-color: var(--stone-900);
}

.btn-row { display: flex; flex-wrap: wrap; gap: 8px; }

/* SR-UX-026 — the draft-editor header used to show "Publish roster" (amber
 * fill) next to "Roz Mode" styled as an animated rainbow-ring pill
 * (`.roz-mode-btn` in packages/ui-react/src/messaging/styles.css) — two
 * loud, differently-coloured CTAs competing for the same glance. Publish
 * stays the one loud amber action; this is Roz Mode's quiet replacement —
 * an outline pill in the Roz/AI indigo accent (DESIGN.md §3), no fill, no
 * animation. Self-contained here (not the shared `.roz-mode-btn` class) so
 * this page doesn't depend on the messaging package's styles for its own
 * header chrome. */
.roz-mode-btn-quiet {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 16px;
  border-radius: 999px;
  border: 1px solid var(--indigo);
  background: var(--surface);
  color: var(--indigo);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font);
  text-decoration: none;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s;
}
.roz-mode-btn-quiet:hover {
  background: var(--indigo-dim);
}
.roz-mode-btn-quiet .roz-mode-spark {
  font-size: 14px;
  line-height: 1;
}

/* ── Accordions (`<details>`/`<summary>`, DESIGN.md §5) ──────────────
 * One shared treatment for every section that expands in place: staff
 * profile sections (`.sp-detail`), rules-panel entity/template groups,
 * chat tool-use cards, past leave, and the public share page's
 * calendar-subscribe link. Quiet stone header, chevron rotates on
 * `[open]`, one border/radius/spacing rhythm — instead of each call site
 * inventing its own marker and box. Add `.accordion` alongside the call
 * site's own class (`class="accordion sp-detail"`); the modifier class
 * keeps only its body-specific padding, `.accordion` owns the shared
 * chrome (border, stone header, chevron).
 *
 * NOT for a `<details>`-as-menu (`.nav-menu`, staff.css — a different
 * interaction, keeps its own styling) or `.srg-chip-wrap` (a floating
 * popover over a roster chip, not a section that expands in place).
 * `summary:focus-visible` above (── Buttons ──) already rings every
 * `<summary>` in the app, amber, matching `.btn` — no separate focus
 * rule needed here. */
.accordion {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg);
}
.accordion > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 8px 12px;
  list-style: none;
  cursor: pointer;
  font-family: var(--font);
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: var(--bg-2);
  border-radius: var(--r-sm);
  transition: background 0.12s ease;
}
.accordion[open] > summary { border-radius: var(--r-sm) var(--r-sm) 0 0; }
.accordion > summary:hover { background: var(--bg-3); }
.accordion > summary::-webkit-details-marker { display: none; }
.accordion > summary::after {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  margin-left: auto;
  border-right: 1.5px solid var(--text-3);
  border-bottom: 1.5px solid var(--text-3);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}
.accordion[open] > summary::after { transform: rotate(45deg); }

/* ── Auth shell (signed-out) ── */
.auth-shell {
  /* 100svh (small viewport height), not 100vh (the LARGE viewport) — on an
     iPhone SE-class screen, 100vh reserves space under Safari's collapsible
     chrome that isn't actually there yet, pushing the centred sign-in card's
     heading above the fold. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  gap: 24px;
}
.auth-logo {
  display: inline-flex;
  align-items: center;
}
.auth-logo img { display: block; height: 36px; width: auto; }
.auth-card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 32px;
  width: 100%;
  max-width: 500px;
  text-align: center;
}
.auth-heading { font-size: 1.5rem; margin-bottom: 6px; }
.auth-sub { color: var(--text-2); font-size: 14px; margin-bottom: 22px; }
.clerk-mount {
  min-height: 360px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  width: 100%;
}
/* Clerk OVERWRITES this element's class= when it hydrates: our
   `class="clerk-mount"` is replaced by `cl-rootBox cl-signIn-root …`, so the
   `.clerk-mount` rule above only styles the pre-hydrate placeholder. Clerk
   leaves id= alone, so every rule for the live widget anchors to the ID.
   Two faults, both measured on prod:
   - Desktop: a display:block 400px box sat flush-left in a 436px content
     area (card centre 725, widget centre 708). Auto margins fix it.
   - Phones: Clerk sizes .cl-cardBox to min(25rem, 100vw - 40px) — 335px at a
     375px viewport — but .auth-card only has vw - 106 = 269px inside. An
     over-wide box gets 0 auto margins, so the widget ran 33px past the
     card's right edge and the page scrolled sideways. Capping the card box
     at the mount's width keeps it inside the card at every viewport.
   Applies to all three shells (auth, welcome, staff) — they share this id. */
#clerk-mount { width: 100%; max-width: 100%; margin-left: auto; margin-right: auto; }
#clerk-mount .cl-cardBox { max-width: 100%; margin-left: auto; margin-right: auto; }
/* Clerk's checkout drawer ("Choose a plan" → card form) has no z-index of its
   own, so the new look's sticky top bar (z 30) hid its title and close
   button, and the side rail (z 26) hid its prices and card logos (measured
   on prod 2026-10-24). Match Clerk's own modal backdrop (z 10000). */
.cl-drawerRoot { z-index: 10000; }
@media (max-width: 540px) {
  /* Less card padding on phones so the Clerk card is not squeezed. */
  .auth-card { padding: 24px 16px; }
}
.clerk-recovery {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px 16px;
  text-align: center;
}
.clerk-recovery-msg { color: var(--text); font-size: 14px; line-height: 1.45; margin: 0; }
.clerk-recovery .btn { align-self: center; }
.clerk-recovery-meta { color: var(--text-2); font-size: 12px; margin: 0; }
.auth-muted { color: var(--text-2); font-size: 13px; }
.auth-link { color: var(--amber); font-weight: 600; }
.auth-link:hover { text-decoration: underline; }

/* ── Provisioning ("Setting up your workspace…") ── */
.provisioning-card { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.provisioning-spinner {
  width: 40px;
  height: 40px;
  margin-bottom: 18px;
  border-radius: 50%;
  border: 3px solid var(--border);
  border-top-color: var(--amber);
  animation: provisioning-spin 0.8s linear infinite;
}
.provisioning-sub { margin-bottom: 0; }
.provisioning-timeout .btn { align-self: center; margin: 4px 0 14px; }

/* "Upgrade your free trial" (trial-offer-shell.tsx): two option cards, the
   card option holding a plan picker. Left-aligned inside the centred card. */
.trial-offer { text-align: left; }
.trial-offer .auth-heading, .trial-offer > .auth-sub { text-align: left; }
.trial-option {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 14px 16px;
  margin: 0 0 12px;
  min-width: 0;
}
/* Selected option: its border takes the option's chip hue (card = sage,
   no card = clay), so both read as real choices. */
.trial-offer[data-choice="card"] [data-trial-option="card"] { border-color: var(--sage); }
.trial-offer[data-choice="nocard"] [data-trial-option="nocard"] { border-color: var(--clay); }
/* The no-card box is tinted clay even when not selected, so it reads as a
   real option at a glance, not a greyed-out fallback. Its note uses the
   chip's darkened clay (AA on the tint). */
[data-trial-option="nocard"] {
  background: color-mix(in oklab, var(--clay) 7%, var(--surface));
  border-color: color-mix(in oklab, var(--clay) 40%, var(--surface));
}
[data-trial-option="nocard"] .trial-option-note {
  color: color-mix(in oklab, var(--clay) 75%, var(--stone-900));
}
.trial-option-head { display: flex; align-items: center; gap: 10px; cursor: pointer; }
.trial-option-head input { order: 3; margin: 0; accent-color: var(--stone-900); }
.trial-option-name { flex: 1; font-weight: 600; }
.trial-option-tag { color: var(--text-2); font-weight: 400; font-size: 13px; }
.trial-days-chip {
  font-size: 12px; padding: 2px 8px; border-radius: 999px;
  background: var(--bg-2); color: var(--text-2);
}
/* Chip text is the hue darkened toward --stone-900 in oklab (hue kept) so
   12px text clears WCAG AA on the 16% tint: sage 5.2:1, clay 5.2:1. The
   card chip (sage) stays the recommended pick; the no-card chip (clay,
   the warm secondary accent) is a valid choice, not a greyed-out one. */
.trial-days-chip.live {
  background: color-mix(in oklab, var(--sage) 16%, var(--surface));
  color: color-mix(in oklab, var(--sage) 70%, var(--stone-900));
}
.trial-days-chip.nocard {
  background: color-mix(in oklab, var(--clay) 16%, var(--surface));
  color: color-mix(in oklab, var(--clay) 75%, var(--stone-900));
}
.trial-option-note { color: var(--text-2); font-size: 13px; margin: 8px 0 0; }
.trial-plans { display: grid; gap: 8px; margin-top: 12px; }
.trial-offer[data-choice="nocard"] .trial-plans,
.trial-offer[data-choice="nocard"] [data-trial-option="card"] .trial-option-note { display: none; }
.trial-plan {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-2); border: 1px solid transparent; border-radius: 8px;
  padding: 10px 12px; cursor: pointer;
}
.trial-plan:has(input:checked) { border-color: var(--stone-500); background: var(--surface); }
.trial-plan input { margin: 0; accent-color: var(--stone-900); }
.trial-plan-text { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.trial-plan-limits { color: var(--text-2); font-size: 12px; }
.trial-plan-price { font-size: 13px; color: var(--text-2); white-space: nowrap; }
.trial-plan-price b { color: var(--stone-900); font-size: 15px; }
.trial-offer-continue { width: 100%; margin-top: 8px; }
.trial-offer #plans-msg { min-height: 1em; margin: 8px 0 0; }
.trial-offer #plans-msg[data-state="error"] { color: var(--rose); }
.trial-offer-fine { margin-top: 6px; }
.trial-offer .setup-call { color: var(--text-2); font-size: 13px; text-align: center; margin-top: 10px; }
@keyframes provisioning-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  /* Honour the user's request: stop the rotation, leave a static ring. */
  .provisioning-spinner { animation: none; }
}

/* ── Responsive ── */
@media (max-width: 820px) {
  .app-sidebar { height: auto; flex-wrap: wrap; padding: 8px 12px; gap: 8px; }
  .app-nav-link { padding: 4px 10px; font-size: 13px; }
  .user-tag { display: none; }
}

/* ── Utility states for htmx swaps ── */
.htmx-indicator { opacity: 0; transition: opacity 0.15s; display: inline-flex; align-items: center; gap: 8px; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* ── Data lists (sites, staff, rules…) ── */
.card-list { padding: 0; overflow: hidden; }
.data-list {
  list-style: none;
  display: flex;
  flex-direction: column;
}
.data-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
}
.data-row:last-child { border-bottom: none; }
/* An email or push link (`/app/approvals#req-<id>`, `#leave-<id>`) lands on
   one row. Mark it so the manager sees which request the link is about. */
.data-row:target {
  background: var(--amber-dim);
  box-shadow: inset 3px 0 0 var(--amber);
  scroll-margin-top: 80px;
}
.data-row-main { flex: 1; min-width: 0; }
.data-row-title { font-weight: 600; font-size: 14px; margin-bottom: 3px; }
.data-row-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  font-size: 12px;
  color: var(--text-2);
}
.data-row-meta span::before { content: "· "; color: var(--text-3); }
.data-row-meta span:first-child::before { content: ""; }
.data-row-actions { display: flex; gap: 4px; flex-shrink: 0; }
.btn-ghost.danger { color: var(--rose); }
.btn-ghost.danger:hover { background: var(--rose-dim); color: var(--rose); }

.empty-state { padding: 40px 24px; text-align: center; color: var(--text-2); }
.empty-state h2 { font-size: 1.05rem; margin-bottom: 6px; color: var(--text); }
.empty-state p { font-size: 14px; }

/* ── Forms ── */
.data-form {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 16px;
}
/* Settings fields' wrapper: disabled (view-only) for a Limited user. It adds
   no box, so the form's flex gap still spaces the rows. */
.settings-fieldset {
  display: contents;
  /* Engines that ignore display: contents on a fieldset still draw no
     box: no border, padding or margin, and no min-content floor. */
  border: 0;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}
.form-row { display: flex; flex-direction: column; gap: 8px; }
.form-row-split { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  display: flex;
  gap: 8px;
  align-items: baseline;
}
.form-hint { font-weight: 400; color: var(--text-2); font-size: 12px; }
.form-input {
  font-family: var(--font);
  font-size: 14px;
  padding: 9px 12px;
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--text);
  width: 100%;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.form-input:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: 0 0 0 3px var(--amber-dim);
}
textarea.form-input { resize: vertical; min-height: 72px; line-height: 1.5; }
.form-input-mono { font-family: var(--mono); font-size: 12px; }
/* Styled native <select> (DESIGN.md §5 — allowed in Hono only, until each
 * call site migrates to Radix `Select`). Same shape as `.form-input` so
 * this is a rename with zero visual change, not a restyle — todo 105. */
.form-select {
  font-family: var(--font);
  font-size: 14px;
  padding: 9px 12px;
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  width: 100%;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.form-select:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: 0 0 0 3px var(--amber-dim);
}
.site-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 6px;
  vertical-align: middle;
  border: 1px solid rgba(0, 0, 0, 0.08);
}
.form-actions { display: flex; gap: 8px; margin-top: 4px; }

/* AI rules editor — used in workspace settings (P3). Each row is
   text input + on/off select + remove button on one line; collapses
   to stacked rows on narrow screens. */
.ai-rules-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.ai-rule-row { display: grid; grid-template-columns: 1fr auto auto; gap: 8px; align-items: center; }
.ai-rule-row .ai-rule-enabled { width: auto; min-width: 70px; }

@media (max-width: 640px) {
  .form-row-split { grid-template-columns: 1fr; }
  .data-row { flex-direction: column; align-items: flex-start; }
  .data-row-actions { align-self: flex-end; }
  .ai-rule-row { grid-template-columns: 1fr; }
  .ai-rule-row .ai-rule-enabled { width: 100%; }
}

/* ── Alerts ── */
.alert {
  padding: 14px 16px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  font-size: 14px;
  line-height: 1.5;
}
.alert-title { font-weight: 700; margin-bottom: 4px; }
.alert-body { margin-bottom: 10px; }
/* Trial ended / payment failed / no plan (components/billing-notice.tsx). */
.billing-notice { margin-bottom: 16px; }
.billing-notice .alert-body { margin-bottom: 0; }
/* On the import and set-up pages: the width of the page's own column. */
.billing-notice-narrow { max-width: 720px; margin: 16px auto; }
.alert-warn {
  background: var(--amber-dim);
  border-color: var(--amber);
  color: var(--stone-900);
}
.alert-error {
  background: var(--rose-dim);
  border-color: var(--rose);
  color: var(--stone-900);
}
.alert-info {
  background: var(--sage-dim);
  border-color: var(--sage);
  color: var(--stone-900);
}

/* ── Chips (status, severity) ── */
.chip {
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 4px;
  background: var(--bg-2);
  color: var(--text-2);
  font-weight: 600;
  margin-left: 8px;
  vertical-align: middle;
}
/* Status chips carry plane identity (see roster planes.ts): draft/validated →
 * Draft plane (amber, active); published → Live Roster plane (sage, settled).
 * The label text distinguishes draft vs validated within the one Draft plane. */
.chip-draft     { background: var(--plane-draft-dim); color: var(--plane-draft); }
.chip-validated { background: var(--sage-dim); color: var(--sage); }
.chip-published { background: var(--plane-live-dim);  color: var(--plane-live); }
/* Live edit = legacy temporary edit on top of the published roster. Treated as
 * a Draft-plane edit-in-progress: amber, outlined + dashed so it's unmistakable
 * from a plain draft. */
.chip-live_edit { background: transparent; color: var(--plane-draft); border: 1px dashed var(--plane-draft); }
.chip-archived  { background: var(--bg-3); color: var(--text-3); }
/* Part C1 — drafts list "N unpublished" chip on a published row. Standalone
 * (not .chip-severity-scoped) since the drafts list has no severity axis. */
.chip-warning   { background: var(--amber-dim); color: var(--amber); }
.chip-pending    { background: var(--bg-3); color: var(--text-2); }
.chip-applied    { background: var(--sage-dim); color: var(--sage); }
.chip-dismissed  { background: var(--bg-3); color: var(--text-3); }
.chip-severity   { font-family: var(--mono); }
.chip-severity.chip-low      { background: var(--sage-dim); color: var(--sage); }
.chip-severity.chip-info     { background: var(--sage-dim); color: var(--sage); }
.chip-severity.chip-medium   { background: var(--amber-dim); color: var(--amber); }
.chip-severity.chip-warning  { background: var(--amber-dim); color: var(--amber); }
.chip-severity.chip-high     { background: var(--rose-dim); color: var(--rose); }
.chip-severity.chip-critical { background: var(--rose-dim); color: var(--rose); }
.chip-kind { background: var(--amber-dim); color: var(--amber); text-transform: none; letter-spacing: 0.02em; }

/* Compact plane chip — the chat/card form of plane identity (Roz roster cards,
 * dense lists). The ambient banner below is for full roster surfaces. */
.chip-plane-live    { background: var(--plane-live-dim);    color: var(--plane-live); }
.chip-plane-draft   { background: var(--plane-draft-dim);   color: var(--plane-draft); }
.chip-plane-actuals { background: var(--plane-actuals-dim); color: var(--plane-actuals); }

/* ── Plane banner ── ambient, always-on plane identity at the top of a roster
 * surface. A manager should know which plane they're standing on without
 * hunting for a chip. One definition, used by both server JSX and the React
 * <PlaneBanner> island component. Identity = colour + glyph (shape) + label, so
 * it never relies on colour alone (a11y). Modelled on .trial-banner. */
.plane-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 14px;
  margin-bottom: 16px;
  border-radius: var(--r);
  border: 1px solid var(--plane, var(--border));
  border-left: 3px solid var(--plane, var(--border));
  background: var(--plane-dim, var(--bg-2));
  color: var(--text);
}
.plane-banner--live     { --plane: var(--plane-live);    --plane-dim: var(--plane-live-dim); }
.plane-banner--draft    { --plane: var(--plane-draft);   --plane-dim: var(--plane-draft-dim); }
.plane-banner--actuals  { --plane: var(--plane-actuals); --plane-dim: var(--plane-actuals-dim); opacity: 0.85; }
/* Archived = de-emphasised Draft history. */
.plane-banner--archived { --plane: var(--text-3); --plane-dim: var(--bg-3); opacity: 0.8; }
.plane-banner__glyph  { color: var(--plane); font-size: 14px; line-height: 1; flex: none; }
.plane-banner__label  { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; color: var(--plane); white-space: nowrap; }
.plane-banner__blurb  { color: var(--text-2); font-size: 12px; }
.plane-banner__lineage { color: var(--text-2); font-size: 12px; flex-basis: 100%; }
.plane-banner__actions { margin-left: auto; display: flex; gap: 8px; align-items: center; flex: none; }
/* Plane group on the Rosters list — bar header + its rosters. */
.plane-group { margin-bottom: 22px; }
.plane-group .plane-banner { margin-bottom: 8px; }
/* Clickable plane bar (Live Roster / Previous) — a shortcut into the canonical
 * Live Roster view. The trailing → in .plane-banner__actions nudges on hover. */
a.plane-banner--link { text-decoration: none; cursor: pointer; transition: filter 0.12s ease; }
a.plane-banner--link:hover { filter: brightness(0.97); }
a.plane-banner--link:hover .plane-banner__actions { transform: translateX(2px); transition: transform 0.12s ease; }
.plane-banner--link .plane-banner__actions { color: var(--plane); font-weight: 700; }

.data-row.is-inactive { opacity: 0.55; }
.template-card { margin-bottom: 16px; }
/* B4/PR1 — templates open the New rule form pre-filled (see
   templateDraft/GET "/new" in htmx-rules.tsx); showing "Start with a
   template" chips alongside an open create form invites re-triggering a
   template mid-edit, so hide them for as long as the create slot holds a
   form. */
[data-section="rules"]:has([id$="-create-slot"] form) .template-card { display: none; }

.data-row-link { font-weight: 600; color: var(--text); }
.data-row-link:hover { color: var(--amber); }
/* Row-title opens the same editor as the row's Edit button (htmx, no href),
 * so it can't be a real <a>. Reset the <button> chrome to read as text —
 * pairs with .data-row-link for the shared font-weight/color/hover. */
.data-row-link-btn { background: none; border: none; padding: 0; margin: 0; font: inherit; cursor: pointer; text-align: left; }

/* ── Roster grid ── site × timeslot × day cell matrix.
   Single row: [collapsible pool | main]. The staff pool is a <details>;
   closed it shrinks to a narrow rail so the grid claims the full viewport
   width. Findings live inside .rg-main directly under the grid so they're
   the first thing visible after the matrix on page load. */
.rg-layout {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 12px;
  align-items: flex-start;
}
.rg-sidebar { display: flex; flex-direction: column; gap: 12px; }
.rg-pool, .rg-template-panel { /* shared sidebar surface */ }
.rg-main { display: flex; flex-direction: column; gap: 12px; }

@media (max-width: 900px) {
  .rg-layout {
    grid-template-columns: 1fr;
  }
}

/* Staff pool -----------------------------------------------------
   Rendered as a <details>: closed = narrow rail (just the summary),
   open = 220px panel with the draggable staff chips. */
.rg-pool {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 10px;
  position: sticky;
  top: calc(var(--topbar-h) + 12px);
  max-height: calc(100vh - var(--topbar-h) - 24px);
  overflow-y: auto;
  width: 220px;
}
.rg-pool:not([open]) {
  width: auto;
  padding: 8px;
  overflow: visible;
}
.rg-pool-head {
  font-family: var(--font-display);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-2);
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  user-select: none;
}
.rg-pool-head::-webkit-details-marker { display: none; }
.rg-pool-head::before {
  content: "";
  width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s;
}
.rg-pool[open] .rg-pool-head::before { transform: rotate(90deg); }
.rg-pool:not([open]) .rg-pool-head {
  padding: 0;
  margin: 0;
  border-bottom: none;
}
.rg-pool-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.rg-pool-chip {
  display: flex; align-items: center; gap: 8px;
  /* Right padding leaves room for the absolutely-positioned
     `.rg-pool-chip-copy` button — without it the in-flow hours
     ("25/38h" / "No target") render under the icon. */
  padding: 6px 32px 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg);
  cursor: grab;
  transition: background 0.12s, border-color 0.12s;
}
.rg-pool-chip:hover { background: #fff; border-color: var(--stone-500); }
.rg-pool-chip:active { cursor: grabbing; }
.rg-pool-chip[data-dragging="1"] { opacity: 0.5; }
.rg-pool-avatar {
  width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-size: 10px; font-weight: 700;
  flex: 0 0 auto;
}
.rg-pool-meta { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.rg-pool-name { font-size: 12px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rg-pool-role { font-size: 10px; color: var(--text-2); text-transform: capitalize; }
.rg-pool-hours { font-family: var(--mono); font-size: 10px; font-weight: 600; flex: 0 0 auto; }
.rg-pool-hours-over { color: var(--rose); }
.rg-pool-hours-ok { color: var(--sage); }
.rg-pool-hours-low { color: var(--amber); }
.rg-pool-hours-neutral { color: var(--text-2); }
.rg-pool-empty { color: var(--text-2); font-size: 12px; padding: 8px; }
.rg-pool-empty a { color: var(--amber); font-weight: 600; }

/* Week nav — only rendered for dated drafts. Left / label / This Week
   / Right. Dateless templates hide this row entirely. */
.rg-weeknav {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  margin-bottom: 8px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.rg-weeknav-label {
  flex: 1;
  text-align: center;
  font-size: 13px;
  font-weight: 700;
  font-family: var(--font-display);
  letter-spacing: -0.01em;
}
.rg-weeknav-home-static {
  font-size: 12px;
  color: var(--text-3);
  padding: 5px 10px;
}

/* Row above the grid that carries the frame-days toggle + filters.
   Stack on narrow screens; align-right the filters on wide ones. */
.rg-subheader {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.rg-subheader > .rg-filter {
  margin-bottom: 0;
  flex: 1;
  min-width: 0;
}
.rg-frame-toggle {
  display: inline-flex;
  gap: 4px;
}

/* Filter toolbar above the grid (P2 chunk f) --------------------- */
.rg-filter {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 8px 12px;
  margin-bottom: 12px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 12px;
}
.rg-filter-field { display: inline-flex; align-items: center; gap: 8px; }
.rg-filter-label { color: var(--text-2); font-weight: 600; text-transform: uppercase; font-size: 10px; letter-spacing: 0.04em; }
.rg-filter-select {
  font: inherit;
  padding: 4px 8px;
  border: 1px solid var(--border-2);
  background: #fff;
  border-radius: 4px;
  min-width: 120px;
}
.rg-filter-count { margin-left: auto; color: var(--text-2); font-family: var(--mono); font-size: 11px; }
.rg-filter-clear {
  color: var(--amber);
  font-weight: 600;
  text-decoration: none;
  padding: 4px 8px;
  border-radius: 4px;
}
.rg-filter-clear:hover { background: var(--amber-dim); }
.rg-filter-legend {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bg-3);
  color: var(--text-2);
  font-size: 11px;
  font-weight: 700;
  cursor: help;
  user-select: none;
}

/* Copy-mode banner (P2 chunk g) --------------------------------- */
.rg-copy-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 12px;
  background: var(--amber-dim);
  border: 1px solid var(--amber);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--text);
}
.rg-copy-banner-body { flex: 1 1 auto; line-height: 1.4; }
.rg-copy-banner-close {
  font: inherit;
  font-weight: 600;
  padding: 4px 10px;
  border: 1px solid var(--amber);
  background: #fff;
  border-radius: 4px;
  cursor: pointer;
}
.rg-copy-banner-close:hover { background: var(--amber); color: #fff; }
.rg-shift[data-copy-source="1"] {
  outline: 2px dashed var(--amber);
  outline-offset: 2px;
}
/* React-island mount points (roster grid, published roster, history, import,
   edit-with-roz). The island hydrates client-side from a content-hashed Vite
   bundle, then React.createRoot().render() fills the div. Until that happens
   — slow network, cold cache, or a bundle that fails to load (the historical
   "blank island" failure mode) — the div would otherwise collapse to 0px and
   the page shows only the toolbar with a blank void below it, with no signal
   to the user that anything is loading.

   Reserve vertical space and paint a centred "Loading…" message while the
   mount point is still EMPTY. `:empty` stops matching the instant React
   commits its first render, so the placeholder removes itself with no JS and
   no per-component markup. */
.rg-react-island {
  display: block;
  min-height: 60vh;
}
.rg-react-island:empty {
  position: relative;
}
.rg-react-island:empty::before {
  content: "Loading…";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-2);
  font-size: 14px;
}

/* Any copy-mode value (`many` from Alt+click, `once` from popover ⎘)
   trips the cell hover hint. */
#roster-grid-root[data-copy-mode] .rg-cell:not(.rg-cell-closed):hover {
  box-shadow: inset 0 0 0 2px var(--amber);
  cursor: copy;
}
/* In one-off mode the + button is the canonical paste target — pull
   it forward visually so users notice it's the action, not a regular
   "add new shift". */
#roster-grid-root[data-copy-mode="once"] .rg-cell-add {
  opacity: 1;
  border-color: var(--amber);
  color: var(--amber);
  cursor: copy;
}

/* Popover ⎘ button — sized as an icon-only btn-sm. */
.rg-popover-copy { padding: 4px 8px; font-size: 14px; line-height: 1; }

/* Shift popover (P2 chunk h) ----------------------------------- */
.rg-popover {
  position: fixed;
  z-index: 50;
  min-width: 240px;
  max-width: 320px;
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 12px 14px;
}
.rg-popover-form { display: flex; flex-direction: column; gap: 8px; }
.rg-popover-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}
.rg-popover-title { font-weight: 600; font-size: 13px; color: var(--text-2); }
.rg-popover-close {
  font: inherit;
  font-size: 18px;
  line-height: 1;
  background: transparent;
  border: 0;
  color: var(--text-2);
  padding: 4px 8px;
  cursor: pointer;
  border-radius: 4px;
}
.rg-popover-close:hover { background: var(--bg-3); color: var(--text); }
.rg-popover-actions { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.rg-popover-spacer { flex: 1 1 auto; }

/* Grid scroll wrapper + per-site sections ------------------------ */
.rg-main { min-width: 0; }
/* The flex column on .rg-main controls vertical rhythm now — null out
   per-block margins that were tuned for a margin-driven layout. */
.rg-main > .rg-weeknav { margin-bottom: 0; }
.rg-scroll {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow-x: auto;
  overflow-y: visible;
}
.rg-empty {
  background: #fff;
  border: 1px dashed var(--border-2);
  border-radius: var(--r);
  padding: 40px;
  text-align: center;
  color: var(--text-2);
  display: flex; flex-direction: column; gap: 12px; align-items: center;
}

/* `min-width: max-content` makes the section stretch to its widest row.
   Without this the flex `.rg-site-head` only spans the visible scroll
   viewport while the grid rows push past on horizontal overflow,
   leaving the orange banner cut off mid-grid on wide rosters. */
.rg-site { border-bottom: 2px solid var(--border); min-width: max-content; }
.rg-site:last-child { border-bottom: none; }

.rg-site-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 12px;
  background: var(--rg-site-stripe, var(--amber));
  color: #fff;
  position: sticky; top: 0;
  z-index: 5;
}
.rg-site-name { font-weight: 700; font-size: 14px; }
.rg-site-stats { font-size: 11px; opacity: 0.85; font-family: var(--mono); }

/* Row = day-headers row OR timeslot row. Same grid template. */
.rg-row {
  display: grid;
  grid-template-columns: var(--rg-cols, 64px repeat(7, minmax(88px, 1fr)));
  min-height: 68px;
}
.rg-row-days {
  min-height: 36px;
  background: var(--bg-2);
  position: sticky; top: 40px; z-index: 4;
}

.rg-row-label {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  padding: 4px;
  font-size: 11px; font-weight: 700; color: var(--text-2);
  display: flex; align-items: center; justify-content: center; text-align: center;
  position: sticky; left: 0; z-index: 3;
}
.rg-day-head {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 4px 6px;
  text-align: center;
  display: flex; flex-direction: column; gap: 1px; justify-content: center;
}
.rg-day-head-short { font-size: 11px; font-weight: 700; color: var(--text); }
.rg-day-head-detail { font-size: 10px; color: var(--text-2); }

/* Cell = one (site, date, timeslot) triple ----------------------- */
.rg-cell {
  position: relative;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 4px;
  display: flex; flex-direction: column; gap: 4px;
  min-height: 0;
}
.rg-cell:hover .rg-cell-add { opacity: 1; }
.rg-cell-closed {
  background: repeating-linear-gradient(135deg, var(--bg-2) 0 6px, transparent 6px 12px);
  align-items: center; justify-content: center;
}
.rg-cell-closed-label {
  font-size: 11px; color: var(--text-2); font-style: italic;
}
.rg-cell-overbook { outline: 2px solid var(--rose); outline-offset: -2px; }
.rg-cell-drop {
  background: var(--sage-dim);
  outline: 2px dashed var(--sage);
  outline-offset: -2px;
}
.rg-shift[data-dragging="1"] { opacity: 0.4; }
.rg-pool-chip[data-dragging="1"] { opacity: 0.4; }
.rg-cell-stack { list-style: none; display: flex; flex-direction: column; gap: 3px; }
.rg-cell-form:empty { display: none; }
.rg-cell-add {
  opacity: 0;
  background: transparent;
  border: 1px dashed var(--border-2);
  color: var(--text-2);
  border-radius: var(--r-sm);
  /* Compact square that sits at the right of the footer row beside the
     target, rather than a full-width button stacked above it. */
  flex: 0 0 auto;
  width: 18px; height: 16px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  font-size: 14px; line-height: 1;
  transition: opacity 0.12s, border-color 0.12s, color 0.12s;
}
.rg-cell-add:hover { border-color: var(--amber); color: var(--amber); }
/* Single footer row — staffing target (ghost "target" link when unset,
   N/M badge when set, inline input in edit mode) on the left, the
   hover-revealed add-shift "+" on the right. Consolidated so the cell
   no longer stacks a separate full-width add button. */
.rg-cell-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 4px;
  min-height: 16px;
  margin-top: auto;
  padding-top: 2px;
}
.rg-cell-target-link {
  background: transparent;
  border: 0;
  font-family: var(--mono);
  font-size: 9px;
  color: var(--text-2);
  padding: 1px 6px;
  border-radius: 3px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s;
  /* Yield to the fixed-width add button in a cramped column rather than
     pushing it out of the cell. */
  flex: 0 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.rg-cell-target-link:hover { color: var(--amber); background: var(--amber-dim); }
.rg-cell-target-badge {
  background: var(--amber-dim);
  color: var(--amber);
  border: 1px solid transparent;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  border-radius: 999px;
  padding: 1px 7px;
  cursor: pointer;
  transition: filter 0.12s;
  flex: 0 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.rg-cell-target-badge:hover { filter: brightness(0.95); }
.rg-cell-target-badge-met { background: var(--sage-dim); color: var(--sage); }
.rg-cell-target-edit { display: inline-flex; margin: 0; }
.rg-cell-target-input {
  width: 44px;
  font-family: var(--mono); font-size: 10px; text-align: center;
  border: 1px solid var(--amber);
  background: #fff;
  color: var(--text);
  border-radius: 999px;
  padding: 1px 4px;
  outline: none;
}
.rg-cell-target-input:focus { box-shadow: 0 0 0 2px var(--amber-dim); }

/* Shift pill — compact one-liner.
   Dot (not full-edge stripe) communicates role colour. Name takes the
   bulk of the width, time clings right. Marks appear only when needed
   (full-day / overtime / issue). Target height ~22px so 4-5 cards
   fit per cell at 88px column width. */
.rg-shift {
  list-style: none;
  position: relative;
  --shift-colour: var(--stone-500);
}
/* Two-column flex: [dot] [name+time stack] [marks]. Stacking the time
   under the name gives the name the full chip width — fits "Dr Lisa
   Wong" without ellipsis at typical column widths. Marks (full-day,
   OT, issue) sit on the right and stay aligned to the name row. */
.rg-shift-btn {
  width: 100%;
  text-align: left;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 3px 6px;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 22px;
  cursor: grab;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.rg-shift-btn:hover { border-color: var(--stone-500); box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.rg-shift-btn:active { cursor: grabbing; }
.rg-shift-dot {
  display: inline-block;
  flex: 0 0 auto;
  width: 8px; height: 22px;
  border-radius: 2px;
  background: var(--shift-colour);
}
.rg-shift-body {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: 0;
}
.rg-shift-name {
  font-size: 12px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  line-height: 1.2;
}
.rg-shift-time {
  font-family: var(--mono); font-size: 10px; color: var(--text-2);
  line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rg-shift-marks {
  flex: 0 0 auto;
  display: inline-flex; gap: 2px;
  align-self: center;
}
.rg-shift-mark {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 14px; height: 14px; border-radius: 999px;
  font-size: 9px; font-weight: 700; line-height: 1;
  padding: 0 3px;
}
.rg-shift-mark-fullday { background: var(--stone-200); color: var(--stone-700); }
.rg-shift-mark-ot { background: var(--amber-dim); color: var(--amber); }
.rg-shift-mark-issue { background: var(--rose); color: #fff; box-shadow: 0 0 0 1px #fff; }
/* Lifecycle marks (shift confirmation/cancellation). */
.rg-shift-mark-offered { background: oklch(0.55 0.04 250 / 0.14); color: var(--slate); }
.rg-shift-mark-unconfirmed { background: var(--amber-dim); color: var(--amber); }
.rg-shift-mark-confirmed { background: oklch(0.62 0.08 155 / 0.14); color: var(--sage); }

/* Inline checkbox row used by the shift form + popover. */
.form-check { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.form-check input { width: 14px; height: 14px; accent-color: var(--amber); }
.shift-form-lifecycle { display: flex; flex-direction: column; gap: 8px; margin: 6px 0; }
.rg-popover-confirm { margin: 6px 0; }
.settings-subhead { font-size: 13px; font-weight: 700; color: var(--stone-700); margin: 18px 0 4px; }

/* ── Rules panel groups (B6 facelift) — group-level headings sit on the
   stone-50 page background; the actual rule rows get the standard
   .card.card-list white-card treatment (see .data-list above). Header
   chrome (border, stone header, chevron) comes from `.accordion`
   (DESIGN.md §5, ── Accordions ──); this modifier only adds inter-group
   spacing and the rule-list body padding. ── */
.rule-group { margin-bottom: 20px; }
.rule-group-title { font-size: 14px; font-weight: 700; color: var(--text); margin: 0 0 8px; }
.rules-section-block { margin-bottom: 20px; }
.rules-section-block .rule-entity-group + .rule-entity-group { margin-top: 12px; }
.rule-entity-group > .card { margin: 10px 12px 12px; }
.rule-entity-count { font-weight: 400; color: var(--text-2); font-size: 12px; }

/* Multi-staff scope picker (new-rule-form.tsx's StaffCheckboxChips) — reuses
   .ts-role-chip's checked-chip styling (site-timeslot-table.tsx), just
   without that component's 320px max-width cap since it lives in a form row
   rather than a table cell. */
.rule-staff-chip-list { display: flex; flex-wrap: wrap; gap: 4px; }

/* ── Staff profile embed — "Applied from group rules" ──
   Shared multi-staff + role rules that apply to this person but are edited
   where they live (their group section on /app/rules). Deliberately much
   denser than .data-row: one line, no actions except the View-in-Rules
   link (applied-rule-row.tsx). */
.applied-rules-block { margin-top: 16px; }
.applied-rules-sub { font-size: 12px; margin: 0 0 8px; }
.applied-rule-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
  overflow: hidden;
}
.applied-rule-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.applied-rule-row:last-child { border-bottom: none; }
.applied-rule-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.applied-rule-name { font-weight: 600; }
.applied-rule-detail { font-size: 12px; }
.chip-applied-origin { margin-left: 0; background: var(--bg-2); color: var(--text-2); }
.applied-rule-view { flex-shrink: 0; white-space: nowrap; }

/* Deep-link flash — session-hydrate.js adds .rule-row-flash to the target
   <li id="rule-{id}"> after a /app/rules#rule-<id> jump (and removes it
   once the ~2s animation is done). Keyframes rather than a transition so
   no global .data-row rule needs touching for the fade-out. */
.rule-row-flash { animation: rule-row-flash 2s ease-out; }
@keyframes rule-row-flash {
  0%, 50% { background-color: var(--amber-dim); }
  100% { background-color: transparent; }
}

.rule-playback { margin: 4px 0 0; font-size: 13px; line-height: 1.5; }
.rule-playback-label { font-weight: 700; }
/* B4/PR1 — the second, muted "structured read" line under an interpreted
   row's restatement (kind + params + scope + confidence). */
.rule-playback-meta { margin: 2px 0 0; }

/* B4/PR1 — the Ask Roz row (button + hint) sits between the scope picker
   and the preview slot in new-rule-form.tsx. */
.rule-ask-roz-row { flex-direction: row; align-items: center; gap: 10px; }
.rule-ask-roz-row .form-hint { margin: 0; }
.rule-preview-slot:empty { margin: 0; }
.rule-preview-slot:not(:empty) { margin: 4px 0; }
.rule-save-hint { margin-top: 4px; }
.rule-stale-hint { margin-top: 4px; color: var(--amber); font-weight: 600; }

/* Ask Roz interpretation preview card — mirrors .sp-roz-strip/.sp-read-line's
   chrome (staff-profile-panel.tsx) so the "Roz read this" moment feels the
   same whether it's a staff profile or a rule. */
.ric-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 12px;
}
.ric-strip { display: flex; align-items: center; gap: 10px; }
.ric-av { width: 28px; height: 28px; }
.ric-status-title { font-size: 13px; font-weight: 700; color: var(--text); }
.ric-status-sub { font-size: 12px; color: var(--text-2); }
.ric-restatement {
  margin: 0;
  font-size: 13px;
  color: var(--text);
  line-height: 1.55;
  font-style: italic;
  background: var(--bg);
  border-left: 3px solid var(--amber);
  border-radius: var(--r-sm);
  padding: 9px 12px;
}
.ric-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.ric-chips .chip { margin-left: 0; }
.ric-hint, .ric-params, .ric-scope { margin: 0; }
/* Secondary restatement line when the labeled field grid is present —
   the grid carries the QA weight; the prose is just Roz's voice. */
.ric-restatement-sub {
  margin: 0;
  font-size: 12px;
  color: var(--text-2);
  line-height: 1.5;
  font-style: italic;
}
/* Labeled QA field grid — mirrors the staff profile's .sp-stats tiles
   (uppercase micro-label + value) so "Roz decomposed this" reads the same
   on rules as it does on staff profiles. */
.ric-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 8px;
}
.ric-field {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.ric-field-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  font-weight: 700;
}
.ric-field-value { font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.4; }
.ric-field-note { font-size: 11px; font-style: italic; }
.ric-field-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.ric-field-chip {
  display: inline-flex;
  align-items: center;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}

.rg-shift-locum .rg-shift-btn { border-style: dashed; }

/* Highlight states (client JS sets data-highlight-mode) */
.rg-shift[data-highlight-mode="dimmed"] .rg-shift-btn { opacity: 0.35; }
.rg-shift[data-highlight-mode="selected"] .rg-shift-btn {
  box-shadow: 0 0 0 2px var(--amber);
  border-color: var(--amber);
}
.rg-shift[data-highlight-mode="staff"] .rg-shift-btn { box-shadow: 0 0 0 1px var(--amber); }
.rg-shift[data-highlight-mode="staff-issue"] .rg-shift-btn {
  box-shadow: 0 0 0 1px var(--rose);
}
.rg-shift[data-highlight-mode="role"] .rg-shift-btn { opacity: 0.75; }

/* Weekly stats table — mounted below the grid in the rg-main column.
   One row per staff with shifts this week, day columns match the grid. */
.rg-stats {
  margin-top: 12px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 12px 14px;
}
.rg-stats-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}
.rg-stats-title { font-size: 14px; font-weight: 700; }
.rg-stats-totals { display: flex; gap: 14px; flex-wrap: wrap; }
.rg-stats-total {
  display: inline-flex; gap: 4px; align-items: baseline;
  font-size: 11px;
}
.rg-stats-total-label { color: var(--text-2); }
.rg-stats-total-val { font-family: var(--mono); font-weight: 700; color: var(--text); }
.rg-stats-total-ot .rg-stats-total-val { color: var(--rose); }
.rg-stats-total-ot .rg-stats-total-label { color: var(--rose); }
.rg-stats-scroll { overflow-x: auto; }
.rg-stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.rg-stats-table thead th {
  text-align: center;
  font-weight: 600;
  color: var(--text-2);
  padding: 6px 6px;
  border-bottom: 1px solid var(--border);
  font-size: 11px;
}
.rg-stats-th-staff { text-align: left !important; }
.rg-stats-th-contract, .rg-stats-th-total, .rg-stats-th-cost { text-align: right !important; }
.rg-stats-th-short { display: block; font-size: 11px; font-weight: 700; color: var(--text); }
.rg-stats-th-detail { display: block; font-size: 9px; color: var(--text-3); font-weight: 500; }
.rg-stats-table tbody tr { border-bottom: 1px solid var(--border); }
.rg-stats-table tbody tr:last-child { border-bottom: none; }
.rg-stats-table tbody tr:hover { background: var(--bg); }
.rg-stats-table td { padding: 6px 6px; vertical-align: middle; }
.rg-stats-td-staff {
  display: flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
.rg-stats-dot {
  display: inline-block;
  width: 8px; height: 8px; border-radius: 999px;
  flex: 0 0 auto;
}
.rg-stats-name { font-weight: 600; color: var(--text); }
.rg-stats-td-day { text-align: center; font-family: var(--mono); font-size: 11px; color: var(--text); }
.rg-stats-td-empty { color: var(--text-3); }
.rg-stats-td-hot { color: var(--rose); font-weight: 700; }
.rg-stats-td-contract {
  text-align: right; font-family: var(--mono); font-size: 11px;
  color: var(--text-2);
}
.rg-stats-td-total {
  text-align: right; font-family: var(--mono); font-weight: 700;
  white-space: nowrap;
}
/* Over-contract row tint — rose-dim background, rose name. Pairs
   with the existing per-cell rg-stats-td-hot (cell-level) so a row
   can still show individual longDay cells in red when the staff is
   under contract overall. */
.rg-stats-row-over td { background: var(--rose-dim); }
.rg-stats-row-over .rg-stats-name { color: var(--rose); }
.rg-stats-row-over .rg-stats-td-total { color: var(--rose); }
.rg-stats-td-cost {
  text-align: right; font-family: var(--mono); font-weight: 700;
  color: var(--text);
}
.rg-stats-ot-badge {
  display: inline-block;
  margin-left: 6px;
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--rose-dim);
  color: var(--rose);
  font-weight: 700;
}

.shift-form {
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.shift-form-heading {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.shift-form .form-row { gap: 4px; }
.shift-form .form-label { font-size: 11px; }
.shift-form .form-input { font-size: 12px; padding: 6px 8px; }
.shift-form .form-actions { margin-top: 2px; }

/* ── Findings panel ──
   Lives below the roster grid and fans findings across the width as a
   card grid, not a cramped right-rail list. */
.draft-findings {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 4px;
}
.draft-findings-head { border-bottom: 1px solid var(--border); padding-bottom: 10px; }
.draft-findings-title { font-size: 16px; margin-bottom: 4px; }
.draft-findings-body { display: flex; flex-direction: column; gap: 10px; }
.findings-list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 10px;
}

.finding-row {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--bg-2);
}
.finding-row.status-applied   { opacity: 0.65; background: var(--sage-dim); }
.finding-row.status-dismissed { opacity: 0.55; background: var(--bg-3); }
.finding-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.finding-rule { font-family: var(--mono); font-size: 11px; color: var(--text-2); }
.finding-title { font-weight: 600; font-size: 13px; }
.finding-desc { font-size: 13px; color: var(--text); line-height: 1.45; }
.finding-actions { display: flex; gap: 8px; margin-top: 4px; }
.finding-affected { font-size: 12px; color: var(--text-2); margin: 2px 0 0; font-style: italic; }
.draft-findings-foot { display: flex; justify-content: flex-end; padding-top: 6px; }

/* ── Chat panels (team chat page: .chat-main, .chat-empty) ── */

.chat-main {
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  min-width: 0;
  overflow: hidden;
}

.persona-picker {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.persona-pill {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--text-2);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
}
.persona-pill:hover { background: var(--bg-3); color: var(--text); }
.persona-pill.is-active { background: var(--stone-900); color: var(--stone-50); border-color: var(--stone-900); }

.chat-empty {
  margin: auto;
  text-align: center;
  color: var(--text-2);
  max-width: 480px;
}
.chat-empty-title { font-size: 1.2rem; margin-bottom: 6px; color: var(--text); }



/* ── Staff AI profile panel (P4 gap 2) ─────────────────────────────
   Rendered under the main staff form in edit mode. The .data-row-edit
   wrapper lets the form and panel live side-by-side inside the <li>
   that swaps with StaffRow on save. */
/* Edit mode renders as ONE card: the <li> row IS the card, and the
   Details form + Roz zone are borderless zones inside it split by a
   divider. The two stay separate DOM siblings (HTML forbids nested
   <form>s and Roz's chip add/remove are their own forms) — CSS alone
   makes them read as a single surface. */
.data-row-edit {
  display: flex;
  flex-direction: column;
  /* override .data-row's align-items:center so both zones stretch full
     width instead of shrink-wrapping to their content. */
  align-items: stretch;
  gap: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: #fff;
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.data-row-edit .data-form {
  border: 0;
  border-radius: 0;
  background: transparent;
  margin-bottom: 0;
  padding: 20px;
}

.sp-panel {
  padding: 20px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Merged header card (compaction option C): notes textarea + Roz's status
   strip + its one-line "read" render as ONE visual block — the box
   chrome that .sp-roz-strip used to carry on its own now lives here, one
   level up, so there's no double border/gap between the two. */
.sp-header-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 12px;
}
/* Roz status strip — the assistant's identity + "read", sitting between
   the notes it consumes and the chip findings it produces. Amber R
   avatar (.roz-av) keeps it on-brand with the home "Meet Roz" card.
   Transparent/unboxed here — it's nested inside .sp-header-card, which
   now owns the shared background/border/padding. */
.sp-roz-strip {
  display: flex;
  align-items: center;
  gap: 11px;
}
.sp-roz-av { width: 32px; height: 32px; }
.sp-roz-status { flex: 1; min-width: 0; }
.sp-roz-status-title { font-size: 13px; font-weight: 700; color: var(--text); }
.sp-roz-status-sub { font-size: 12px; color: var(--text-2); margin-top: 1px; }
.sp-roz-status-sub.stale { color: var(--text-3); font-style: italic; }
.sp-gen-btn { white-space: nowrap; flex-shrink: 0; }
.sp-regen-label { font-weight: 600; }
/* One-line prose "read" above the chip findings. */
.sp-read-line {
  font-size: 13px;
  color: var(--text);
  line-height: 1.55;
  background: var(--bg);
  border-left: 3px solid var(--amber);
  border-radius: var(--r-sm);
  padding: 9px 12px;
  font-style: italic;
}
.sp-read-line b { font-style: normal; font-weight: 700; }

.sp-profile-form { display: flex; flex-direction: column; gap: 4px; }
.sp-profile-form textarea { min-height: 60px; }
.sp-save-flash {
  font-size: 12px;
  color: var(--sage);
  font-weight: 600;
  font-family: var(--mono);
  opacity: 1;
  transition: opacity 1.4s ease 0.5s;
}
.sp-save-flash-error { color: var(--rose); }
.sp-save-flash-slot { min-height: 14px; font-size: 11px; }

.sp-body { display: flex; flex-direction: column; gap: 16px; }

.sp-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
.sp-stat {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.sp-stat-wide { grid-column: span 2; }
.sp-stat-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  font-weight: 700;
}
.sp-stat-value { font-size: 15px; font-weight: 700; color: var(--text); }

.sp-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.sp-section:first-of-type { padding-top: 0; border-top: none; }
.sp-section-sub {
  font-size: 11px;
  color: var(--text-2);
  font-weight: 600;
  display: block;
  margin-bottom: 4px;
}
.sp-section-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
@media (max-width: 600px) {
  .sp-section-split { grid-template-columns: 1fr; }
}

/* Team dynamics + Skills & certifications: collapsed-by-default <details>
   sections that sit side-by-side ≥900px, stacked below — one level up
   from .sp-section-split's own 2-col sub-layout. */
.sp-sections-2col {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 900px) {
  .sp-sections-2col { grid-template-columns: 1fr 1fr; }
}

/* Collapsible section box shared by Team dynamics, Skills &
   certifications, and Rules for {name}. Native <details>/<summary> keeps
   per-element open/closed state; session-hydrate.js re-applies `open`
   across the panel's outerHTML swaps (chip add/remove, Generate). Header
   chrome (border, stone header, chevron) comes from `.accordion`
   (DESIGN.md §5, above) — this modifier only adds the content padding
   under the header. */
.sp-detail > *:not(summary) { padding: 0 12px 12px; }

.sp-hard-constraints {
  background: var(--amber-dim);
  padding: 10px 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--amber);
  border-top-color: var(--amber);
}

.sp-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.sp-chips-multiline {
  flex-direction: column;
  align-items: stretch;
}
.sp-chips-empty {
  font-size: 11px;
  color: var(--text-3);
  font-style: italic;
}
.sp-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 4px 2px 10px;
  font-size: 12px;
}
.sp-chips-sage .sp-chip { background: var(--sage-dim); border-color: var(--sage); color: var(--sage); }
.sp-chips-rose .sp-chip { background: var(--rose-dim); border-color: var(--rose); color: var(--rose); }
.sp-chips-amber .sp-chip { background: #fff; border-color: var(--amber); color: var(--stone-900); }
.sp-chips-multiline .sp-chip { border-radius: var(--r-sm); padding: 6px 10px; }
.sp-chip-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 180px; }
.sp-chips-multiline .sp-chip-label { white-space: normal; max-width: none; flex: 1 1 auto; }
.sp-chip-remove {
  background: transparent;
  border: none;
  color: inherit;
  opacity: 0.5;
  font-size: 14px;
  line-height: 1;
  padding: 0 4px;
  border-radius: 999px;
  cursor: pointer;
}
.sp-chip-remove:hover { opacity: 1; color: var(--rose); background: var(--bg); }
.sp-chip-add {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: transparent;
  border: 1px dashed var(--border-2);
  border-radius: 999px;
  padding: 2px 4px 2px 10px;
}
.sp-chips-multiline .sp-chip-add { border-radius: var(--r-sm); padding: 4px 6px 4px 10px; }
.sp-chip-input {
  border: none;
  background: transparent;
  font-family: var(--font);
  font-size: 12px;
  padding: 2px 2px;
  outline: none;
  width: 120px;
  min-width: 0;
}
.sp-chips-multiline .sp-chip-input { width: 100%; }
.sp-chip-add-btn {
  background: transparent;
  border: none;
  color: var(--text-2);
  font-size: 14px;
  padding: 0 6px;
  line-height: 1;
  cursor: pointer;
  border-radius: 999px;
}
/* Amber, not indigo — this is a generic "add a chip" affordance, not an AI
 * surface (DESIGN.md §3); matches .rg-cell-add:hover's amber treatment. */
.sp-chip-add-btn:hover { color: var(--amber); background: var(--amber-dim); }

/* Roster templates panel — mirrors .rg-pool's visual chrome (white card,
   sticky sidebar, narrow rail when closed) so the sidebar stack feels
   like one surface. Mounted inside .rg-sidebar next to .rg-pool. */
.rg-template-panel {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 10px;
  width: 220px;
}
.rg-template-panel:not([open]) {
  width: auto;
  padding: 8px;
}
.rg-template-head {
  font-family: var(--font-display);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-2);
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  user-select: none;
}
.rg-template-head::-webkit-details-marker { display: none; }
.rg-template-head::before {
  content: "";
  width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s;
}
.rg-template-panel[open] .rg-template-head::before { transform: rotate(90deg); }
.rg-template-panel:not([open]) .rg-template-head {
  padding: 0; margin: 0; border-bottom: none;
}
.rg-template-count { color: var(--text-3); font-weight: 500; }

.rg-template-body { display: flex; flex-direction: column; gap: 10px; font-size: 13px; }
.rg-template-save { display: flex; flex-direction: column; gap: 8px; }
.rg-template-save-row { display: flex; gap: 8px; align-items: center; }
.rg-template-save-row .form-input { flex: 1 1 auto; min-width: 0; }
.rg-template-empty { color: var(--text-3); font-size: 12px; line-height: 1.4; }
.rg-template-list { list-style: none; display: flex; flex-direction: column; gap: 8px; padding: 0; margin: 0; }
.rg-template-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg-2);
}
.rg-template-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rg-template-name { font-weight: 600; color: var(--text); word-break: break-word; }
.rg-template-detail { color: var(--text-2); font-size: 11px; word-break: break-word; }
.rg-template-actions { display: flex; gap: 8px; }

/* Workspace roles editor (P5) — section block embedded on the
   settings page, beneath the main settings form. */
.settings-roles {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}
.settings-roles-head { margin-bottom: 12px; }
.settings-roles-title {
  font-size: 16px;
  font-weight: 700;
  font-family: var(--font-display);
  margin-bottom: 4px;
}
.roles-list { list-style: none; padding: 0; margin: 12px 0; display: flex; flex-direction: column; gap: 8px; }
.role-row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 8px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.role-swatch {
  width: 22px; height: 22px;
  border-radius: 4px;
  border: 1px solid rgba(0,0,0,0.08);
}
.role-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.role-name { font-weight: 600; }
.role-slug { font-family: var(--mono); font-size: 11px; color: var(--text-2); }
.roles-add-form { margin-top: 14px; padding: 12px; background: var(--bg-2); border-radius: var(--r-sm); border: 1px dashed var(--border-2); }
.roles-add-row { display: grid; grid-template-columns: 1.4fr 1fr 1fr auto; gap: 8px; margin-top: 6px; }
@media (max-width: 720px) {
  .roles-add-row { grid-template-columns: 1fr; }
  .role-row { grid-template-columns: 28px 1fr; }
  .role-row .btn { grid-column: 1 / -1; justify-self: end; }
}

/* Settings → Members pane (bl-025). Mirrors .roles-list/.role-row — no
   swatch column, and the role <select> is width-capped so it doesn't
   stretch the whole row. */
.members-list { list-style: none; padding: 0; margin: 12px 0; display: flex; flex-direction: column; gap: 8px; }
.member-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 8px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.member-meta { display: flex; align-items: center; gap: 8px; min-width: 0; }
.member-name { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-you { margin-left: 6px; font-size: 11px; color: var(--text-2); }
.member-invited { font-size: 11px; color: var(--text-2); }
.member-role-badge { font-size: 12px; font-weight: 600; color: var(--text-2); }
.member-role-select { max-width: 150px; }
.member-row-actions { display: inline-flex; align-items: center; gap: 8px; }
.members-invites { margin-top: 8px; }
.members-invite-form { margin-top: 14px; padding: 12px; background: var(--bg-2); border-radius: var(--r-sm); border: 1px dashed var(--border-2); }
.members-invite-row { display: grid; grid-template-columns: 1.6fr 1fr auto; gap: 8px; margin-top: 6px; }
/* Invites from this pane are always Manager, so there is no role select to
   place — the email input takes the space the select used to occupy. */
.members-invite-row--no-role { grid-template-columns: 1fr auto; }
@media (max-width: 720px) {
  .members-invite-row { grid-template-columns: 1fr; }
  .member-role-select { max-width: none; }
}

/* Public holidays editor (P6) — sibling section to .settings-roles */
.settings-holidays {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}
.holidays-list { list-style: none; padding: 0; margin: 12px 0; display: flex; flex-direction: column; gap: 8px; }
.holiday-row {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 6px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.holiday-date { font-family: var(--mono); font-size: 12px; color: var(--text-2); }
.holiday-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.holiday-name { font-weight: 600; }
.holiday-source { font-size: 11px; color: var(--text-2); font-family: var(--mono); }
.holidays-add-row { display: grid; grid-template-columns: 160px 1fr auto; gap: 8px; margin-top: 6px; }
@media (max-width: 720px) {
  .holidays-add-row { grid-template-columns: 1fr; }
  .holiday-row { grid-template-columns: 1fr; }
}

/* P2b — copy whole staff week button on staff chips + dedicated
   banner + visual states for source / target chips. */
.rg-pool-chip { position: relative; }
.rg-pool-chip-copy {
  position: absolute;
  top: 4px;
  right: 4px;
  background: transparent;
  border: 1px solid var(--border-2);
  color: var(--text-3);
  font-size: 13px;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 3px;
  cursor: pointer;
  /* Always visible so users discover the "copy whole week" flow without
     having to hover-hunt — earlier opacity:0 default made the feature
     effectively invisible on first contact. */
  opacity: 0.65;
  transition: opacity 0.12s, color 0.12s, border-color 0.12s, background 0.12s;
}
.rg-pool-chip:hover .rg-pool-chip-copy,
.rg-pool-chip:focus-within .rg-pool-chip-copy { opacity: 1; }
.rg-pool-chip-copy:hover {
  color: var(--text);
  border-color: var(--border-2);
  background: var(--bg-3);
}
/* Amber, not slate (design review, 2026-08-26) — slate's chroma (0.04,
 * near-gray) read as a focus ring, not a highlight, so the copy-week
 * source chip and banner blended into the page instead of calling
 * attention to themselves. Amber is the same action accent .rg-cell-add
 * uses, still distinct from indigo (reserved for Roz/AI surfaces,
 * DESIGN.md §3) — this isn't an AI surface, it's a manager action. */
.rg-copy-banner-week {
  background: var(--amber-dim);
  border-color: var(--amber);
}
.rg-pool-chip[data-copy-week-source="1"] {
  outline: 2px solid var(--amber);
  outline-offset: -2px;
}
.rg-pool-chip[data-copy-week-target="1"] {
  cursor: copy;
}
.rg-pool-chip[data-copy-week-target="1"]:hover {
  background: var(--amber-dim);
}

/* Roles typeahead — multi-select with pills + filtering input.
   Replaces the legacy chip checkbox grid and the older comma input. */
.roles-typeahead {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.roles-typeahead-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.roles-typeahead-pills:empty { display: none; }
.roles-typeahead-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 6px;
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  font-size: 13px;
  user-select: none;
}
.roles-typeahead-pill-label { font-weight: 600; }
.roles-typeahead-pill-remove {
  background: transparent;
  border: 0;
  color: var(--text-3);
  font-size: 16px;
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  border-radius: 3px;
}
.roles-typeahead-pill-remove:hover { color: var(--rose); background: var(--bg-3); }
.roles-typeahead-input-wrap { position: relative; }
.roles-typeahead-input {
  width: 100%;
  padding: 8px 10px;
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  font-size: 14px;
  font-family: inherit;
}
.roles-typeahead-input:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: 0 0 0 2px var(--amber-dim);
}
.roles-typeahead-listbox {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 50;
  list-style: none;
  margin: 0;
  padding: 4px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: 0 4px 16px rgba(0,0,0,0.08);
  max-height: 240px;
  overflow-y: auto;
}
.roles-typeahead-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}
.roles-typeahead-option:hover,
.roles-typeahead-option.is-active {
  background: var(--bg-3);
}

/* Staff typeahead — single-select cousin of roles-typeahead. The
   wrapper holds the (now-hidden) source-of-truth <select> + the
   filtering text input + popover listbox. Visuals mirror the form
   inputs so the field reads as a normal control. */
.staff-typeahead { position: relative; }
.staff-typeahead-select-hydrated {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}
.staff-typeahead-input-wrap { position: relative; }
.staff-typeahead-input { width: 100%; }
.staff-typeahead-listbox {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 50;
  list-style: none;
  margin: 0;
  padding: 4px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: 0 4px 16px rgba(0,0,0,0.08);
  max-height: 240px;
  overflow-y: auto;
}
.staff-typeahead-option {
  padding: 6px 8px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}
.staff-typeahead-option:hover,
.staff-typeahead-option.is-active {
  background: var(--bg-3);
}
.roles-typeahead-empty {
  padding: 12px;
  background: var(--bg-2);
  border: 1px dashed var(--border-2);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--text-2);
}
.roles-typeahead-empty a { color: var(--amber); font-weight: 600; }
.roles-typeahead-empty a:hover { text-decoration: underline; }

/* A4: informational, non-blocking heads-up when two timeslots on a site
 * share an identical start+end window — the only case Roz (and a manager
 * scanning the grid) can't tell apart by time alone. */
.ts-duplicate-warning {
  margin: 4px 0 8px;
  padding: 6px 10px;
  border: 1px dashed var(--amber);
  background: var(--amber-dim);
  border-radius: var(--r-sm);
  color: var(--amber);
}

/* P4: structured timeslot table on the site form. Layout mimics
 * data-form's spacing; the role-chip column re-uses the staff-form
 * chip pattern. */
.ts-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  margin-bottom: 8px;
}
.ts-table thead th {
  text-align: left;
  font-weight: 600;
  color: var(--text-2);
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}
.ts-table .ts-table-actions {
  width: 32px;
}
.ts-table tbody td {
  padding: 6px 6px;
  vertical-align: top;
  border-bottom: 1px solid var(--border-2);
}
.ts-row .form-input-sm {
  font-size: 13px;
  padding: 6px 8px;
}
.ts-th-handle { width: 32px; }
.ts-row-handle {
  width: 32px;
  cursor: grab;
  text-align: center;
  user-select: none;
  color: var(--text-3);
}
.ts-row-handle:active { cursor: grabbing; }
.ts-row-handle-grip {
  display: inline-block;
  font-weight: 700;
  letter-spacing: -2px;
  font-size: 16px;
  line-height: 1;
}
.ts-row-handle:hover { color: var(--text); }
.ts-row-dragging { opacity: 0.55; }
.ts-row-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 320px;
}
.ts-role-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px 2px 4px;
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  font-size: 12px;
  cursor: pointer;
  user-select: none;
  transition: background 0.12s, border-color 0.12s;
}
.ts-role-chip:hover { background: var(--bg-3); }
.ts-role-chip:has(input:checked) {
  background: var(--sage-dim);
  border-color: var(--sage);
  font-weight: 600;
}
.ts-role-chip input { margin: 0; }
.ts-table-footer {
  margin-top: 4px;
}

/* ── Role-scoped drafts ──
 * The create-draft role picker (draft-role-picker.tsx) reuses .ts-role-chip;
 * its preview (draft-scope-preview.tsx); the role chips on the Rosters list
 * (draft-role-chips.tsx, both looks); and the slot editor's weekday chips
 * and "any role" warning (site-timeslot-row-extras.tsx). */
.draft-roles-field { border: 0; padding: 0; min-width: 0; }
.draft-role-picker { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.ts-role-chip.is-disabled,
.ts-role-chip.is-disabled:hover {
  cursor: not-allowed;
  background: var(--bg-2);
  color: var(--text-2);
}
.role-swatch-sm { width: 12px; height: 12px; border-radius: 4px; }
.draft-scope-preview { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.draft-scope-preview p { margin: 0; }
.draft-scope-warn,
.ts-any-role-warning {
  padding: 4px 8px;
  border-left: 3px solid var(--amber);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--amber-dim);
  color: var(--text);
}
.ts-any-role-warning { margin: 4px 0 0; max-width: 320px; }
.draft-roles {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-left: 8px;
  font-size: var(--fs-meta);
  font-weight: 400;
  color: var(--text-2);
  vertical-align: middle;
}
.ldr-roles .draft-roles { margin-left: 0; }
.draft-role { display: inline-flex; align-items: center; gap: 4px; }
.draft-role-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.ts-day-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 240px;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* P4: visually mark a "single" cell that's already filled — the +
 * button is hidden in this state, so a soft dashed outline tells the
 * user the slot is fully assigned. Pairs with .rg-cell .rg-cell-stack. */
.rg-cell-single-cap .rg-cell-add { display: none; }
.rg-cell-single-cap { background-image: linear-gradient(to bottom, var(--bg-2), transparent 30%); }

/* Screen-reader-only utility used in the timeslot table actions header. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* P6b: Mon-Sun business hours editor in site-form. Same visual
 * vocabulary as the timeslot table — small inputs, soft grid lines —
 * scoped to .bh-table so it doesn't compete with .ts-table. */
.bh-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  margin-bottom: 8px;
}
.bh-table thead th {
  text-align: left;
  font-weight: 600;
  color: var(--text-2);
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}
.bh-table tbody th,
.bh-table tbody td {
  padding: 6px 8px;
  vertical-align: middle;
  border-bottom: 1px solid var(--border-2);
}
.bh-day-label {
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  width: 110px;
}
.bh-closed-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
}
.bh-closed-toggle input { margin: 0; }
.bh-row:has(input[name="bhClosed"]:checked) input[name="bhOpen"],
.bh-row:has(input[name="bhClosed"]:checked) input[name="bhClose"] {
  opacity: 0.5;
}

/* ── Comms channels (item 5a, single-shared-bot pilot) ────────────── */
.comms-channel-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e5e1d8);
  border-radius: 12px;
  margin-bottom: 12px;
}
.comms-channel-card-greyed { opacity: 0.7; background: var(--bg-2, #f5f3ee); }
.comms-channel-icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex-shrink: 0;
  background: var(--bg-2, #f5f3ee);
}
.comms-channel-icon-telegram { background: var(--channel-telegram); color: #fff; }
.comms-channel-icon-whatsapp { background: var(--channel-whatsapp); color: #fff; }
.comms-channel-icon-imessage { background: var(--channel-imessage); color: #fff; }
.comms-channel-icon-msteams  { background: var(--channel-msteams);  color: #fff; }
.comms-channel-meta { flex: 1; min-width: 0; }
.comms-channel-name {
  font-weight: 600;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.comms-channel-sub { font-size: 12px; color: var(--text-2, #6b6358); line-height: 1.5; }
.comms-channel-actions { display: flex; gap: 8px; }

.comms-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  font-weight: 500;
  padding: 3px 8px;
  border-radius: 20px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.comms-badge::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}
.comms-badge-ok { background: var(--sage-dim); color: var(--sage); }
.comms-badge-warn { background: var(--amber-dim); color: var(--amber); }
.comms-badge-muted { background: var(--bg-2, #f5f3ee); color: var(--text-2, #6b6358); }

.settings-comms-disclosure { margin: 12px 0 20px; }
.settings-comms-disclosure code { font-family: var(--font-mono, monospace); }
.settings-comms-actions { margin: 16px 0; }
.settings-comms-greyed { display: flex; flex-direction: column; gap: 8px; }
.settings-comms-footnote { font-size: 11px; color: var(--text-3, #8a8378); margin-top: 8px; }
.settings-divider { border: 0; border-top: 1px solid var(--border, #e5e1d8); margin: 24px 0; }

.comms-linked-staff { margin: 12px 0 20px; }
.comms-linked-staff .data-table,
.deputy-push-summary .data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.comms-linked-staff .data-table th,
.comms-linked-staff .data-table td,
.deputy-push-summary .data-table th,
.deputy-push-summary .data-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border, #e5e1d8);
}
.comms-linked-staff .data-table th,
.deputy-push-summary .data-table th {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-2, #6b6358);
}
.comms-platform-pill {
  display: inline-block;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--bg-2, #f5f3ee);
  color: var(--text-2, #6b6358);
  margin-right: 6px;
}

/* ── Shared modal class family (DESIGN.md §5, todo 105) ──
 * The one Hono modal (the comms invite/test/confirm dialogs — HTMX swap
 * into #comms-modal-slot, server-owned open/close state) is the documented
 * exception to "migrate every modal to Radix Dialog": converting it would
 * mean duplicating that server state client-side for no gain. It still
 * gets the same shared shell classes as everything else, so it isn't a
 * one-off `comms-*`-prefixed pattern anymore, plus Escape/overlay-click
 * close (utils.js) to match Dialog's close-affordance guarantees by hand. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(40, 36, 28, 0.45);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 48px 16px;
  z-index: 300;
  overflow-y: auto;
}
.modal {
  background: var(--surface, #fff);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.2);
  width: 100%;
  max-width: 520px;
}
.modal-header {
  padding: 18px 22px 14px;
  border-bottom: 1px solid var(--border, #e5e1d8);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.modal-title { font-size: 16px; font-weight: 700; }
.modal-close {
  width: 28px;
  height: 28px;
  background: var(--bg-2, #f5f3ee);
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  color: var(--text-2, #6b6358);
}
.modal-body { padding: 18px 22px 22px; }
.modal-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 18px;
}

/* Invite result */
.comms-invite-result { margin-top: 16px; }
.comms-invite-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-2, #f5f3ee);
  border: 1px solid var(--border, #e5e1d8);
  border-radius: 8px;
  margin: 8px 0 4px;
}
.comms-invite-link code {
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  flex: 1;
  overflow-wrap: anywhere;
}
.comms-invite-link-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
}

.comms-payload-preview {
  background: var(--bg-2, #f5f3ee);
  border: 1px solid var(--border, #e5e1d8);
  border-radius: 8px;
  padding: 12px;
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  white-space: pre-wrap;
  margin: 8px 0;
}

/* ════════════════ Logged-in home / dashboard (item 26) ════════════════ */

/* Trial chip styles live in the item-25 block above (escalating
   ok/warn/ended states). Item 26's simpler chip rules were removed here to
   avoid a later-cascade override. */
.upgrade-link { font-size: 12px; font-weight: 600; color: var(--amber); }
.upgrade-link:hover { text-decoration: underline; }

/* The Roz face (components/roz-face.tsx draws the kit SVG inside). */
.roz-av {
  display: inline-block;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  line-height: 0;
}
.roz-av svg {
  width: 100%;
  height: 100%;
}

.home-card { padding: 20px; }
.home-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.home-card-link { font-size: 12px; font-weight: 600; color: var(--text-2); }
.home-card-link:hover { color: var(--text); }
.home-muted { color: var(--text-3); font-size: 13px; }
.home-grid-2 {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 22px;
  align-items: start;
}
.home-side { display: flex; flex-direction: column; gap: 22px; }

/* Hero next-step (empty state) */
.home-hero {
  background: linear-gradient(135deg, oklch(0.72 0.16 70 / 0.10), oklch(0.72 0.16 70 / 0.03));
  border: 1px solid oklch(0.72 0.16 70 / 0.25);
  border-radius: var(--r);
  padding: 26px 28px;
  display: flex;
  gap: 22px;
  align-items: center;
  margin-bottom: 22px;
}
.home-hero-av { width: 52px; height: 52px; }
.home-hero-body { flex: 1; }
.home-hero-kicker {
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--amber);
  margin-bottom: 5px;
}
.home-hero-title { font-family: var(--font-display); font-size: 1.45rem; font-weight: 800; line-height: 1.15; }
.home-hero-text { color: var(--text-2); font-size: 14px; margin-top: 7px; max-width: 520px; line-height: 1.6; }
.home-hero-cta { margin-top: 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.home-hero-example { font-size: 12px; color: var(--text-2); font-style: italic; }

/* Two-path start chooser (empty state). Import leads; build-with-Roz second. */
.home-start-grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 16px;
  margin-bottom: 22px;
}
.home-start-card {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 24px 26px;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.home-start-card.is-primary {
  border-color: oklch(0.72 0.16 70 / 0.30);
  background: linear-gradient(135deg, oklch(0.72 0.16 70 / 0.10), oklch(0.72 0.16 70 / 0.03));
}
.home-start-title { font-family: var(--font-display); font-size: 1.3rem; font-weight: 800; line-height: 1.15; }
.home-start-text { color: var(--text-2); font-size: 14px; line-height: 1.6; flex: 1; }
.home-start-cta { margin-top: 6px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }

/* "Fastest" pill — on the import start card and the import checklist step. */
.home-check-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  color: var(--amber);
  background: var(--amber-dim);
  padding: 2px 7px;
  border-radius: 99px;
  vertical-align: middle;
}
.home-check-title .home-check-badge { margin-left: 7px; }
.home-start-card .home-check-badge { align-self: flex-start; }

/* Setup checklist */
.home-progress { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.home-progress-track { flex: 1; height: 6px; border-radius: 99px; background: var(--bg-3); overflow: hidden; }
.home-progress-fill { height: 100%; background: var(--amber); border-radius: 99px; }
.home-progress-label { font-family: var(--mono); font-size: 11px; color: var(--text-2); white-space: nowrap; }
.home-check { display: flex; align-items: center; gap: 12px; padding: 13px 0; border-top: 1px solid var(--border); }
.home-check:first-of-type { border-top: none; }
.home-check-box {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
}
.home-check-box.done { background: var(--sage); color: #fff; }
.home-check-box.todo { border: 2px solid var(--border-2); }
.home-check-main { flex: 1; }
.home-check-title { font-size: 14px; font-weight: 600; }
.home-check.is-done .home-check-title { color: var(--text-3); text-decoration: line-through; }
.home-check-sub { font-size: 12px; color: var(--text-2); margin-top: 1px; }
.home-check-cta { font-size: 12px; font-weight: 700; color: var(--amber); white-space: nowrap; }
/* Steps an import fills in (staff / sites / roster) nest under the import step:
   indented with a left rail so they read as "what import sets up for you". */
.home-check.is-nested { margin-left: 11px; padding-left: 22px; border-left: 1px solid var(--border); border-top-color: transparent; }
.home-check.is-nested .home-check-title { font-size: 13px; }

/* Meet Roz card */
.home-meet-roz { display: flex; flex-direction: column; gap: 14px; }
.home-meet-top { display: flex; align-items: center; gap: 12px; }
.home-meet-name { font-family: var(--font-display); font-size: 16px; font-weight: 700; }
.home-meet-sub { font-size: 12px; color: var(--text-3); }
.home-meet-text { font-size: 13px; color: var(--text-2); line-height: 1.6; }
.home-roz-quote {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--amber);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  font-size: 13px;
  color: var(--text);
  font-style: italic;
}
.home-meet-cta { align-self: flex-start; }

/* This week at a glance */
.home-week { margin-bottom: 22px; }
.home-week-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.home-week-open { margin-left: auto; }
.home-week-more { margin-top: 10px; }
.home-cov-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 11px;
  border-radius: 99px;
  font-size: 12px;
  font-weight: 600;
  background: var(--sage-dim);
  color: var(--sage);
}
.home-cov-pill .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--sage); }

.home-mini-grid { border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.home-mg-row { display: grid; grid-template-columns: 130px repeat(var(--cols, 7), 1fr); border-bottom: 1px solid var(--border); }
.home-mg-row:last-child { border-bottom: none; }
.home-mg-row.head { background: var(--bg-2); }
.home-mg-col {
  padding: 7px 8px;
  font-family: var(--mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-3);
  border-right: 1px solid var(--border);
}
.home-mg-col:last-child { border-right: none; }
.home-mg-col.today { color: var(--amber); background: var(--amber-dim); font-weight: 600; }
.home-mg-name {
  padding: 9px 10px;
  font-size: 12px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 7px;
  border-right: 1px solid var(--border);
}
.home-mg-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.home-mg-cell { padding: 7px 5px; border-right: 1px solid var(--border); min-height: 34px; }
.home-mg-cell:last-child { border-right: none; }
.home-mg-cell.today { background: oklch(0.72 0.16 70 / 0.05); }
.home-mg-chip {
  display: inline-block;
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 600;
  font-family: var(--mono);
  background: var(--bg-3);
  color: var(--text-2);
}

/* Needs attention feed */
.home-feed-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 4px;
  border-top: 1px solid var(--border);
  border-radius: 6px;
  color: inherit;
  text-decoration: none;
  transition: background 0.12s;
}
.home-feed-row:first-of-type { border-top: none; }
.home-feed-row:hover { background: var(--bg-2); }
.home-feed-icon {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  flex-shrink: 0;
}
.home-feed-icon.rose { background: var(--rose-dim); }
.home-feed-icon.amber { background: var(--amber-dim); }
.home-feed-icon.slate { background: var(--slate-dim); }
.home-feed-icon.indigo { background: var(--indigo-dim); }
.home-feed-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.home-feed-title { font-size: 14px; font-weight: 600; }
.home-feed-sub { font-size: 12px; color: var(--text-3); margin-top: 1px; }
.home-feed-tag {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 7px;
  border-radius: 4px;
  white-space: nowrap;
}
.home-feed-tag.rose { background: var(--rose-dim); color: var(--rose); }
.home-feed-tag.amber { background: var(--amber-dim); color: var(--amber); }
.home-feed-tag.slate { background: var(--slate-dim); color: var(--slate); }
.home-feed-tag.indigo { background: var(--indigo-dim); color: var(--indigo); }
.home-feed-chevron { color: var(--text-3); font-size: 16px; }
.home-feed-empty { text-align: center; padding: 24px; color: var(--text-3); font-size: 13px; }

/* Quick actions */
.home-quick { display: flex; flex-direction: column; gap: 10px; }
.home-quick .btn { justify-content: center; }

@media (max-width: 820px) {
  .home-grid-2 { grid-template-columns: 1fr; }
  .home-start-grid { grid-template-columns: 1fr; }
  .home-hero { flex-direction: column; align-items: flex-start; }
  .home-mini-grid { overflow-x: auto; }
}

/* ── Staff-card Leave section ─────────────────────────────────────────
   Manager surface in the staff edit row: add form + future list + past
   (collapsed). Mirrors the grid leave-slot tones (muted, type-coloured).
   Same 20px card padding as .sp-panel (the profile/rules panel directly
   above it in the edit row) so this reads as a continuation of one card
   instead of a flush, unstyled strip — bl-041 fix. */
.staff-leave { padding: 20px; border-top: 1px solid var(--border); }
.staff-leave-head { margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--text); }
.leave-add { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
/* Without this, the flex column's default align-items:stretch pulls the
   submit button to full container width — reads as a full-bleed black
   bar instead of a normal button (bl-041 fix). */
.leave-add > .btn { align-self: flex-start; }
.leave-add-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.leave-field { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--text-2); }
.leave-field input, .leave-field select, .leave-field-desc {
  font-family: var(--font); font-size: 13px; padding: 6px 8px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text);
}
.leave-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.leave-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 7px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-2);
  font-size: 12px;
}
.leave-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.leave-dates { font-weight: 600; color: var(--text); }
.leave-type { font-weight: 600; color: var(--text-2); }
.leave-desc { color: var(--text-2); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.leave-badge { margin-left: auto; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: 2px 7px; border-radius: 999px; }
.leave-badge-submitted { background: var(--amber-dim, #fbeec9); color: var(--amber, #9a7a1e); }
.leave-badge-approved  { background: var(--sage-dim, #d8ebe4); color: var(--sage, #2f7a5f); }
.leave-badge-declined  { background: var(--rose-dim, #f3dadd); color: var(--rose, #a23b48); }
.leave-actions { display: flex; gap: 5px; align-items: center; }
.leave-del { color: var(--text-2); }
.leave-empty { font-size: 12px; color: var(--text-2); margin: 4px 0 0; }
/* Header chrome (border, stone header, chevron) comes from `.accordion`
   (DESIGN.md §5, ── Accordions ──); this modifier only adds the gap above
   the box and the list's inset padding under the header. */
.leave-past { margin-top: 12px; }
.leave-past > .leave-list { margin: 0; padding: 8px 12px 12px; }

/* ── Staff-card Roster link section (Epic C, Roster Access C1) ────────
   Manager mint/copy/reset/revoke for the public magic link. Sibling of
   .staff-leave in the staff edit row; same border-top rhythm + 20px card
   padding as .sp-panel/.staff-leave (bl-041 fix). Also covers
   .share-link-panel-loading, the hx-trigger="load" placeholder rendered
   before the real panel swaps in (routes/htmx.tsx) — it was never styled
   at all, so a slow load showed a completely bare "Roster link" strip. */
.share-link-panel, .share-link-panel-loading { padding: 20px; border-top: 1px solid var(--border); }
.share-link-head { margin: 0 0 4px; font-size: 13px; font-weight: 700; color: var(--text); }
.share-link-hint { margin: 0 0 10px; }
.share-link-mint-form, .share-link-reset-form { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.share-link-scope-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-2); }
.share-link-url-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.share-link-url {
  font-family: var(--font-mono, monospace); font-size: 12px; padding: 5px 8px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg-2); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; min-width: 0;
}
.share-link-meta { display: flex; gap: 18px; margin: 0 0 10px; font-size: 12px; }
.share-link-meta-item { display: flex; flex-direction: column; gap: 2px; }
.share-link-meta-item dt { color: var(--text-2); font-weight: 600; }
.share-link-meta-item dd { margin: 0; color: var(--text); }
.share-link-expired-note { margin: 0 0 10px; }
.share-link-notice { display: block; margin: 0 0 10px; padding: 8px 10px; font-size: 12px; }
.share-link-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.share-link-email[disabled] { opacity: 0.55; cursor: not-allowed; }

/* ── Bulk "Share access" overview (Epic C, Roster Access C1, C-PR3) ────
   /app/staff's "Share access" tab pane — grouped Unenrolled/Enrolled list
   of every active staff row, reusing .data-row/.data-list from the staff
   list itself so rows match the rest of the page. */
.share-link-overview-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
.share-link-overview-title { margin: 0 0 4px; font-size: 15px; font-weight: 700; color: var(--text); }
.share-link-overview-group { margin-bottom: 20px; }
.share-link-overview-group-title { margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--text); }
.share-link-overview-row .data-row-actions { gap: 8px; }
.share-link-bulk-summary-line { margin: 0 0 6px; font-weight: 600; }
.share-link-bulk-list { margin: 0; padding-left: 18px; font-size: 12px; }
.share-link-bulk-list li { margin: 2px 0; }

/* Post-publish hint (Epic C, C-PR3) — Live Roster shell, ?published=1. */
.published-hint { margin: 0 16px 12px; }
.published-hint a { font-weight: 600; }

/* ── Help centre (public, /help) — todo 108 ─────────────────────────
   Was a hand-written template-literal <style> block injected by
   src/routes/help.tsx, independent of this file, with several raw-hex
   "defensive" fallbacks (var(--indigo,#4f46e5) etc.) even though Head
   always loads tokens.css before this page renders — the fallbacks never
   fired, they just hid a future retune of --indigo/--stone-* from this
   page. Moved here so the token layer actually governs it. #2 of the
   same todo (migrate .help-cta/.help-card onto the shared button/card
   classes) is still open, blocked on todo 104's canonical variant set. */
.help-body { background: var(--bg); color: var(--text); margin: 0; font-family: var(--font); }
.help-header {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1rem 1.5rem; border-bottom: 1px solid var(--border); background: var(--surface);
}
.help-header img { height: 28px; display: block; }
.help-header-nav { display: flex; align-items: center; gap: 1rem; }
.help-header-nav a { color: var(--text); text-decoration: none; font-weight: 600; font-size: .95rem; }
.help-header-nav a.help-cta { color: var(--surface); }
.help-cta { background: var(--amber); padding: .5rem .9rem; border-radius: var(--r); }
.help-main { max-width: 760px; margin: 0 auto; padding: 2.5rem 1.5rem 4rem; }
.help-lede { color: var(--text-2); font-size: 1.05rem; margin: .25rem 0 2rem; }
.help-cat {
  margin: 2rem 0 .5rem; font-family: var(--font-display); font-size: .8rem;
  text-transform: uppercase; letter-spacing: .06em; color: var(--stone-500);
}
.help-card {
  display: block; padding: 1rem 1.1rem; margin: .6rem 0; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r); text-decoration: none;
  color: var(--text); box-shadow: var(--shadow-card);
}
.help-card:hover { border-color: var(--amber); }
.help-card h3 { margin: 0 0 .25rem; font-size: 1.02rem; }
.help-card p { margin: 0; color: var(--text-2); font-size: .92rem; }
.help-back { display: inline-block; margin-bottom: 1.25rem; color: var(--amber); text-decoration: none; font-weight: 600; font-size: .9rem; }
.help-prose h1 { font-family: var(--font-display); font-size: 1.9rem; margin: 0 0 1rem; }
.help-prose h2 { font-family: var(--font-display); font-size: 1.3rem; margin: 2rem 0 .6rem; }
.help-prose h3 { font-size: 1.05rem; margin: 1.4rem 0 .4rem; }
.help-prose p, .help-prose li { line-height: 1.65; }
.help-prose ul, .help-prose ol { padding-left: 1.3rem; }
/* var(--amber) on white fails WCAG AA for normal text (~2.6:1) — use
   var(--text) + underline for this in-prose link instead, same as
   .help-footer a below. amber stays reserved for .help-cta/.help-back,
   which match the existing bold-nav-link pattern (see .auth-link). */
.help-prose a { color: var(--text); text-decoration: underline; }
.help-prose code { font-family: var(--mono); background: var(--stone-100); padding: .1rem .3rem; border-radius: 4px; font-size: .9em; }
.help-footer { max-width: 760px; margin: 0 auto; padding: 1.5rem; border-top: 1px solid var(--border); color: var(--text-2); font-size: .92rem; }
.help-footer a { color: var(--text); text-decoration: underline; }

/* ── Request timeline (Roz fix plan phase 2: F16, US11) ──
   One list of steps for a shift request: sent, the colleague's answer, the
   manager's decision, the Pick up board. Rendered by
   components/request-timeline.tsx on the manager web and the staff web. */
.req-timeline {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.req-step {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  font-size: var(--fs-micro);
  color: var(--text-2);
}
.req-step-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.5px solid var(--border-2);
  background: var(--surface);
  transform: translateY(1px);
}
.req-step-done .req-step-dot { background: var(--sage); border-color: var(--sage); }
.req-step-current .req-step-dot { background: var(--amber); border-color: var(--amber); }
.req-step-current .req-step-label { color: var(--text); font-weight: 600; }
.req-step-stopped .req-step-dot { background: var(--rose); border-color: var(--rose); }
.req-step-todo .req-step-label { color: var(--text-3); }
.req-step-at { color: var(--text-3); }
.req-step-note { color: var(--text); font-style: italic; }

/* Stage chip: where the request is up to. */
.req-stage { text-transform: none; letter-spacing: 0.01em; }
.req-stage-waiting { background: var(--amber-dim); color: var(--amber); }
.req-stage-approved { background: var(--sage-dim); color: var(--sage); }
.req-stage-open { background: var(--indigo-dim); color: var(--indigo); }
.req-stage-declined,
.req-stage-withdrawn { background: var(--rose-dim); color: var(--rose); }

/* "Starts in 2 h" on a queue row (US6). */
.req-soon { background: var(--rose-dim); color: var(--rose); text-transform: none; }

/* A refused decision inside its own row (US16). */
.req-row-error { margin-top: 8px; }
.req-row-error-link { color: inherit; font-weight: 600; text-decoration: underline; }
.req-leave-clash { color: var(--text); }

/* ── Roz chat: the one composer + message renderer (ui-react roz-chat/) ──
 * RozComposer (.rc-composer) and RozTurn (.rc-msg) on every web Roz
 * surface: the Roz page in both looks, the side bar Roz panel, the
 * edit-with-Roz takeover and the draft notes "Ask Roz". Lives here because
 * app.css loads on every page in both looks.
 * Spec: apps/smartroster/docs/sandbox/roz-chat-uplift/ (the /go/ chat):
 * Roz = indigo tint, you = amber tint with dark text, the kit face (RozFace)
 * next to the newest words, one card shell, Approve / Decline. */
.rc-msg {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.rc-msg.is-user {
  justify-content: flex-end;
}
.rc-user {
  max-width: 88%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.rc-bubble {
  max-width: 100%;
  min-width: 0;
  padding: 10px 12px;
  border-radius: var(--r);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
}
.rc-msg.is-user .rc-bubble {
  background: color-mix(in oklch, var(--amber) 16%, var(--surface));
  color: var(--text);
  border-bottom-right-radius: var(--r-sm);
  white-space: pre-wrap;
}
.rc-msg.is-user .rc-bubble.is-pill {
  padding: 4px 12px;
  border-radius: 14px;
  background: var(--indigo-dim);
  color: var(--indigo);
  font-weight: 600;
}
/* The Roz face stays in view while a long reply scrolls past it. */
.rc-face {
  flex: none;
  position: sticky;
  top: 8px;
  width: 28px;
  height: 28px;
}
.rc-turn {
  min-width: 0;
  max-width: 92%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.rc-turn > * {
  max-width: 100%;
}
.rc-msg.is-roz .rc-bubble {
  background: var(--indigo-dim);
  color: var(--text);
  border-top-left-radius: var(--r-sm);
}
.rc-name {
  display: block;
  margin-bottom: 2px;
  color: var(--indigo);
  font-size: var(--fs-meta);
  font-weight: 700;
}
.rc-msg.is-roz .tok-date {
  background: color-mix(in oklch, var(--surface) 70%, transparent);
}
.rc-muted {
  color: var(--text-2);
}
.rc-import {
  margin-bottom: 16px;
}
/* What Roz is doing: one plain line next to the working face. */
.rc-status {
  display: block;
  min-height: 28px;
  padding-top: 4px;
  color: var(--text-2);
}
.rc-steps {
  display: grid;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--text-2);
  font-size: var(--fs-meta);
}
.rc-steps li {
  display: flex;
  align-items: center;
  gap: 6px;
}
.rc-steps li.is-done svg {
  color: var(--sage);
}
.rc-spin {
  flex: none;
  width: 12px;
  height: 12px;
  border: 2px solid var(--stone-200);
  border-top-color: var(--indigo);
  border-radius: 50%;
}
@media (prefers-reduced-motion: no-preference) {
  .rc-spin {
    animation: rc-spin 0.9s linear infinite;
  }
}
@keyframes rc-spin {
  to {
    transform: rotate(360deg);
  }
}
.rc-did-wrap {
  width: 100%;
}
.rc-did {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-2);
  font: inherit;
  font-size: var(--fs-meta);
  cursor: pointer;
}
.rc-did:hover {
  color: var(--text);
}
.rc-did svg.is-open {
  transform: rotate(90deg);
}
/* Under a finished reply: Copy. Quiet until the turn is hovered. */
.rc-meta {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 20px;
  color: var(--text-2);
  font-size: var(--fs-meta);
}
.rc-act {
  width: 28px;
  height: 28px;
  display: inline-grid;
  place-items: center;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-2);
  cursor: pointer;
}
.rc-act:hover,
.rc-act:focus-visible {
  background: var(--stone-100);
  color: var(--text);
}
@media (hover: hover) {
  .rc-msg.is-roz .rc-meta {
    opacity: 0;
    transition: opacity 0.15s;
  }
  .rc-msg.is-roz:hover .rc-meta,
  .rc-msg.is-roz:focus-within .rc-meta,
  .rc-msg.is-roz[data-face="done"] .rc-meta {
    opacity: 1;
  }
}

/* Times: the user's under the bubble (right), Roz's beside Copy. */
.rc-user .rc-meta {
  justify-content: flex-end;
}
@media (hover: hover) {
  .rc-msg.is-user .rc-meta {
    opacity: 0;
    transition: opacity 0.15s;
  }
  .rc-msg.is-user:hover .rc-meta {
    opacity: 1;
  }
}
/* A day line where the thread's day changes. */
.rc-day {
  display: flex;
  justify-content: center;
  margin: 4px 0 16px;
}
.rc-day span {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--stone-100);
  color: var(--text-2);
  font-size: var(--fs-meta);
}
/* "Jump to latest" floats above the chat bar while the reader is up the thread. */
.rc-foot {
  position: relative;
}
.rc-jump {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  z-index: 2;
  height: 32px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 12px;
  border: 0;
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--shadow-pop);
  color: var(--stone-700);
  font: inherit;
  font-size: var(--fs-meta);
  font-weight: 700;
  cursor: pointer;
  transform: translateX(-50%);
}

/* Buttons on every Roz card: Approve (amber), Decline (grey), a line button,
 * red only for a delete. */
.rc-btn {
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.rc-btn:disabled {
  opacity: 0.5;
  cursor: default;
}
.rc-btn.is-yes {
  background: var(--amber);
  color: var(--stone-900);
}
.rc-btn.is-no {
  background: var(--stone-100);
  color: var(--stone-700);
}
.rc-btn.is-line {
  background: var(--surface);
  color: var(--stone-700);
  box-shadow: inset 0 0 0 1px var(--border-2);
}
.rc-btn.is-danger {
  background: var(--rose);
  color: var(--surface);
}
.rc-btn.is-sm {
  height: 28px;
  padding: 0 10px;
  font-size: var(--fs-meta);
}
.rc-btn:not(:disabled):hover {
  filter: brightness(0.97);
}

/* One card shell: confirm cards, and the other Roz cards as they move to it. */
.rc-card {
  width: 100%;
  max-width: 460px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}
.rc-card-head {
  padding: 12px 14px 8px;
}
.rc-card-title {
  font-weight: 700;
}
.rc-card-lines {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  color: var(--text-2);
  font-size: var(--fs-meta);
  white-space: pre-wrap;
}
.rc-card-ask {
  padding: 12px 14px;
  border-top: 1px solid var(--border);
  background: var(--bg);
}
.rc-card-effect {
  margin: 0;
  color: var(--text-2);
  font-size: var(--fs-meta);
}
.rc-ask.is-delete .rc-card-effect {
  color: var(--rose);
  font-weight: 600;
}
.rc-card-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.rc-card-done {
  margin: 8px 0 0;
  color: var(--text-2);
  font-size: var(--fs-meta);
  font-weight: 600;
}
.rc-card-done.is-ok {
  color: var(--sage);
}
.rc-card-error {
  margin: 6px 0 0;
  color: var(--rose);
  font-size: var(--fs-meta);
  font-weight: 600;
}

/* Empty chat: Roz says hello; starters send at once. */
.rc-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: auto;
  padding: 32px 16px;
  text-align: center;
}
.rc-empty h2 {
  margin: 4px 0 0;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 700;
}
.rc-empty p {
  max-width: 36ch;
  margin: 0;
  color: var(--text-2);
}
.rc-starters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: 440px;
  margin-top: 6px;
}
.rc-starter {
  min-height: 32px;
  padding: 0 12px;
  border: 0;
  border-radius: 999px;
  background: var(--stone-100);
  color: var(--stone-700);
  font: inherit;
  font-size: var(--fs-meta);
  font-weight: 700;
  cursor: pointer;
}
.rc-starter:hover {
  background: var(--stone-200);
  color: var(--text);
}
.rc-md > * + * {
  margin-top: 6px;
}
.rc-md ul,
.rc-md ol {
  padding-left: 18px;
}
.rc-md ul {
  list-style: disc;
}
.rc-md ol {
  list-style: decimal;
}
.rc-md table {
  border-collapse: collapse;
  font-size: var(--fs-meta);
}
.rc-md th,
.rc-md td {
  padding: 4px 8px;
  border: 1px solid var(--border);
  text-align: left;
}

/* Files on a sent user turn: image thumbnails and file chips. */
.rc-files {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.rc-file-thumb {
  display: block;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
}
.rc-file-thumb img {
  display: block;
  max-width: 200px;
  max-height: 140px;
  object-fit: cover;
}
.rc-file {
  max-width: 220px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 15px;
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-meta);
  text-decoration: none;
}
.rc-file span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Composer (option A, Sam 20 Oct): one floating card — chips, the words
 * across the full width, then [+ … send/stop]. */
.rc-composer {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 12px 10px 16px;
  border: 1px solid oklch(0.18 0.02 50 / 0.07);
  border-radius: 22px;
  background: var(--surface);
  box-shadow:
    0 1px 2px oklch(0.18 0.02 50 / 0.05),
    var(--shadow-card);
}
.rc-composer:focus-within {
  border-color: var(--border-2);
}
.rc-composer.is-drop {
  border-color: var(--indigo);
  border-style: dashed;
  background: var(--indigo-dim);
}
.rc-drop-hint {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  border-radius: 22px;
  color: var(--indigo);
  font-weight: 600;
  pointer-events: none;
}
.rc-composer.is-drop textarea,
.rc-composer.is-drop .rc-actions,
.rc-composer.is-drop .rc-chips {
  opacity: 0.25;
}
.rc-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: -4px;
}
.rc-grow {
  flex: 1;
}
.rc-composer textarea {
  width: 100%;
  min-height: 24px;
  max-height: 200px;
  padding: 2px 0;
  border: 0;
  outline: none;
  resize: none;
  font: inherit;
  line-height: 1.4;
  color: var(--text);
  background: none;
  box-shadow: none;
}
.rc-icon-btn,
.rc-send,
.rc-stop {
  flex: none;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 0;
  border-radius: var(--r-sm);
  font: inherit;
  font-size: var(--fs-meta);
  font-weight: 600;
  cursor: pointer;
}
.rc-icon-btn {
  width: 32px;
  background: none;
  color: var(--text-2);
}
.rc-icon-btn.rc-plus {
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--border-2);
  color: var(--stone-700);
}
.rc-icon-btn:hover {
  background: var(--stone-100);
  color: var(--text);
}
.rc-send,
.rc-stop {
  width: 32px;
  border-radius: 50%;
}
.rc-send {
  background: var(--amber);
  color: var(--stone-900);
}
.rc-send:hover {
  filter: brightness(1.08);
}
.rc-send:disabled {
  background: var(--stone-200);
  color: var(--text-2);
  cursor: default;
  filter: none;
}
.rc-icon-btn:disabled {
  opacity: 0.4;
  cursor: default;
  filter: none;
}
.rc-stop {
  background: var(--stone-900);
  color: var(--surface);
}
.rc-note {
  display: block;
  margin: 0;
  padding: 0 4px;
  color: var(--text-2);
  font-size: var(--fs-meta);
}
.rc-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.rc-chip {
  position: relative;
  max-width: 240px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg-2);
  font-size: var(--fs-meta);
}
.rc-chip.is-error {
  border-color: var(--rose);
  background: var(--rose-dim);
}
.rc-chip-thumb,
.rc-chip-icon {
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 4px;
}
.rc-chip-thumb {
  object-fit: cover;
}
.rc-chip-icon {
  display: grid;
  place-items: center;
  background: var(--stone-100);
  color: var(--text-2);
}
.rc-chip-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.rc-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  font-weight: 600;
}
.rc-chip-meta {
  color: var(--text-2);
}
.rc-chip.is-error .rc-chip-meta {
  color: var(--rose);
}
.rc-chip-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  background: var(--indigo);
}
.rc-chip-x {
  width: 22px;
  height: 22px;
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 11px;
  background: none;
  color: var(--text-2);
  cursor: pointer;
}
.rc-chip-x:hover {
  background: var(--stone-200);
  color: var(--text);
}

/* Side bar: a narrower composer. */
.rc-composer.is-rail .rc-chip {
  max-width: 100%;
}
.rc-composer.is-rail textarea {
  max-height: 120px;
}

/* Old look: the intent pill keeps its amber tint. (The old solid amber user
 * bubble with white text failed contrast; both looks now share the tint.) */
body:not(.look-d) .rc-msg.is-user .rc-bubble.is-pill {
  background: var(--amber-dim);
  color: var(--amber);
}

/* ── Send to Deputy review (deputy-push-sections.tsx) ── */
.deputy-push-section { margin-top: 16px; }
.deputy-push-summary .data-table { margin: 8px 0; }
.deputy-push-group + .deputy-push-group { margin-top: 6px; }
.deputy-push-group > :not(summary) { margin: 8px 12px; }
#deputy-push-panel .alert a { font-weight: 600; text-decoration: underline; }

/* "Invite your team" on Home (components/invite-team-nudge.tsx). */
.invite-team-nudge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 0 16px 12px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--amber);
  border-radius: 12px;
  background: var(--surface, #fff);
  font-size: 14px;
  color: var(--text-2);
}
.invite-team-nudge strong {
  color: var(--text);
}
.invite-result-spacer,
.invite-all-result {
  margin: 0 0 12px;
}
/* "Demo data removed" line on Home (components/demo-removed-notice.tsx). */
.demo-removed-notice {
  margin: 0 16px 12px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface, #fff);
  font-size: 13px;
  color: var(--text-2);
}
.demo-removed-notice a {
  color: var(--text);
  font-weight: 600;
  text-decoration: underline;
}
