/* =============================================================
   Applicable — Stripe-inspired white UI
   Tinted canvas, white cards, blue accent #0c64ae, navy text,
   generous whitespace. Internal CRM — login, dashboard, settings,
   admin user management.
   ============================================================= */

/* ---------- web fonts ----------
   Geist (Vercel, OFL-1.1) — variable-weight woff2, self-hosted
   from /static/fonts/. One file covers 100–900 per family;
   browsers interpolate to the requested weight. font-display:
   swap means text renders in the fallback stack immediately and
   re-paints when Geist loads, avoiding the FOIT flash. */
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-Variable.woff2") format("woff2-variations"),
       url("/static/fonts/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/static/fonts/GeistMono-Variable.woff2") format("woff2-variations"),
       url("/static/fonts/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* ---------- reset + base ---------- */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
:root {
  /* ---- surfaces ----
     The canvas is TINTED and cards/rails are WHITE. That contrast is
     what does the work: a white sidebar and white shadowed cards read
     as raised against #f7f9fc, where before everything was #ffffff on
     #ffffff and only a hairline told you where one surface ended.

     --bg is the page canvas ONLY. Controls that must stay white on
     that canvas (inputs, ghost buttons, menus) use --card. Those two
     were the same value historically, so any rule that wants white
     must say --card explicitly. */
  --bg:           #f7f9fc;
  --card:         #ffffff;
  /* Hover on a white surface. Must be legible against --card without
     reading as the canvas — hence a step past #f7f9fc, not equal to it
     (the old #fafbfc was invisible on white). */
  --hover:        #f2f5fa;
  /* A recessed surface, one step darker than --hover: neutral pill
     backgrounds, tab counters, the current lineage step. It was used in
     a dozen rules before it was ever defined here, so every one of them
     resolved to nothing and those elements rendered with no background
     at all. Value taken from the only rule that carried a fallback. */
  --surface-2:    #eef1f6;

  --border:       #dbe2ec;
  --border-light: #e8edf4;

  /* Text ramps to navy rather than neutral slate, so type sits in the
     same hue family as the accent instead of beside it. */
  --text:         #0b2036;
  --text-muted:   #5b6472;
  --text-dim:     #7d8899;

  /* ---- accent ----
     #0c64ae, matching FileIt. --primary-soft is the active-nav /
     selected-tab wash and is a TINT OF THE ACCENT — the old #f1f0ff was
     lavender sitting under a blue accent, which is what made every
     active nav item look faintly wrong. */
  --primary:        #0c64ae;
  --primary-hover:  #0a5290;
  --primary-soft:   #e9f1fb;
  --primary-line:   #cfe2f6;

  --success:        #0d7a56;
  --success-soft:   #e2f5ec;
  --danger:         #c0392b;
  --danger-soft:    #fdecea;
  --warn:           #96700e;
  --warn-soft:      #fdf6e3;

  /* Two-layer shadows: a tight 1px seat plus a wide soft ambient. A
     single blurred layer reads as fog; the tight layer is what makes an
     edge look like it's resting on the canvas. */
  --shadow-sm: 0 1px 2px rgba(16, 32, 54, 0.06), 0 1px 1px rgba(16, 32, 54, 0.04);
  --shadow-md: 0 1px 3px rgba(16, 32, 54, 0.08), 0 8px 24px rgba(16, 32, 54, 0.06);
  --shadow-lg: 0 10px 26px rgba(16, 32, 54, 0.10), 0 4px 8px rgba(16, 32, 54, 0.05);

  --radius-sm: 6px;
  --radius:    8px;
  --radius-lg: 12px;
  --radius-xl: 14px;

  /* Referenced by .code-hint (and others) with an inline fallback but
     never actually defined, so every one of those resolved to the
     fallback. Defined here so the fallbacks are now dead weight, not
     load-bearing. */
  --mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
body {
  /* Geist primary (self-hosted woff2, see @font-face above). The
     system stack stays as the swap-fallback while Geist loads
     and as a safety net if /static/fonts/ ever 404s. */
  font-family: "Geist", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
  font-size: 15px;
  letter-spacing: -0.005em;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: var(--primary); text-decoration: none; }
a:hover { color: var(--primary-hover); text-decoration: underline; }
.muted    { color: var(--text-muted); }
.dim      { color: var(--text-dim); }
.muted em { font-style: normal; color: var(--text-dim); }
code {
  font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  background: var(--hover);
  padding: 1px 6px;
  border-radius: 4px;
  color: var(--text-muted);
}
.inline { display: inline; margin: 0; }
/* ---------- Font Awesome icon stability ----------
   Kit 65630e4a4f is configured `method: "css"` — a WEBFONT kit. It
   never replaces <i> with inline SVG (that's the kit's other mode), so
   the old `.svg-inline--fa` selector here matched nothing and the
   comment describing SVG injection described a setup this project
   doesn't use.

   The flicker it was guarding against is still real in webfont mode:
   until pro.min.css lands, an <i> is an empty inline box of zero width
   that then jumps to its glyph width. So we still reserve the box —
   but at FA's OWN metrics, not invented ones.

   Matching `width: var(--fa-width, 1.25em)` matters more than it
   looks. Duotone renders as two stacked glyphs: `::before` (the
   primary layer) is `position: absolute`, while `::after` (the
   secondary) stays in normal flow and is centred by `text-align`.
   They only land on top of each other when the box is the width FA
   expects. The previous `width: 1em` was narrower than FA's 1.25em
   default and would have slid the two layers out of register. Reading
   through the var also means `.fa-fw` and any per-icon --fa-width keep
   working instead of being overridden. `height` is deliberately not
   set: FA sizes vertically via line-height, and a hard 1em clips the
   taller glyphs. */
i.fa-solid,
i.fa-brands,
i.fa-regular,
i.fa-classic,
i.fa-duotone,
i.fa-light,
i.fa-thin,
i.fa-sharp {
  display: inline-block;
  width: var(--fa-width, 1.25em);
  line-height: 1;
  text-align: center;
}
.fa-fw { width: 1.25em; }
/* ---------- duotone ----------
   The primary layer takes `color` outright; the secondary inherits the
   same colour at --fa-secondary-opacity. So one `color` on the <i>
   gives the two-tone look for free and every existing rule that colours
   an icon keeps working untouched. Dropping the secondary from FA's
   default .4 to .32 keeps the back layer a wash rather than a second
   competing shape at 15px nav sizes. */
.fad, .fa-duotone { --fa-secondary-opacity: 0.32; }
.link-btn {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  color: var(--primary);
  padding: 0;
}
.link-btn:hover { color: var(--primary-hover); text-decoration: underline; }
/* ---------- layout shell ---------- */

.content {
  flex: 1;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 56px 32px 80px;
}
.content.with-sidebar {
  /* Dashboard layout uses its own grid; cancel the centered max-width
     so .dash-layout can fill the viewport. */
  max-width: none;
  margin: 0;
  padding: 0;
}
/* ---------- buttons ---------- */

/* The bare `.btn` (no .primary / .ghost) is used at 14 call sites as the
   secondary action next to a primary one. It previously set no
   background at all and only survived by sitting on a white page — now
   that the canvas is tinted, an unstyled button would read as a hole
   punched in it. Defaulting to the white secondary surface makes those
   call sites correct without touching a single template; .primary and
   .ghost both out-specify this and are unaffected. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 18px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow-sm);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.005em;
  cursor: pointer;
  text-decoration: none;
  transition: background 100ms, border-color 100ms, box-shadow 100ms, transform 50ms;
  font-family: inherit;
  line-height: 1;
}
.btn:hover { background: var(--hover); color: var(--text); text-decoration: none; }
/* Icons inside buttons ride one notch quieter than the label, except on
   a filled primary where the label is already white on accent. */
.btn i { color: var(--text-dim); }
.btn:hover i { color: var(--text-muted); }
.btn.primary i, .btn.primary:hover i { color: currentColor; }
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: var(--primary);
  color: #fff;
  box-shadow: var(--shadow-sm);
}
.btn.primary:hover {
  background: var(--primary-hover);
  text-decoration: none;
  color: #fff;
  box-shadow: var(--shadow-md);
}
.btn.primary:disabled {
  background: #a8c7e4;
  cursor: not-allowed;
  box-shadow: none;
}
.btn.ghost {
  background: var(--card);
  color: var(--text);
  border-color: var(--border);
  box-shadow: var(--shadow-sm);
}
.btn.ghost:hover {
  background: var(--hover);
  border-color: var(--border);
  text-decoration: none;
  color: var(--text);
}
.btn.block {
  display: flex;
  width: 100%;
  text-align: center;
  padding: 12px 18px;
}
.btn-sm {
  padding: 6px 12px;
  font-size: 13px;
}
.form-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 18px;
}
/* ---------- forms ---------- */

