/* ============================================================================
   Kenhos-FS UI — ported verbatim from the Django proof-of-concept design system
   (dev-stuff/djangosrc/templates/base.html + per-page extra_css). Foundation,
   shell (sidebar/topbar), and shared components live here. White-label colours
   come from CSS vars set by App.razor (defaults match the Django palette).
   ============================================================================ */

:root {
    --accent: #3b82f6;
    --sidebar-bg: #1e293b;
    --sidebar-active: #334155;
    --sidebar-fg: #cbd5e1;
    --shadow: 0 1px 3px rgba(0,0,0,.08);
}

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

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-size: 0.875rem;
    background: #f5f5f5;
    color: #333;
}

/* ---- Layout shell (base.html) ---- */
.layout { display: flex; min-height: 100vh; }

.sidebar { width: 240px; flex-shrink: 0; background: var(--sidebar-bg); color: #fff; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; }
.sidebar-header { padding: 20px; font-size: 1.25rem; font-weight: 600; border-bottom: 1px solid var(--sidebar-border, #334155); }
.sidebar-logo { max-height: 28px; display: block; }
.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; }
.sidebar-nav::-webkit-scrollbar { width: 8px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.12); border-radius: 4px; }

.nav-icon { width: 18px; height: 18px; flex-shrink: 0; vertical-align: middle; }
.sidebar-nav a { display: flex; align-items: center; gap: 10px; padding: 10px 20px; color: var(--sidebar-fg); text-decoration: none; transition: background 0.15s; }
.sidebar-nav a:hover, .sidebar-nav a.active { background: var(--sidebar-active); color: #fff; text-decoration: none; }
.nav-label { display: flex; align-items: center; gap: 10px; }

.nav-parent-toggle { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 10px 20px; color: var(--sidebar-fg); background: none; border: none; font: inherit; text-align: left; cursor: pointer; transition: background 0.15s; }
.nav-parent-toggle:hover { background: var(--sidebar-active); color: #fff; }
.nav-arrow::after { content: "\25B8"; font-size: 0.75rem; transition: transform 0.15s; display: inline-block; }
.nav-parent-toggle.open .nav-arrow::after { transform: rotate(90deg); }
.nav-children { display: none; }
.nav-children.open { display: block; }
.nav-children a { padding-left: 48px; }

/* The tenant section label is the tenant's name, which can be a single 50-char word —
   let it break anywhere so it wraps inside the fixed-width sidebar instead of clipping. */
.nav-section-label { padding: 32px 20px 4px; font-size: 0.6rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--sidebar-fg); opacity: .6; overflow-wrap: anywhere; }
.nav-section-label:first-child { padding-top: 4px; }

.nav-coming-soon { display: flex; align-items: center; gap: 10px; padding: 10px 20px; color: #475569; font-size: inherit; }
.nav-coming-soon .badge-soon, .nav-child-coming-soon .badge-soon { font-size: 0.55rem; background: #334155; color: #64748b; padding: 1px 6px; border-radius: 9999px; margin-left: auto; }
.nav-child-coming-soon { display: flex; align-items: center; padding: 8px 20px 8px 48px; color: #475569; font-size: inherit; }

.sidebar-footer { padding: 14px 20px; border-top: 1px solid var(--sidebar-border, #334155); display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nav-version { font-size: .72rem; color: var(--sidebar-muted, #94a3b8); font-variant-numeric: tabular-nums; }
.role-badge { font-size: 0.62rem; text-transform: uppercase; letter-spacing: .05em; background: #334155; color: #94a3b8; padding: 3px 8px; border-radius: 6px; }

.content-area { flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* ---- Top bar (base.html) ---- */
.topbar { background: var(--topbar-bg, #fff); color: var(--topbar-fg, #334155); border-bottom: 1px solid rgba(120,120,120,.22); padding: 0 20px; height: 44px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; box-shadow: 0 1px 3px rgba(0,0,0,0.06); position: sticky; top: 0; z-index: 20; }
.topbar-breadcrumb { display: flex; align-items: center; gap: 5px; font-size: 0.8rem; color: var(--topbar-fg, #5a6472); }
.topbar-breadcrumb a { color: var(--topbar-fg, #8b95a1); opacity: .68; text-decoration: none; }
.topbar-breadcrumb a:hover { opacity: 1; }
.topbar-breadcrumb .separator { color: var(--topbar-fg, #c8cdd4); opacity: .4; }
.topbar-breadcrumb .current { color: var(--topbar-fg, #0d1117); font-weight: 500; }
.topbar-right { display: flex; align-items: center; gap: 16px; }
.topbar-user { font-size: 0.8rem; color: var(--topbar-fg, #5a6472); opacity: .82; text-decoration: none; }
.topbar-user:hover { opacity: 1; }
.topbar-logout { font-size: 0.8rem; color: var(--topbar-fg, #5a6472); background: none; border: 1px solid rgba(120,120,120,.35); border-radius: 4px; padding: 4px 10px; cursor: pointer; font-family: inherit; }
.topbar-logout:hover { background: rgba(120,120,120,.12); }
.topbar-divider { width: 1px; height: 20px; background: var(--topbar-fg, #dde1e7); opacity: .25; }

.tenant-selector { display: flex; align-items: center; }
.tenant-current { font-size: 0.8rem; color: #374151; font-weight: 500; }
.tenant-dropdown { position: relative; }
.tenant-dropdown-toggle { display: flex; align-items: center; gap: 6px; font-size: 0.8rem; font-family: inherit; color: #374151; background: #f9fafb; border: 1px solid #d1d5db; border-radius: 4px; padding: 4px 10px; cursor: pointer; white-space: nowrap; }
.tenant-dropdown-toggle:hover { background: #f3f4f6; }
/* The label span must be allowed to shrink inside the flex button, or a max-length name
   (tenant names go to 100 chars) blows the button out past its container and gets hard-clipped.
   Ellipsise instead; the caret always stays visible. */
.tenant-dropdown-toggle > span:first-child { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; text-align: left; }
.tenant-dropdown-toggle .caret { flex: none; }
/* Top-bar tenant switcher: make it generously wide (name left, caret right), but never so wide
   a long tenant name crowds the breadcrumb or pushes Sign out off-screen. */
.topbar-right .tenant-dropdown-toggle { min-width: 300px; max-width: 460px; justify-content: space-between; }
.topbar-right .tenant-dropdown-menu { min-width: 300px; }
/* SearchSelect used as a toolbar filter: match the height/shape of .input / select.input next to it. */
.tenant-dropdown.ss-input { min-width: 200px; }
.tenant-dropdown.ss-input .tenant-dropdown-toggle { width: 100%; height: 36px; padding: 7px 10px; font-size: inherit; border-radius: 6px; background: #fff; justify-content: space-between; }
/* Wide variant: a roomier field whose dropdown panel matches the field width exactly. */
.tenant-dropdown.ss-wide { width: 380px; max-width: 100%; }
/* Match the height/shape of .input / select.input so it lines up with adjacent form controls. */
.tenant-dropdown.ss-wide .tenant-dropdown-toggle { width: 100%; height: 36px; padding: 7px 10px; font-size: inherit; border-radius: 6px; background: #fff; justify-content: space-between; }
.tenant-dropdown.ss-wide .tenant-dropdown-menu { left: 0; right: auto; width: 100%; min-width: 0; max-width: none; }
/* Locked/disabled search-select (e.g. Timezone while Business hours = Same as tenant). */
.tenant-dropdown-toggle:disabled { background: #f8fafc; color: #94a3b8; cursor: not-allowed; }
.tenant-dropdown-menu { display: none; position: absolute; top: calc(100% + 4px); right: 0; background: #fff; border: 1px solid #d1d5db; border-radius: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); min-width: 330px; max-width: 480px; z-index: 100; overflow: hidden; }
.tenant-dropdown-menu.open { display: block; }
.tenant-search-wrap { padding: 8px; border-bottom: 1px solid #e5e7eb; }
.tenant-search { width: 100%; padding: 6px 8px; border: 1px solid #d1d5db; border-radius: 4px; font-size: 0.8rem; font-family: inherit; }
.tenant-search:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); }
.tenant-options { max-height: 240px; overflow-y: auto; }
.tenant-option { padding: 8px 12px; font-size: 0.8rem; color: #374151; cursor: pointer; }
.tenant-option:hover { background: #f3f4f6; }
.tenant-option.selected { background: #eff6ff; color: #1e40af; font-weight: 500; }

.main-content { flex: 1; padding: 32px; background-color: var(--app-bg, #f5f5f5); background-image: var(--app-bg-image, none); background-size: cover; background-position: center; background-attachment: fixed; }

/* Dashboard charts. Fixed chart height (an svg with height:100% balloons with no bound).
   Inside the dashboard every panel is a white card (consistent with the bar + totals boxes). */
.dash .graph-card { background: #fff; box-shadow: var(--shadow); border: none; }
.dash-chart { width: 100%; height: 220px; display: block; }
.chart-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: .78rem; color: #64748b; margin-top: 8px; padding-left: 16px; }
.chart-legend .sw, .calls-breakdown .sw, .history-totals .sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: middle; }

/* Grouped calls figures bar (active now / historical totals) — big, equal tiles. Spans its
   container (full width as the "Now" KPI strip; one chart-card wide inside the History grid). */
.callsbar { display: flex; background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 18px 8px; }
.callsbar .cbig { flex: 1; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 0 6px; border-right: 1px solid #f1f5f9; }
.callsbar .cbig:last-child { border-right: none; }
.callsbar .cbig.divide { border-left: 2px solid #e5e7eb; }
.callsbar .cbig .n { font-size: 1.9rem; font-weight: 600; line-height: 1; color: #1e293b; white-space: nowrap; }
.callsbar .cbig .n .of { font-size: 1rem; font-weight: 500; color: #94a3b8; }
.callsbar .cbig .t { font-size: .78rem; color: #64748b; margin-top: 6px; }

/* Dashboard: a 3-column grid; panels span 1/3 (.c1), 2/3 (.c2) or full width (default).
   align-items:stretch keeps every panel in a row the same height. */
.dash { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; align-items: stretch; }
.dash > * { grid-column: 1 / -1; }            /* full width unless told otherwise */
.dash > .c1 { grid-column: span 1; }
.dash > .c2 { grid-column: span 2; }
.dash-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0; padding-bottom: 8px; border-bottom: 1px solid #e5e7eb; }
.dash-section-head:not(:first-child) { margin-top: 10px; }
.dash-section-head h2 { font-size: .95rem; font-weight: 600; color: var(--header-text, #1e293b); margin: 0; display: flex; align-items: center; gap: 8px; }
@media (max-width: 980px) { .dash { grid-template-columns: 1fr; } .dash > .c1, .dash > .c2 { grid-column: 1 / -1; } }

/* History totals (1/3): five mini-boxes filling the row height; coloured numbers are the chart key */
.totals-stack { display: flex; flex-direction: column; gap: 10px; }
/* min-height:0 + compact rows so the five boxes divide the chart's height beside them, rather than
   forcing the row taller than the chart. */
.totals-stack .tbox { flex: 1; min-height: 0; background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 8px 16px; display: flex; align-items: center; gap: 10px; }
.totals-stack .tbox .t { font-size: .95rem; color: #475569; }
.totals-stack .tbox .n { margin-left: auto; font-size: 1.6rem; font-weight: 600; line-height: 1; color: #1e293b; }
/* Stat boxes that deep-link to their detail page (dashboard Now / History). */
.callsbar a.cbig, .totals-stack a.tbox { text-decoration: none; color: inherit; cursor: pointer; transition: background .12s; }
.callsbar a.cbig:hover, .totals-stack a.tbox:hover { background: #f8fafc; }
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: #16a34a; box-shadow: 0 0 0 0 rgba(22,163,74,.5); animation: live-pulse 2s infinite; }
@keyframes live-pulse { 0%,100% { box-shadow: 0 0 0 0 rgba(22,163,74,.5); } 50% { box-shadow: 0 0 0 5px rgba(22,163,74,0); } }

/* Chart axes */
.chart-area { display: flex; gap: 8px; }
.chart-yaxis { display: flex; flex-direction: column; justify-content: space-between; height: 200px; min-width: 30px; text-align: right; font-size: .7rem; color: #94a3b8; }
.chart-plot { flex: 1; min-width: 0; }
.chart-xaxis { display: flex; justify-content: space-between; font-size: .7rem; color: #94a3b8; margin-top: 4px; padding-left: 38px; }

/* ---- Page banner alert (base.html) ---- */
.alert { background: #fef3c7; border: 1px solid #f59e0b; color: #92400e; padding: 12px 16px; border-radius: 6px; margin-bottom: 24px; font-size: 0.875rem; }
.alert a { color: #92400e; font-weight: 600; }
.alert-success { background: #f0fdf4; color: #166534; border: 1px solid #bbf7d0; padding: 10px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.85rem; }
.alert-error { background: #fef2f2; color: #991b1b; border: 1px solid #fecaca; padding: 10px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.85rem; }
.alert-info { background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe; padding: 10px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.85rem; }
.alert-ok { background: #f0fdf4; color: #166534; border: 1px solid #bbf7d0; padding: 10px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.85rem; }
.alert-warn { background: #fffbeb; color: #92400e; border: 1px solid #fde68a; padding: 10px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.85rem; }

/* ---- Pagination (base.html) ---- */
.pagination-bar { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; font-size: 0.75rem; color: #64748b; }
.pagination-bar .pagination-info { display: flex; align-items: center; gap: 8px; }
.pagination-bar select { padding: 4px 6px; border: 1px solid #d1d5db; border-radius: 4px; font-size: 0.75rem; font-family: inherit; background: #fff; cursor: pointer; }
.pagination-nav { display: flex; align-items: center; gap: 2px; }
.pagination-nav a, .pagination-nav span { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 6px; border: 1px solid #d1d5db; border-radius: 4px; text-decoration: none; color: #374151; font-size: 0.75rem; background: #fff; }
.pagination-nav a:hover { background: #f3f4f6; }
.pagination-nav .current { background: var(--accent, #1e293b); color: #fff; border-color: var(--accent, #1e293b); }
.pagination-nav .disabled { color: #c8cdd4; cursor: default; pointer-events: none; }

/* ============================================================================
   Shared components (ported from the Django per-page extra_css blocks)
   ============================================================================ */

h1 { font-size: 1.5rem; font-weight: 600; color: var(--header-text, #1e293b); margin: 0 0 22px; }
.page-header { margin-bottom: 24px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.page-header h1 { margin: 0; }
/* Header-aligned Save/Cancel. Save is the solid dark primary; Cancel is a solid light-grey
   button — still obviously a button, but easy to tell apart from Save at a glance. */
.header-actions { display: flex; align-items: center; gap: 8px; }
.header-actions .btn { min-width: 92px; justify-content: center; }
.header-actions .btn-secondary { background: #e2e8f0; border-color: #cbd5e1; color: #334155; }
.header-actions .btn-secondary:hover { background: #cbd5e1; }
h1:focus, h1:focus-visible, [tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; }
h2 { font-size: 1.05rem; font-weight: 600; color: var(--header-text, #1e293b); }
h3 { font-size: 0.95rem; font-weight: 600; color: #1e293b; }
a { color: var(--accent, #2563eb); text-decoration: none; }
a:hover { text-decoration: underline; }
.muted { color: #94a3b8; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem; }
.page-lead { color: #64748b; margin: 0 0 20px; max-width: 80ch; }
/* Inline page breadcrumbs are redundant — the topbar renders the breadcrumb trail. */
.breadcrumb { display: none; }

/* Toolbar above tables */
.toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.toolbar .spacer { flex: 1; }
.search-box, input.search { padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: inherit; width: 240px; font-family: inherit; }
.search-box:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.15); }

/* Stat cards */
.stat-bar, .stat-cards { display: flex; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; }
.stat-card { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 14px 20px; flex: 1; min-width: 160px; }
/* Fixed-width cards (pack from the left, don't stretch) — keeps a lone card from ballooning. */
.stat-cards.compact .stat-card { flex: 0 0 270px; }
/* Reusable half-width variant — packs more, narrower cards. Opt in per page. */
.stat-cards.narrow .stat-card { flex: 0 0 135px; min-width: 0; }
/* …but a card carrying a wider value (e.g. an HH:MM:SS talk-time total) can opt back out. */
.stat-cards.narrow .stat-card.wide { flex: 0 0 200px; }
/* Queue statistics: roughly double-width cards that still GROW to fill each row, so the block always uses
   the full page width however many figures there are. */
.stat-cards.fill .stat-card { flex: 1 1 230px; min-width: 230px; }
/* A settings row whose control cell spans the whole row, so something (a help icon) can sit hard against
   the right-hand edge. The select inside keeps its own width; .row-end's spacer does the pushing. */
.row-end { display: flex; align-items: center; gap: .4rem; width: 100%; }
.row-end > .spacer { flex: 1; }
/* The cell spans the row so the icon can reach its right edge, but the CONTROL must keep the width it has
   on every other row — 640px, the .rg-editor control column — instead of stretching to fill. */
.row-end > .input { flex: 0 1 640px; }
.stat-card .value { font-size: 1.25rem; font-weight: 700; color: #1e293b; }
.stat-card .label { font-size: 0.7rem; color: #94a3b8; text-transform: uppercase; letter-spacing: .03em; margin-top: 2px; }
.stat-card.green .value { color: #166534; }
.stat-card.red .value { color: #991b1b; }
.stat-card.amber .value { color: #92400e; }
/* Call Handling's per-type cards read in the same colour as that type's pill, so the count and the rows it
   counts are obviously the same thing. Text-weight colours (the pill's foreground), not the pale fill. */
.stat-card.type-ring_group .value { color: #1e40af; }
.stat-card.type-announcement .value { color: #6d28d9; }
.stat-card.type-ivr .value { color: #9f1239; }
.stat-card.type-cfc .value { color: #92400e; }
.stat-card.type-tc .value { color: #166534; }

/* Hub / settings index card grid */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
/* Integrations list: 25% wider cards, all equal width (1fr) and equal height (grid stretch + flex card). */
.integrations-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(325px, 1fr)); gap: 16px; align-items: stretch; }
.integrations-grid > .card { display: flex; flex-direction: column; }
.integrations-card-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; }
.integrations-card-head .badges { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }
.card { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 18px 20px; }
.card-link { display: block; color: #333; }
.card-link:hover { box-shadow: 0 2px 8px rgba(0,0,0,.12); text-decoration: none; }
.card-kicker { font-size: 0.68rem; text-transform: uppercase; letter-spacing: .05em; color: #94a3b8; font-weight: 600; }
.card-value { font-size: 1.25rem; font-weight: 600; margin: 4px 0 6px; color: #1e293b; }
.card-desc { color: #64748b; font-size: 0.82rem; }
.card-section-title { font-weight: 600; color: #1e293b; font-size: 0.85rem; padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid #e5e7eb; }
.card-columns { column-gap: 16px; column-count: 3; }
@media (max-width: 1300px) { .card-columns { column-count: 2; } }
@media (max-width: 860px) { .card-columns { column-count: 1; } }
.card-columns > .card { break-inside: avoid; margin-bottom: 16px; display: inline-block; width: 100%; }

/* Form cards (Django .form-card + .card-title). .panel/.panel-head/.panel-body
   are kept as aliases so existing pages keep rendering during migration. */
.form-card, .panel { background: #fff; border-radius: 8px; box-shadow: var(--shadow); margin-bottom: 16px; }
.card-title, .panel-head { padding: 12px 16px; font-weight: 600; color: #1e293b; font-size: 0.85rem; border-bottom: 1px solid #e5e7eb; background: #f9fafb; border-radius: 8px 8px 0 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.panel-body { padding: 6px 16px; }
.form-card .card-body { padding: 16px; }

/* Settings table (Django .settings-tbl): name | control | description */
.settings-tbl { width: 100%; border-collapse: collapse; }
.settings-tbl td { padding: 12px 16px; border-bottom: 1px solid #f1f5f9; vertical-align: middle; }
.settings-tbl tr:last-child td { border-bottom: none; }
.settings-tbl .setting-name { font-weight: 500; color: #374151; white-space: nowrap; width: 220px; }
.settings-tbl .setting-control { width: 420px; }
.settings-tbl .setting-desc { color: #94a3b8; font-size: 0.78rem; }
.dependent-row { display: none; }
.dependent-row.visible { display: table-row; }

/* Generic label|control|desc grid rows (.setting-row) — alias of settings-tbl for Razor components */
.setting-row { display: grid; grid-template-columns: 190px minmax(0, 320px) minmax(140px, 1fr); gap: 14px; align-items: start; padding: 12px 16px; border-bottom: 1px solid #f1f5f9; }
.setting-row:last-child { border-bottom: none; }
.setting-row > .lbl { font-weight: 500; color: #374151; padding-top: 7px; }
.setting-row > .help { font-size: 0.78rem; color: #94a3b8; }
/* A toggle's track is only 20px tall, but the label above is padded (7px) to sit on the centre-line of a
   36px .input — so a toggle left at its natural height renders visibly ABOVE its own label. Give every
   toggle inside a settings row the same 36px line box as an input; .toggle is already a centring
   inline-flex, so the switch lands exactly on the label's line. Applies everywhere at once (ring group
   General / Business Hours / Voicemail, extension, tenant, host…) rather than per page. */
.setting-row .toggle, .setting-row .toggle-switch { min-height: 36px; }
@media (max-width: 980px) { .setting-row { grid-template-columns: 1fr; gap: 6px; } }
/* User editor: wide Account panel on the left, narrow Tools card on the right quarter. */
.editor-split { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); gap: 16px; align-items: start; }
/* Account fields are 50% wider than the default control column (320 → 480px). */
.user-editor .setting-row { grid-template-columns: 190px minmax(0, 480px) minmax(140px, 1fr); }
@media (max-width: 980px) { .editor-split { grid-template-columns: 1fr; } .user-editor .setting-row { grid-template-columns: 1fr; } }
/* Host admin: full-width settings — wider label column, vertically-centred rows, single wide control. */
.host-settings .setting-row { grid-template-columns: 240px minmax(0, 1fr); align-items: center; }
.host-settings .setting-row > .lbl { padding-top: 0; }
.host-settings .setting-row > .help { display: none; }

/* Voicemail settings tab: wide label column (≥380px, grows to fit) + single-line labels so the long
   option text never wraps. */
.vm-settings .setting-row { grid-template-columns: minmax(380px, max-content) minmax(0, 1fr); align-items: center; }
.vm-settings .setting-row > .lbl { padding-top: 0; white-space: nowrap; }
/* The Security tab's labels are longer ("Prohibited recent passwords (history)"…), so widen just its
   label column; the other host tabs keep the standard 240px. */
.host-settings .wide-label-rows .setting-row { grid-template-columns: 360px minmax(0, 1fr); }
@media (max-width: 760px) { .host-settings .setting-row,
                            .host-settings .wide-label-rows .setting-row { grid-template-columns: 1fr; align-items: start; } }
/* Rows whose control stacks multiple items (input + warning/button): align the label to the
   top so it lines up with the input, not the centre of the whole stack. */
.host-settings .setting-row.row-stacked { align-items: start; }
.host-settings .setting-row.row-stacked > .lbl { padding-top: 8px; }
/* Call-alert rows: an on/off toggle next to the recipient list, sharing the standard label column. */
.host-settings .setting-row.row-muted { opacity: .55; }
/* Alert rows: a consistent 3-column grid shared by the "Call alerts" and "Alerts" cards —
   alert name | on/off toggle (just wide enough for the toggle) | email field. The toggle cell is
   empty on the default-email row; the name column is wide enough for the longest title not to wrap. */
.host-settings .alert-grid { display: grid; grid-template-columns: 320px 56px minmax(0, 1fr); gap: 14px; align-items: center; padding: 12px 16px; }
.host-settings .alert-grid > .lbl { font-weight: 500; color: #374151; }
.host-settings .alert-grid .input { width: 100%; }
@media (max-width: 760px) { .host-settings .alert-grid { grid-template-columns: 1fr; gap: 6px; } }
/* Auto-ban config cards sit in the firewall page's narrow 1/4-width right column. The default
   3-column setting-row (190px label + control + help) collapses the control track to ~0 there,
   crushing the threshold number inputs to a sliver. Keep label (left) and control (right) side
   by side, but give the control a real fixed width so the toggle/number box always fits. */
.autoban-config .setting-row { grid-template-columns: minmax(0, 1fr) 130px; gap: 12px; align-items: center; padding: 10px 16px; }
.autoban-config .setting-row > .lbl { padding-top: 0; }
.autoban-config .setting-row > .help { display: none; }
/* Header "Power" split-button dropdown (Host page). */
.power-menu { position: relative; display: inline-block; z-index: 101; }
.power-menu-list { display: none; position: absolute; top: calc(100% + 4px); right: 0; background: #fff; border: 1px solid #d1d5db; border-radius: 6px; box-shadow: 0 8px 24px rgba(0,0,0,.12); min-width: 170px; z-index: 101; overflow: hidden; }
.power-menu-list.open { display: block; }
.power-menu-list button { display: block; width: 100%; text-align: left; padding: 9px 14px; background: #fff; border: none; font: inherit; cursor: pointer; color: #374151; }
.power-menu-list button:hover { background: #f3f4f6; }
.power-menu-list button.danger { color: #dc2626; }
.power-backdrop { position: fixed; inset: 0; z-index: 99; }
/* Raw email source / headers block in the Email Log detail modal. */
.email-raw { white-space: pre-wrap; word-break: break-word; font-size: .76rem; background: #0b1020; color: #d1d5db; padding: 12px; border-radius: 6px; margin: 0; }
/* Packet capture: live output on the left, a fixed-width settings column on the right that
   collapses below it on narrow screens. */
/* Fill the viewport (minus the 44px topbar and 32px×2 main-content padding) so the
   trailing console flexes to whatever height is left, with no window scrollbar. Shared
   by the packet-capture and voice-engine-log consoles. */
.fill-page { display: flex; flex-direction: column; height: calc(100vh - 108px); }
.capture-grid { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 16px; align-items: stretch; flex: 1 1 auto; min-height: 0; }
.capture-grid > .panel { display: flex; flex-direction: column; min-height: 0; margin-bottom: 0; }
.capture-grid > .panel > .panel-body { flex: 1 1 auto; min-height: 0; display: flex; }
.capture-grid > div:last-child { min-height: 0; overflow-y: auto; }   /* settings column scrolls internally if ever taller than the viewport */
@media (max-width: 900px) { .fill-page { height: auto; } .capture-grid { grid-template-columns: 1fr; } }
/* Settings form: stack label over a full-width control so fields use the column width. */
.capture-form .setting-row { grid-template-columns: 1fr; gap: 5px; padding: 10px 16px; align-items: start; }
.capture-form .setting-row > .lbl { padding-top: 0; font-size: .8rem; }
.capture-form .setting-row > .help { display: none; }
/* Live packet-capture output — uses most of the viewport height, with its own scrollbar. */
.capture-out { white-space: pre-wrap; word-break: break-word; font-size: .76rem; line-height: 1.45; background: #0b1020; color: #cbd5e1; padding: 12px; border-radius: 6px; margin: 0; flex: 1 1 auto; min-height: 200px; overflow: auto; }
/* Inline checkbox / radio labels (filter builder). */
.chk-inline, .radio-inline { display: inline-flex; align-items: center; gap: 6px; font-size: .85rem; color: #374151; cursor: pointer; }
.chk-inline input, .radio-inline input { cursor: pointer; }
/* Aligned checkbox grid (filter builder services/protocols). */
.chk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; }

/* Editor with wide controls (no help column). The card stays full width (as always); the control
   column is bounded so inputs are roomy but don't stretch the whole screen. Announcement editor. */
.wide-rows .setting-row { grid-template-columns: 190px minmax(0, 560px); }
/* Tenant editor (General/Limits/Features) — a ~50% wider label column so the longer labels sit on one line. */
.tenant-edit-rows .setting-row { grid-template-columns: 285px minmax(0, 560px); }
.wide-rows .setting-row > .help { display: none; }
.wide-rows input[type=range] { width: 100%; }

/* Outbound route editor: If… then Then… stacked, each kept at the exact width the side-by-side
   columns had (half the container minus half the 18px gap) — i.e. the width is unchanged. */
.route-split { display: grid; grid-template-columns: calc(50% - 9px); gap: 18px; align-items: start; }
.route-split .setting-row { grid-template-columns: 190px minmax(0, 1fr); }
.route-split .setting-row > .help { display: none; }
.route-split .setting-row.row-muted { opacity: .55; }
/* Ring group editor: double the control column width (320px → 640px) across its tabs. */
.rg-editor .setting-row { grid-template-columns: 190px minmax(0, 640px) minmax(140px, 1fr); }
/* A row whose control cell spans the WHOLE row, so something (a help icon) can sit hard against the right
   edge of the row rather than the right edge of the field. Stated after .rg-editor's own column rule
   because that one is equally specific and would otherwise win on source order. */
.setting-row.row-wide, .rg-editor .setting-row.row-wide { grid-template-columns: 190px minmax(0, 1fr); }
.setting-row.row-wide > .help { display: none; }
/* Component-driven play button: icon is rendered via CSS (not textContent) so the Blazor renderer
   and the JS player never both mutate the button's DOM. .playing (on the row) = paused/playing icon;
   .busy = a spinner while audio is being generated. */
.track-play[data-managed="true"]::before { content: "\25B6"; } /* ▶ */
.track-item.playing .track-play[data-managed="true"]::before { content: "\25A0"; } /* ■ stop — it halts and rewinds, so it isn't a pause */
.track-play[data-managed="true"].busy::before { content: ""; display: inline-block; width: 11px; height: 11px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: btn-spin 0.6s linear infinite; vertical-align: middle; }
@keyframes btn-spin { to { transform: rotate(360deg); } }
/* Inline spinner for buttons in a busy/working state. */
.btn-spinner { display: inline-block; width: 13px; height: 13px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: btn-spin 0.6s linear infinite; vertical-align: middle; margin-right: 8px; }
/* "Saving…" / "Generating…" — the ellipsis pulses so a slow round-trip (a synthesis call takes seconds)
   looks like work in progress rather than a frozen button. */
.dots::after { content: "…"; animation: dots-pulse 1.1s ease-in-out infinite; }
@keyframes dots-pulse { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }

/* Generate / Reset / Clear under the text-to-speech box: one row, filling the width of the box above. */
.tts-actions { display: flex; gap: 8px; margin-top: 10px; }
.tts-actions .btn { flex: 1 1 0; justify-content: center; }

/* Render the searchable voice picker as a full-width form control (not the compact chip). */
.wide-rows .tenant-dropdown { width: 100%; }
.wide-rows .tenant-dropdown-toggle { width: 100%; justify-content: space-between; background: #fff; height: 36px; padding: 7px 10px; font-size: inherit; border-radius: 6px; }
.wide-rows .tenant-dropdown-menu { left: 0; right: auto; width: 100%; min-width: 0; max-width: none; }

/* Inputs */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.field label { font-weight: 500; font-size: 0.8rem; color: #374151; }
.field-inline { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 18px; }
@media (max-width: 760px) { .grid-2 { grid-template-columns: 1fr; } }
.input, select.input, .settings-tbl input, .settings-tbl select, .settings-tbl textarea {
    padding: 7px 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: inherit;
    font-family: inherit; background: #fff; color: #1e293b; width: 100%;
}
select.input, .input { height: 36px; }
select, select.input {
    appearance: none; -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2394a3b8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 12px center; padding-right: 30px;
}
textarea.input { height: auto; min-height: 70px; resize: vertical; }
.input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent, #3b82f6); box-shadow: 0 0 0 3px rgba(59,130,246,.15); }
.input:disabled, .input[readonly] { background: #f8fafc; color: #64748b; }
.input-color { width: 56px; height: 36px; padding: 3px; }
.readonly-field { height: 36px; display: flex; align-items: center; padding: 0 10px; background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 6px; color: #64748b; }

/* Buttons */
.btn, .btn-primary, .btn-secondary, .btn-dark, .btn-outline, .btn-outline-danger, .btn-delete, .btn-warning {
    padding: 8px 16px; border-radius: 6px; font-size: inherit; font-weight: 500; cursor: pointer;
    text-decoration: none; border: 1px solid transparent; font-family: inherit; display: inline-flex;
    align-items: center; gap: 6px; white-space: nowrap;
}
.btn, .btn-secondary, .btn-outline { background: #fff; color: #374151; border-color: #d1d5db; }
.btn:hover, .btn-secondary:hover, .btn-outline:hover { background: #f9fafb; text-decoration: none; }
/* Primary button follows the brand primary accent; .btn-dark stays a fixed neutral. Declared AFTER
   the generic .btn rule so it wins for `btn btn-primary` (equal specificity → source order). */
.btn-primary { background: var(--accent, #2563eb); color: #fff; border-color: transparent; }
.btn-primary:hover { background: var(--accent, #2563eb); filter: brightness(1.12); text-decoration: none; }
.btn-dark { background: #1e293b; color: #fff; border-color: transparent; }
.btn-dark:hover { background: #334155; text-decoration: none; }
.btn-warning { background: #fff; color: #d97706; border-color: #fbbf24; }
.btn-warning:hover { background: #fffbeb; }
.btn-delete, .btn-outline-danger, .btn-danger { background: #fff; color: #dc2626; border-color: #fca5a5; }
.btn-delete:hover, .btn-outline-danger:hover { background: #fef2f2; border-color: #f87171; }
.btn-block { width: 100%; justify-content: center; }
.btn:disabled, .btn-primary:disabled, .btn-dark:disabled { opacity: .55; cursor: default; }
.btn-sm, .action-btn { display: inline-flex; align-items: center; padding: 4px 10px; font-size: 0.75rem; border-radius: 4px; cursor: pointer; border: 1px solid #d1d5db; background: #fff; color: #374151; }
.btn-sm:hover, .action-btn:hover { background: #f3f4f6; text-decoration: none; }
.btn-sm.btn-danger { color: #dc2626; border-color: #fca5a5; }
.btn-sm:disabled, .action-btn:disabled { opacity: .4; cursor: default; background: #fff; pointer-events: none; }
.svc-status { width: 1%; text-align: right; white-space: nowrap; padding-right: 4px; }
.kv-table { width: 100%; border-collapse: collapse; }
.kv-table th, .kv-table td { padding: 9px 16px; text-align: left; font-size: .85rem; border-bottom: 1px solid #f1f5f9; vertical-align: top; }
.kv-table tr:last-child th, .kv-table tr:last-child td { border-bottom: none; }
.kv-table th { width: 1%; min-width: 260px; color: #64748b; font-weight: 500; white-space: nowrap; padding-right: 24px; }
.kv-table td { color: #1e293b; font-weight: 500; }
.stat-bad { color: #dc2626; font-weight: 600; }

/* Extension status chip (page header) + diagnostics tab */
.ext-status-chip { display: inline-flex; align-items: center; gap: 10px; background: #fff; border: 1px solid #e5e7eb; border-radius: 9999px; padding: 9px 18px; box-shadow: var(--shadow); }
.ext-status-chip .dot { width: 12px; height: 12px; flex-shrink: 0; }
.ext-status-label { font-weight: 600; color: #1e293b; font-size: .92rem; line-height: 1.25; white-space: nowrap; }
.ext-status-sub { font-size: .74rem; color: #64748b; line-height: 1.2; white-space: nowrap; }
.diag-offline { display: flex; align-items: center; gap: 14px; padding: 8px 0; }
.diag-offline .dot { width: 14px; height: 14px; }
.diag-wrap { word-break: break-all; white-space: normal; }

/* Diagnostics summary tiles */
.diag-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin-bottom: 18px; }
.diag-tile { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 14px 16px; }
.diag-tile-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: #94a3b8; font-weight: 600; margin-bottom: 6px; }
.diag-tile-value { font-size: 1.1rem; font-weight: 700; color: #1e293b; }
.diag-tile-value.ok { color: #16a34a; }
.diag-tile-value.warn { color: #d97706; }

/* Per-call quality metric chips */
.diag-call { padding: 6px 0 14px; border-bottom: 1px solid #f1f5f9; }
.diag-call:last-child { border-bottom: none; }
.diag-call-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.diag-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 10px; margin-bottom: 12px; }
.qmetric { text-align: center; border-radius: 8px; padding: 10px 6px; background: #f8fafc; border: 1px solid #eef2f7; }
.qmetric .qv { font-size: 1.05rem; font-weight: 700; color: #1e293b; font-variant-numeric: tabular-nums; }
.qmetric .ql { font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: #94a3b8; margin-top: 2px; }
.qmetric.q-good { background: #ecfdf5; border-color: #a7f3d0; } .qmetric.q-good .qv { color: #16a34a; }
.qmetric.q-warn { background: #fffbeb; border-color: #fde68a; } .qmetric.q-warn .qv { color: #d97706; }
.qmetric.q-bad  { background: #fef2f2; border-color: #fecaca; } .qmetric.q-bad .qv  { color: #dc2626; }

/* Firewall add-rule rows */
.fw-add { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; padding: 4px 0; }

/* Bulk selection bar + edit panel (DID Numbers) */
.bulk-bar { display: flex; align-items: center; gap: 10px; background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 8px; padding: 10px 14px; margin-bottom: 14px; }
.bulk-bar .spacer { flex: 1; }
.bulk-panel { margin-bottom: 16px; }
.bulk-field { padding: 12px 0; border-bottom: 1px solid #f1f5f9; display: flex; flex-direction: column; gap: 10px; }
.bulk-field:last-of-type { border-bottom: none; }
.bulk-check { display: inline-flex; align-items: center; gap: 8px; font-size: .9rem; cursor: pointer; }
.bulk-routing { display: flex; flex-wrap: wrap; gap: 10px; padding-left: 26px; }
/* Bulk-edit grid: each editable field is always shown as [☑ field name] | [control]; the control
   is greyed/inert until its checkbox is ticked, so the modal is compact and a constant height. */
.bulk-grid { margin: 14px 0 4px; }
.bulk-row { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 16px; align-items: start; padding: 14px 0; border-bottom: 1px solid #f1f5f9; }
.bulk-row:last-child { border-bottom: none; }
.bulk-row-head { display: inline-flex; align-items: center; gap: 9px; font-weight: 500; color: #374151; cursor: pointer; user-select: none; padding-top: 7px; }
.bulk-row-control { min-width: 0; }
.bulk-row-control .input { width: 100%; }
.data-table tr.row-selected td { background: #eef2ff; }
/* Install preview: a package apt would REMOVE — the one thing the operator must notice. */
.data-table tr.row-removed td { background: #fef2f2; color: #991b1b; }
/* "Check Route" result: pulse the matched row a few times, then settle on a soft green highlight. */
@keyframes route-pulse { 0%,100% { background: #bbf7d0; } 50% { background: #ecfdf5; } }
.data-table tr.route-pulse td { animation: route-pulse 0.7s ease-in-out 3; animation-fill-mode: forwards; background: #dcfce7; }
.data-table tr.route-pulse td:first-child { box-shadow: inset 4px 0 0 #16a34a; }
/* Drag-and-drop reordering of outbound routes */
.drag-grip { color: #cbd5e1; cursor: grab; user-select: none; font-size: 1rem; line-height: 1; padding: 0 2px; }
.drag-grip:active { cursor: grabbing; }
.data-table tr[draggable="true"]:hover .drag-grip { color: #64748b; }
.data-table tr.dragging td { opacity: .4; }
.data-table tr.drag-over td { box-shadow: inset 0 2px 0 #6366f1; }

/* DID page top settings — wider control column; the unassigned-number row pairs two selects inline.
   Hide the empty SettingRow help cell so the 2-column override doesn't wrap it to an implicit second
   grid row (which added a big row-gap between the rows). */
.did-settings .setting-row { grid-template-columns: 240px minmax(0, 640px); }
/* Stack the routing-type select and its secondary select vertically (second below the first). */
.did-setting-row { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
@media (max-width: 980px) { .did-settings .setting-row { grid-template-columns: 1fr; } }

/* Placeholder showing an inherited default: it has to read as "what you'd get", never as a value
   somebody typed, so it sits well below the muted text colour. */
.ph-inherited::placeholder { color: #cbd5e1; }

/* Click-to-edit inline cell (DID description) */
.inline-edit-cell { position: relative; }
.inline-edit { width: 100%; min-width: 140px; border: 1px solid transparent; background: transparent; border-radius: 5px; padding: 5px 8px; font: inherit; color: #1e293b; }
.inline-edit::placeholder { color: #cbd5e1; }
.inline-edit:hover { border-color: #e2e8f0; background: #f8fafc; }
.inline-edit:focus { outline: none; border-color: #3b82f6; background: #fff; box-shadow: 0 0 0 3px rgba(59,130,246,.12); }
.inline-saved { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: #16a34a; font-weight: 700; pointer-events: none; }
.perf-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.perf-col .perf-card .spark { height: 120px; }
.core-grid { display: flex; flex-wrap: wrap; gap: 18px; padding: 18px 20px; }
.core-cell { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.core-label { font-size: .7rem; color: #94a3b8; font-variant-numeric: tabular-nums; }
.core-pct { font-size: .72rem; font-weight: 600; color: #475569; font-variant-numeric: tabular-nums; min-width: 4ch; text-align: center; }
.core-bar { width: 32px; height: 96px; background: #eef2f7; border-radius: 5px; display: flex; align-items: flex-end; overflow: hidden; box-shadow: inset 0 0 0 1px #e2e8f0; }
.core-fill { width: 100%; background: var(--accent, #3b82f6); border-radius: 0 0 5px 5px; transition: height .5s ease; }
.core-fill.warm { background: #f59e0b; }
.core-fill.hot { background: #ef4444; }

/* Per-interface split: details left, live graphs right */
.net-split { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; padding: 14px 16px 16px; align-items: start; }
@media (max-width: 1000px) { .net-split { grid-template-columns: 1fr; } }
.net-left { min-width: 0; }
.net-right { display: flex; flex-direction: column; gap: 16px; }
.graph-card { background: #f9fafb; border: 1px solid #eef2f7; border-radius: 8px; padding: 12px 14px; }
.graph-card .spark { height: 110px; }

/* Live readouts: fixed-width slots so updating figures never shift surrounding text */
.metric-now.legend { display: inline-flex; gap: 14px; }
.metric-now.legend > span { display: inline-block; min-width: 8.5ch; text-align: right; font-variant-numeric: tabular-nums; }
.metric-now.legend .dn { color: #6366f1; }
.metric-now.legend .up { color: #f59e0b; }
.metric-now { font-variant-numeric: tabular-nums; }
.kv-table td, .kv-table th, .data-table td, .data-table th, .mono { font-variant-numeric: tabular-nums; }
.net-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.net-table td:first-child, .net-table th:first-child { color: #64748b; }
.net-table tr.net-bad td { color: #dc2626; font-weight: 600; }
.form-card + .form-card { margin-top: 16px; }
.form-actions { display: flex; align-items: center; gap: 8px; margin-top: 20px; flex-wrap: wrap; }

/* Toggle switch */
.toggle, .toggle-switch { position: relative; display: inline-flex; align-items: center; gap: 10px; cursor: pointer; }
.toggle input, .toggle-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-track, .toggle-slider { width: 36px; height: 20px; background: #d1d5db; border-radius: 10px; transition: background .2s; position: relative; flex-shrink: 0; }
.toggle-track::after, .toggle-slider::after { content: ''; position: absolute; width: 16px; height: 16px; background: #fff; border-radius: 50%; top: 2px; left: 2px; transition: transform .2s; }
.toggle input:checked + .toggle-track, .toggle-switch input:checked + .toggle-slider { background: var(--accent, #1e293b); }
.toggle input:checked + .toggle-track::after, .toggle-switch input:checked + .toggle-slider::after { transform: translateX(16px); }
/* Disabled toggle: greyed track, no pointer — reads clearly as locked. */
.toggle input:disabled + .toggle-track, .toggle-switch input:disabled + .toggle-slider { opacity: .4; }
.toggle:has(input:disabled), .toggle-switch:has(input:disabled) { cursor: not-allowed; }
/* A whole card row whose control is locked: dim the label + control together. */
.side-card .body.is-locked { opacity: .55; cursor: not-allowed; }
.side-card .body.is-locked .toggle { pointer-events: none; }

/* Badges */
/* A pill never wraps — a two-word label broken across lines reads as two pills. */
.badge { display: inline-block; padding: 2px 8px; border-radius: 9999px; font-size: 0.7rem; font-weight: 500; white-space: nowrap; }
.badge-blue { background: #dbeafe; color: #1e40af; }
.badge-green { background: #dcfce7; color: #166534; }
.badge-amber { background: #fef3c7; color: #92400e; }
.badge-gray { background: #f1f5f9; color: #475569; }
.badge-red { background: #fee2e2; color: #991b1b; }
.badge-purple { background: #ede9fe; color: #6d28d9; }
.badge-yellow { background: #fef08a; color: #854d0e; }
.badge-action { background: #e0e7ff; color: #3730a3; }
/* Auto attendant on the call-handling type column: indigo sat too close to the ring group's blue to tell
   apart at pill size, so it gets a colour from the other end of the wheel. */
.badge-rose { background: #ffe4e6; color: #9f1239; }
/* Queues — the six call-flow object types each need a distinguishable pill on the Call Handling list. */
.badge-teal { background: #ccfbf1; color: #115e59; }
.data-table .badge + .badge { margin-left: 4px; }
/* Object-type pills all take the width of the widest label ("Call Flow Control"), so a column of them lines
   up instead of stepping in and out with the length of each word. */
.badge-type { min-width: 122px; text-align: center; }

/* Data tables (list pages) */
/* A wide table (the call-flow index tables carry many fixed-width columns plus a free-width destination
   cell) scrolls INSIDE this wrapper, so the page body never gets a horizontal scrollbar. The toolbar,
   pagination and everything else stay put; only the table slides. */
.table-scroll { overflow-x: auto; max-width: 100%; }
.data-table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); }
.data-table th { background: #f8fafc; padding: 10px 16px; text-align: left; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: #64748b; border-bottom: 1px solid #e2e8f0; white-space: nowrap; }
.data-table td { padding: 10px 16px; border-bottom: 1px solid #f1f5f9; }
.data-table tbody tr:hover { background: #f8fafc; }
.data-table tr:last-child td { border-bottom: none; }
.row-actions { display: flex; gap: 6px; white-space: nowrap; align-items: center; justify-content: flex-end; }
/* Keep the actions cell a real table-cell (NOT display:flex) so its bottom border lines up with the
   rest of the row; right-align the inline buttons and space them with a margin instead of flex gap. */
td.actions-cell { white-space: nowrap; text-align: right; vertical-align: middle; }
td.actions-cell .action-btn + .action-btn, td.actions-cell .btn-sm + .btn-sm { margin-left: 6px; }
th.actions-cell { text-align: right; }

/* Tabs */
.tabs, .tab-bar { display: flex; border-bottom: 2px solid var(--tab-line, #e5e7eb); margin-bottom: 20px; gap: 0; flex-wrap: wrap; }
.tab, .tab-btn { padding: 8px 18px; border: none; background: none; font-size: inherit; font-family: inherit; color: var(--tab-inactive, #64748b); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -2px; font-weight: 500; }
.tab:hover, .tab-btn:hover { opacity: .8; }
.tab.active, .tab-btn.active { color: var(--tab-active, #1e293b); border-bottom-color: var(--tab-active, #1e293b); }

/* Vertical tabs inside a horizontal tab's body — the auto attendant's three messages (menu prompt, invalid
   input, no input). The nav sits to the left of the card so the card itself needs no changes. */
.vtabs { display: flex; gap: 20px; align-items: flex-start; }
.vtabs-nav { display: flex; flex-direction: column; gap: 2px; min-width: 220px; flex: 0 0 auto;
             border-right: 2px solid var(--tab-line, #e5e7eb); padding-right: 14px; }
.vtabs-body { flex: 1 1 auto; min-width: 0; }
.vtab { position: relative; text-align: left; padding: 10px 14px; border: none; background: none;
        font-family: inherit; font-size: inherit; color: var(--tab-inactive, #64748b); cursor: pointer;
        border-radius: 6px; display: flex; flex-direction: column; gap: 2px; }
.vtab:hover { background: var(--vtab-hover, rgba(100,116,139,.08)); }
.vtab.active { color: var(--tab-active, #1e293b); background: var(--vtab-active, rgba(30,41,59,.06)); font-weight: 600; }
.vtab-label { font-size: .95rem; }
.vtab-hint { font-size: .75rem; opacity: .75; font-weight: 400; line-height: 1.25; }
/* A filled dot marks a message that has audio, so "what have I recorded?" is answerable at a glance. */
.vtab-dot { position: absolute; top: 12px; right: 10px; width: 8px; height: 8px; border-radius: 50%;
            background: var(--ok, #16a34a); }
@media (max-width: 900px) {
  .vtabs { flex-direction: column; }
  .vtabs-nav { flex-direction: row; flex-wrap: wrap; min-width: 0; border-right: none;
               border-bottom: 2px solid var(--tab-line, #e5e7eb); padding: 0 0 10px; width: 100%; }
}
.tab.is-soon { opacity: .5; cursor: default; }
/* "Used By" parked at the right-hand end of an editor's tab bar (see UsedBy.razor): the reverse-reference
   count belongs with the object, not inside its General card, where it cost a whole form row. */
.tab-used-by { margin-left: auto; align-self: center; padding: 0 4px 6px 18px; color: #64748b; font-size: .9rem; }
.tab-used-by a { margin-left: 6px; }

/* Two-column editor (form + right-hand cards) */
/* Extension editor: no help column, wider control so inputs (esp. the SIP secret) use the space. */
.ext-editor .setting-row { grid-template-columns: 190px minmax(0, 640px); }
.ext-editor .setting-row > .help { display: none; }
/* Notes tab (extension + tenant editors): a single full-width free-text field that fills most of the
   viewport. Height tracks the window (like .fill-page): 100vh minus the 44px topbar, the 32px×2
   main-content padding, and the page-header + tabs row above it. Still user-resizable. */
.notes-field { display: block; width: 100%; box-sizing: border-box; height: calc(95vh - 224px);
    min-height: 240px; resize: vertical; padding: 12px 14px; font: inherit; line-height: 1.5;
    border: 1px solid var(--border, #d1d5db); border-radius: 8px; background: var(--input-bg, #fff); color: inherit; }
.notes-field:focus { outline: none; border-color: var(--tab-active, #1e293b); }
.editor-grid { display: flex; gap: 16px; align-items: flex-start; }
.editor-grid > div:first-child { flex: 1; min-width: 0; }
.editor-grid > div:nth-child(2) { width: 340px; flex-shrink: 0; }
@media (max-width: 1100px) { .editor-grid { flex-direction: column; } .editor-grid > div:nth-child(2) { width: 100%; } }
.side-card { background: #fff; border-radius: 8px; box-shadow: var(--shadow); margin-bottom: 16px; }
.side-card .head { padding: 12px 16px; font-weight: 600; font-size: 0.85rem; border-bottom: 1px solid #e5e7eb; background: #f9fafb; color: #1e293b; border-radius: 8px 8px 0 0; }
.side-card .body { padding: 12px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.side-card .body .desc { color: #94a3b8; font-size: 0.78rem; }

/* Chips (legacy assignment control) */
.chips { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 8px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 36px; background: #fff; }
.chip { display: inline-flex; align-items: center; gap: 4px; background: #dbeafe; color: #1e40af; padding: 2px 8px; border-radius: 9999px; font-size: 0.75rem; white-space: nowrap; }
.chip button { border: none; background: none; cursor: pointer; color: #1e40af; font-size: .9rem; line-height: 1; padding: 0; opacity: .65; }
.chip button:hover { opacity: 1; }
.chips-empty { color: #94a3b8; font-size: 0.8rem; padding: 2px; }

/* Searchable multi-select chip box + checkbox dropdown */
.add-member-row { display: flex; align-items: flex-start; gap: 8px; padding: 12px 0 16px; flex-wrap: nowrap; }
.add-member-type-select { padding: 7px 28px 7px 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: inherit; font-family: inherit; background: #fff; appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2394a3b8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; flex-shrink: 0; }
.add-member-number { flex: 1; padding: 8px 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: inherit; font-family: monospace; }
.add-member-number:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.15); }
.btn-add-member { padding: 8px 14px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff; color: #374151; font-size: inherit; font-family: inherit; cursor: pointer; white-space: nowrap; flex-shrink: 0; }
.btn-add-member:hover { background: #f9fafb; }
.btn-add-member:disabled { opacity: .5; cursor: not-allowed; }
.ext-search-wrap { position: relative; flex: 1; min-width: 0; }
.ext-chip-box { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 5px 8px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff; cursor: text; min-height: 38px; }
.ext-chip-box:focus-within { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.15); }
.ext-sel-chip { display: inline-flex; align-items: center; gap: 4px; background: #dbeafe; color: #1e40af; padding: 2px 8px; border-radius: 9999px; font-size: .75rem; white-space: nowrap; }
.ext-sel-chip-remove { background: none; border: none; color: #1e40af; font-size: .85rem; cursor: pointer; padding: 0; line-height: 1; opacity: .6; }
.ext-sel-chip-remove:hover { opacity: 1; }
.ext-search-input { border: none; outline: none; font-size: inherit; font-family: inherit; padding: 2px 4px; min-width: 120px; flex: 1; background: transparent; }
.ext-search-input::placeholder { color: #94a3b8; }
.ext-search-dropdown { position: absolute; top: calc(100% + 3px); left: 0; right: 0; background: #fff; border: 1px solid #d1d5db; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,.1); z-index: 200; overflow: hidden; }
.ext-check-header { display: flex; align-items: center; justify-content: space-between; padding: 6px 12px; border-bottom: 1px solid #e5e7eb; background: #f9fafb; }
.ext-check-header-label { font-size: .75rem; color: #94a3b8; }
.ext-select-all { font-size: .75rem; color: var(--accent, #3b82f6); cursor: pointer; border: none; background: none; font-family: inherit; padding: 0; }
.ext-select-all:hover { text-decoration: underline; }
.ext-check-list { max-height: 220px; overflow-y: auto; }
.ext-search-option { display: flex; align-items: center; gap: 8px; padding: 7px 12px; cursor: pointer; font-size: .875rem; color: #1e293b; }
.ext-search-option:hover { background: #f1f5f9; }
.ext-search-option input[type="checkbox"] { flex-shrink: 0; accent-color: var(--accent, #1e293b); width: 14px; height: 14px; cursor: pointer; }
.ext-search-option.no-results { color: #94a3b8; cursor: default; font-style: italic; padding: 12px; }

/* Ordered member list */
.member-list { list-style: none; margin: 0; padding: 0; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; }
.member-item { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-bottom: 1px solid #f1f5f9; background: #fff; }
.member-item:last-child { border-bottom: none; }
.member-item[draggable="true"] { cursor: grab; }
.member-item.dragging { opacity: .4; }
.member-item.drag-over { box-shadow: inset 0 2px 0 #6366f1; }
.member-empty { padding: 18px 12px; color: #94a3b8; font-style: italic; font-size: .85rem; }
/* Position number: no fixed width — as narrow as its digits need (so "1" doesn't reserve room for "100")
   and it simply grows when a group gets that big. A little right padding keeps it off the type pill. */
.member-idx { flex: 0 0 auto; padding-right: 4px; color: #94a3b8; font-size: .8rem; text-align: left; }
/* The type pill is either "Extension" or "External": pin one width (the wider word) and centre the text so
   the member names line up in a column instead of shifting with the label, with a little breathing room
   before the name. */
.member-item .badge { min-width: 5.5rem; text-align: center; flex-shrink: 0; margin: 0 6px; }
.member-label { color: #1e293b; font-size: .875rem; }
.member-item .spacer { flex: 1 1 auto; }
.member-remove { background: none; border: none; color: #64748b; cursor: pointer; padding: 4px 6px; border-radius: 4px; flex-shrink: 0; font-size: 1rem; line-height: 1; }
.member-remove:hover { color: #dc2626; background: #fef2f2; }
.icon-btn { border: none; background: none; cursor: pointer; color: #64748b; padding: 4px 6px; border-radius: 4px; }
.icon-btn:hover:not(:disabled) { color: #1e293b; background: #f1f5f9; }
.icon-btn:disabled { color: #cbd5e1; cursor: default; }

/* Business-hours day grid */
.quick-actions { display: flex; gap: 6px; flex-wrap: wrap; padding: 12px 16px; border-bottom: 1px solid #f1f5f9; }
.btn-quick { padding: 4px 10px; border: 1px solid #d1d5db; border-radius: 4px; background: #fff; color: #374151; font-size: .78rem; font-family: inherit; cursor: pointer; white-space: nowrap; }
.btn-quick:hover { background: #f9fafb; }
.day-grid { padding: 4px 0; }
.day-row { display: flex; align-items: center; gap: 0; padding: 9px 16px; border-bottom: 1px solid #f1f5f9; }
.day-row:last-child { border-bottom: none; }
.day-row.weekend { background: #fafafa; }
.day-name { width: 110px; font-weight: 500; color: #374151; font-size: .875rem; flex-shrink: 0; }
.day-row.weekend .day-name { color: #64748b; }
.day-times { display: flex; align-items: center; gap: 8px; margin-left: 20px; }
.day-times input[type="time"] { width: 120px; }
.time-sep { color: #94a3b8; font-size: .8rem; flex-shrink: 0; }
.day-closed-label { margin-left: 20px; color: #94a3b8; font-size: .85rem; font-style: italic; }
/* Extension Business Hours schedule: reuse the .setting-row grid so each day's time selectors line up
   vertically with the dropdowns in the cards above, and right-align the open/closed toggle in the
   label column. */
/* Top-align the row so the day label + toggle line up with the FIRST time window (not the vertical
   centre of a multi-window stack); the label cell is 36px tall so its centred text/toggle matches an
   input row. Extra windows stack below with a small gap. */
.bh-day-row { align-items: start; }
.bh-day-row.weekend { background: #fafafa; }
.bh-day-row > .lbl { display: flex; align-items: center; justify-content: space-between; padding-top: 0; min-height: 36px; }
.bh-day-row .day-times, .bh-day-row .day-closed-label { margin-left: 0; }
.bh-day-row .day-times + .day-times { margin-top: 6px; }
.bh-day-row .day-closed-label { display: inline-flex; align-items: center; min-height: 36px; }
/* Compact square icon button (e.g. the ×/+ window controls). */
.btn-icon { padding: 0; width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; font-size: 1.05rem; line-height: 1; flex-shrink: 0; }
.holiday-add-row { display: flex; align-items: center; gap: 8px; padding: 12px 16px; background: #f9fafb; border-bottom: 1px solid #e5e7eb; }

/* Profile multi-card grid */
.profile-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: stretch; }
.profile-grid .full-width { grid-column: 1 / -1; }
@media (max-width: 980px) { .profile-grid { grid-template-columns: 1fr; } }
.detail-row { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid #f1f5f9; }
.detail-row:last-child { border-bottom: none; }
.detail-label { width: 140px; flex-shrink: 0; font-weight: 500; color: #64748b; font-size: .75rem; text-transform: uppercase; letter-spacing: .03em; }
.detail-value { color: #1e293b; }
.help-text { font-size: .8rem; color: #64748b; margin-top: 8px; }

/* Notification template editor */
.content-layout { display: grid; grid-template-columns: 1fr 260px; gap: 20px; align-items: start; }
@media (max-width: 980px) { .content-layout { grid-template-columns: 1fr; } }
.sidebar-panel { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 20px; }
.sidebar-panel h3 { font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: #64748b; margin: 0 0 12px; }
.placeholder-list { list-style: none; padding: 0; margin: 0; }
.placeholder-list li { padding: 8px 0; border-bottom: 1px solid #f1f5f9; }
.placeholder-list li:last-child { border-bottom: none; }
.placeholder-code { display: inline-block; font-family: monospace; font-size: .75rem; background: #f1f5f9; color: #1e293b; padding: 2px 6px; border-radius: 4px; user-select: all; }
.placeholder-desc { display: block; font-size: .7rem; color: #64748b; margin-top: 2px; }
.section-title { font-weight: 600; color: #374151; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 1px solid #e5e7eb; }

/* Voicemail greeting controls */
.greeting-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.greeting-filename { color: #374151; font-size: .85rem; font-family: monospace; }
.greeting-none { color: #94a3b8; font-size: .85rem; font-style: italic; }
.btn-upload-greeting { padding: 5px 12px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff; color: #374151; font-size: .8rem; font-family: inherit; cursor: pointer; white-space: nowrap; }
.btn-upload-greeting:hover { background: #f9fafb; }
.btn-record-greeting { padding: 5px 12px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff; color: #374151; font-size: .8rem; font-family: inherit; cursor: pointer; white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }
.btn-record-greeting:hover { background: #f9fafb; }
.rec-dot { width: 8px; height: 8px; border-radius: 50%; background: #dc2626; flex-shrink: 0; }

/* Modal */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 1000; display: none; align-items: center; justify-content: center; }
.modal-overlay.active { display: flex; }
/* overflow-wrap: an entity name inside a title or message ("Delete 111111111111…?") can be one unbroken
   token, and an unbreakable token otherwise runs past the box edge. `anywhere` only bites when a token
   exceeds the line, so ordinary text still wraps at spaces — and the full name stays readable, which is
   the whole point of a confirm dialog (never ellipsize WHAT is about to be deleted). */
.modal-box { position: relative; background: #fff; border-radius: 10px; width: 420px; max-width: calc(100vw - 32px); box-shadow: 0 20px 50px rgba(0,0,0,.25); padding: 24px; overflow-wrap: anywhere; }
.modal-title { font-weight: 600; color: #1e293b; font-size: 1rem; margin-bottom: 16px; padding-right: 28px; }
.modal-close { position: absolute; top: 14px; right: 14px; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border: none; background: transparent; color: #64748b; font-size: 1.4rem; line-height: 1; cursor: pointer; border-radius: 6px; }
.modal-close:hover { background: #f1f5f9; color: #1e293b; }
.modal-footer { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
/* Wide modal hosting a familiar label|control settings table */
.modal-wide { width: 760px; }
.modal-confirm { width: 840px; }   /* confirmations: double the default — two labelled buttons side by side must fit (user, 2026-10-06) */
/* The ring-strategy help is a three-column table; 760px squeezes every cell to two or three words. */
.modal-xwide { width: 1320px; }
/* A help modal can run to several screens of prose. Cap it well inside the viewport and scroll its BODY,
   not the box: the box owns the close button, and a × that scrolls off the top is a dialog you can only
   leave by guessing at the overlay. */
.modal-scroll { display: flex; flex-direction: column; max-height: min(84vh, 900px); }
.modal-scroll > .modal-body { overflow-y: auto; min-height: 0; margin: 0 -24px -24px; padding: 0 24px 24px; }

/* Multi-line explanation rows: top-align every cell so the strategy name sits level with the first line of
   the sentence describing it. */
.strategy-help td { vertical-align: top; padding: 14px 20px; line-height: 1.6; }
/* ---- Queue statistics -------------------------------------------------------------------------------
   The volume and what became of it, first and largest; a proportion you can read without adding numbers
   up. Durations are a different question and get their own, quieter row. */
.qstat-headline { display: flex; align-items: stretch; gap: 40px; background: #fff; border-radius: 8px;
                  box-shadow: var(--shadow); padding: 28px 32px; margin-bottom: 20px; flex-wrap: wrap; }
.qstat-total { flex: 0 0 auto; padding-right: 40px; border-right: 1px solid #e2e8f0;
               display: flex; flex-direction: column; justify-content: center; }
/* Figures are read, not skimmed: give the digits room rather than the tight tracking display type wants,
   and use tabular numerals so every digit is the same width and columns of them line up. */
.qstat-total .value { font-size: 3.6rem; font-weight: 700; color: #1e293b; line-height: 1;
                      letter-spacing: .035em; font-variant-numeric: tabular-nums; }
.qstat-total .label { font-size: .72rem; color: #94a3b8; text-transform: uppercase; letter-spacing: .06em; margin-top: 6px; }
.qstat-outcome { flex: 1 1 340px; display: flex; flex-direction: column; justify-content: center; gap: 18px; min-width: 0; }
.qstat-bar { display: flex; height: 18px; border-radius: 9px; overflow: hidden; background: #f1f5f9; }
.qstat-seg { min-width: 3px; }
.qstat-seg.ok { background: #16a34a; } .qstat-seg.bad { background: #dc2626; }
.qstat-seg.warn { background: #d97706; } .qstat-seg.other { background: #64748b; }
.qstat-legend { display: flex; flex-wrap: wrap; gap: 10px 30px; font-size: .86rem; color: #475569; }
.qstat-legend strong { color: #1e293b; margin-left: 5px; letter-spacing: .02em; font-variant-numeric: tabular-nums; }
.qstat-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; vertical-align: baseline; }
.qstat-dot.ok { background: #16a34a; } .qstat-dot.bad { background: #dc2626; }
.qstat-dot.warn { background: #d97706; } .qstat-dot.other { background: #64748b; }
/* Durations: equal columns that fill the width, so the last row never stretches oddly. */
.qstat-figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
.qstat-figures > div { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 20px 24px; }
.qstat-figures .value { font-size: 1.75rem; font-weight: 700; color: #1e293b; line-height: 1.15;
                        letter-spacing: .03em; font-variant-numeric: tabular-nums; }
.qstat-figures .value.ok { color: #166534; } .qstat-figures .value.warn { color: #92400e; }
.qstat-figures .label { font-size: .72rem; color: #94a3b8; text-transform: uppercase; letter-spacing: .06em; margin-top: 6px; }
/* By member: the name column takes whatever is left, every figure column is a fixed width set by colgroup.
   Ragged figure columns made the table read as a list of sentences rather than something to scan down. */
.qstat-members { table-layout: fixed; }
.qstat-members th:not(:first-child), .qstat-members td:not(:first-child) { white-space: nowrap; }
/* The header line carries a control at its far end. The rule moves off the <h2> and onto the row, so it
   runs the full width of the table below instead of stopping at the end of the word "member" — padding is
   inside the border box, so insetting the toggle from the right does not shorten it. Padding-bottom keeps
   both the heading and the toggle clear of the rule rather than sitting on it. */
.qstat-members-head { display: flex; align-items: center; justify-content: space-between;
                      gap: 1rem; margin: 26px 0 14px; padding: 0 14px 10px 0;
                      border-bottom: 1px solid #e5e7eb; }
.qstat-members-head .section-title { margin: 0; padding-bottom: 0; border-bottom: none; }
.qstat-period { margin: 20px 0 0; font-size: .84rem; }

/* A row for somebody who is a member but not currently taking calls: still listed, visibly stood down. */
.data-table tr.row-muted td { opacity: .55; }
/* Same-width digits down a column of counts and durations, so they can be compared at a glance. */
.data-table td { font-variant-numeric: tabular-nums; }
.strategy-help th { padding: 12px 20px; }
.modal-form { margin: 14px 0 6px; }
/* Modal forms are label | control (no help column). Label is TOP-aligned, never vertically
   centred against a tall control, and the control fills the full available width. */
.modal-form .setting-row { grid-template-columns: 190px minmax(0, 1fr); padding-left: 0; padding-right: 0; align-items: start; }
.modal-form .setting-row > .lbl { padding-top: 8px; }
.modal-form .setting-row > .help { display: none; }
.modal-form .input, .modal-form textarea.input { width: 100%; max-width: 100%; }
/* Add Defaults modal is wide — use ONE fixed label column across all rows (each row is its own grid,
   so a fixed width keeps every control the same width and aligned) wide enough that the longer
   "(optional)" labels don't wrap. */
.defaults-modal .modal-form .setting-row { grid-template-columns: 250px minmax(0, 1fr); }
.defaults-modal .modal-form .setting-row > .lbl { white-space: nowrap; }
/* Cap the modal to the viewport and scroll only the middle, so the title and footer (Create/Cancel)
   stay in view no matter how tall the options + preview grow. */
.defaults-modal { display: flex; flex-direction: column; max-height: calc(100vh - 32px); }
.defaults-modal .modal-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* Route picker: route select with its dependent secondary control stacked neatly below */
.route-fields { display: flex; flex-direction: column; gap: 12px; align-items: stretch; width: 100%; }
.route-fields .input { width: 100%; min-width: 0; }
/* Segmented two-option toggle (e.g. Paste text | Upload file) */
.seg-toggle { display: flex; width: max-content; border: 1px solid #d1d5db; border-radius: 8px; overflow: hidden; margin: 16px 0 14px; }
.seg-toggle button { border: none; background: #fff; color: #475569; padding: 7px 16px; font-size: .85rem; cursor: pointer; }
.seg-toggle button + button { border-left: 1px solid #d1d5db; }
.seg-toggle button.active { background: var(--accent, #1e293b); color: #fff; }
/* CSV source area: fixed height so the modal doesn't resize when switching paste/upload. */
/* Add-DID body: fixed height so switching Individual / DID range doesn't resize the modal. */
.didmodal-body { min-height: 420px; }
.csv-source-body { min-height: 200px; }
.csv-source-body textarea.input { height: 192px; resize: none; }
.file-input { display: block; padding: 10px 0; }

/* Auth pages — column layout so optional header/footer bars sit above/below the centred card. */
.auth-shell {
    min-height: 100vh; display: flex; flex-direction: column;
    background-color: var(--login-bg, #f5f5f5);
    background-image: var(--login-bg-image, none); background-size: cover; background-position: center;
}
.auth-main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 20px; }
/* Optional login header/footer bars (content is admin HTML). */
.auth-banner { display: flex; align-items: center; justify-content: center; padding: 10px 20px; text-align: center; }
.auth-banner a { color: inherit; text-decoration: underline; }
.auth-card { width: 400px; background: #fff; border-radius: 10px; box-shadow: 0 4px 24px rgba(0,0,0,.08); padding: 44px 40px; }
.auth-brand { text-align: center; margin-bottom: 32px; }
.auth-logo { max-height: 52px; }
/* Product-name wordmark (shown when no logo is set; both come from branding settings). */
.auth-wordmark { font-size: 2rem; font-weight: 700; color: #1e293b; letter-spacing: -0.01em; }
.auth-title { font-size: 1.15rem; text-align: center; margin-bottom: 24px; color: #1e293b; }

/* Login/account card: roomier layout with bold field titles. Scoped so the app's
   other forms (which share .field/.input) are unaffected. */
.auth-card .field { gap: 8px; margin-bottom: 22px; }
.auth-card .field label { font-weight: 700; font-size: .85rem; color: #1e293b; }
.auth-card .input, .auth-card select.input { height: 44px; }
.auth-card .btn-block { height: 46px; margin-top: 10px; }
/* The sign-in button uses the login-page accent colour (independent of the app accent). */
.auth-card .btn-primary { background: var(--login-accent, #1e293b); }
.auth-card .btn-primary:hover { background: var(--login-accent, #1e293b); filter: brightness(1.15); }

/* Small file-upload button (wraps a hidden InputFile). */
.btn-upload { display: inline-flex; align-items: center; align-self: flex-start; gap: 6px; cursor: pointer;
    font-size: .8rem; color: #334155; background: #f1f5f9; border: 1px solid #e2e8f0;
    border-radius: 6px; padding: 6px 12px; margin-top: 2px; }
.btn-upload:hover { background: #e2e8f0; }

/* Branding settings: logo-on-background preview box. */
.brand-preview { display: flex; align-items: center; gap: 14px; padding: 16px 18px; border-radius: 8px; border: 1px solid #e2e8f0; margin-top: 6px; }
.brand-preview .btn-primary { color: #fff; padding: 8px 16px; border-radius: 6px; border: 0; }
.auth-link { color: #94a3b8; }
.auth-link:hover { color: #64748b; }

/* ── Branding admin: image-upload controls ────────────────────────────── */
.brand-upload { display: flex; gap: 16px; align-items: flex-start; margin-top: 4px; }
.brand-upload-body { display: flex; flex-direction: column; gap: 8px; }
.brand-upload-actions { display: flex; gap: 8px; }
.btn-upload-clear { color: #b91c1c; background: #fef2f2; border-color: #fecaca; }
.btn-upload-clear:hover { background: #fee2e2; }
.brand-hint { margin: 0; font-size: .78rem; color: #64748b; }
/* Current-image thumbnail. Checkerboard shows transparency; contain keeps aspect. */
.brand-thumb { width: 96px; height: 64px; border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden;
    display: flex; align-items: center; justify-content: center; flex: none; background: #fff; }
.brand-thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
.brand-thumb-check { background-image:
    linear-gradient(45deg,#eef2f7 25%,transparent 25%), linear-gradient(-45deg,#eef2f7 25%,transparent 25%),
    linear-gradient(45deg,transparent 75%,#eef2f7 75%), linear-gradient(-45deg,transparent 75%,#eef2f7 75%);
    background-size: 14px 14px; background-position: 0 0,0 7px,7px -7px,-7px 0; }
.brand-thumb-empty { font-size: .72rem; color: #94a3b8; }

/* Logo vs. text radio chooser. */
.brand-choice { display: flex; gap: 20px; margin-bottom: 12px; }
.brand-choice label { display: inline-flex; align-items: center; gap: 7px; font-size: .88rem; color: #334155; cursor: pointer; }
.brand-choice input[type=radio] { accent-color: var(--accent, #2563eb); }
/* Every checkbox/radio follows the brand accent unless a more specific rule overrides it. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent, #2563eb); }

/* Toggle switch (header/footer on/off) — label sits beside the switch. */
.brand-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; padding: 6px 0 10px; }
.brand-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.brand-switch-track { position: relative; width: 40px; height: 22px; background: #cbd5e1; border-radius: 999px; transition: background .15s; flex: none; }
.brand-switch-track::after { content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; background: #fff; border-radius: 50%; transition: transform .15s; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
.brand-switch input:checked + .brand-switch-track { background: var(--accent, #2563eb); }
.brand-switch input:checked + .brand-switch-track::after { transform: translateX(18px); }
.brand-switch input:focus-visible + .brand-switch-track { outline: 2px solid var(--accent, #2563eb); outline-offset: 2px; }
.brand-switch-label { font-size: .9rem; font-weight: 500; color: #334155; }
.panel-note { margin: 0 0 8px; font-size: .85rem; color: #64748b; }

/* ── Branding admin: faithful previews ────────────────────────────────── */
/* Browser-tab (favicon) preview. */
.bp-tab { display: inline-flex; align-items: center; gap: 8px; max-width: 240px; padding: 8px 10px;
    background: #f1f5f9; border: 1px solid #e2e8f0; border-bottom: none; border-radius: 8px 8px 0 0; }
.bp-tab-icon { width: 16px; height: 16px; object-fit: contain; flex: none; }
.bp-tab-icon-blank { background: #cbd5e1; border-radius: 3px; }
.bp-tab-title { font-size: .82rem; color: #334155; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bp-tab-x { margin-left: auto; color: #94a3b8; font-size: 1rem; line-height: 1; }

/* Login-page preview: a scaled sign-in card on the branded backdrop. */
/* All three previews (login / application / email) share this height so the right column is
   consistent across the tabs — tall enough for the largest. */
.bp-login { display: flex; flex-direction: column; border-radius: 10px;
    border: 1px solid #e2e8f0; min-height: 360px; overflow: hidden; }
.bp-login-main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 24px; }
.bp-login-banner { display: flex; align-items: center; justify-content: center; padding: 8px 16px; text-align: center; }
.bp-login-banner a { color: inherit; text-decoration: underline; }
/* Preview cards keep the real login:email width ratio (~400:600 = 2:3). The email card below
   is 460px, so the login card is ~300px. */
.bp-login-card { width: 300px; max-width: 100%; background: #fff; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,.12); padding: 26px 24px; }
.bp-login-brand { text-align: center; margin-bottom: 20px; min-height: 34px; display: flex; align-items: center; justify-content: center; }
.bp-login-brand img { max-height: 34px; max-width: 80%; }
.bp-login-wordmark { font-size: 1.3rem; font-weight: 700; color: #1e293b; }
.bp-field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.bp-field-label { font-size: .68rem; font-weight: 700; color: #1e293b; }
.bp-field-box { height: 28px; border: 1px solid #d1d5db; border-radius: 6px; background: #f8fafc; }
.bp-login-btn { text-align: center; color: #fff; font-size: .8rem; font-weight: 600; padding: 9px; border-radius: 6px; margin-top: 4px; }
.bp-login-link { text-align: center; font-size: .72rem; color: #94a3b8; margin-top: 12px; }

/* Application (sidebar) preview. */
.bp-sidebar { width: 200px; border-radius: 10px; overflow: hidden; padding-bottom: 10px; border: 1px solid #e2e8f0; }
.bp-sidebar-head { padding: 16px; font-size: 1rem; font-weight: 600; border-bottom: 1px solid; min-height: 24px; display: flex; align-items: center; }
.bp-sidebar-head img { max-height: 24px; max-width: 90%; }
.bp-sidebar-label { padding: 4px 16px 2px; font-size: .58rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; opacity: .6; }
.bp-sidebar-item { margin: 6px 10px 0; padding: 8px 12px; border-radius: 6px; font-size: .82rem; opacity: .85; }
.bp-sidebar-active { opacity: 1; color: #fff; font-weight: 600; }

/* Email/alerts preview: a scaled version of the branded HTML shell. */
.bp-email { padding: 16px; background: #f4f5f7; border-radius: 10px; border: 1px solid #e2e8f0; min-height: 360px; display: flex; align-items: center; justify-content: center; }
/* Real email card is a wide ~600px card with thin margins, so let it fill the backdrop. */
.bp-email-card { width: 460px; max-width: 100%; background: #fff; border: 1px solid #e5e7eb; border-top: 4px solid #2563eb; border-radius: 10px; overflow: hidden; }
.bp-email-head { padding: 18px 22px 4px; min-height: 28px; display: flex; align-items: center; }
.bp-email-head img { max-height: 30px; max-width: 60%; }
.bp-email-wordmark { font-size: 1.1rem; font-weight: 700; }
.bp-email-body { padding: 10px 22px 20px; font-size: .82rem; line-height: 1.55; color: #1f2937; }
.bp-email-body p { margin: 0 0 12px; }
.bp-email-subject { font-weight: 700; font-size: .92rem; color: #111827; }
.bp-email-btn { display: inline-block; color: #fff; font-size: .8rem; font-weight: 600; padding: 9px 18px; border-radius: 6px; }
.bp-email-foot { padding: 14px 22px; border-top: 1px solid #eef0f2; font-size: .72rem; line-height: 1.5; color: #9ca3af; white-space: normal; overflow-wrap: anywhere; word-break: break-word; }
/* A custom footer preserves the admin's line breaks (and still wraps long lines). */
.bp-email-foot-custom { white-space: pre-line; }

/* Email footer textarea: narrow (≈one-third) but multi-line. */
.brand-footer-input { width: 100%; resize: vertical; font-family: inherit; line-height: 1.5; padding: 8px 10px; }

/* Combined application preview: sidebar + header bar + content window, all branded. */
.bp-app { display: flex; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; min-height: 230px; max-width: 560px; }
.bp-app-full { max-width: 100%; min-height: 360px; }
.bp-app-full .bp-app-side { width: 190px; }
.bp-app-side { width: 156px; flex: none; display: flex; flex-direction: column; }
.bp-app-side .bp-sidebar-head { font-size: .9rem; padding: 14px 16px; }
.bp-app-side-foot { margin-top: auto; padding: 12px 14px; border-top: 1px solid; }
.bp-pill { font-size: .55rem; text-transform: uppercase; letter-spacing: .05em; background: rgba(148,163,184,.28); padding: 3px 8px; border-radius: 6px; opacity: .9; }
.bp-app-right { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.bp-app-topbar { height: 36px; flex: none; display: flex; align-items: center; justify-content: space-between; padding: 0 14px; font-size: .72rem; border-bottom: 1px solid rgba(120,120,120,.22); }
.bp-app-topbar-user { opacity: .8; }
.bp-app-content { flex: 1; padding: 16px; background-size: cover; background-position: center; }
/* Tab strip inside the preview — mirrors the real underline-style tabs. */
.bp-tabs { display: flex; gap: 0; border-bottom: 2px solid #e5e7eb; }
.bp-app-content .bp-tabs { margin-bottom: 14px; }
/* Underline-style strip tabs (distinct class from the favicon browser-tab mock .bp-tab). */
.bp-ttab { padding: 8px 18px; font-size: .82rem; font-weight: 500; border-bottom: 2px solid transparent; margin-bottom: -2px; background: none; }
.bp-ttab-active { font-weight: 600; }

/* Compact colour grid — packs several colour pickers into the panel width. The left
   indent (label column 190px + 14px gap + 16px base) lines the pickers up with the
   control column of the SettingRow controls (logo radio, window background). */
.color-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 14px 20px; padding: 8px 16px 14px; }
/* Label sits above its picker, both left-aligned, so each label unambiguously belongs to the
   picker directly beneath it — and lines up with the stacked radio controls above/below. */
.color-field { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; cursor: pointer; }
.color-field > span { font-size: .85rem; color: #334155; }

/* Settings on the left, live preview on the right. Columns size to their own content
   (top-aligned) — no forced equal height. The preview sticks near the top as you scroll. */
/* Left stretches to the taller of the two columns; the right (preview) keeps its natural
   height (align-self: start). So the left column is always at least as tall as the right. */
.brand-split { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: stretch; }
/* Make the left CONTENT fill that stretched height: the last panel grows into any slack, so
   the settings are never visibly shorter than the preview. */
.brand-split-left { min-width: 0; display: flex; flex-direction: column; }
.brand-split-left > .panel:last-child { flex: 1; margin-bottom: 0; }
.brand-split-right { min-width: 0; align-self: start; position: sticky; top: 16px; }
/* In the split, stacked controls drop the fixed label column so they left-align with the colour grid. */
.brand-split .setting-row.row-stacked { grid-template-columns: 1fr; }
@media (max-width: 1100px) { .brand-split { grid-template-columns: 1fr; } .brand-split-right { position: static; } }

.bp-app-h1 { font-size: .95rem; font-weight: 600; margin-bottom: 10px; }
.bp-app-h2 { font-size: .78rem; font-weight: 600; margin-bottom: 6px; }
.bp-app-panel { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; font-size: .74rem; color: #475569; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
.bp-app-text { margin: 0 0 12px; }
.bp-app-controls { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.bp-app-btn { display: inline-block; padding: 7px 14px; border-radius: 6px; color: #fff; font-size: .74rem; font-weight: 600; }
.bp-app-toggle { display: inline-flex; align-items: center; gap: 8px; font-size: .74rem; color: #475569; }
.bp-app-toggle-track { position: relative; width: 34px; height: 19px; border-radius: 999px; flex: none; }
.bp-app-toggle-track::after { content: ""; position: absolute; top: 2px; right: 2px; width: 15px; height: 15px; background: #fff; border-radius: 50%; box-shadow: 0 1px 2px rgba(0,0,0,.25); }

/* Centred header block (title + description) used on MFA and similar account pages. */
.auth-header { text-align: center; margin-bottom: 30px; }
.auth-header .auth-title { margin-bottom: 10px; }
.auth-subtitle { margin: 0; color: #64748b; font-size: .9rem; line-height: 1.55; }

/* One-time-code entry: roomy, centred, spaced digits. */
.auth-card .input.auth-code {
    height: 52px; text-align: center; font-size: 1.5rem; letter-spacing: .4em;
    text-indent: .4em; font-variant-numeric: tabular-nums;
}
.auth-card .input.auth-code::placeholder { letter-spacing: .4em; color: #cbd5e1; }

/* Onboarding wizard step indicator (forced password change -> MFA enrolment). */
.auth-steps { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 26px; }
.auth-steps .s { display: flex; align-items: center; gap: 8px; font-size: .8rem; color: #94a3b8; }
.auth-steps .s .n { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
    background: #e2e8f0; color: #64748b; font-weight: 700; font-size: .72rem; }
.auth-steps .s.active { color: #1e293b; font-weight: 600; }
.auth-steps .s.active .n { background: var(--login-accent, #1e293b); color: #fff; }
.auth-steps .s.done .n { background: #16a34a; color: #fff; }
.auth-steps .sep { width: 26px; height: 2px; border-radius: 2px; background: #e2e8f0; }

/* Two-factor enrolment block (QR + manual key). */
.mfa-setup { text-align: center; }
.mfa-setup img { border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px; background: #fff; }
.mfa-key { margin: 16px 0 8px; font-size: .8rem; color: #64748b; }
.mfa-key code { display: block; margin-top: 6px; background: #f1f5f9; border: 1px solid #e2e8f0;
    border-radius: 6px; padding: 9px 12px; font-size: .95rem; letter-spacing: .12em; color: #334155; }

.validation-message { color: #dc2626; font-size: 0.78rem; }
#blazor-error-ui { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: #fee2e2; color: #991b1b; padding: 14px 24px; z-index: 1000; border-top: 1px solid #fecaca; font-size: .95rem; }
#blazor-error-ui .reload { margin-left: 14px; }
#blazor-error-ui .dismiss { position: absolute; right: 16px; top: 12px; cursor: pointer; }

/* Collapsible nav groups implemented with <details> (works without JS) */
.sidebar-nav details > summary { list-style: none; }
.sidebar-nav details > summary::-webkit-details-marker { display: none; }
.sidebar-nav details[open] > .nav-children { display: block; }
.sidebar-nav details[open] .nav-arrow::after { transform: rotate(90deg); }

/* MOH track upload (drop zone + progress) */
.upload-area { position: relative; border: 2px dashed #cbd5e1; border-radius: 8px; padding: 22px; text-align: center; background: #f9fafb; transition: border-color .15s, background .15s; }
.upload-area:hover { border-color: #3b82f6; background: #f0f6ff; }
.upload-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.upload-input:disabled { cursor: default; }
.upload-inner { display: flex; flex-direction: column; gap: 4px; pointer-events: none; }
.upload-inner strong { color: #1e293b; }
.upload-inner .muted { font-size: .78rem; }
.upload-job { margin-top: 12px; }
.upload-job-head { display: flex; justify-content: space-between; align-items: center; font-size: .82rem; margin-bottom: 4px; }
.progress { height: 8px; background: #e5e7eb; border-radius: 9999px; overflow: hidden; }
.progress-bar { height: 100%; background: #1e293b; border-radius: 9999px; transition: width .15s ease; }
.progress-bar.indeterminate { width: 40% !important; animation: progress-slide 1.1s ease-in-out infinite; }
@keyframes progress-slide { 0% { margin-left: -40%; } 100% { margin-left: 100%; } }

/* Integration pages (e.g. ElevenLabs): 2-col label|value rows, values vertically centred against the
   label — same feel as /admin/host (.host-settings), just a narrower label to suit the 1/3 column. */
.integration-rows .setting-row { grid-template-columns: 205px minmax(0, 1fr); align-items: center; }
.integration-rows .setting-row > .lbl { padding-top: 0; }
.integration-rows .setting-row > .help { display: none; }
/* A row whose value is multi-line (e.g. number + progress bar): top-align so the label lines up with the
   first line of the value, not the vertical centre of the whole block. */
.integration-rows .setting-row.row-top { align-items: start; }

/* ElevenLabs integration page: settings/account (1/3) on the left, "Used by" (2/3) on the right. */
.el-layout { display: grid; grid-template-columns: 1fr 2fr; gap: 16px; align-items: start; }
/* The section-title's default 26px top margin would double up with the page-header gap — drop it for the
   heading at the top of each column so content sits under the page title like elsewhere. */
.el-layout > div > .section-title:first-child { margin-top: 0; }
@media (max-width: 1100px) { .el-layout { grid-template-columns: 1fr; } }

/* MOH track row: [num] [name] ......scrubber(only while playing)...... [play][len][size][up][down][del] */
.track-item { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid #f1f5f9; background: #fff; }
.track-item:last-child { border-bottom: none; }
.track-name { color: #1e293b; font-size: .875rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 280px; flex-shrink: 0; }
.track-scrubber { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; min-width: 40px; padding: 0 20px; visibility: hidden; }
.track-item.playing .track-scrubber { visibility: visible; }
.track-cur { font-size: .72rem; color: #64748b; min-width: 30px; }
.scrubber-bar { flex: 1; height: 6px; background: #e5e7eb; border-radius: 9999px; cursor: pointer; overflow: hidden; }
.scrubber-fill { height: 100%; width: 0%; background: #1e293b; border-radius: 9999px; }
.track-play { width: 30px; height: 30px; flex-shrink: 0; border: 1px solid #d1d5db; background: #fff; border-radius: 50%; color: #1e293b; cursor: pointer; font-size: .7rem; line-height: 1; display: inline-flex; align-items: center; justify-content: center; }
.track-play:hover { background: #f1f5f9; }
.track-len, .track-size { font-size: .78rem; white-space: nowrap; flex-shrink: 0; min-width: 42px; text-align: right; }

/* FreeSWITCH live log console */
.log-console { background: #0d1117; color: #d1d5db; border-radius: 8px; padding: 12px 14px; flex: 1 1 auto; min-height: 200px; overflow-y: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .78rem; line-height: 1.5; box-shadow: var(--shadow); }
.log-empty { color: #6b7280; font-style: italic; }
.log-line { display: flex; gap: 10px; padding: 1px 0; align-items: flex-start; }
.log-time { flex-shrink: 0; width: 62px; color: #6b7280; font-variant-numeric: tabular-nums; }
.log-lvl { flex-shrink: 0; width: 64px; color: #6b7280; text-transform: uppercase; font-size: .68rem; padding-top: 1px; }
.log-text { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
.log-line.lvl-err { color: #fca5a5; }
.log-line.lvl-err .log-lvl { color: #ef4444; }
.log-line.lvl-warn { color: #fcd34d; }
.log-line.lvl-warn .log-lvl { color: #f59e0b; }
.log-line.lvl-notice .log-lvl { color: #60a5fa; }
.log-line.lvl-debug { color: #94a3b8; }
.log-line.lvl-info { color: #d1d5db; }

/* Hover/focus info tooltip */
.info-tip { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; background: #e2e8f0; color: #475569; font-size: .72rem; cursor: help; flex-shrink: 0; outline: none; }
.info-tip:hover, .info-tip:focus { background: #cbd5e1; color: #1e293b; }
.info-pop { display: none; position: absolute; top: calc(100% + 8px); left: 0; width: 360px; max-width: 70vw; background: #fff; color: #374151; border: 1px solid #d1d5db; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.14); padding: 12px 14px; z-index: 300; font-size: .78rem; line-height: 1.45; cursor: default; }
.info-tip:hover .info-pop, .info-tip:focus .info-pop, .info-tip:focus-within .info-pop { display: block; }
.info-pop strong { color: #1e293b; }
.info-pop p { margin: 6px 0 8px; color: #64748b; }
.info-pop ul { margin: 0; padding-left: 0; list-style: none; }
.info-pop li { padding: 3px 0; border-top: 1px solid #f1f5f9; }
.info-pop li b { color: #1e293b; }

/* System Health */
.metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 16px; }
@media (max-width: 1100px) { .metric-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .metric-grid { grid-template-columns: 1fr; } }
.metric-card { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 14px 16px; }
.metric-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 8px; }
.metric-head > span:first-child { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: #94a3b8; font-weight: 600; }
.metric-now { font-size: 1.15rem; font-weight: 700; color: #1e293b; font-variant-numeric: tabular-nums; }
.spark { width: 100%; height: 44px; display: block; }
.metric-sub { font-size: .72rem; color: #64748b; margin-top: 6px; }

/* Task-manager style Performance tab: big, double-size charts */
.perf-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 18px; }
@media (max-width: 900px) { .perf-grid { grid-template-columns: 1fr; } }
.perf-card { background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 18px 20px; }
.perf-card .metric-head > span:first-child { font-size: .8rem; }
.perf-card .metric-now { font-size: 1.4rem; }
.perf-card .spark { height: 150px; margin-top: 8px; }
.perf-card .metric-sub { font-size: .76rem; margin-top: 8px; }

/* Processes tab: scrollable, sortable table */
.proc-scroll { max-height: calc(100vh - 300px); overflow-y: auto; border-radius: 8px; box-shadow: var(--shadow); background: #fff; }
.proc-scroll .data-table { box-shadow: none; }
.proc-scroll thead th { position: sticky; top: 0; z-index: 1; background: #f9fafb; }
.data-table th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.data-table th.sortable:hover { color: #1e293b; }
.data-table th.num, .data-table td.num { text-align: right; }
/* Dial-number ("Ext") column — fixed, identical width on every call-flow-object list
   (announcements, ring groups, auto attendants, time conditions, call flow controls). */
/* Shared column sizing for the call-flow-object index pages (call handling, ring groups, announcements,
   auto attendants, call flow controls, time conditions) so the same table reads the same everywhere:
   Ext hard left at a fixed 5-digit width, Name takes whatever is left (width:100% squeezes every other
   column to its content), DIDs immediately after Name at a one-number width (see DidCell.razor), and
   Used By / Active / Edit fixed at the right-hand end. */
/* Each fixed column asks for a real width AND a min-width: in an auto-layout table a bare `width` is only a
   hint the browser drops when space is tight, which is how these ended up squished. Name carries no width at
   all — it's the column the leftover space goes to — so nothing else has to give up room for it. */
.data-table th.col-ext, .data-table td.col-ext { width: 100px; min-width: 100px; }
.data-table th.col-type, .data-table td.col-type { width: 154px; min-width: 154px; }
.data-table th.col-name, .data-table td.col-name { min-width: 135px; }
/* DidCell shows one number (+"+N" link), so size to content rather than reserving three DIDs' width. */
.data-table th.col-dids, .data-table td.col-dids { width: 1%; min-width: 90px; }
/* Used By is a count, so it right-aligns; the extra padding keeps it off the Active pill next to it. */
.data-table th.col-used, .data-table td.col-used { width: 92px; min-width: 92px; text-align: right; padding-right: 24px; }
.data-table th.col-status, .data-table td.col-status { width: 130px; min-width: 130px; padding-left: 22px; }
/* Call-flow index tables: nothing wraps except the Name column, which is the one with room to spare. */
.cfo-table td { white-space: nowrap; }
.cfo-table td.col-name { white-space: normal; }
.data-table th.actions-cell, .data-table td.actions-cell { width: 1%; }
/* "+2" after the first DID — the rest are one click away rather than three columns wide. */
/* Call-flow-control state switch (ForkSwitch.razor): both positions labelled and visible, the current one
   filled — green Normal / red Override, the same colours the BLF key shows on a handset. A bare lamp read as
   a status badge to anyone who didn't already know it was clickable. */
.fork-switch { display: inline-flex; align-items: center; gap: 2px; padding: 3px; border: 1px solid #cbd5e1;
               border-radius: 9999px; background: #f1f5f9; }
.fork-opt { display: inline-flex; align-items: center; gap: 7px; border: 0; background: transparent;
            border-radius: 9999px; padding: 6px 15px; font: inherit; font-size: .78rem; font-weight: 600;
            color: #64748b; cursor: pointer; transition: background-color .12s ease, color .12s ease; }
.fork-opt .fork-dot { width: 9px; height: 9px; border-radius: 50%; background: #cbd5e1; flex: 0 0 auto; }
.fork-opt:hover:not(:disabled) { color: #334155; }
.fork-opt:disabled { cursor: default; }
.fork-switch.is-normal .fork-normal { background: #16a34a; color: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.18); }
.fork-switch.is-normal .fork-normal .fork-dot { background: #fff; box-shadow: 0 0 6px rgba(255,255,255,.85); }
.fork-switch.is-override .fork-override { background: #dc2626; color: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.18); }
.fork-switch.is-override .fork-override .fork-dot { background: #fff; box-shadow: 0 0 6px rgba(255,255,255,.85); }
.fork-switch.is-compact .fork-opt { padding: 4px 11px; font-size: .72rem; gap: 6px; }
.fork-switch.is-compact .fork-dot { width: 8px; height: 8px; }
.did-more { margin-left: 8px; font-size: .8rem; white-space: nowrap; }
/* A modal is rendered where its trigger lives — including inside a right-aligned cell — so it sets its own
   alignment instead of inheriting one. */
.modal-box { text-align: left; }
/* Call Handling: one routing destination per line (Open: …, Closed: …). */
/* One destination per line, and each line stays on ITS line: "Auto attendant: Main Menu" broken after the
   colon reads as two destinations. The column takes the width it needs (Name is the elastic one). */
.dest-line { white-space: nowrap; }
.dest-line + .dest-line { margin-top: 3px; }
.dest-line .muted { margin-right: 3px; }
/* Same wherever a destination is rendered on its own (ring-group Timeout, CFC Normal/Override,
   TC Open/Closed) — keep "Ring group:" and the name on one line where they fit, but cap the width so a
   long target name wraps rather than stretching the column (which forced these tables to scroll). */
.cfo-dest { display: inline-block; vertical-align: top; max-width: 130px; white-space: nowrap; }
.cfo-dest .muted { white-space: nowrap; }
/* The target name may wrap across lines, but only at word boundaries — break-word (not anywhere) so a
   normal name like "St Columba" never splits mid-word; only a single token longer than the cell breaks. */
.cfo-dest a { white-space: normal; overflow-wrap: break-word; }
/* Top-align every cell so a multi-line Destinations column doesn't leave Name/Ext floating mid-row. */
.data-table.top-align td { vertical-align: top; }
.data-table th.sort-asc::after { content: " \25B2"; font-size: .6rem; }
.data-table th.sort-desc::after { content: " \25BC"; font-size: .6rem; }

/* User-resizable / show-hide columns (ListViewPrefs). table-layout:fixed makes the <col> widths
   authoritative so a dragged px width sticks; the one column left without a width (Name) absorbs the
   slack, so at default widths the table fills its container without a horizontal scrollbar. */
/* Full width: the table fills the page, and the ONE flexible column (no set width — usually Name) soaks up
   the spare space, so under table-layout:fixed every other column renders at exactly its set width (the
   tight structural columns never grow). When the fixed columns are wider than the viewport the flex column
   shrinks and the table scrolls inside .table-scroll. */
.resizable-table { table-layout: fixed; width: 100%; }
.resizable-table th { position: relative; }
/* Slightly tighter cells so a fixed column fits more of its content before truncating. */
.resizable-table td, .resizable-table th { padding-left: 12px; padding-right: 12px; }
/* The row-select checkbox column is structural (not resizable), so pin it tight to the checkbox instead of
   the generic default width — otherwise a wide, un-shrinkable gap sits between the checkbox and the first
   data column. !important beats the inline <col> width the sizing sets. */
.resizable-table colgroup col[data-col="select"] { width: 34px !important; }
.resizable-table td:first-child, .resizable-table th:first-child { padding-right: 2px; }
/* Used By carries a big right padding in the plain tables to sit off the Active pill; in resizable tables
   it's the last data column before the (now tightly-sized) actions, so trim it to close the gap there. */
.resizable-table td.col-used, .resizable-table th.col-used { padding-right: 10px; }
/* Left-align the row actions so Edit/Delete hug the last data column (mirroring the checkbox hugging the
   left) instead of being pinned to the far right with the column's slack showing as a gap before them. */
.resizable-table td.actions-cell { text-align: left; }
/* Truncate, don't wrap: each fixed-width column keeps its content on one line and clips the overflow with
   an ellipsis. Widen the column (drag) to see the rest. */
.resizable-table td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.resizable-table td.col-name { white-space: nowrap; }
.resizable-table .cfo-dest { display: block; max-width: 100%; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; }
.resizable-table .cfo-dest a { white-space: nowrap; overflow-wrap: normal; }
/* While kfsAutoFitTable measures (columns collapsed to 1px), nothing inside a cell may wrap, or a multi-word label
   would report the width of its longest word instead of its single line. */
.resizable-table.kfs-measuring td, .resizable-table.kfs-measuring td * { white-space: nowrap !important; overflow-wrap: normal !important; }
/* Structural cells (row-select checkbox, row actions) must never truncate/clip their controls. */
.resizable-table td.actions-cell, .resizable-table th.actions-cell,
.resizable-table td:first-child, .resizable-table th:first-child { overflow: visible; text-overflow: clip; white-space: nowrap; }
/* Left-align the leading column (row-select checkbox / presence dot) so the header's select-all box and
   every row box line up on the left — !important to beat the per-row inline text-align:center. */
.resizable-table td:first-child, .resizable-table th:first-child { text-align: left !important; }
.col-resizer { position: absolute; top: 0; right: -3px; width: 7px; height: 100%; cursor: col-resize;
    z-index: 2; touch-action: none; }
.col-resizer:hover { background: #c7d2fe; }
/* A very subtle grip in each resizable header cell so it's discoverable that the column can be dragged. */
.col-resizer::after { content: ""; position: absolute; right: 3px; top: 34%; height: 32%; width: 2px;
    border-left: 1px solid #d0d7e2; border-right: 1px solid #d0d7e2; }
.col-resizer:hover::after { border-color: #94a3b8; }

/* The "choose columns" popover. Floated into the table's header row (top-right) via .table-area, which
   is OUTSIDE the horizontal scroll container so the dropdown is never clipped by it. */
.table-area { position: relative; }
/* The chooser icon floats over the top-right of the header row: the last header cell reserves that room so its label
   ("Actions") is never hidden behind the icon — auto-fit measures the cell including this padding. */
.table-area > .table-scroll > table.resizable-table thead th:last-child, .table-area > table.resizable-table thead th:last-child { padding-right: 44px; }
/* Floated into the header's top-right. A header-coloured background (with a short fade on its left) keeps
   the icon legible and masks any header text behind it when a wide table has scrolled the actions column
   out of view. */
.table-area > .col-chooser { position: absolute; top: 5px; right: 10px; z-index: 5; background: #f8fafc;
    border-radius: 6px; box-shadow: -10px 0 8px -6px #f8fafc; }
.col-chooser { position: relative; display: inline-flex; }
.col-chooser-backdrop { position: fixed; inset: 0; z-index: 40; }
.col-chooser-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 41; min-width: 190px;
    background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; box-shadow: var(--shadow); padding: 8px; }
.col-chooser-head { font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
    color: #64748b; padding: 2px 6px 6px; }
.col-chooser-item { display: flex; align-items: center; gap: 8px; padding: 5px 6px; border-radius: 6px;
    cursor: pointer; font-size: .9rem; white-space: nowrap; }
.col-chooser-item:hover { background: #f1f5f9; }
.col-chooser-reset { margin-top: 6px; width: 100%; border: none; background: none; text-align: left;
    padding: 5px 6px; border-radius: 6px; cursor: pointer; color: #4f46e5; font-size: .85rem; }
.col-chooser-reset:hover { background: #eef2ff; }
.health-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 1000px) { .health-cols { grid-template-columns: 1fr; } }
.disk-row { padding: 8px 0; }
.disk-row:not(:last-child) { border-bottom: 1px solid #f1f5f9; }
.disk-label { display: flex; justify-content: space-between; font-size: .8rem; margin-bottom: 5px; }
.disk-bar { height: 8px; background: #e5e7eb; border-radius: 9999px; overflow: hidden; }
.disk-fill { height: 100%; background: #10b981; border-radius: 9999px; }
.disk-fill.warm { background: #f59e0b; }
.disk-fill.hot { background: #ef4444; }

/* Extension presence traffic-light */
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: #cbd5e1; vertical-align: middle; }
.dot-available { background: #22c55e; box-shadow: 0 0 0 2px rgba(34,197,94,.18); }
.dot-busy { background: #ef4444; box-shadow: 0 0 0 2px rgba(239,68,68,.18); }
.dot-ringing { background: #f59e0b; box-shadow: 0 0 0 2px rgba(245,158,11,.18); animation: dot-pulse 1s ease-in-out infinite; }
.dot-offline { background: #cbd5e1; }
.dot-info { background: #3b82f6; box-shadow: 0 0 0 2px rgba(59,130,246,.18); }
@keyframes dot-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
.trunk-status { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.input.invalid { border-color: #dc2626; box-shadow: 0 0 0 2px rgba(220,38,38,.12); }
/* kfs-flash is toggled by kfsMarkInvalid() to (re)play a brief red pulse when a field is flagged
   invalid on blur/save — the .invalid border persists after the pulse fades. */
.input.invalid.kfs-flash { animation: kfs-invalid-flash .55s ease; }
@keyframes kfs-invalid-flash {
    0%   { background: #fee2e2; box-shadow: 0 0 0 4px rgba(220,38,38,.4); }
    100% { background: #fff;    box-shadow: 0 0 0 2px rgba(220,38,38,.12); }
}
.field-error { color: #dc2626; font-size: .8rem; margin-top: 4px; }

/* Trunk provider picker */
.section-title { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: #64748b; font-weight: 600; margin: 26px 0 12px; }
.provider-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; max-width: 1000px; }
.provider-card { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
    position: relative; background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; padding: 40px 24px; min-height: 190px;
    text-decoration: none; color: #1e293b; box-shadow: var(--shadow); transition: border-color .12s, box-shadow .12s, transform .12s; }
.provider-card:hover:not(.disabled) { border-color: #c7d2fe; box-shadow: 0 4px 14px rgba(0,0,0,.08); transform: translateY(-1px); }
.provider-logo { height: 72px; display: flex; align-items: center; justify-content: center; }
.provider-logo img { max-height: 72px; max-width: 220px; object-fit: contain; }
.provider-generic-icon { width: 64px; height: 64px; color: #6366f1; }
.provider-name { font-weight: 600; font-size: 1.05rem; text-align: center; }
.provider-card.disabled { opacity: .55; filter: grayscale(1); cursor: not-allowed; }
.provider-card.disabled .badge-soon { position: absolute; top: 10px; right: 10px; }

/* Trunk gateway event timeline (Diagnostics tab) */
.trunk-events { width: 100%; border-collapse: collapse; }
.trunk-events th, .trunk-events td { text-align: left; padding: 8px 12px; border-bottom: 1px solid #f1f5f9; font-size: .9rem; }
.trunk-events th { color: #64748b; font-weight: 600; }
.trunk-events th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.trunk-events th.sortable:hover { color: #1e293b; }
.trunk-events .when { color: #64748b; white-space: nowrap; }
.evt-badge { display: inline-flex; align-items: center; gap: 7px; }
.evt-empty { color: #94a3b8; padding: 14px 12px; }
.pager { display: flex; align-items: center; gap: 10px; margin-top: 12px; font-size: .85rem; }
.data-table tr.row-muted td { opacity: .55; }
/* Same-width digits down a column of counts and durations, so they can be compared at a glance. */
.data-table td { font-variant-numeric: tabular-nums; }
/* Top-align a setting-row's label when its control is tall (e.g. the codec list). */
.setting-row.row-top { align-items: start; }
.setting-row.row-top > .lbl { padding-top: 9px; }
/* Toggle rows: a switch is shorter than a text input, so vertically centre the row and drop the
   label's input-baseline padding — otherwise the toggle sits slightly above the label text. */
.setting-row.row-toggle { align-items: center; }
.setting-row.row-toggle > .lbl { padding-top: 0; }
/* Center a row whose value is short content (text/badges) rather than a full-height input, so the
   label and value sit on the same line (the default start-align + label padding-top reads misaligned). */
.setting-row.row-center { align-items: center; }
.setting-row.row-center > .lbl { padding-top: 0; }
/* A toggle paired with an input on one row (e.g. "Alert on match" + recipients). */
.alert-row { display: flex; align-items: center; gap: 12px; }
.alert-row > .input { flex: 1; min-width: 0; }

.codec-order { display: flex; flex-direction: column; gap: 6px; max-width: 400px; }
.codec-row { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid #e5e7eb; border-radius: 8px; background: #fff; }
.codec-row.on { border-color: #c7d2fe; background: #f5f7ff; }
.codec-row:not(.on) .codec-opt { color: #94a3b8; }
.codec-rank { flex: 0 0 16px; text-align: center; font-size: .78rem; font-weight: 700; color: #6366f1; }
.codec-opt { flex: 1 1 auto; display: inline-flex; align-items: center; gap: 8px; font-size: .9rem; white-space: nowrap; cursor: pointer; }
.codec-move { flex: 0 0 auto; display: flex; align-items: center; }
.codec-row.dragging { opacity: .4; }
.codec-row.drag-over { box-shadow: inset 0 2px 0 #6366f1; }
.status-legend { display: flex; gap: 14px; font-size: .72rem; color: #64748b; }
.status-legend span { display: inline-flex; align-items: center; gap: 5px; }

/* A warning row: message on the left, the action that resolves it right-aligned on the same line. */
.restart-row { display: flex; gap: 16px; align-items: flex-start; justify-content: space-between; }
.restart-row + .restart-row { margin-top: 8px; }
.restart-row > a, .restart-row > button { white-space: nowrap; flex: none; }

/* ---- Call Flow Designer ------------------------------------------------------------------------
   ONE surface, not three cards. The start selector, the palette, the canvas and the settings are regions
   of a single panel divided by hairlines, so the eye reads it as one workspace rather than three things
   that happen to sit beside each other. */
.cfd { display: flex; flex-direction: column; background: #fff; border: 1px solid #e5e7eb;
       border-radius: 10px; overflow: hidden; height: calc(100vh - 188px); min-height: 460px;
       margin-bottom: 8px; }
.cfd-bar { display: flex; align-items: center; gap: 12px; padding: 12px 16px;
           border-bottom: 1px solid #eef2f7; flex: none; }
.cfd-bar .spacer { flex: 1; }
/* The start picker carries a number, a name and what kind of thing it is, so it needs room. */
.cfd-bar .tenant-dropdown.cfd-start { width: 850px; max-width: 100%; }
.cfd-bar .tenant-dropdown.cfd-start .tenant-dropdown-toggle { width: 100%; height: 36px; padding: 7px 10px;
    font-size: inherit; border-radius: 6px; background: #fff; justify-content: space-between; }
.cfd-bar .tenant-dropdown.cfd-start .tenant-dropdown-menu { left: 0; right: auto; width: 100%;
    min-width: 0; max-width: none; }
.cfd-bar .tenant-dropdown.cfd-start .tenant-options { max-height: 420px; }
.cfd-body { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 340px; }
/* The palette as a row of chips under the start picker, so the canvas keeps the width. */
/* padding-left lines "Add:" up with the TEXT in the start picker above it, not with the picker's border —
   the picker's own 10px inset plus its 1px border is what the extra 11 is. */
.cfd-add { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 9px 16px 9px 27px;
           border-bottom: 1px solid #eef2f7; flex: none; }
.cfd-add-label { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
                 color: #94a3b8; margin-right: 2px; }
.cfd-add-note { font-size: .78rem; color: #92400e; }
.cfd-add .tenant-dropdown.cfd-existing { width: 300px; }
.cfd-add .tenant-dropdown.cfd-existing .tenant-dropdown-toggle { width: 100%; height: 30px; padding: 4px 10px;
    font-size: .8rem; border-radius: 999px; background: #fff; justify-content: space-between; }
.cfd-add .tenant-dropdown.cfd-existing .tenant-dropdown-menu { left: 0; right: auto; width: 420px;
    min-width: 0; max-width: none; }
.cfd-side { display: flex; flex-direction: column; min-height: 0; border-right: 1px solid #eef2f7; }
.cfd-side-right { border-right: none; border-left: 1px solid #eef2f7; }
.cfd-col-head { padding: 10px 14px; border-bottom: 1px solid #eef2f7; font-size: .78rem; font-weight: 700;
                text-transform: uppercase; letter-spacing: .04em; color: #64748b; flex: none; }
.cfd-col-body { padding: 12px; overflow-y: auto; min-height: 0; flex: 1; }
.cfd-blank { flex: 1; display: flex; align-items: center; justify-content: center; color: #94a3b8;
             font-size: .88rem; padding: 40px; }

/* Toolbox */
.cfd-tool { display: inline-flex; align-items: center; gap: 7px; padding: 5px 10px;
            border: 1px solid #e5e7eb; border-radius: 999px; background: #fff; cursor: grab;
            font-size: .8rem; white-space: nowrap; font-family: inherit; color: #0f172a; user-select: none; }
.cfd-tool:hover { border-color: var(--accent, #3b82f6); background: #f8fafc; }
.cfd-tool.is-disabled { opacity: .5; cursor: not-allowed; }
.cfd-tool:active { cursor: grabbing; }
/* What follows the cursor while a type is being dragged out of the palette. */
.cfd-drop-ghost { position: fixed; z-index: 3000; pointer-events: none; padding: 6px 10px;
                  border-radius: 8px; background: #fff; border: 1px solid var(--accent, #3b82f6);
                  box-shadow: 0 6px 18px rgba(15,23,42,.18); font-size: .82rem; color: #0f172a; }
.cfd-tool-swatch { width: 12px; height: 12px; border-radius: 4px; flex: none;
                   background: var(--cfd-bg); border: 1px solid var(--cfd-fg); }

/* Canvas — a region of the panel, so no border or corners of its own; the dot grid is what sets it apart. */
.cfd-canvas { position: relative; overflow: hidden; min-height: 0; background-color: #f8fafc;
              background-image: radial-gradient(#dbe3ec 1px, transparent 1px); background-size: 18px 18px;
              touch-action: none; }
.cfd-wires { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
/* Coloured by the object the wire leaves, matching that object's title bar: --cfd-bg comes from the
   .cfd-t-* class copied onto the path. Deepened towards that object's text colour, because a title-bar
   pastel is a fill — spread over a 2.5px line on a white canvas it is barely there. Same hue, enough of
   it to follow across the page. */
.cfd-wire { fill: none; stroke-width: 2.5;
            stroke: color-mix(in srgb, var(--cfd-bg, #94a3b8) 55%, var(--cfd-fg, #475569)); }
.cfd-wire.cfd-wire-inactive { stroke-dasharray: 5 4; stroke: #cbd5e1; }
.cfd-wire-ghost { stroke: var(--accent, #3b82f6); stroke-dasharray: 4 4; }
/* An invisible fat copy of each wire, so a 2px line can actually be clicked. */
.cfd-wire-hit { fill: none; stroke: transparent; stroke-width: 16; pointer-events: stroke; cursor: pointer; }
/* The one you picked: bold and in the accent, with everything else faded, so a single wire can be followed
   end to end through a busy flow. */
.cfd-wire.cfd-wire-on { stroke: var(--accent, #3b82f6); stroke-width: 3.5; }
.cfd-wires.has-pick .cfd-wire:not(.cfd-wire-on) { opacity: .18; }
.cfd-node.is-wire-end { border-color: var(--accent, #3b82f6); box-shadow: 0 0 0 3px rgba(59,130,246,.18); }
/* Cut the highlighted wire. Sits on the wire itself, because that is where you are already looking. */
.cfd-wire-del { pointer-events: auto; cursor: pointer; }
.cfd-wire-del circle { fill: #fff; stroke: var(--accent, #3b82f6); stroke-width: 1.5; }
.cfd-wire-del path { stroke: var(--accent, #3b82f6); stroke-width: 1.8; }
.cfd-wire-del:hover circle { fill: #fee2e2; stroke: #dc2626; }
.cfd-wire-del:hover path { stroke: #dc2626; }
/* The end-the-call drop target, shown only while a wire is in hand. Outside the transformed pane, so it
   stays in the same place whatever the canvas is zoomed or panned to. */
.cfd-endzone { display: none; position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
               z-index: 3; align-items: center; gap: 8px; padding: 9px 16px; border-radius: 999px;
               background: #fff; border: 1px dashed #94a3b8; color: #64748b; font-size: .82rem;
               pointer-events: none; box-shadow: 0 4px 14px rgba(15,23,42,.10); }
.cfd-canvas.is-wiring .cfd-endzone { display: inline-flex; }
.cfd-endzone.is-over { border-color: #dc2626; border-style: solid; background: #fee2e2; color: #991b1b; }
.cfd-endzone-x { font-size: 1rem; line-height: 1; }
/* A notice ON the canvas. Absolute so it can never resize the canvas under the flow being read. */
.cfd-note { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 4;
            display: inline-flex; align-items: center; gap: 12px; max-width: calc(100% - 32px);
            padding: 8px 10px 8px 14px; border-radius: 8px; font-size: .82rem;
            box-shadow: 0 4px 14px rgba(15,23,42,.12); }
.cfd-note.is-error { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }
.cfd-note.is-ok { background: #ecfdf5; border: 1px solid #a7f3d0; color: #065f46; }
.cfd-note button { border: none; background: none; cursor: pointer; color: inherit; font-size: 1rem;
                   line-height: 1; padding: 0 2px; font-family: inherit; }
/* The pane is only a positioning box — it must not intercept clicks aimed at the wires beneath it. */
.cfd-pane { position: absolute; inset: 0; transform-origin: 0 0; pointer-events: none; }
.cfd-node { pointer-events: auto; }

/* Type colour, once. The node's header bar, the swatch in the Add list and the type pill on
   /tenant/call-handling are the same colour because they read from the same pair of variables — the exact
   values the .badge-* pills use, so the two pages can't drift apart. */
/* The neutral first: DIDs and the terminals have no pill on the list page. The type rules below carry the
   same specificity, so they have to come after it to win. */
.cfd-node, .cfd-tool   { --cfd-bg: #f1f5f9; --cfd-fg: #475569; }
.cfd-t-ringgroup       { --cfd-bg: #dbeafe; --cfd-fg: #1e40af; }
.cfd-t-announcement    { --cfd-bg: #ede9fe; --cfd-fg: #6d28d9; }
.cfd-t-ivr             { --cfd-bg: #ffe4e6; --cfd-fg: #9f1239; }
.cfd-t-callflowcontrol { --cfd-bg: #fef3c7; --cfd-fg: #92400e; }
.cfd-t-timecondition   { --cfd-bg: #dcfce7; --cfd-fg: #166534; }
.cfd-t-callqueue       { --cfd-bg: #ccfbf1; --cfd-fg: #115e59; }
/* Extensions and music on hold have no pill on the list page, so these two are new. They sit in the gaps
   the six routing colours leave — indigo between blue and purple, orange between amber and rose — so a
   header still says which kind of thing it is at a glance. */
.cfd-t-extension       { --cfd-bg: #e0e7ff; --cfd-fg: #3730a3; }
.cfd-t-holdmusic       { --cfd-bg: #ffedd5; --cfd-fg: #9a3412; }
/* Voicemail — a terminal object; slate, distinct from the routing colours above. */
.cfd-t-voicemailbox    { --cfd-bg: #e2e8f0; --cfd-fg: #334155; }

/* One node */
.cfd-node { position: absolute; width: 216px; background: #fff; border: 1px solid #cbd5e1; border-radius: 10px;
            box-shadow: 0 1px 3px rgba(15,23,42,.08); cursor: grab; user-select: none; }
.cfd-node.is-selected { border-color: var(--accent, #3b82f6); box-shadow: 0 0 0 3px rgba(59,130,246,.18); }
.cfd-node.is-target { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16,185,129,.2); }
/* One width for every node. A narrower box for an extension or a playlist made the columns ragged and
   read as a different KIND of thing rather than a different role. */
.cfd-node.is-terminal { background: #f8fafc; }
.cfd-node.is-start { border-color: #6366f1; }
/* Remove-from-flow, on the selected node. Hidden otherwise: every node carrying a × is noise. */
.cfd-node-x { display: none; position: absolute; top: -9px; right: -9px; width: 20px; height: 20px;
              border-radius: 50%; border: 1px solid var(--accent, #3b82f6); background: #fff;
              color: var(--accent, #3b82f6); font-size: .9rem; line-height: 1; cursor: pointer;
              align-items: center; justify-content: center; padding: 0; font-family: inherit; z-index: 2; }
.cfd-node.is-selected .cfd-node-x { display: inline-flex; }
.cfd-node-x:hover { background: #fee2e2; border-color: #dc2626; color: #dc2626; }

.cfd-node-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
                 padding: 6px 10px; border-bottom: 1px solid rgba(15,23,42,.08); border-radius: 9px 9px 0 0;
                 background: var(--cfd-bg); color: var(--cfd-fg); }
.cfd-node-type { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: inherit; }
.cfd-node-num { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .7rem; color: inherit; opacity: .85; }
.cfd-node-title { padding: 8px 10px 6px; font-size: .9rem; font-weight: 600; color: #0f172a; word-break: break-word; }
.cfd-node-badges { display: flex; flex-wrap: wrap; gap: 4px; padding: 0 10px 8px; }
.cfd-badge { font-size: .66rem; font-weight: 700; padding: 2px 7px; border-radius: 999px; white-space: nowrap; }
/* The exact .badge-* pairs the list pages use, so a state reads the same colour wherever it is shown:
   gray = Disabled, green = Active / In business hours, amber = Out of Hours, red = Out of business hours. */
.cfd-badge.tone-neutral { background: #f1f5f9; color: #475569; }
.cfd-badge.tone-good { background: #dcfce7; color: #166534; }
.cfd-badge.tone-warn { background: #fef3c7; color: #92400e; }
.cfd-badge.tone-bad { background: #fee2e2; color: #991b1b; }
.cfd-node-used { padding: 0 10px 8px; font-size: .68rem; }
.cfd-node-used { color: #94a3b8; }

/* Ports: the input dot on the left edge, one labelled output row per branch. */
.cfd-in { position: absolute; left: -7px; top: 16px; width: 12px; height: 12px; border-radius: 50%;
          background: #fff; border: 2px solid #94a3b8; }
.cfd-ports { list-style: none; margin: 0; padding: 0 0 6px; border-top: 1px solid #eef2f7; }
.cfd-port { position: relative; display: flex; align-items: center; justify-content: flex-end; gap: 8px;
            padding: 4px 14px 4px 10px; font-size: .76rem; color: #475569; }
/* A branch not currently in play: an unassigned menu key waiting to be given a job, or an out-of-hours
   rule switched off. Light grey is the whole message for a key — what an unrecognised one does is the
   Invalid input branch, which is listed right below it. */
.cfd-port.is-off { color: #c2cad6; }
.cfd-port.is-off:not(.is-end) { font-style: normal; }
.cfd-port-dot { position: absolute; right: -7px; width: 12px; height: 12px; border-radius: 50%;
                background: #fff; border: 2px solid #94a3b8; cursor: crosshair; }
.cfd-port-dot:hover { border-color: var(--accent, #3b82f6); background: var(--accent, #3b82f6); }
/* A branch that ends the call says so here rather than wiring to a box that only ever said "Hang up". */
.cfd-port-end { font-size: .66rem; font-style: italic; color: #94a3b8; }
.cfd-port.is-end .cfd-port-dot { background: #cbd5e1; border-color: #cbd5e1; }
.cfd-port.is-off .cfd-port-dot { border-style: dashed; }

/* Canvas controls, floating over the top-right of the canvas. */
.cfd-controls { position: absolute; top: 10px; right: 10px; display: flex; gap: 6px; z-index: 2; }
.cfd-controls button { width: 30px; height: 30px; border: 1px solid #e5e7eb; border-radius: 7px; background: #fff;
                       cursor: pointer; font-size: .95rem; line-height: 1; color: #475569; }

/* A toggle among push-buttons has to look held down, or there is nothing to tell you which way it is set. */
.cfd-controls button.is-on { background: var(--accent, #3b82f6); border-color: var(--accent, #3b82f6);
                             color: #fff; }
.cfd-controls button:hover { border-color: var(--accent, #3b82f6); color: var(--accent, #3b82f6); }
.cfd-controls button.wide { width: auto; padding: 0 10px; font-size: .78rem; }

/* Settings panel */
.cfd-empty { color: #94a3b8; font-size: .85rem; padding: 8px 2px; }
.cfd-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.cfd-usedby { font-size: .78rem; margin-bottom: 10px; }
.cfd-field { margin-bottom: 12px; }
/* :not(.toggle) so a switch keeps its own inline-flex layout — a block label would sit the track
   on top of its own text. */
.cfd-field > label:not(.toggle) { display: block; font-size: .74rem; font-weight: 600; color: #475569; margin-bottom: 4px; }
.cfd-field > label.toggle { font-size: .8rem; color: #334155; }
.cfd-port-edit { border: 1px solid #eef2f7; border-radius: 8px; padding: 10px; margin-bottom: 10px; }
.cfd-port-edit > .cfd-port-name { font-size: .74rem; font-weight: 700; color: #334155; margin-bottom: 6px; }
.cfd-keymap { width: 100%; border-collapse: collapse; font-size: .76rem; }
.cfd-keymap td { padding: 3px 0; border-bottom: 1px solid #f1f5f9; }
.cfd-keymap td:first-child { width: 46px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #475569; }

/* Violations */
.cfd-issues { list-style: none; margin: 0 0 12px; padding: 0; }
.cfd-issue { display: flex; gap: 8px; padding: 8px 10px; border-radius: 8px; margin-bottom: 6px; font-size: .8rem; }
.cfd-issue.blocking { background: #fef2f2; color: #991b1b; }
.cfd-issue.advice { background: #fffbeb; color: #92400e; }
.cfd-issue button { border: none; background: none; padding: 0; color: inherit; text-decoration: underline;
                    cursor: pointer; font-family: inherit; font-size: inherit; text-align: left; }
.cfd-dirty { font-size: .78rem; color: #92400e; background: #fef3c7; border-radius: 999px; padding: 3px 10px; }
/* System → Updates: while the portal is restarting mid-update (kenhos-updates.js polls /health). */
body.kfs-portal-down .page-header h1::after { content: " — reconnecting…"; font-weight: 400; color: #92400e; font-size: .9rem; }

/* System → Updates → Activity tab: the live console fills the viewport below the tabs, leaving the page's normal
   32px bottom margin (header 40 + 24, breadcrumb 30, tabs 46, main padding 2×32 — matches .fill-page's intent). */
/* Height is set by kenhosUpdates.fit() from the element's actual top to the viewport bottom (a message banner or
   notice above it would otherwise push a fixed-height box below the fold and give the page a scrollbar). */
.activity-fill { display: flex; flex-direction: column; min-height: 320px; height: 60vh; }
.activity-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; font-size: .85rem; margin: 0 0 10px; color: #334155; }
/* The status line is a row of separate facts (badge · what · when · error), never run-together text. */
.console-status { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.console-status .err { color: #991b1b; }
.activity-fill .capture-out { flex: 1 1 auto; min-height: 0; }

/* Updates → Settings tick lists and the Status facts table. */
.tick-list { display: flex; flex-direction: column; gap: 8px; padding-top: 7px; }
.tick-list label { display: flex; align-items: center; gap: 8px; cursor: pointer; white-space: nowrap; }
/* Updates → Status guide card: one state line, one obvious action (Windows Update / macOS Software Update style). */
.update-guide { display: flex; align-items: center; gap: 16px; background: #fff; border-radius: 8px; box-shadow: var(--shadow); padding: 18px 20px; margin-bottom: 16px; border-left: 5px solid #94a3b8; }
.update-guide.green { border-left-color: #16a34a; } .update-guide.amber { border-left-color: #d97706; } .update-guide.red { border-left-color: #dc2626; }
.update-guide-dot { width: 12px; height: 12px; border-radius: 50%; background: #94a3b8; flex: 0 0 12px; }
.update-guide.green .update-guide-dot { background: #16a34a; } .update-guide.amber .update-guide-dot { background: #d97706; } .update-guide.red .update-guide-dot { background: #dc2626; }
.update-guide-text { flex: 1; min-width: 0; }
.update-guide .state { font-size: 1.05rem; font-weight: 600; color: #1e293b; }
.update-guide .detail { color: #64748b; font-size: .85rem; margin-top: 4px; }
.update-guide-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.update-guide-actions .btn { min-width: 150px; white-space: nowrap; justify-content: center; text-align: center; }
/* Release notes (What's new panel, Release notes modal): one block per version. */
.release-note { padding: 10px 0 12px; border-bottom: 1px solid #f1f5f9; }
.release-note:last-child { border-bottom: none; }
.release-note-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: .9rem; }
.release-note-body { font-size: .88rem; color: #334155; line-height: 1.5; }
.release-note-body h3, .release-note-body h4 { font-size: .95rem; margin: 8px 0 4px; }
.release-note-body p { margin: 4px 0; }
.release-note-body ul { margin: 4px 0 4px 20px; padding: 0; }
.release-note-body li { margin: 2px 0; }
.release-note-body code { background: #f1f5f9; padding: 1px 5px; border-radius: 4px; font-size: .82rem; }
.status-facts { margin-bottom: 20px; }
.status-facts th { white-space: nowrap; width: 200px; }


/* ---- Setup wizard ("Let's get started", /setup) — docs/features/setup-wizard.md (v2: full page, wide rail) ------- */
.setup-shell .auth-main.setup-main { align-items: stretch; padding: 0; }
.setup-page { display: grid; grid-template-columns: 340px minmax(0, 1fr); min-height: 100vh; width: 100%; background: #fff; }
.setup-rail { background: #f8fafc; border-right: 1px solid #e2e8f0; padding: 30px 24px; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; overflow: auto; }
.setup-rail .auth-brand { margin: 0 0 26px 10px; text-align: left; }
.setup-rail .auth-logo { max-height: 40px; }
.setup-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.setup-steps li { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 8px; color: #64748b; font-size: .95rem; cursor: pointer; white-space: nowrap; }
.setup-steps li:hover { background: #eef2ff; }
.setup-steps li .n { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; background: #e2e8f0; color: #64748b; font-weight: 700; font-size: .78rem; flex: none; }
.setup-steps li .t { overflow: hidden; text-overflow: ellipsis; }
.setup-steps li.active { background: #fff; color: #1e293b; font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
.setup-steps li.active .n { background: var(--accent, #2563eb); color: #fff; }
.setup-steps li.done .n { background: #16a34a; color: #fff; }
.setup-steps li.skipped .n { background: #fde68a; color: #92400e; }
.setup-steps li .state { margin-left: auto; font-size: .72rem; color: #94a3b8; font-weight: 500; }
.setup-steps li.locked { cursor: default; opacity: .6; }
.setup-steps li.locked:hover { background: none; }
.setup-rail .setup-progress { margin-top: auto; padding: 18px 10px 0; font-size: .8rem; color: #64748b; }
.setup-rail .progress { margin-top: 8px; }
.setup-pane { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
.setup-head { padding: 34px 56px 0; display: flex; align-items: flex-start; gap: 16px; }
.setup-head > div:first-child { max-width: 1100px; }
.setup-head h1 { margin: 0 0 6px; font-size: 1.7rem; color: #1e293b; }
.setup-head p { margin: 12px 0 0; color: #475569; font-size: 1.02rem; }
.setup-head .spacer { flex: 1; }
.setup-head .exit { color: #64748b; font-size: .88rem; white-space: nowrap; margin-top: 8px; }
.setup-body { padding: 28px 56px 30px; flex: 1; min-width: 0; }
.setup-body .host-settings { max-width: 100%; }
.setup-prose { max-width: 1180px; color: #334155; font-size: 1.02rem; line-height: 1.6; }
.setup-prose .greeting { font-size: 1.15rem; font-weight: 600; color: #1e293b; margin-bottom: 10px; }
.setup-prose p { margin: 0 0 14px; }
.setup-actions { display: flex; align-items: center; gap: 14px; margin-top: 14px; }
.setup-footer { padding: 16px 56px 24px; border-top: 1px solid #e2e8f0; display: flex; align-items: center; gap: 10px; position: sticky; bottom: 0; background: #fff; }
.setup-footer .spacer { flex: 1; }
.setup-footer .muted { font-size: .85rem; }
.setup-summary { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 8px 24px; max-width: 1180px; }
.setup-summary li { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border: 1px solid #e2e8f0; border-radius: 8px; }
.setup-summary li .n { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font-size: .72rem; font-weight: 700; background: #e2e8f0; color: #64748b; flex: none; }
.setup-summary li.done .n { background: #16a34a; color: #fff; }
.setup-summary li.skipped .n { background: #fde68a; color: #92400e; }
.setup-summary li a { margin-left: auto; font-size: .82rem; }
.setup-todo { margin: 0; padding-left: 1.2rem; color: #334155; line-height: 1.6; max-width: 1180px; }
.setup-todo li { margin: 6px 0; }
.dns-check { margin-top: 14px; }
.dns-check .dns-result { margin-top: 10px; }
.dns-records { margin: 8px 0 0; padding: 10px 14px; background: #0f172a; color: #e2e8f0; border-radius: 6px; font-size: .85rem; white-space: pre; overflow-x: auto; }
@media (max-width: 900px) { .setup-page { grid-template-columns: 1fr; } .setup-rail { position: static; height: auto; border-right: 0; border-bottom: 1px solid #e2e8f0; } .setup-head, .setup-body, .setup-footer { padding-left: 20px; padding-right: 20px; } }
/* Shared settings editors: main column + side column. Inside the wizard's narrower pane the side column stacks below
   so the inputs keep their full width (the Certificates tab's hostname/email boxes were squeezed to a few characters). */
.editor-grid { display: grid; grid-template-columns: 3fr 1fr; gap: 16px; align-items: start; }
.setup-body .editor-grid { grid-template-columns: 1fr; }

/* ---- Alert severities (AlertCatalog) — the Alerts tab's group headings and the wizard's category names ---- */
.alert-sev { font-size: .85rem; font-weight: 600; margin: 0; padding: 14px 16px 0; }
.alert-sev + .alert-grid { padding-top: 6px; }
.alert-sev-critical { color: #b91c1c; }
.alert-sev-error { color: #c2410c; }
.alert-sev-warning { color: #b45309; }
.alert-sev-notice { color: #1d4ed8; }

/* ---- Compact (wizard) rendering of the shared settings editors: one question per block ---- */
.wiz-q { margin: 0 0 22px; max-width: 72ch; }
.wiz-q-toggle { display: flex; align-items: center; gap: 12px; }
.wiz-q-label { font-weight: 600; color: #1e293b; font-size: .95rem; display: block; }
.wiz-q-toggle .wiz-q-label { display: inline; }
.wiz-q-note { margin: 8px 0 0; color: #475569; font-size: .88rem; }
.wiz-q .seg-toggle { margin: 10px 0 14px; }
.wiz-q .panel { margin-top: 10px; }
.wiz-q .input.wiz-wide { width: 100%; max-width: 520px; margin-top: 8px; }
.wiz-cats { display: flex; flex-direction: column; gap: 12px; }
.wiz-cat { display: flex; align-items: flex-start; gap: 12px; }
.wiz-cat .toggle { margin-top: 2px; }
.wiz-cat-text { color: #334155; font-size: .9rem; line-height: 1.45; }
.wiz-cat-text .muted { margin-left: 4px; }
.wiz-test { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.wiz-test .wiz-test-result { margin: 0; }
.wiz-test-ask { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: .9rem; color: #1e293b; }

/* Compact (wizard) rendering of the shared settings editors: one question sentence, stacked labelled fields, the
   verdict inline. No Panel chrome. */
.compact-editor .compact-note { margin: 0 0 14px; color: #475569; font-size: 1rem; line-height: 1.55; max-width: 1180px; }
.compact-editor .compact-fields { display: grid; grid-template-columns: 1fr; gap: 14px; max-width: 820px; }
.compact-editor .compact-field { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 18px; align-items: center; margin: 0; }
.compact-editor .compact-field > span { font-weight: 500; color: #374151; font-size: .9rem; white-space: nowrap; }
.compact-editor .compact-field .input { width: 100%; }
.compact-editor .compact-actions { display: flex; gap: 10px; align-items: center; }
.compact-editor .alert-ok, .compact-editor .alert-warn, .compact-editor .alert-error { margin: 0; line-height: 1.5; }
/* Setup guide — admin access step: the SSH allow-list with its "+" row. */
.compact-toggle { display: inline-flex; align-items: center; gap: 10px; margin: 10px 0; }
.ip-list { margin-top: 10px; max-width: 820px; }   /* same width as the field grid above it */
.ip-row { display: flex; align-items: center; gap: 12px; padding: 8px 12px; border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 6px; background: #fff; }
.ip-row .spacer { flex: 1; }
.ip-add { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.ip-add .input { flex: 1; min-width: 280px; }

/* ---- Setup guide layout kit (v2.1, 2026-10-04): every compact step uses these so spacing, widths and the 2×2 field
   grid are identical. The guide is designed for a 1600 px window: rail 340 + pane ≥ 1200. ---------------------------- */
.setup-body .compact-editor { max-width: none; }
.compact-section { margin-top: 30px; }
.compact-section:first-child { margin-top: 0; }
h2.compact-q, p.compact-q, .compact-q { margin: 0 0 14px; font-size: 1.12rem; font-weight: 600; color: #1e293b; line-height: 1.3; }
.compact-q + .compact-lead { margin-top: 0; }

.compact-lead { margin: 0 0 16px; color: #475569; font-size: 1rem; line-height: 1.55; max-width: 1180px; }
/* The field grid: two columns — label left, field right — ONE field per row (user, 2026-10-04: "two columns, title and
   field, not two sets of title&field columns"). Fields fill the column; the whole grid is capped so inputs stay sane. */
.compact-grid { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 14px 18px; align-items: center; max-width: 820px; }
.compact-grid > label { color: #334155; font-weight: 500; white-space: nowrap; }
.compact-grid > .input, .compact-grid .input, .compact-grid select, .compact-grid .tenant-dropdown { width: 100%; max-width: none; }
.compact-grid .span-2 { grid-column: 2; }

/* A bordered block for a check the operator performs (send a test, verify a licence, DNS result). */
.compact-card { border: 1px solid #e2e8f0; border-radius: 10px; padding: 20px 24px; margin-top: 22px; background: #fff; max-width: 1180px; }
.compact-card h3 { margin: 0 0 10px; font-size: 1.05rem; color: #1e293b; }
.compact-card p { font-size: 1rem; line-height: 1.55; color: #334155; margin: 0 0 10px; }
.compact-card .actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 12px; }
.compact-card .result { margin-top: 14px; }
.compact-toggle-row { display: flex; align-items: center; gap: 12px; margin: 22px 0 8px; font-weight: 600; color: #1e293b; }
.compact-toggle-row .toggle { margin: 0; }
/* A segmented choice inside a section gets clear air before the fields it reveals; a lead that follows a grid (the computed window sentence) sits off the fields. */
.compact-section .seg-toggle { margin: 12px 0 22px; }
.compact-grid + .compact-lead { margin: 14px 0 0; }
.compact-records { margin: 10px 0 0; padding: 12px 16px; background: #0f172a; color: #e2e8f0; border-radius: 8px; font-size: .9rem; white-space: pre; overflow-x: auto; }
.setup-body .host-settings .setting-row { grid-template-columns: 220px minmax(0, 1fr); }
.setup-body .panel { max-width: 1180px; }
/* Install-updates step: the preview of what Install now will change (components as a list, OS packages as a fixed table). */
.install-plan { max-width: 1180px; }
.install-plan-list { margin: 0 0 14px; padding-left: 1.2rem; color: #334155; line-height: 1.7; }
.install-plan-list .mono { font-size: .9em; }
.install-plan .section-title { margin-top: 6px; }
.install-plan-table th, .install-plan-table td { white-space: nowrap; }
.install-plan-table th:first-child, .install-plan-table td:first-child { width: 40%; }
.install-plan-more { margin: 8px 0 0; }
/* Email + Alerts steps (v2.1 review): the segmented choice sits 22 px above its field grid, a warning after the grid
   keeps the grid's width, verdicts inside a .compact-card read at body size, "Did it arrive?" is a real button row, and
   the alert categories are a vertical list with 14 px between rows (toggle | bold label — sentence / muted examples). */
.compact-section .seg-toggle { margin: 10px 0 22px; }
.compact-grid + .alert-warn { margin: 16px 0 0; max-width: 1180px; font-size: .95rem; }
.compact-grid + .compact-lead, .alert-warn + .compact-lead { margin-top: 16px; }
.compact-section + .compact-card { margin-top: 30px; }
.compact-card > .compact-lead { margin-bottom: 0; }
.compact-card .result .alert-ok, .compact-card .result .alert-warn, .compact-card .result .alert-error { margin: 0; padding: 14px 18px; font-size: 1rem; line-height: 1.55; }
.compact-card .result .compact-status { margin: 0; color: #475569; font-size: 1rem; }
.compact-ask { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px; font-size: 1.05rem; font-weight: 600; color: #1e293b; }
.compact-ask .btn { font-weight: 500; font-size: 1rem; }
.compact-choice-list { display: flex; flex-direction: column; gap: 14px; max-width: 1180px; }
.compact-choice-row { display: flex; align-items: flex-start; gap: 14px; }
.compact-choice-row .toggle { margin-top: 3px; }
.compact-choice-text { color: #334155; font-size: 1rem; line-height: 1.5; }
.compact-choice-text .alert-sev { padding: 0; font-size: inherit; }
.compact-choice-text .muted { display: block; font-size: .92rem; }

/* Dashboard "finish setting up" nudge: text left, Continue link right, a small ✕ at the far right to hide it. */
.nudge-row { display: flex; align-items: center; gap: 14px; }
.nudge-row .spacer { flex: 1; }
.nudge-row .nudge-link { margin-right: 10px; white-space: nowrap; }
.nudge-close { border: 0; background: none; color: #92400e; font-size: 1rem; cursor: pointer; padding: 2px 6px; border-radius: 4px; line-height: 1; }
.nudge-close:hover { background: rgba(146, 64, 14, .12); }
/* Content revealed by a toggle (password fields, the SSH list) sits clear of the toggle row. */
.compact-reveal { margin-top: 16px; }
.compact-toggle-row { margin-bottom: 8px; }
/* Dashboard trunk summary: a plain table with fixed widths (no list kit). */
.home-trunks { table-layout: fixed; width: 100%; }
.home-trunks th, .home-trunks td { overflow: hidden; text-overflow: ellipsis; }
.test-ok { color: #166534; }
.test-err { color: #991b1b; }
.licensing-components { table-layout: fixed; }
.licensing-components td, .licensing-components th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Setup guide: required-field marker, one-per-line lists, and the two DNS check cards (2026-10-06) */
.compact-grid label.req::after { content: " *"; color: #dc2626; font-weight: 600; }
textarea.compact-lines { min-height: 96px; resize: vertical; line-height: 1.45; }
.dns-checks { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 12px; }   /* one above the other (user, 2026-10-06) */
.dns-check { border: 1px solid var(--border, #e5e7eb); border-radius: 8px; padding: 12px 14px; background: #fff; }
.dns-check-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.dns-check-n { display: inline-flex; width: 22px; height: 22px; border-radius: 50%; align-items: center; justify-content: center; background: #2563eb; color: #fff; font-size: .75rem; font-weight: 700; flex: none; }
.dns-check-verdict { margin-left: auto; font-size: .75rem; font-weight: 700; letter-spacing: .04em; padding: 3px 10px; border-radius: 999px; background: #f3f4f6; color: #6b7280; white-space: nowrap; }
.dns-check.dns-pass { border-color: #86efac; background: #f0fdf4; }
.dns-check.dns-pass .dns-check-verdict { background: #16a34a; color: #fff; }
.dns-check.dns-fail { border-color: #fca5a5; background: #fef2f2; }
.dns-check.dns-fail .dns-check-verdict { background: #dc2626; color: #fff; }
.dns-check-body { display: flex; flex-direction: column; gap: 5px; font-size: .9rem; }
.dns-check-body .mono { word-break: break-all; }
.dns-check-why { color: #991b1b; }

/* Setup guide: hostname "Test now" beside the field; the install console takes the remaining height (2026-10-06) */
.host-test-row { display: flex; gap: 10px; align-items: center; }
.host-test-row .input { flex: 1; min-width: 0; }
.host-test-row .btn { flex: none; white-space: nowrap; }
.host-test-btn { padding-left: 22px; padding-right: 22px; font-weight: 600; }
/* The install step must never make the PAGE scroll: the pane is exactly one viewport tall, the body (not the page) scrolls
   if the cards and plan table outgrow it, and the console is a bounded flex child with its own scrollbar that the
   step keeps pinned to the bottom while anything runs (kenhosUpdates.follow) — user, 2026-10-06. */
.setup-pane:has(.setup-body-fill) { height: 100vh; }
.setup-body-fill { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow: auto; }
.install-step { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.install-step .install-console { flex: 1 1 auto; width: 100%; box-sizing: border-box; min-height: 220px; max-height: none; overflow: auto; }

/* Setup guide: the "nothing to do on this step" block (licence valid, certificate installed) — unmistakable (2026-10-06) */
.compact-done { display: flex; gap: 16px; align-items: flex-start; border: 1px solid #86efac; background: #f0fdf4; border-radius: 10px; padding: 18px 20px; }   /* full width: lines up with the step cards below (user, 2026-10-06) */
.compact-done .tick { flex: none; width: 40px; height: 40px; border-radius: 50%; background: #16a34a; color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: 700; }
.compact-done .title { font-size: 1.08rem; font-weight: 700; color: #14532d; margin-bottom: 4px; }

/* Setup guide → Install updates: the state banner (running vs finished must be obvious, 2026-10-06) */
.install-status { display: flex; align-items: center; gap: 16px; padding: 16px 20px; border-radius: 10px; border: 1px solid #cbd5e1; background: #f8fafc; margin-bottom: 14px; width: 100%; box-sizing: border-box; }
.install-status-icon { flex: none; width: 44px; height: 44px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 1.4rem; font-weight: 700; color: #fff; background: #64748b; }
.install-status-icon .btn-spinner.big { width: 22px; height: 22px; border-width: 3px; margin: 0; }
.install-status-text { flex: 1; min-width: 0; }
.install-status-title { font-size: 1.12rem; font-weight: 700; color: #0f172a; }
.install-status-sub { color: #475569; margin-top: 3px; }
.install-status-busy { border-color: #93c5fd; background: #eff6ff; }
.install-status-busy .install-status-icon { background: #2563eb; }
.install-status-busy .install-status-title { color: #1e3a8a; }
.install-status-ok { border-color: #86efac; background: #f0fdf4; }
.install-status-ok .install-status-icon { background: #16a34a; }
.install-status-ok .install-status-title { color: #14532d; }
.install-status-warn { border-color: #fcd34d; background: #fffbeb; }
.install-status-warn .install-status-icon { background: #d97706; }
.install-status-warn .install-status-title { color: #78350f; }
.install-status-bad { border-color: #fca5a5; background: #fef2f2; }
.install-status-bad .install-status-icon { background: #dc2626; }
.install-status-bad .install-status-title { color: #7f1d1d; }
.compact-toggle-inline { display: flex; align-items: center; gap: 12px; min-height: 40px; }

/* Admin access: the operator's own address is missing from a restricted SSH list — red, explicit, own button (2026-10-06) */
.own-ip-warning { display: flex; align-items: center; gap: 16px; margin-top: 10px; padding: 14px 16px; border: 1px solid #fca5a5; background: #fef2f2; border-radius: 8px; color: #7f1d1d; }
.own-ip-warning .own-ip-text { flex: 1; min-width: 0; }
.own-ip-warning .own-ip-text strong { display: block; margin-bottom: 2px; color: #991b1b; }
.own-ip-warning .btn { flex: none; white-space: nowrap; }