.auth-form { display: flex; flex-direction: column; gap: 14px; }
.auth-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 500; color: var(--text-muted); }
/* Field wrapper for rich-text editors: same layout as a form label, but
   NOT a <label> — a <label> wrapper steals focus from the editor's
   contenteditable (clicks bubble up and refocus the hidden textarea). */
.auth-form .field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 500; color: var(--text-muted); }
/* Control styling is shared with .record-inline-form — the one-line
   "add a person" / "add a task" rows on the CRM record page. Those must
   NOT be .auth-form (it stacks label above control, which is wrong for a
   single-line row) but they still need the app's input chrome. Without
   this, every control inside the record page's tabs rendered as a raw
   unstyled browser widget. */
.auth-form input, .auth-form select, .auth-form textarea,
.record-inline-form input, .record-inline-form select, .record-inline-form textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  color: var(--text);
  background: var(--card);
  transition: border-color 100ms, box-shadow 100ms;
}
/* Inline rows size to content, not full width. */
.record-inline-form input, .record-inline-form select {
  width: auto;
  padding: 7px 10px;
  font-size: 13px;
}
.auth-form textarea {
  resize: vertical;
  min-height: 88px;
  line-height: 1.45;
}
/* Monospace textarea for editing raw HTML / code (e.g. glossary
   Contents). Smaller type + no wrapping keeps markup readable. */
.auth-form textarea.code-area {
  font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  white-space: pre;
  overflow-wrap: normal;
}
.auth-form input:focus, .auth-form select:focus, .auth-form textarea:focus,
.record-inline-form input:focus, .record-inline-form select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(12, 100, 174, 0.16);
}
.auth-form input:disabled {
  background: var(--hover);
  color: var(--text-muted);
  cursor: not-allowed;
}
/* Hairline divider between logical sections inside one form. */
.form-section-rule {
  border: 0;
  border-top: 1px solid var(--border-light);
  margin: 18px 0 6px;
}
.auth-form .row.two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
/* Section eyebrow inside a long form, with breathing room above it.
   Replaces an inline style="margin-top:16px" that was in lead_detail. */
.card-eyebrow.eyebrow-spaced { display: block; margin-top: 16px; }
.no-top-margin { margin-top: 0; }
/* Group heading inside a long form (Seeds new/detail). Separates
   Company / Contact / Qualification / Assignment blocks without
   splitting them into separate cards. */
.form-section-title {
  margin: 22px 0 2px;
  padding-top: 16px;
  border-top: 1px solid var(--border-light);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.form-section-title:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
/* ---------- auth card (login page, centered) ---------- */

.auth-card {
  max-width: 420px;
  margin: 60px auto;
  padding: 36px 36px 28px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  background: var(--card);
  box-shadow: var(--shadow-md);
}
.auth-card h1 {
  margin: 0 0 6px;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.auth-card .muted { margin: 0 0 18px; }
.auth-card form { margin-top: 18px; }
.auth-brand {
  text-align: center;
  margin-bottom: 22px;
}
.auth-brand img {
  height: 48px;
  width: auto;
  display: inline-block;
}
.auth-alt {
  margin: 22px 0 0;
  text-align: center;
  font-size: 13px;
}
/* ---------- alerts ---------- */

.alert {
  padding: 12px 14px;
  border-radius: var(--radius);
  font-size: 14px;
  border: 1px solid transparent;
  margin: 14px 0;
  line-height: 1.45;
}
.alert.error   { background: var(--danger-soft);  color: var(--danger);  border-color: #f4c0b3; }
.alert.success { background: var(--success-soft); color: var(--success); border-color: #b9e3d2; }
.alert.warn    { background: var(--warn-soft);    color: var(--warn);    border-color: #f5c79b; }
/* ---------- dashboard layout ---------- */

.dash-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  min-height: 100vh;
  background: var(--bg);
}
/* 3-column variant: parent template adds .has-subnav when the
   secondary nav (`_subnav.html`) is included. Overview (and any
   other leaf at the top level) stays 2-column. */
.dash-layout.has-subnav {
  grid-template-columns: 240px 260px 1fr;
}
/* The two nav rails are WHITE against the tinted canvas — that split is
   the whole point of the surface tokens. Sidebar and subnav together
   form one continuous rail, divided from each other by a hairline and
   from the work area by the canvas colour itself. */
.dash-sidebar {
  border-right: 1px solid var(--border-light);
  padding: 24px 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: sticky;
  top: 0;
  height: 100vh;
  background: var(--card);
}
.sidebar-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px 8px 16px;
  line-height: 0;
}
.sidebar-brand:hover { text-decoration: none; }
.sidebar-brand img {
  height: 92px;
  width: auto;
  display: block;
}
.sidebar-brand-text {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  line-height: 1;
}
.dash-sidebar nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }
.nav-section {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  padding: 14px 12px 6px;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  transition: background 80ms, color 80ms;
}
.nav-item:hover {
  background: var(--hover);
  color: var(--text);
  text-decoration: none;
}
.nav-item.active {
  background: var(--primary-soft);
  color: var(--primary-hover);
  font-weight: 600;
}
/* Icons track the label but one step quieter, and go full accent only
   on the active row. `color: inherit` (the old rule) made every icon
   exactly as loud as its text, which is what gave the rail its flat,
   uniform weight — nothing drew the eye to where you actually were. */
.nav-item i {
  width: 18px;
  flex: 0 0 auto;
  text-align: center;
  font-size: 15px;
  color: var(--text-dim);
  transition: color 80ms;
}
.nav-item:hover i { color: var(--text-muted); }
.nav-item.active i { color: var(--primary); }
/* ---------- secondary nav (column 2) ---------- */
/* Rendered by templates/_subnav.html when the active section is a
   group (Settings, Marketing, …). Hidden on Overview-style leaves
   so the content area gets the full canvas. */

.dash-subnav {
  border-right: 1px solid var(--border-light);
  padding: 28px 14px 16px;
  background: var(--card);
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 6px;
  overflow-y: auto;
}
.dash-subnav-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 12px 14px;
  border-bottom: 1px solid var(--border-light);
  margin-bottom: 8px;
  color: var(--text);
}
.dash-subnav-head i { color: var(--primary); font-size: 15px; }
.dash-subnav-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.dash-subnav-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dash-subnav-item {
  display: block;
  padding: 8px 12px;
  border-radius: var(--radius);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-muted);
  transition: background 80ms, color 80ms;
}
.dash-subnav-item:hover {
  background: var(--hover);
  color: var(--text);
  text-decoration: none;
}
.dash-subnav-item.active {
  background: var(--primary-soft);
  color: var(--primary-hover);
  font-weight: 600;
}
.dash-subnav-empty {
  padding: 8px 12px;
  font-size: 12.5px;
  font-style: italic;
  color: var(--text-dim);
}
.sidebar-footer {
  /* Hairline divider removed — workspace logo sits cleanly above
     the user card without it. Spacing kept so the user card has
     breathing room from whatever sits above. */
  padding-top: 4px;
  margin-top: 8px;
}
/* Workspace (per-tenant) logo, sits in the sidebar footer just
   above the user card. Fills the sidebar's content width; height
   auto-scales with the image's natural aspect ratio. Caps the
   max-height so a tall/portrait logo doesn't push the user card
   off-screen — the actual logo gets `object-fit: contain` so it
   shrinks to fit when the cap kicks in.

   `transform: translateY(-20px)` shifts the logo upward visually
   without affecting layout flow (the user card stays put). The
   `flex: 1` on .dash-sidebar nav above means margin tricks would
   just be absorbed by the nav, so transform is the clean route. */
.sidebar-app-logo {
  display: block;
  padding: 0 4px;
  margin: 0 0 6px;
  transform: translateY(-20px);
  text-decoration: none;
  transition: opacity 80ms;
}
.sidebar-app-logo:hover { opacity: 0.85; text-decoration: none; }
.sidebar-app-logo img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 120px;
  object-fit: contain;
}
.sidebar-userline {
  display: flex;
  align-items: center;
  gap: 6px;
}
.user-card {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  transition: background 80ms;
  min-width: 0;
}
.user-card:hover { background: var(--hover); text-decoration: none; }
.user-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex-shrink: 0;
  object-fit: cover;
  background: linear-gradient(135deg, #7fb4e4 0%, #0c64ae 100%);
}
.user-avatar.fallback {
  display: inline-block;
  background: linear-gradient(135deg, #7fb4e4 0%, #0c64ae 100%);
}
.user-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-size: 13px;
}
.user-name {
  color: var(--text);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.user-meta {
  color: var(--text-dim);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.user-logout {
  background: none;
  border: 1px solid transparent;
  cursor: pointer;
  padding: 8px;
  border-radius: var(--radius);
  color: var(--text-dim);
  transition: background 80ms, color 80ms;
}
.user-logout:hover { background: var(--hover); color: var(--text); }
.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  padding: 6px 10px;
  font-size: 14px;
  color: var(--text);
}
.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(15, 19, 36, 0.4);
  z-index: 90;
}
.dash-main { min-width: 0; }
.dash-inner {
  padding: 32px 40px 64px;
}
/* ---------- content-column vertical rhythm ----------
   THE GUTTER IS OWNED BY THE CONTAINER, NOT BY EACH WIDGET.

   Every top-level block in the content column — a .dash-card, a
   .dash-grid of stat tiles, a .data-table-wrap, an .alert, a filter
   panel — gets the same gap from whatever precedes it, whatever the two
   happen to be. Add a new widget and it is spaced correctly by
   existing; nobody has to remember a margin.

   This replaces `.dash-card + .dash-card`, which only ever fired when
   two cards were LITERALLY adjacent. Every real page interleaves types
   — card then grid, table then card then table — so the rule missed
   almost every boundary that mattered and those widgets sat flush.

   Deliberately kept to ONE class of specificity (0,1,0). This rule sets
   the child's own margin-top, so anything more specific here would
   silently override a widget's intentional spacing rather than defer to
   it — which is exactly what a `:not()`-guarded version of this rule did
   to .form-section-title, flattening its 22px to 18px. Overlays and
   hidden elements need no guard: position:fixed and display:none
   generate no in-flow margin box, so the rule is inert on both.

   A preceding sibling's margin-BOTTOM still collapses against this
   normally — .dash-header's 24px wins over this 18px, as intended.
   Widgets wanting MORE room above declare it with a second class of
   specificity, as .form-section-title does just below.

   Containers opt in by name. `.record-panel` is the body of a record tab
   and `.settings-content` the right pane of a settings page; both stack
   widgets that are NOT children of .dash-inner, so neither inherits the
   rhythm implicitly. This selector is for containers holding MIXED
   content, where the gap falls between different element types and
   adjacency alone can't express it. */
.dash-inner > * + *,
.record-panel > * + *,
.settings-content > * + * {
  margin-top: 18px;
}
/* Universal fallback: two cards stacked ANYWHERE else.
   The named list above will always lag reality — cards also stack inside
   .auth-form, and every new container starts life unlisted. Card-to-card
   adjacency needs no container name, so it catches those for free and
   stops this from being a bug that gets rediscovered one screen at a
   time. It cannot replace the rule above (it says nothing about a card
   followed by a grid or a table), and the rule above cannot replace it
   (it only knows named containers) — they cover different gaps. */
.dash-card + .dash-card { margin-top: 18px; }
/* …except inside a container that already spaces with `gap`. Gap and
   margin ADD rather than override — and in a flex/grid container margins
   don't collapse — so leaving these in place gives 14px + 18px = 32px in
   .auth-form and double spacing in .dash-grid. Any future gap-based
   container that stacks cards belongs on this list. */
.dash-grid > .dash-card + .dash-card,
.auth-form > .dash-card + .dash-card { margin-top: 0; }
/* A section heading divides the page; it earns more air above it than
   the gap between two ordinary widgets. Two classes, so it beats the
   flow rule above regardless of source order. */
.dash-inner > .form-section-title,
.record-panel > .form-section-title,
.settings-content > .form-section-title {
  margin-top: 30px;
}
.dash-header { margin: 0 0 24px; }
.dash-header h1 {
  margin: 0 0 6px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.015em;
}
.dash-header .muted { margin: 0; font-size: 14px; }
.dash-header-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 16px;
  flex-wrap: wrap;
}
.dash-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 16px;
}
.back-link:hover { color: var(--primary); text-decoration: none; }
/* ---------- dashboard cards ---------- */

.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 18px;
}
.dash-card {
  background: var(--card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-xl);
  padding: 22px 24px;
  box-shadow: var(--shadow-sm);
  transition: border-color 120ms, box-shadow 120ms, transform 120ms;
}
/* The old rule declared `transition: box-shadow` and then never set a
   shadow on any state, so the hover was a 1px border tint and nothing
   else. A card that lifts is the single cheapest signal that it's a
   surface rather than a rectangle drawn on the page. */
.dash-card:hover { border-color: var(--border); box-shadow: var(--shadow-md); }
/* Only cards that go somewhere translate — a static card lifting under
   the cursor promises a click that isn't there. */
a.dash-card:hover { transform: translateY(-2px); text-decoration: none; }
/* Card gutters are no longer the card's business — `.dash-inner > * + *`
   spaces every top-level widget uniformly, so the old
   `.dash-card + .dash-card` pair (and its grid-reset counterpart) is
   gone. .dash-card still deliberately carries no margin of its own,
   which is what lets .dash-grid space its children with `gap` alone. */
.dash-card h3 {
  margin: 0 0 6px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}
.dash-card p { margin: 4px 0 14px; color: var(--text-muted); font-size: 14px; line-height: 1.55; }
.card-eyebrow {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin-bottom: 10px;
}
.kv {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 16px;
  margin: 8px 0 0;
  font-size: 14px;
}
.kv dt { color: var(--text-dim); }
.kv dd { margin: 0; color: var(--text); }
/* ---------- pills (status / role) ---------- */

.pill {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  background: var(--hover);
  color: var(--text-muted);
}
.pill.active     { background: var(--success-soft); color: var(--success); border-color: #b9e3d2; }
.pill.disabled   { background: var(--hover);        color: var(--text-dim); border-color: var(--border-light); }
.pill.queued     { background: var(--hover);        color: var(--text-muted); border-color: var(--border-light); }
.pill.running    { background: #fff4ed;             color: var(--warn);     border-color: #f5c79b; }
.pill.complete   { background: var(--success-soft); color: var(--success);  border-color: #b9e3d2; }
.pill.failed     { background: var(--danger-soft);  color: var(--danger);   border-color: #f4c0b3; }
.pill.cancelled  { background: var(--hover);        color: var(--text-dim); border-color: var(--border-light); }
.pill.role-admin {
  background: var(--primary-soft);
  color: var(--primary);
  border-color: var(--primary-line);
}
.pill.role-user {
  background: var(--hover);
  color: var(--text-muted);
  border-color: var(--border-light);
}
/* ---------- data table ---------- */

.data-table-wrap {
  border: 1px solid var(--border-light);
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--card);
  box-shadow: var(--shadow-sm);
}
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.data-table thead th {
  text-align: left;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 12px 18px;
  border-bottom: 1px solid var(--border-light);
  background: var(--hover);
}
.data-table tbody td {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-light);
  color: var(--text);
  vertical-align: middle;
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr.clickable-row { cursor: pointer; transition: background 80ms; }
.data-table tbody tr.clickable-row:hover { background: var(--hover); }
.col-actions { width: 1%; text-align: right; }
.row-title {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-weight: 500;
}
.row-title:hover { color: var(--primary); text-decoration: none; }
/* Small monospace code sub-line under a row title (e.g. item code). */
.code-hint {
  display: block;
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 11px;
  font-weight: 400;
  margin-top: 2px;
}
/* ===== Invoice print view ===== */
.inv-print-shell { max-width: 820px; margin: 24px auto; padding: 0 16px; }
.inv-toolbar { display: flex; gap: 8px; justify-content: flex-end; margin-bottom: 16px; }
.inv-doc {
  background: #fff;
  color: #1a1a1a;
  border: 1px solid var(--border, #e4e4e7);
  border-radius: 10px;
  padding: 40px 44px;
}
.inv-head { margin-bottom: 28px; }
.inv-logo { max-height: 56px; max-width: 220px; margin-bottom: 16px; display: block; }
.confirm-recipient {
  font-size: 16px;
  padding: 12px 16px;
  background: var(--surface-2, #f4f4f5);
  border: 1px solid var(--border, #e4e4e7);
  border-radius: 8px;
  margin: 8px 0 12px;
}
.inv-title { display: flex; align-items: center; gap: 12px; }
.inv-title h1 { margin: 0; font-size: 26px; letter-spacing: 0.5px; }
.inv-parties { display: flex; justify-content: space-between; gap: 24px; margin-bottom: 28px; }
.inv-party { font-size: 13px; line-height: 1.55; }
.inv-party strong { font-size: 15px; display: block; margin-bottom: 2px; }
.inv-party-right { text-align: right; }
.inv-eyebrow {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-dim, #71717a);
  margin-bottom: 4px;
}
.inv-meta {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px 20px;
  padding: 16px 0;
  border-top: 1px solid var(--border, #e4e4e7);
  border-bottom: 1px solid var(--border, #e4e4e7);
  margin-bottom: 24px;
}
.inv-meta div { display: flex; flex-direction: column; }
.inv-meta span { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim, #71717a); }
.inv-meta strong { font-size: 14px; margin-top: 2px; }
.inv-items { width: 100%; border-collapse: collapse; font-size: 13px; margin-bottom: 8px; }
.inv-items thead th {
  background: #1f3a5f;
  color: #fff;
  text-align: left;
  padding: 9px 12px;
  font-weight: 600;
}
.inv-items thead th.num, .inv-items td.num { text-align: right; }
.inv-items tbody td { padding: 10px 12px; border-bottom: 1px solid var(--border, #eee); }
.inv-items tfoot td { padding: 8px 12px; }
.inv-items tfoot .inv-sub td { color: var(--text-dim, #555); }
.inv-items tfoot .inv-grand td { font-weight: 700; font-size: 15px; border-top: 2px solid #1f3a5f; }
.inv-notes { margin-top: 20px; font-size: 13px; }
.inv-bank { white-space: pre-wrap; }
.inv-foot { margin-top: 28px; font-size: 12px; text-align: center; }
@media print {
  .no-print { display: none !important; }
  .dash-sidebar, .dash-subnav { display: none !important; }
  body, .content { background: #fff !important; }
  .inv-print-shell { margin: 0; max-width: none; padding: 0; }
  .inv-doc { border: none; border-radius: 0; padding: 0; }
}
.row-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  object-fit: cover;
}
.row-avatar.fallback {
  background: linear-gradient(135deg, #7fb4e4 0%, #0c64ae 100%);
  display: inline-block;
}
.row-action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius);
  color: var(--text-dim);
  transition: background 80ms, color 80ms;
}
.row-action-icon:hover { background: var(--hover); color: var(--text); text-decoration: none; }
/* ---------- empty state ---------- */

.empty-state {
  text-align: center;
  padding: 48px 32px;
}
.empty-state h3 { margin: 0 0 6px; }
.empty-state p { margin: 0 0 16px; }
/* ---------- settings tabs ---------- */

.settings-layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 32px;
  align-items: flex-start;
  margin-top: 8px;
}
.tabs.vertical {
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-right: 1px solid var(--border-light);
  padding-right: 16px;
}
.tabs.vertical .tab {
  display: block;
  padding: 8px 12px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  transition: background 80ms, color 80ms;
}
.tabs.vertical .tab:hover { background: var(--hover); color: var(--text); text-decoration: none; }
.tabs.vertical .tab.active { background: var(--primary-soft); color: var(--primary); }
.settings-content { min-width: 0; }
/* ---------- profile photo ---------- */

.photo-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 14px;
}
.photo-current {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  background: var(--hover);
}
.photo-preview {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  display: inline-block;
}
.photo-preview.fallback {
  background: linear-gradient(135deg, #7fb4e4 0%, #0c64ae 100%);
}
/* Logo variant: square (rounded), larger, with object-fit:contain
   so non-square logos aren't cropped. */
.photo-preview.logo {
  width: 96px;
  height: 96px;
  border-radius: var(--radius);
  object-fit: contain;
  background: var(--hover);
  border: 1px solid var(--border-light);
  padding: 8px;
}
.photo-preview.logo.fallback {
  background: var(--hover);
  border: 1px dashed var(--border);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  font-size: 24px;
}
.photo-current-meta { font-size: 13px; }
.file-input input[type="file"] {
  border: 1px dashed var(--border);
  background: var(--card);
  padding: 18px;
  width: 100%;
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
}

/* Workspace logo on the App Info -> General tab. It shares the card
   with the name/industry form, so the whole control collapses into
   the preview row: picker and button sit beside the thumbnail rather
   than stacking a full-width dropzone above the fields it isn't part
   of. */
.logo-form { margin: 0 0 20px; }
.logo-form .photo-current { align-items: flex-start; }
.logo-form .photo-current-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 0;
}
.logo-form .field-label {
  font-weight: 500;
  font-size: 14px;
  color: var(--text);
}
/* Picker on its own line, button under it. Not a fallback from a
   wrapped row: the native file input renders at whatever width the
   engine feels like ("Choose File — No file chosen" is wider than it
   looks), so pinning it beside the button squeezes one or the other
   at exactly the widths this card is used at. */
.logo-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}
.logo-controls .file-input { flex: 1 1 100%; }
/* Compact picker: the dashed dropzone is right for a page that is
   only an upload, but oversized next to a text field. */
.logo-controls .file-input input[type="file"] {
  padding: 8px 10px;
  font-size: 13px;
}
@media (max-width: 640px) {
  .logo-form .photo-current { flex-direction: column; }
}
/* ---------- breadcrumbs (file manager) ---------- */

.breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.breadcrumbs a {
  color: var(--text-muted);
  text-decoration: none;
  padding: 2px 4px;
  border-radius: var(--radius-sm);
}
.breadcrumbs a:hover { color: var(--primary); background: var(--hover); }
.breadcrumbs .sep   { color: var(--text-dim); }
.breadcrumbs .here  { color: var(--text); font-weight: 600; }
/* ---------- modal ---------- */
/* Move file (files.html, its own inline script) and every dialog
   opened by static/js/modal.js — logging a call or adding a note on
   the record page and on the Call Centre queues. */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 19, 36, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  padding: 16px;
}
/* The HTML `hidden` attribute is normally enough, but our
   .modal-overlay rule sets display:flex, which overrides
   the user-agent's display:none for [hidden]. Reassert
   here so `<div class="modal-overlay" hidden>` actually
   stays hidden on initial render. */
.modal-overlay[hidden] { display: none; }
.modal {
  width: 100%;
  max-width: 460px;
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* A dialog holding a whole form (log a call) is taller than a short
     laptop viewport. Cap it and let .modal-body scroll, so the title
     and the submit button stay reachable instead of running off the
     bottom of the screen. */
  max-height: calc(100vh - 32px);
}
/* A dialog carrying a two-column .auth-form needs more than the 460px
   a "pick one folder" dialog does — at 460px the .row.two grid gives
   each control ~200px and the date/time pair stops being usable. */
.modal-wide { max-width: 620px; }
.modal > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-light);
}
.modal > header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}
.modal-close {
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px 8px;
  color: var(--text-dim);
  border-radius: var(--radius);
}
.modal-close:hover { background: var(--hover); color: var(--text); }
.modal-body {
  padding: 18px;
  /* Pairs with .modal's max-height: the body is the part that scrolls;
     header and footer stay pinned. min-height:0 is what actually lets
     a flex child shrink below its content height. */
  overflow-y: auto;
  min-height: 0;
}
/* When the <form> IS the dialog — header, then form wrapping
   .modal-body + footer, so the submit button is inside the form it
   submits. The form has to become the flex column .modal's children
   otherwise are, and .auth-form's own 14px gap has to go: it would
   open a stripe of card background between the body and the footer.
   The field spacing moves onto .modal-body instead. */
.modal > form.auth-form {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-height: 0;
}
.modal > form.auth-form > .modal-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* The body supplies the spacing; a <p>'s own margin on top of the flex
   gap leaves the intro line ("Call with <company>") floating. */
.modal > form.auth-form > .modal-body > p { margin: 0; }
.modal-body label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
}
.modal-body select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  color: var(--text);
  background: var(--card);
}
.modal-body select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(12, 100, 174, 0.16);
}
/* `.modal footer`, not `.modal > footer`: when the dialog's form wraps
   the body and the footer (see .modal > form.auth-form above) the
   footer is a grandchild. Widened rather than duplicated — files.html's
   direct-child footer still matches. */
.modal footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  border-top: 1px solid var(--border-light);
  background: var(--hover);
}
/* ---------- right-click context menu ---------- */
/* Used today by the Clients list. Generic enough to drop into
   any data-table — the JS template handles activation; the CSS
   below just paints the panel + items. */

.context-menu {
  position: fixed;
  z-index: 200;
  min-width: 180px;
  padding: 4px 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
.context-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 14px;
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  color: var(--text);
}
/* `display: flex` above beats a bare [hidden]; reset it so action
   items the shared context_menu.js suppresses (e.g. Delete on
   surfaces with no delete route) actually disappear. Same gotcha as
   the modal-overlay `[hidden]` rule. */
.context-menu button[hidden] { display: none; }
.context-menu button:hover { background: var(--hover); }
.context-menu button i { color: var(--text-dim); font-size: 13px; }
.context-menu button.danger { color: var(--danger); }
.context-menu button.danger:hover { background: var(--danger-soft); }
.context-menu button.danger i { color: var(--danger); }
/* ---------- content assets (media library) ---------- */

/* Row thumbnail in the Assets list. */
.asset-thumb {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--border-light);
  background: var(--hover);
}
.asset-thumb-fallback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
}
/* Larger preview on the asset detail page. */
.asset-preview {
  max-width: 100%;
  max-height: 360px;
  border-radius: 8px;
  border: 1px solid var(--border-light);
  background: var(--hover);
}
/* ---------- invoice line-item editor ---------- */

.invoice-items-wrap {
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  overflow: hidden;
  margin: 6px 0 12px;
  background: var(--card);
}
.invoice-items-wrap > button {
  margin: 8px;
}
table.invoice-items {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
table.invoice-items thead th {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-light);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: var(--hover);
  text-align: left;
}
table.invoice-items thead th.num,
table.invoice-items td.num { text-align: right; }
table.invoice-items tbody td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border-light);
  vertical-align: middle;
  color: var(--text);
}
table.invoice-items tbody tr:last-child td { border-bottom: none; }
table.invoice-items input,
table.invoice-items select {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  background: var(--card);
}
table.invoice-items input:focus,
table.invoice-items select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 2px rgba(12, 100, 174, 0.16);
}
table.invoice-items input[type="number"] {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
table.invoice-items td[data-row-total] {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}
table.invoice-items .col-actions { width: 1%; }
/* Give the Type column room (it now holds a searchable input) and keep
   the numeric Qty / Months columns compact. Columns: 1=Type, 2=Title,
   3=Qty, 4=Months, 5=Amount, 6=Total, 7=actions. */
table.invoice-items th:nth-child(1),
table.invoice-items td:nth-child(1) { width: 30%; }
table.invoice-items th:nth-child(3),
table.invoice-items td:nth-child(3),
table.invoice-items th:nth-child(4),
table.invoice-items td:nth-child(4) { width: 64px; }
.row-remove {
  color: var(--danger);
  background: transparent;
  border: none;
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
}
.row-remove:hover { background: var(--danger-soft); }
.invoice-totals {
  margin: 14px 0 8px;
  padding: 12px 14px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  background: var(--hover);
}
.invoice-totals .totals-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4px 0;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
}
.invoice-totals .totals-tax-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 14px;
}
.invoice-totals .totals-tax-label input {
  width: 68px;
  padding: 4px 8px;
  text-align: right;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-variant-numeric: tabular-nums;
}
.invoice-totals .totals-grand {
  padding-top: 10px;
  border-top: 1px solid var(--border);
  margin-top: 4px;
  font-size: 15px;
}
/* Right-align numeric columns in any data-table that uses .num. */
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
/* Inline checkbox + label, used by Partners' "Invoice in local
   currency" toggle. Sits flush with the form's other rows. */
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  color: var(--text);
  cursor: pointer;
}
.checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 0;
  flex-shrink: 0;
  accent-color: var(--primary);
  cursor: pointer;
}
.checkbox-row span { flex: 1; }
/* ---------- Table toolbar (search + filter) ---------- */

.table-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
  align-items: center;
}
.table-search-wrap {
  flex: 1;
  min-width: 200px;
  position: relative;
}
.table-search {
  width: 100%;
  padding: 8px 12px 8px 34px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 14px;
  background: var(--card);
  color: var(--text);
  transition: border-color 120ms, box-shadow 120ms;
}
.table-search-wrap::before {
  content: '';
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px; height: 14px;
  background: no-repeat center / contain url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><path d='m21 21-4.3-4.3'/></svg>");
  pointer-events: none;
}
.table-search:focus-visible {
  outline: none;
  border-color: var(--primary, #6366f1);
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
}
.table-filter {
  padding: 8px 28px 8px 12px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 14px;
  background: var(--card);
  color: var(--text);
  cursor: pointer;
  min-width: 140px;
}
.table-filter:focus-visible {
  outline: none;
  border-color: var(--primary, #6366f1);
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
}
.table-no-results {
  margin: 16px 0;
  text-align: center;
  font-size: 14px;
}
/* ---------- Sortable table headers ---------- */

.data-table th.th-sortable {
  cursor: pointer;
  user-select: none;
  position: relative;
  padding-right: 22px;
  transition: color 120ms;
}
.data-table th.th-sortable:hover { color: var(--text); }
.data-table th.th-sortable:focus-visible {
  outline: 2px solid var(--primary, #6366f1);
  outline-offset: -2px;
}
.data-table th.th-sortable::after {
  content: '';
  position: absolute;
  right: 8px;
  top: 50%;
  width: 0; height: 0;
  border: 4px solid transparent;
  opacity: 0.25;
  transform: translateY(-50%);
}
.data-table th.th-sort-asc::after {
  border-bottom-color: currentColor;
  opacity: 1;
  transform: translateY(-75%);
}
.data-table th.th-sort-desc::after {
  border-top-color: currentColor;
  opacity: 1;
  transform: translateY(-25%);
}
/* ---------- KPI tiles (dashboards) ---------- */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin-bottom: 24px;
}
.kpi {
  background: var(--card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
}
.kpi-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin: 0 0 8px;
}
.kpi-value {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  line-height: 1.1;
}
.kpi-sub {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--text-muted);
}
/* ---------- Funnel / proportional bars ---------- */

.funnel { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.funnel-row {
  display: grid;
  grid-template-columns: 140px 1fr 56px;
  gap: 12px;
  align-items: center;
  font-size: 13px;
}
.funnel-row .funnel-label { color: var(--text-muted); }
.funnel-row .funnel-count { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.funnel-bar {
  height: 18px;
  background: var(--hover);
  border-radius: 4px;
  overflow: hidden;
  position: relative;
}
.funnel-bar > span {
  display: block;
  height: 100%;
  background: var(--primary, #6366f1);
  border-radius: 4px;
  transition: width 240ms ease;
}
.funnel-bar.tone-new      > span { background: #93c5fd; }
.funnel-bar.tone-working  > span { background: #fcd34d; }
.funnel-bar.tone-proposal > span { background: #c4b5fd; }
.funnel-bar.tone-won      > span { background: #6ee7b7; }
.funnel-bar.tone-lost     > span { background: #fca5a5; }
/* Pipeline funnel (Seed -> Lead -> Opportunity -> Client), phase 8.
   Deepening blues so the narrowing funnel reads as one progression. */
.funnel-bar.tone-seed   > span { background: #bfdbfe; }
.funnel-bar.tone-lead   > span { background: #93c5fd; }
.funnel-bar.tone-opp    > span { background: #60a5fa; }
.funnel-bar.tone-client > span { background: #2563eb; }
/* ---------- Section quick-jump tiles (Overview) ---------- */

.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-top: 8px;
}
.tile {
  background: var(--card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms, transform 60ms, box-shadow 120ms;
}
.tile:hover { border-color: var(--border); transform: translateY(-1px); box-shadow: 0 1px 4px rgba(0,0,0,.05); }
.tile-icon { font-size: 20px; color: var(--primary, #6366f1); }
.tile-title { font-size: 15px; font-weight: 600; }
.tile-desc { font-size: 12px; color: var(--text-muted); }
/* ---------- FAQ accordion ---------- */

.faq-list { list-style: none; padding: 0; margin: 0; }
.faq-item { border-top: 1px solid var(--border-light); }
.faq-item:first-child { border-top: 0; }
.faq-item details { padding: 12px 0; }
.faq-item summary {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; cursor: pointer; list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-q { font-weight: 600; }
.faq-meta { display: flex; align-items: center; gap: 8px; }
.faq-a {
  margin-top: 8px;
  white-space: pre-wrap; line-height: 1.5;
  color: var(--text);
}
.faq-tags { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 4px; }
.faq-color-dot {
  width: 10px; height: 10px; border-radius: 50%;
  display: inline-block;
}
/* ---------- Ticket / message thread ---------- */

.ticket-thread { display: flex; flex-direction: column; gap: 12px; margin-top: 8px; }
.thread-msg {
  border: 1px solid var(--border-light);
  border-radius: 8px;
  padding: 10px 12px;
  background: var(--card);
}
.thread-msg-customer { background: var(--hover); }
.thread-msg-meta { font-size: 12px; margin-bottom: 6px; }
.thread-msg-body { white-space: pre-wrap; line-height: 1.45; }
.thread-msg-attachment { margin-top: 6px; font-size: 12px; }
.message-body {
  white-space: pre-wrap;
  line-height: 1.5;
  margin-top: 8px;
}
/* ---------- Kanban board ---------- */

.kanban-board {
  display: grid;
  grid-template-columns: repeat(5, minmax(220px, 1fr));
  gap: 12px;
  margin-top: 8px;
  overflow-x: auto;
}
.kanban-col {
  background: var(--hover);
  border: 1px solid var(--border-light);
  border-radius: 8px;
  display: flex; flex-direction: column;
  min-height: 240px;
}
.kanban-col-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border-light);
}
.kanban-col-header h4 { margin: 0; font-size: 13px; font-weight: 600; }
.kanban-count {
  background: var(--border-light); color: var(--text-muted);
  border-radius: 999px; padding: 1px 8px;
  font-size: 11px; font-weight: 600;
}
.kanban-col-body {
  padding: 8px; display: flex; flex-direction: column; gap: 8px;
  flex: 1; min-height: 60px; transition: background 120ms;
}
.kanban-col-body.is-over { background: #eef2ff; }
.kanban-card {
  background: var(--card);
  border: 1px solid var(--border-light);
  border-radius: 6px;
  padding: 10px;
  cursor: grab;
  user-select: none;
  transition: box-shadow 120ms, transform 60ms;
}
.kanban-card:active { cursor: grabbing; }
.kanban-card.is-dragging { opacity: 0.5; transform: rotate(1deg); }
.kanban-card:hover { box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.kanban-card-link { display: block; color: inherit; text-decoration: none; }
.kanban-card-title { font-weight: 600; margin-bottom: 4px; }
.kanban-card-meta  { font-size: 11px; }
.kanban-card-contact { font-size: 12px; margin-top: 6px; }
.kanban-card-tags { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.kanban-empty {
  font-size: 11px; color: var(--text-dim);
  text-align: center; padding: 10px;
  border: 1px dashed var(--border-light); border-radius: 6px;
  margin-top: auto;
}
.kanban-col-body .kanban-card + .kanban-empty { margin-top: 4px; }
.kanban-col.col-new      .kanban-col-header { border-bottom-color: #93c5fd; }
.kanban-col.col-working  .kanban-col-header { border-bottom-color: #fcd34d; }
.kanban-col.col-proposal .kanban-col-header { border-bottom-color: #c4b5fd; }
.kanban-col.col-won      .kanban-col-header { border-bottom-color: #6ee7b7; }
.kanban-col.col-lost     .kanban-col-header { border-bottom-color: #fca5a5; }
/* Stages added in phase 6. col-default catches any stage added to
   _STAGE_LABELS without an entry in _STAGE_CSS. */
.kanban-col.col-discovery   .kanban-col-header { border-bottom-color: #a5b4fc; }
.kanban-col.col-demo        .kanban-col-header { border-bottom-color: #7dd3fc; }
.kanban-col.col-negotiation .kanban-col-header { border-bottom-color: #fdba74; }
.kanban-col.col-hold        .kanban-col-header { border-bottom-color: #d4d4d8; }
.kanban-col.col-default     .kanban-col-header { border-bottom-color: var(--border); }
/* Secondary line inside a card or a table cell. Replaces the inline
   style="display:block; font-size:12px" this file's markup used to
   repeat — no inline styles (CLAUDE.md). */
.cell-sub { display: block; font-size: 12px; }
.kanban-sub { display: block; font-size: 11px; }
.kanban-age { font-size: 11px; }
.kanban-card-title .fa-thumbtack { font-size: 11px; }
/* ---------- Misc utilities ---------- */

.danger-heading { color: var(--danger); }
.btn-danger { color: var(--danger); }
/* ---------- responsive ---------- */

@media (max-width: 900px) {
  .dash-layout,
  .dash-layout.has-subnav { grid-template-columns: 1fr; }
  /* Subnav hidden on mobile — drawer-style sidebar remains the
     navigation control. Sub-items are reachable by clicking the
     parent in the sidebar (which navigates to the first sub-item's
     URL). Revisit if/when we add multi-child groups. */
  .dash-subnav { display: none; }
  .dash-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 100;
    width: 260px;
    transform: translateX(-100%);
    transition: transform 200ms ease;
    box-shadow: var(--shadow-lg);
  }
  body.menu-open .dash-sidebar { transform: translateX(0); }
  body.menu-open .nav-backdrop { display: block; }
  .nav-toggle {
    display: inline-flex;
    position: fixed;
    top: 14px;
    left: 14px;
    z-index: 101;
    background: var(--card);
  }
  body.menu-open .nav-toggle {
    /* While the drawer is open, the toggle moves with the sidebar
       so the X is always at the top-left of the panel. */
    position: absolute;
  }
  .dash-inner { padding: 64px 22px 48px; }
  .dash-header h1 { font-size: 22px; }
  .settings-layout {
    grid-template-columns: 1fr;
    gap: 18px;
  }
  .tabs.vertical {
    flex-direction: row;
    border-right: none;
    border-bottom: 1px solid var(--border-light);
    padding-right: 0;
    padding-bottom: 8px;
    overflow-x: auto;
  }
  .auth-form .row.two { grid-template-columns: 1fr; }
  .dash-header-row { flex-direction: column; align-items: stretch; }
  .data-table thead { display: none; }
  .data-table tbody td { display: block; padding: 8px 16px; border: none; }
  .data-table tbody tr {
    display: block;
    padding: 12px 0;
    border-bottom: 1px solid var(--border-light);
  }
  .data-table tbody tr:last-child { border-bottom: none; }
}
/* ---- Contents editor: token-insert dropdowns (Segments / Assets) ----
   Rendered by static/js/editor_init.js above a rich-text editor when the
   textarea carries data-editor-variables. Used by Segments / Landing Pages. */
.editor-variable-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
}
.editor-variable-select {
  font-size: 13px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface, #fff);
  color: var(--text);
}
/* ---- Contents → Logs (generated-output browser) ---- */
.logs-breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 13px; margin: 4px 0 14px; }
.logs-breadcrumb a { color: var(--text); text-decoration: none; }
.logs-breadcrumb a:hover { text-decoration: underline; }
.logs-row-actions { display: flex; gap: 10px; }
.code-block {
  background: var(--surface, #f7f7f8);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 12px 14px;
  font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre;
  overflow: auto;
}
.logs-file { max-height: 60vh; margin: 12px 0 0; }
/* ============================================================
   CRM record page — seeds / leads / opportunities
   ============================================================
   One shared layout for all three pipeline stages, rendered by
   templates/_record_macros.html. The three surfaces used to be
   hand-written templates that drifted apart; if you find yourself
   adding a .seed-only / .lead-only rule here, they are drifting
   again — fix the macro instead.

   Tabs are same-page sections, not separate URLs. record_tabs.js
   toggles .active; the first tab ships active from the server so
   the page renders correctly with JS disabled. */

.record-header { align-items: flex-start; gap: 16px; }
.record-ident { min-width: 0; }
.record-number { margin: 2px 0 0; font-family: var(--mono); font-size: 12.5px; color: var(--text-muted); }
.record-pills { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.record-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
/* Interest and priority pills. Values come from the shared enum maps
   (clsCrmEnums.INTEREST_LABELS / PRIORITY_LABELS) — the number in the
   class is the enum int, so adding a value means adding a rule here. */
.pill.interest-0 { background: var(--surface-2); color: var(--text-muted); }
.pill.interest-1 { background: var(--warn-soft); color: var(--warn); }
.pill.interest-2 { background: var(--danger-soft); color: var(--danger); }
.pill.priority-0 { background: var(--surface-2); color: var(--text-muted); }
.pill.priority-1 { background: var(--surface-2); color: var(--text-dim); }
.pill.priority-2 { background: var(--warn-soft); color: var(--warn); }
.pill.priority-3 { background: var(--danger-soft); color: var(--danger); }
/* Opportunity stage strip — the pipeline rendered as steps on the
   record page. Labels and order come from clsOpportunities._stages()
   via lookups.Stages; the accent class is _STAGE_CSS, the SAME
   col-* vocabulary the Kanban columns use, so a stage only ever needs
   one colour defined (see .stage-step.col-* below). Never spell the
   stage names out in a template. */
.stage-strip { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.stage-step {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 999px;
  border: 1px solid var(--border-light);
  background: var(--surface-2);
  font-size: 12px; color: var(--text-muted); white-space: nowrap;
}
.stage-step .stage-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--border); flex-shrink: 0;
}
.stage-step.current { color: var(--text); font-weight: 600; border-color: var(--border); }
.stage-step.col-new         .stage-dot { background: #93c5fd; }
.stage-step.col-working     .stage-dot { background: #fcd34d; }
.stage-step.col-discovery   .stage-dot { background: #a5b4fc; }
.stage-step.col-demo        .stage-dot { background: #7dd3fc; }
.stage-step.col-proposal    .stage-dot { background: #c4b5fd; }
.stage-step.col-negotiation .stage-dot { background: #fdba74; }
.stage-step.col-won         .stage-dot { background: #6ee7b7; }
.stage-step.col-lost        .stage-dot { background: #fca5a5; }
.stage-step.col-hold        .stage-dot { background: #d4d4d8; }
.stage-step.col-default     .stage-dot { background: var(--border); }
/* An inline checkbox row inside an .auth-form. `.auth-form label`
   forces flex-direction: column and out-specifies .checkbox-row, so
   the row direction has to be restated here or the box lands above
   its caption. */
.auth-form label.checkbox-row { flex-direction: row; align-items: center; }
.record-tabs { display: flex; gap: 2px; overflow-x: auto; border-bottom: 1px solid var(--border); margin: 18px 0 0; }
.record-tab {
  appearance: none; background: none; border: 0; cursor: pointer;
  padding: 9px 13px; font-size: 13.5px; white-space: nowrap;
  color: var(--text-muted); border-bottom: 2px solid transparent;
}
.record-tab:hover { color: var(--text); }
.record-tab.active { color: var(--text); border-bottom-color: var(--primary); }
.record-tab-count {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  background: var(--surface-2); border-radius: 10px;
  font-size: 11.5px; color: var(--text-muted);
}
.record-panel { display: none; padding: 18px 0 0; }
.record-panel.active { display: block; }
/* Row gap was 0. With four field groups in a 3-up auto-fit grid the
   fourth wraps to row 2 — "Follow-up" landing directly under "Company"
   with its heading flush against the last field's rule, reading as one
   run-on list rather than two sections. The row gap has to carry that
   separation because each group's own <h3> is :first-of-type inside its
   wrapper, so the section-title top margin/border zeroes itself out. */
.record-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 26px 28px; }
.record-field-group { min-width: 0; }
.record-field { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--border-light); }
.record-field-label { font-size: 13px; color: var(--text-muted); }
.record-field-value { font-size: 13px; text-align: right; word-break: break-word; }
.record-field-value.dim { color: var(--text-muted); }
.record-notes { margin-top: 20px; }
.record-notes p { white-space: pre-wrap; font-size: 13.5px; }
/* People. The owner row has no Remove control on purpose: the key
   account holder is a column on the record, changed on the edit page,
   and crm_record_users only mirrors it. */
.record-people-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin: 18px 0 6px; }
.record-people-head:first-child { margin-top: 0; }
.record-people-head p { margin: 2px 0 0; font-size: 12.5px; }
.person-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border-light); }
.person-name { flex: 1; min-width: 0; font-size: 13.5px; }
.person-role-form { margin: 0; }
.person-role-form select { font-size: 12.5px; padding: 3px 6px; }
.person-add-form { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
/* Tasks. Applying a template copies it into a real row — a task never
   points back at the template it came from. */
.record-tasks-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.record-tasks-head p { margin: 0; font-size: 13px; }
.apply-templates-form { display: flex; gap: 8px; margin: 0; }
.task-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border-light); }
.task-row.done .task-title { color: var(--text-muted); text-decoration: line-through; }
.task-toggle { appearance: none; background: none; border: 0; cursor: pointer; color: var(--text-muted); font-size: 15px; padding: 0; }
.task-row.done .task-toggle { color: var(--success); }
.task-title { flex: 1; min-width: 0; font-size: 13.5px; }
.task-meta { font-size: 12.5px; color: var(--text-muted); white-space: nowrap; }
.task-meta.overdue { color: var(--danger); }
.task-add-form { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.task-add-form input[type="text"] { flex: 1; min-width: 180px; }
/* Activity — what happened, as opposed to tasks, which are what should
   happen. Immutable except for deleting a mis-logged row. */
/* The Activity tab is the timeline and nothing else; the two ways of
   adding to it open as modals from here. This bar sits above the first
   entry, so it needs a rule under it or the buttons read as part of
   the newest row. */
.record-activity-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-bottom: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border-light);
}
.record-activity-actions .activity-actions-label {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 12.5px;
}
.activity-row { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--border-light); }
.activity-main { min-width: 0; }
.activity-type { font-size: 13.5px; }
.activity-subject { margin-left: 6px; font-size: 13.5px; color: var(--text-dim); }
.activity-notes { margin: 3px 0 0; font-size: 12.5px; color: var(--text-muted); white-space: pre-wrap; }
.activity-meta { display: flex; align-items: center; gap: 10px; font-size: 12.5px; color: var(--text-muted); white-space: nowrap; }
/* Lineage — Seed -> Lead -> Opportunity -> Client, from any stage. */
.record-lineage { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.lineage-step {
  display: inline-flex; flex-direction: column; gap: 1px;
  padding: 7px 14px; border-radius: var(--radius-sm);
  border: 1px dashed var(--border); color: var(--text-muted);
  font-size: 13px; text-decoration: none;
}
.lineage-step.done { background: var(--primary-soft); border-style: solid; border-color: transparent; color: var(--primary); }
.lineage-step.current { background: var(--surface-2); border-style: solid; border-color: var(--border); color: var(--text); }
.lineage-number { font-family: var(--mono); font-size: 11.5px; opacity: .8; }
.lineage-arrow { color: var(--text-muted); font-size: 11px; }
/* ---- in-place row editor (task sets) ----
   A task set has no separate item page to send anyone to, so the row IS
   the editor: a <details> in a full-width row directly under the row it
   belongs to, because a form of this size crammed into the actions
   column is unusable, and a <details> needs no JavaScript to open.

   That row is marked `data-row-detail` in the markup; the attribute is
   what table_sort.js reads to keep it attached to its parent through
   sorting, searching and filtering. It is a CSS hook here only via
   .row-editor-row.

   The Call Centre queues used to carry the same shape for their
   "log a call" form (.call-log / .call-log-row, now deleted). One
   collapsed toggle under every one of 500 rows doubled the height of
   the page, which is why the padding here is so tight — and in the end
   why those two lists moved to a single modal per page instead. */
.data-table tbody tr.row-editor-row > td {
  border-top: none;
  padding-top: 0;
  padding-bottom: 4px;
}
.row-editor > summary {
  cursor: pointer;
  display: inline-block;
  font-size: 12.5px;
  color: var(--text-muted);
  padding: 0 0 2px;
}
.row-editor > summary:hover { color: var(--primary); }
.row-editor[open] > summary {
  color: var(--text);
  font-weight: 500;
  padding-bottom: 6px;
}
/* .auth-form supplies the stacked label layout and the .row.two grid;
   this only insets and boxes the opened form so it reads as belonging
   to the row above it. */
.row-editor[open] > form {
  margin: 0 0 14px;
  padding: 14px 16px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  /* Keeps the form readable on a wide table without letting it stretch
     the full 9-column width into one enormous line of controls. */
  max-width: 46rem;
}
/* Two or more controls in one row's actions cell — the Call Centre
   queues put "Log a call" next to the +5d snooze. .col-actions is
   text-align:right, which does nothing for a <form> block child, so
   the flex row does the alignment instead. */
.row-actions { display: flex; gap: 8px; justify-content: flex-end; }
.row-actions form { margin: 0; }
/* ---------------------------------------------------------------
   Seed types + task sets (Settings, migration 011)

   Two settings surfaces whose whole job is to make one idea legible:
   a task SET is a reusable process assigned by role, and a seed TYPE
   binds people to it. Everything below exists to show that on screen —
   there is no new interaction here, only naming.
   --------------------------------------------------------------- */

/* The countries picker on a seed type. A multi-select over the
   `countries` global, so the stored values are always real ISO-2 codes;
   a native multi-select otherwise renders at whatever height the browser
   feels like, which is one row in some engines. */
.country-multi {
  min-height: 150px;
  padding: 6px 8px;
}
.country-multi option { padding: 2px 2px; }
/* An inherited person on a record's People tab: they come from the
   record's type and are resolved live on every read, so there is no
   per-record row to edit and no Remove button on the row. The tint is
   the only thing that says "this one is not yours to change here" once
   the section heading has scrolled away. */
.person-row.inherited {
  background: var(--surface-2, var(--hover));
  border-left: 2px solid var(--primary-soft);
  padding-left: 8px;
}
.pill.inherited-pill {
  background: var(--primary-soft);
  color: var(--primary);
  border-color: transparent;
}
/* ---------------------------------------------------------------
   Per-user roles matrix (Settings -> Users -> Roles)

   One row per permission module, one switch per action. The whole
   surface is a table because that is what it is: a grid you read
   across (what can this person do here?) and down (who can touch
   this module?). The switches are real checkboxes — the track is
   drawn from the input's own state, so the page works with the
   keyboard and without JS; roles_matrix.js only adds the group and
   full-access conveniences on top.
   --------------------------------------------------------------- */

/* Copy-from-another-user row. Sits above the toolbar as its own
   banded strip so it reads as a different action from the Save
   below it — one loads, the other writes. */
.perm-copy {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  background: var(--hover);
}
.perm-copy-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 260px;
}
.perm-copy-field > span {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
}
.perm-copy-field select { min-width: 260px; }
.perm-copy-hint { font-size: 12px; padding-bottom: 8px; }
@media (max-width: 640px) {
  .perm-copy-field, .perm-copy-field select { min-width: 0; width: 100%; }
}

.perm-toolbar,
.perm-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.perm-toolbar { margin: 0 0 14px; }
.perm-footer  { margin: 16px 0 0; }
.perm-footer p { margin: 0; max-width: 46rem; font-size: 13px; }
.perm-toolbar-actions { display: flex; gap: 8px; }

/* Action columns are fixed-width so the switches line up in a grid
   rather than drifting with the module name's length. */
.perm-table .perm-col-action {
  width: 92px;
  text-align: center;
}
.perm-table .perm-col-title { text-align: left; }
.perm-table td.perm-col-title { padding-top: 10px; padding-bottom: 10px; }

.perm-label { font-weight: 500; }
.perm-note {
  display: block;
  margin-top: 2px;
  color: var(--text-dim);
  font-size: 12px;
}
.perm-na { color: var(--border); }

/* Group header row — a band across the table, with its own switch on
   the right that ticks everything below it. */
.perm-group-row > th {
  padding-top: 16px;
  text-align: left;
  font-size: 13px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--surface-2);
}
/* The group's own switch sits to the LEFT of its name, not out in the
   action columns — it toggles the whole group, so it belongs with the
   thing it names rather than under a column header it doesn't answer
   to. Kept inline (NOT flex on the cell: `display:flex` on a <th>
   drops it out of the table box model and the colspan stops working). */
.perm-group-switch {
  margin-right: 10px;
  vertical-align: middle;
}
.perm-group-row > th > span { vertical-align: middle; }

.perm-row.locked .perm-label { color: var(--text-muted); }
.pill.admin-only {
  margin-left: 8px;
  background: var(--warn-soft);
  color: var(--warn);
  border-color: #f5c79b;
}

/* The switch. 36x20 track, 14px knob, 3px inset — small enough to sit
   in a table row without stretching it. */
.perm-switch {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}
.perm-switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.perm-switch-track {
  position: relative;
  display: block;
  width: 36px;
  height: 20px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  transition: background 120ms ease, border-color 120ms ease;
}
.perm-switch-track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--card);
  box-shadow: var(--shadow-sm);
  transition: transform 120ms ease;
}
.perm-switch input:checked + .perm-switch-track {
  background: var(--primary);
  border-color: var(--primary);
}
.perm-switch input:checked + .perm-switch-track::after {
  transform: translateX(16px);
}
/* Focus lands on the hidden input, so the visible ring has to be
   drawn by its sibling — without this the switch is unusable by
   keyboard. */
.perm-switch input:focus-visible + .perm-switch-track {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
/* Half-on: some but not all of the row/group is granted. */
.perm-switch input:indeterminate + .perm-switch-track {
  background: var(--primary-soft);
  border-color: var(--primary-line);
}
.perm-switch input:indeterminate + .perm-switch-track::after {
  transform: translateX(8px);
}

.alert.info {
  background: var(--primary-soft);
  color: var(--primary);
  border-color: var(--primary-line);
}
.alert.info a { color: inherit; text-decoration: underline; }

/* Visible to screen readers only — the switches carry no visible
   label of their own, the column header is the label. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 720px) {
  .perm-table .perm-col-action { width: 64px; }
}
