:root {
    --brand-primary: #105d89;
    --brand-primary-dark: #0d4a6e;
    --brand-primary-darker: #0a3854;
    --brand-primary-light: #e8f3f9;
    --brand-primary-mid: #d0e8f5;
    --brand-accent: #1a7ab5;
    --text-dark: #1a1a2e;
    --text-muted: #6b7280;
    --border-color: #e5e7eb;
    --surface: #f9fafb;
    /* Card/topbar/dropdown surfaces. Same as --white in light mode, but the two
       must stay separate tokens: dark mode flips --card to a dark surface while
       --white stays literal white (it is also the text color on brand-blue
       fills). New rules: --card for backgrounds, --white for text. */
    --card: #ffffff;
    --white: #ffffff;
    --success: #059669;
    --warning: #d97706;
    --danger: #dc2626;
    --shadow-sm: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,.1);
    --shadow-lg: 0 10px 30px rgba(0,0,0,.12);
    --radius: 8px;
    --radius-lg: 12px;
    --transition: all .2s ease;
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
    font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
    font-size: 16px;
    color: var(--text-dark);
    background: var(--card);
    margin: 0;
    padding: 0;
    line-height: 1.6;
}

/* Blazor default overrides */
a, .btn-link { color: var(--brand-primary); }
.btn-primary  { background: var(--brand-primary); border-color: var(--brand-primary-dark); }
.btn-primary:hover { background: var(--brand-primary-dark); border-color: var(--brand-primary-darker); }
h1:focus { outline: none; }
.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }
.invalid { outline: 1px solid var(--danger); }
.validation-message { color: var(--danger); }
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}
.blazor-error-boundary::after { content: "An error has occurred." }
.darker-border-checkbox.form-check-input { border-color: #929292; }
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: var(--bs-secondary-color); text-align: end; }
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { text-align: start; }

/* ── Utilities ── */
.text-brand    { color: var(--brand-primary); }
.text-muted-c  { color: var(--text-muted); }
.bg-brand      { background: var(--brand-primary); color: var(--white); }
.bg-brand-light{ background: var(--brand-primary-light); }

/* ── Buttons ── */
.btn-brand {
    background: var(--brand-primary);
    color: var(--white);
    border: none;
    padding: .6rem 1.4rem;
    border-radius: var(--radius);
    font-weight: 600;
    font-size: .9rem;
    cursor: pointer;
    transition: var(--transition);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.btn-brand:hover { background: var(--brand-primary-dark); color: var(--white); text-decoration: none; }
.btn-brand-lg   { padding: .85rem 2rem; font-size: 1rem; }

.btn-outline-brand {
    background: transparent;
    color: var(--brand-primary);
    border: 2px solid var(--brand-primary);
    padding: .6rem 1.4rem;
    border-radius: var(--radius);
    font-weight: 600;
    font-size: .9rem;
    cursor: pointer;
    transition: var(--transition);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.btn-outline-brand:hover { background: var(--brand-primary); color: var(--white); text-decoration: none; }

.btn-ghost {
    background: rgba(255,255,255,.15);
    color: var(--white);
    border: 2px solid rgba(255,255,255,.5);
    padding: .6rem 1.4rem;
    border-radius: var(--radius);
    font-weight: 600;
    font-size: .9rem;
    cursor: pointer;
    transition: var(--transition);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.btn-ghost:hover { background: rgba(255,255,255,.25); color: var(--white); text-decoration: none; }

/* ── Cards ── */
.card {
    background: var(--card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 1.5rem;
    box-shadow: var(--shadow-sm);
}
.card-hover { transition: var(--transition); }
.card-hover:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }

/* ── Section spacing ── */
.section     { padding: 5rem 0; }
.section-sm  { padding: 3rem 0; }
.section-lg  { padding: 7rem 0; }
.container   { max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; }
.container-sm{ max-width: 860px;  margin: 0 auto; padding: 0 1.5rem; }

/* ── Typography ── */
h1 { font-size: clamp(2rem, 4vw, 3.2rem); font-weight: 800; line-height: 1.15; margin: 0 0 1rem; }
h2 { font-size: clamp(1.6rem, 3vw, 2.4rem); font-weight: 700; line-height: 1.2; margin: 0 0 .75rem; }
h3 { font-size: 1.35rem; font-weight: 700; margin: 0 0 .5rem; }
h4 { font-size: 1.1rem; font-weight: 700; margin: 0 0 .4rem; }
p  { margin: 0 0 1rem; }

.section-label {
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--brand-primary);
    margin-bottom: .5rem;
    display: block;
}

/* ── Badges ── */
.badge {
    display: inline-block;
    padding: .25rem .7rem;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;
}
.badge-brand   { background: var(--brand-primary-light); color: var(--brand-primary); }
.badge-success { background: #d1fae5; color: var(--success); }
.badge-warning { background: #fef3c7; color: var(--warning); }
.badge-danger  { background: #fee2e2; color: var(--danger); }
.badge-gray    { background: #f3f4f6; color: #4b5563; }

/* ── Forms ── */
.form-control-custom {
    width: 100%;
    padding: .65rem 1rem;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    font-size: .95rem;
    transition: var(--transition);
    background: var(--card);
    color: var(--text-dark);
    font-family: inherit;
}
.form-control-custom:focus {
    outline: none;
    border-color: var(--brand-primary);
    box-shadow: 0 0 0 3px rgba(16,93,137,.15);
}
.form-label-custom { font-weight: 600; font-size: .875rem; margin-bottom: .4rem; display: block; color: var(--text-dark); }
.form-group-custom { margin-bottom: 1.25rem; }

/* ── Tables ── */
.table-wrapper { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; }
.data-table thead tr { background: var(--surface); }
.data-table th { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); padding: .85rem 1rem; text-align: left; border-bottom: 2px solid var(--border-color); white-space: nowrap; }
.data-table td { padding: .9rem 1rem; border-bottom: 1px solid var(--border-color); font-size: .9rem; }
.data-table tbody tr:hover { background: var(--brand-primary-light); }

/* ── Dividers ── */
hr { border: none; border-top: 1px solid var(--border-color); margin: 2rem 0; }

/* ── Stat blocks ── */
.stat-card { text-align: center; }
.stat-card .value { font-size: 2.2rem; font-weight: 800; color: var(--brand-primary); line-height: 1; }
.stat-card .delta { font-size: .78rem; margin-top: .2rem; font-weight: 600; }
.stat-card .delta.up   { color: var(--success); }
.stat-card .delta.down { color: var(--danger); }
.stat-card .label { font-size: .82rem; color: var(--text-muted); margin-top: .3rem; }

/* ── Progress ── */
.progress-track {
    background: var(--border-color);
    border-radius: 999px;
    height: 8px;
    overflow: hidden;
    margin-top: .4rem;
}
.progress-fill {
    background: var(--brand-primary);
    height: 100%;
    border-radius: 999px;
    transition: width .4s ease;
}

/* ── Alerts ── */
.alert-custom {
    padding: .85rem 1.2rem;
    border-radius: var(--radius);
    font-size: .9rem;
    margin-bottom: 1rem;
    border-left: 4px solid;
}
.alert-info    { background: var(--brand-primary-light); border-color: var(--brand-primary); color: var(--brand-primary-dark); }
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
.alert-warning { background: #fef3c7; border-color: var(--warning); color: #92400e; }
.alert-danger  { background: #fee2e2; border-color: var(--danger); color: #991b1b; }
/* A follow-up action inside an alert. The link inherits the alert's own text
   colour so contrast holds in every variant, and keeps its underline so it still
   reads as a link against text of the same colour. Scoped to .alert-action, not
   to every link in an alert, so it leaves the existing ones alone. */
.alert-action { margin-top: .5rem; }
.alert-action a { color: inherit; font-weight: 600; text-decoration: underline; }

/* ── Grid helpers ── */
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }
/* Two-column feature split used by the public pages — stacks on mobile.
   align-items/margins vary per use and stay inline (the media query below
   only touches columns + gap, so inline overrides of those are safe). */
.split-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; align-items: center; }
@media(max-width:900px) { .split-2 { grid-template-columns: 1fr; gap: 2rem; } }
@media(max-width:900px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media(max-width:700px) { .grid-3 { grid-template-columns: repeat(2, 1fr); } .grid-2, .grid-4 { grid-template-columns: 1fr; } }
@media(max-width:480px) { .grid-3 { grid-template-columns: 1fr; } }

/* ── Customizable page cards (CardCanvas / PageCard) ──
   Per-user page layouts, first used by the dashboard. .pc-canvas is a
   4-column grid; every card sits in a .pc-slot spanning 1, 2 or 4 columns
   (.pc-w-*). Visual order comes from each slot's CSS `order`, so the DOM
   stays in declaration order and cards reflow instead of remounting.
   Customize mode (.pc-editing on the canvas, .pc-edit on each slot) adds the
   chrome bar, drag affordances and the hidden-cards tray. Everything reads
   from the theme variables, so dark mode needs nothing extra here. */
.pc-toolbar { display: flex; justify-content: flex-end; margin: -.5rem 0 .75rem; }
/* Reads as a real button at rest — it used to be borderless muted text until
   hovered, and users simply never found the feature (Mike, 2026-08-15). A
   compact sibling of .btn-outline-brand: brand border and text on the card
   color, filling brand on hover, same in dark mode via the swapped tokens. */
.pc-customize {
    background: var(--card); border: 1.5px solid var(--brand-primary); border-radius: var(--radius);
    color: var(--brand-primary); font-size: .82rem; font-weight: 600;
    padding: .35rem .8rem; cursor: pointer; transition: var(--transition);
    display: inline-flex; align-items: center; gap: .35rem;
}
.pc-customize:hover { background: var(--brand-primary); color: var(--white); }

.pc-editbar {
    flex: 1; background: var(--card); border: 1px dashed var(--brand-primary);
    border-radius: var(--radius-lg); padding: .8rem 1rem;
}
.pc-editbar-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
.pc-editbar-hint { font-size: .85rem; color: var(--text-muted); }
.pc-editbar-actions { display: flex; gap: .5rem; align-items: center; }
.pc-reset {
    background: none; border: 1px solid var(--border-color); border-radius: var(--radius);
    color: var(--danger); font-size: .8rem; font-weight: 600; padding: .35rem .8rem;
    cursor: pointer; transition: var(--transition);
}
.pc-reset:hover { border-color: var(--danger); }
.pc-done { font-size: .8rem; padding: .35rem 1rem; }
.pc-tray {
    display: flex; flex-wrap: wrap; gap: .4rem; align-items: center;
    margin-top: .7rem; padding-top: .7rem; border-top: 1px solid var(--border-color);
}
.pc-tray-label { font-size: .78rem; color: var(--text-muted); }
.pc-chip {
    background: none; border: 1px solid var(--brand-primary); color: var(--brand-primary);
    border-radius: 999px; font-size: .78rem; font-weight: 600; padding: .25rem .75rem;
    cursor: pointer; transition: var(--transition);
}
.pc-chip:hover { background: var(--brand-primary); color: #fff; }

.pc-canvas { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }
.pc-slot { min-width: 0; display: flex; flex-direction: column; }
.pc-w-quarter { grid-column: span 1; }
.pc-w-half    { grid-column: span 2; }
.pc-w-full    { grid-column: span 4; }
/* The card fills its slot so half-width neighbours come out equal height,
   same as the old grid-2/grid-4 stretch behaviour. */
.pc-content { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.pc-content > .card { flex: 1; }
@media(max-width:900px) {
    .pc-canvas { grid-template-columns: repeat(2, 1fr); }
    .pc-w-half, .pc-w-full { grid-column: span 2; }
}
@media(max-width:700px) {
    .pc-canvas { grid-template-columns: 1fr; }
    .pc-w-quarter, .pc-w-half, .pc-w-full { grid-column: span 1; }
}

/* Customize mode */
.pc-editing .pc-slot { cursor: grab; }
.pc-editing .pc-slot:active { cursor: grabbing; }
/* Card content is inert while customizing: the whole surface drags, and a
   stray click can't navigate away mid-edit. */
.pc-edit .pc-content { pointer-events: none; user-select: none; }
.pc-dragging { opacity: .35; }
.pc-chrome {
    display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
    background: var(--card); border: 1px solid var(--border-color);
    border-bottom: 1px dashed var(--border-color);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0; padding: .35rem .6rem;
}
.pc-edit .pc-content > .card { border-top-left-radius: 0; border-top-right-radius: 0; border-top: 0; }
.pc-grip { color: var(--text-muted); font-size: .9rem; line-height: 1; }
.pc-chrome-title {
    font-size: .78rem; font-weight: 700; flex: 1; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pc-chrome-tools { display: flex; gap: .25rem; align-items: center; margin-left: auto; }
.pc-tool {
    width: 24px; height: 22px; display: inline-flex; align-items: center; justify-content: center;
    background: none; border: 1px solid var(--border-color); border-radius: 4px;
    color: var(--text-muted); font-size: .72rem; font-weight: 700;
    cursor: pointer; padding: 0; transition: var(--transition);
}
.pc-tool:hover { color: var(--brand-primary); border-color: var(--brand-primary); }
.pc-tool.active { background: var(--brand-primary); color: #fff; border-color: var(--brand-primary); }
.pc-tool.pc-remove:hover { color: var(--danger); border-color: var(--danger); }
.pc-tool-gap { width: .3rem; }

.pc-empty {
    display: flex; flex-direction: column; align-items: center; gap: .5rem;
    text-align: center; color: var(--text-muted); font-size: .9rem;
    padding: 2.5rem 1rem; border: 1px dashed var(--border-color); border-radius: var(--radius-lg);
}

/* ── Sidebar portal layout ── */
.portal-shell { display: flex; min-height: 100vh; }

.portal-sidebar {
    width: 260px;
    flex-shrink: 0;
    background: var(--brand-primary-darker);
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0; left: 0; bottom: 0;
    z-index: 100;
    overflow-y: auto;
}
.sidebar-logo {
    padding: .9rem 1.5rem;
    border-bottom: 1px solid rgba(255,255,255,.1);
}
.sidebar-logo .logo-text { display: block; text-decoration: none; }
/* Capped rather than full-bleed: the rail is 260px, and the lockup reads better
   with the padding intact than stretched to the border. */
.sidebar-logo .logo-text img { width: 100%; max-width: 175px; height: auto; display: block; }
.sidebar-logo .logo-sub  { color: rgba(255,255,255,.5); font-size: .72rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; margin-top: .35rem; display: block; }

.sidebar-section-label {
    padding: .7rem 1.5rem .25rem;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: rgba(255,255,255,.38);
}
/* A section label that is also a collapse toggle (the Admin group). It wears
   .sidebar-section-label for the look; this just resets the button chrome and
   rotates the chevron when open. */
.sidebar-section-toggle {
    display: flex;
    align-items: center;
    gap: .4rem;
    width: 100%;
    background: none;
    border: none;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
}
.sidebar-section-toggle:hover { color: rgba(255,255,255,.7); }
.sidebar-section-toggle .chev { flex-shrink: 0; transition: transform .15s ease; }
.sidebar-section-toggle .chev.open { transform: rotate(90deg); }

.sidebar-nav { list-style: none; padding: 0 .75rem; margin: 0 0 .25rem; }
/* Kept deliberately compact (tightened 2026-08-15): with the Admin section
   visible the rail outgrew a typical screen, and scrolling the nav is worse
   than smaller rows. line-height caps the row height the 1rem icons would
   otherwise set. */
.sidebar-nav li a, .sidebar-nav li button {
    display: flex;
    align-items: center;
    gap: .75rem;
    width: 100%;
    padding: .4rem .85rem;
    color: rgba(255,255,255,.72);
    font-size: .88rem;
    font-weight: 500;
    line-height: 1.25;
    border-radius: var(--radius);
    text-decoration: none;
    border: none;
    background: transparent;
    cursor: pointer;
    transition: var(--transition);
    text-align: left;
}
.sidebar-nav li a:hover,
.sidebar-nav li button:hover { background: rgba(255,255,255,.1); color: var(--white); }
.sidebar-nav li a.active-link { background: var(--brand-primary); color: var(--white) !important; font-weight: 600; }
.sidebar-nav .icon { width: 18px; text-align: center; font-size: .9rem; flex-shrink: 0; }
/* "New release notes" dot on the Release Notes link — cleared by visiting the
   page. The sidebar is always the dark brand shade, so this uses the brighter
   amber the dark theme uses for warning text rather than --warning itself. */
.sidebar-nav .nav-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: #fbbf24;
    margin-left: auto; flex-shrink: 0;
}

.sidebar-footer {
    margin-top: auto;
    padding: .75rem 1.25rem;
    border-top: 1px solid rgba(255,255,255,.1);
}
.sidebar-user { display: flex; align-items: center; gap: .75rem; }
.sidebar-user .avatar {
    width: 36px; height: 36px;
    border-radius: 50%;
    background: var(--brand-primary);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .85rem; color: var(--white);
    flex-shrink: 0;
}
.sidebar-user .name { font-size: .85rem; font-weight: 600; color: var(--white); }
.sidebar-user .role { font-size: .72rem; color: rgba(255,255,255,.48); }
.sidebar-signout {
    width: 30px; height: 30px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--radius);
    color: rgba(255,255,255,.45);
    font-size: 1rem; text-decoration: none;
    transition: background .15s ease, color .15s ease;
}
.sidebar-signout:hover { background: rgba(255,255,255,.1); color: var(--white); }
.sidebar-version {
    margin-top: .35rem;
    font-size: .65rem;
    letter-spacing: .02em;
    color: rgba(255,255,255,.32);
    text-align: right;
}

.portal-main {
    margin-left: 260px;
    flex: 1;
    /* Flex items default to min-width:auto, so wide intrinsic content (e.g.
       the builder stepper) would force the whole page wider than the viewport
       instead of letting inner overflow-x scrollers do their job. */
    min-width: 0;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background: var(--surface);
}
.portal-topbar {
    background: var(--card);
    border-bottom: 1px solid var(--border-color);
    padding: 0 2rem;
    height: 64px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: 50;
    box-shadow: var(--shadow-sm);
}
.portal-topbar .page-title-group { display: flex; flex-direction: column; line-height: 1.2; }
.portal-topbar .page-title { font-weight: 700; font-size: 1.1rem; color: var(--text-dark); }
.portal-topbar .page-subtitle { font-size: .72rem; font-weight: 600; color: var(--text-muted); letter-spacing: .01em; }

/* Mobile nav toggle — hidden on desktop; the media block below turns the
   sidebar into an off-canvas drawer and shows this. */
.topbar-hamburger {
    display: none; flex-direction: column; justify-content: center; gap: 4px;
    width: 40px; height: 40px; padding: 0 8px; margin-right: .35rem;
    border: none; background: none; cursor: pointer; border-radius: var(--radius);
}
.topbar-hamburger span { display: block; height: 2px; border-radius: 2px; background: var(--text-dark); }
.topbar-hamburger:hover { background: var(--surface); }
.sidebar-backdrop { display: none; }

@media (max-width: 900px) {
    .portal-sidebar {
        transform: translateX(-100%);
        transition: transform .25s ease;
        box-shadow: 8px 0 32px rgba(15, 23, 42, .25);
    }
    .portal-sidebar.open { transform: translateX(0); }
    .sidebar-backdrop { display: block; position: fixed; inset: 0; background: rgba(15, 23, 42, .45); z-index: 99; }
    .portal-main { margin-left: 0; }
    .portal-topbar { padding: 0 1rem; }
    .topbar-hamburger { display: flex; }
    /* main.-qualified: the base .portal-content rule is declared later in the
       file and would otherwise win the cascade, keeping desktop padding. */
    main.portal-content { padding: 1rem; }
}
.topbar-actions { display: flex; align-items: center; gap: .75rem; }
.topbar-avatar {
    width: 36px; height: 36px;
    border-radius: 50%;
    background: var(--brand-primary-light);
    border: 2px solid var(--brand-primary);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .85rem; color: var(--brand-primary);
    cursor: pointer;
}
.topbar-avatar-wrap { position: relative; }
.topbar-avatar:hover { background: var(--brand-primary); color: var(--white); transition: var(--transition); }
.avatar-dropdown {
    position: absolute;
    top: calc(100% + .6rem); right: 0;
    width: 240px;
    background: var(--card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: 0 8px 24px rgba(0,0,0,.1);
    z-index: 200;
    overflow: hidden;
    animation: fadeSlideDown .15s ease;
}
@keyframes fadeSlideDown {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.avatar-dropdown-header { padding: 1rem 1rem .75rem; }
.avatar-dropdown-divider { height: 1px; background: var(--border-color); margin: 0; }
.avatar-dropdown-item {
    display: flex; align-items: center; gap: .65rem;
    padding: .65rem 1rem;
    font-size: .875rem; font-weight: 500; color: var(--text-dark);
    text-decoration: none;
    transition: background .15s ease;
}
.avatar-dropdown-item:hover { background: var(--surface); color: var(--brand-primary); }
.avatar-dropdown-signout { color: var(--text-muted); }
.avatar-dropdown-signout:hover { background: #fff5f5; color: #c0392b; }

.dropdown-overlay { position: fixed; inset: 0; z-index: 199; }
.topbar-notif-wrap { position: relative; }
.topbar-notification {
    position: relative;
    padding: .4rem;
    border-radius: var(--radius);
    cursor: pointer;
    color: var(--text-muted);
    transition: var(--transition);
    border: none; background: transparent;
    font-size: 1.1rem;
}
.topbar-notification:hover { background: var(--surface); color: var(--brand-primary); }
.notif-badge {
    position: absolute; top: 2px; right: 2px;
    min-width: 17px; height: 17px;
    background: var(--danger);
    border-radius: 999px;
    border: 2px solid var(--card);
    display: flex; align-items: center; justify-content: center;
    font-size: .62rem; font-weight: 700; color: var(--white);
    line-height: 1; padding: 0 3px;
}
.notif-dropdown {
    position: absolute;
    top: calc(100% + .6rem); right: 0;
    width: 320px;
    background: var(--card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: 0 8px 24px rgba(0,0,0,.1);
    z-index: 200;
    overflow: hidden;
    animation: fadeSlideDown .15s ease;
}
.notif-dropdown-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: .85rem 1rem;
    border-bottom: 1px solid var(--border-color);
}
.notif-clear {
    font-size: .78rem; font-weight: 600; color: var(--brand-primary);
    background: none; border: none; cursor: pointer; padding: 0;
}
.notif-clear:hover { text-decoration: underline; }
/* A <button> since the rows became clickable (they open the linked page) —
   the resets live HERE so .notif-unread below still wins the background. */
.notif-item {
    display: flex; align-items: start; gap: .75rem;
    width: 100%;
    padding: .85rem 1rem;
    border: none;
    border-bottom: 1px solid var(--border-color);
    background: none;
    font: inherit; color: inherit; text-align: left; cursor: pointer;
    transition: background .15s ease;
}
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: var(--surface); }
.notif-unread { background: var(--brand-primary-light); }
.notif-unread:hover { background: #dbedf8; }
.notif-icon { font-size: 1.15rem; margin-top: .1rem; flex-shrink: 0; }
.notif-title { font-size: .875rem; font-weight: 600; color: var(--text-dark); margin-bottom: .15rem; }
.notif-body  { font-size: .8rem; color: var(--text-muted); line-height: 1.45; margin-bottom: .25rem; }
.notif-time  { font-size: .72rem; color: var(--text-muted); }

/* ── In-app notification toast ──
   Pops when a notification lands while the tab is open; auto-dismisses. Fixed
   under the topbar so it never shoves the layout around. Clickable (goes where
   the notification points) with its own × for just dismissing. */
.portal-toast {
    position: fixed; top: 76px; right: 1.25rem; z-index: 1050;
    display: flex; align-items: start; gap: .7rem;
    width: min(360px, calc(100vw - 2rem));
    padding: .85rem 1rem;
    background: var(--card);
    border: 1px solid var(--border-color);
    border-left: 4px solid var(--brand-primary);
    border-radius: var(--radius-lg);
    box-shadow: 0 12px 32px rgba(0,0,0,.18);
    cursor: pointer;
    animation: toastSlideIn .25s ease;
}
.portal-toast-icon  { font-size: 1.25rem; margin-top: .05rem; flex-shrink: 0; }
.portal-toast-text  { flex: 1; min-width: 0; }
.portal-toast-title { font-size: .875rem; font-weight: 700; color: var(--text-dark); }
.portal-toast-body  { font-size: .8rem; color: var(--text-muted); line-height: 1.45; margin-top: .15rem; }
.portal-toast-close {
    background: none; border: none; cursor: pointer; flex-shrink: 0;
    font-size: 1.1rem; line-height: 1; color: var(--text-muted);
    padding: .1rem .3rem; border-radius: var(--radius);
}
.portal-toast-close:hover { background: var(--surface); color: var(--text-dark); }
@keyframes toastSlideIn { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }

/* ── Generic portal modal ──
   The left-aligned form sibling of the Quote Builder's centered confirm dialog
   (.qbp-confirm) — first used by the contract reminder settings on Service
   Contracts. Reuses the qbp fade/pop keyframes. */
.portal-modal-backdrop {
    position: fixed; inset: 0; z-index: 1100;
    display: flex; align-items: center; justify-content: center; padding: 1.25rem;
    background: rgba(15, 30, 45, .45); backdrop-filter: blur(2px);
    animation: qbp-confirm-fade .15s ease;
}
.portal-modal {
    width: min(480px, 100%);
    max-height: calc(100vh - 2.5rem); overflow-y: auto;
    background: var(--card); border-radius: 16px; padding: 1.6rem 1.6rem 1.4rem;
    box-shadow: 0 24px 64px rgba(15, 30, 45, .3);
    animation: qbp-confirm-pop .2s cubic-bezier(.2, .9, .3, 1.15);
}
.portal-modal h4 { margin: 0 0 .35rem; font-size: 1.15rem; font-weight: 700; color: var(--text-dark); }
.portal-modal-sub { margin: 0 0 1.25rem; font-size: .85rem; line-height: 1.5; color: var(--text-muted); }
.portal-modal-actions { display: flex; gap: .6rem; justify-content: flex-end; margin-top: 1.25rem; }
.portal-modal-actions button {
    min-width: 110px; padding: .55rem 1rem; border-radius: 9px;
    font-weight: 600; font-size: .85rem; cursor: pointer; white-space: nowrap;
}
.portal-modal-cancel {
    border: 1px solid var(--border-color); background: var(--card); color: var(--text-dark);
    transition: background .15s;
}
.portal-modal-cancel:hover { background: var(--surface); }

/* ── Contract reminder settings form ── */
.reminder-field { margin-bottom: 1.1rem; }
.reminder-label {
    font-size: .72rem; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
    color: var(--text-muted); margin-bottom: .4rem;
}
.reminder-check {
    display: flex; align-items: center; gap: .55rem;
    font-size: .9rem; color: var(--text-dark);
    margin-bottom: .45rem; cursor: pointer;
}
.reminder-check input { width: 16px; height: 16px; accent-color: var(--brand-primary); cursor: pointer; flex-shrink: 0; }
.reminder-inline { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.reminder-inline > span { font-size: .88rem; color: var(--text-dark); }
.reminder-days { width: 90px; }
.reminder-time { width: 130px; }
.reminder-zone { flex: 1; min-width: 160px; }
.day-chips { display: flex; gap: .4rem; flex-wrap: wrap; }
.day-chip {
    width: 38px; height: 38px; border-radius: 50%;
    border: 1px solid var(--border-color); background: var(--card);
    font-size: .78rem; font-weight: 600; color: var(--text-muted); cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.day-chip:hover { border-color: var(--brand-primary); color: var(--brand-primary); }
.day-chip.active { background: var(--brand-primary); border-color: var(--brand-primary); color: var(--white); }
.reminder-hint { font-size: .78rem; color: var(--text-muted); margin-top: .4rem; line-height: 1.45; }
.reminder-footnote { font-size: .78rem; color: var(--text-muted); line-height: 1.5; margin: 0; }
.reminder-error {
    margin-top: .85rem; padding: .6rem .8rem; border-radius: var(--radius);
    background: rgba(220, 38, 38, .08); border-left: 3px solid var(--danger);
    font-size: .82rem; color: var(--danger);
}

.portal-content { padding: 2rem; flex: 1; }

/* ── Public nav ── */
.public-nav {
    position: sticky;
    top: 0;
    z-index: 200;
    background: var(--card);
    border-bottom: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
}
.public-nav .nav-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 1.5rem;
    height: 70px;
    display: flex;
    align-items: center;
    gap: 2rem;
}
.nav-logo { text-decoration: none; display: flex; align-items: center; flex-shrink: 0; }
/* The Sail lockup already reads "sail — powered by Ingage", so the bar carries the
   image alone. Dark variant: the public nav sits on white. */
.nav-logo img { height: 36px; width: auto; display: block; }
.nav-right { display: flex; align-items: center; gap: .75rem; margin-left: auto; }
.nav-links { display: flex; align-items: center; gap: .15rem; }
.nav-links a {
    color: var(--text-dark);
    font-weight: 500;
    font-size: .9rem;
    text-decoration: none;
    padding: .45rem .8rem;
    border-radius: var(--radius);
    transition: color .2s ease;
    white-space: nowrap;
    position: relative;
}
.nav-links a::after {
    content: '';
    position: absolute;
    bottom: 2px; left: 50%;
    width: 0; height: 2px;
    background: var(--brand-primary);
    border-radius: 2px;
    transition: width .25s ease, left .25s ease;
}
.nav-links a:hover { color: var(--brand-primary); }
.nav-links a.active { color: var(--brand-primary); font-weight: 600; }
.nav-links a.active::after { width: calc(100% - 1.6rem); left: .8rem; }
.nav-cta { flex-shrink: 0; }

/* Mobile nav toggle — hidden on desktop; the media block below collapses the
   links into a dropdown panel and shows this. */
.nav-hamburger {
    display: none; flex-direction: column; justify-content: center; gap: 4px;
    width: 40px; height: 40px; padding: 0 8px;
    border: none; background: none; cursor: pointer; border-radius: var(--radius);
    flex-shrink: 0;
}
.nav-hamburger span { display: block; height: 2px; border-radius: 2px; background: var(--text-dark); }
.nav-hamburger:hover { background: var(--surface); }
.nav-mobile-backdrop { display: none; }
.nav-mobile-menu { display: none; }

@media (max-width: 900px) {
    .public-nav .nav-inner { padding: 0 1rem; height: 60px; gap: 1rem; position: relative; z-index: 2; background: var(--card); }
    .nav-links { display: none; }
    .nav-right { gap: .5rem; }
    .nav-hamburger { display: flex; }
    /* The lockup is wide (~4.9:1), so on phones it has to scale with the viewport
       or it pushes the login button and burger off the right edge. */
    .nav-logo img { height: clamp(22px, 6.5vw, 32px); }
    /* Dims the page below the bar (bar height above matches the 60px top). */
    .nav-mobile-backdrop { display: block; position: fixed; top: 60px; left: 0; right: 0; bottom: 0; background: rgba(15, 23, 42, .45); z-index: 1; }
    .nav-mobile-menu {
        display: flex; flex-direction: column; gap: .15rem;
        position: absolute; top: 100%; left: 0; right: 0; z-index: 2;
        background: var(--card);
        border-bottom: 1px solid var(--border-color);
        box-shadow: 0 16px 32px rgba(15, 23, 42, .18);
        padding: .5rem .75rem .75rem;
    }
    .nav-mobile-menu a {
        color: var(--text-dark);
        font-weight: 500;
        font-size: 1rem;
        text-decoration: none;
        padding: .8rem .75rem;
        border-radius: var(--radius);
    }
    .nav-mobile-menu a:hover { background: var(--surface); color: var(--brand-primary); }
    .nav-mobile-menu a.active { background: var(--brand-primary-light); color: var(--brand-primary); font-weight: 600; }
}

@media (max-width: 380px) {
    /* Tiny phones: tighten the bar so logo + login button + burger still fit. */
    .public-nav .nav-inner { padding: 0 .75rem; }
    .nav-cta .btn-brand { padding: .5rem .9rem; font-size: .85rem; }
}

/* ── Footer ── */
.public-footer {
    background: var(--text-dark);
    color: rgba(255,255,255,.65);
    padding: 4rem 0 2rem;
}
.footer-grid {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1fr;
    gap: 3rem;
}
/* White lockup — the footer band is var(--text-dark). */
.footer-brand .logo { display: block; margin-bottom: 1.1rem; }
.footer-brand .logo img { height: 34px; width: auto; display: block; }
.footer-brand p { font-size: .875rem; line-height: 1.7; max-width: 280px; }
.footer-col h4 { color: var(--white); font-size: .8rem; text-transform: uppercase; letter-spacing: .1em; margin-bottom: 1rem; }
.footer-col a { display: block; color: rgba(255,255,255,.58); font-size: .875rem; text-decoration: none; margin-bottom: .5rem; transition: var(--transition); }
.footer-col a:hover { color: var(--white); }
.footer-bottom { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid rgba(255,255,255,.1); display: flex; align-items: center; justify-content: center; text-align: center; font-size: .8rem; flex-wrap: wrap; gap: .75rem; }
@media(max-width:900px)  { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media(max-width:600px)  { .footer-grid { grid-template-columns: 1fr; } }

/* ── Hero ── */
.hero {
    background: linear-gradient(135deg, var(--brand-primary-darker) 0%, var(--brand-primary) 55%, var(--brand-accent) 100%);
    color: var(--white);
    padding: 6rem 0 5rem;
    position: relative;
    overflow: hidden;
}
.hero::before {
    content: '';
    position: absolute;
    top: -30%; right: -8%;
    width: 550px; height: 550px;
    border-radius: 50%;
    background: rgba(255,255,255,.04);
    pointer-events: none;
    animation: heroCircle1 18s ease-in-out infinite;
}
.hero::after {
    content: '';
    position: absolute;
    bottom: -25%; left: -5%;
    width: 380px; height: 380px;
    border-radius: 50%;
    background: rgba(255,255,255,.03);
    pointer-events: none;
    animation: heroCircle2 24s ease-in-out infinite;
}

@keyframes heroCircle1 {
    0%   { transform: translate(0, 0); }
    25%  { transform: translate(-60px, 80px); }
    50%  { transform: translate(-120px, 30px); }
    75%  { transform: translate(-50px, 110px); }
    100% { transform: translate(0, 0); }
}
@keyframes heroCircle2 {
    0%   { transform: translate(0, 0); }
    25%  { transform: translate(80px, -60px); }
    50%  { transform: translate(40px, -120px); }
    75%  { transform: translate(100px, -40px); }
    100% { transform: translate(0, 0); }
}
.hero-content { position: relative; z-index: 1; max-width: 780px; }
.hero-eyebrow { font-size: .82rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; opacity: .78; margin-bottom: 1rem; }
.hero h1 { color: var(--white); margin-bottom: 1.25rem; }
.hero .lead { font-size: 1.15rem; opacity: .9; max-width: 620px; margin-bottom: 2rem; line-height: 1.75; }
.hero-actions { display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
.hero-stats { display: flex; gap: 3.5rem; margin-top: 3.5rem; padding-top: 2.5rem; border-top: 1px solid rgba(255,255,255,.18); flex-wrap: wrap; gap: 2.5rem; }
.hero-stat .value { font-size: 2rem; font-weight: 800; line-height: 1; }
.hero-stat .label { font-size: .82rem; opacity: .7; margin-top: .2rem; }

/* ── Page hero banners (reuse home hero circle animations) ── */
.dealers-hero {
    background: linear-gradient(135deg, var(--brand-primary-darker) 0%, var(--brand-primary) 100%);
    color: var(--white);
    padding: 4.5rem 0 5.5rem;
    position: relative;
    overflow: hidden;
}
.dealers-hero::before {
    content: '';
    position: absolute;
    top: -15%; right: -8%;
    width: 500px; height: 500px;
    border-radius: 50%;
    background: rgba(255,255,255,.04);
    pointer-events: none;
    animation: heroCircle1 18s ease-in-out infinite;
}
.dealers-hero::after {
    content: '';
    position: absolute;
    bottom: -30%; left: -6%;
    width: 360px; height: 360px;
    border-radius: 50%;
    background: rgba(255,255,255,.03);
    pointer-events: none;
    animation: heroCircle2 24s ease-in-out infinite;
}

/* ── About hero (reuses home hero circle animations) ── */
.about-hero {
    background: var(--brand-primary);
    color: var(--white);
    padding: 4rem 0 5rem;
    position: relative;
    overflow: hidden;
}
.about-hero::before {
    content: '';
    position: absolute;
    top: -20%; right: -5%;
    width: 500px; height: 500px;
    border-radius: 50%;
    background: rgba(255,255,255,.04);
    pointer-events: none;
    animation: heroCircle1 18s ease-in-out infinite;
}
.about-hero::after {
    content: '';
    position: absolute;
    bottom: -30%; left: -8%;
    width: 360px; height: 360px;
    border-radius: 50%;
    background: rgba(255,255,255,.03);
    pointer-events: none;
    animation: heroCircle2 24s ease-in-out infinite;
}

/* ── Pillar / feature cards ── */
.pillar-number {
    width: 48px; height: 48px;
    background: var(--brand-primary);
    color: var(--white);
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: 1.1rem;
    margin-bottom: 1.25rem;
    flex-shrink: 0;
}
.feature-icon {
    width: 52px; height: 52px;
    background: var(--brand-primary-light);
    color: var(--brand-primary);
    border-radius: var(--radius);
    display: flex; align-items: center; justify-content: center;
    font-size: 1.5rem;
    margin-bottom: 1.25rem;
}

/* ── Pipeline kanban ── */
.pipeline-board { display: flex; gap: 1rem; overflow-x: auto; padding-bottom: 1rem; }
.pipeline-col {
    flex: 0 0 210px;
    background: var(--surface);
    border-radius: var(--radius-lg);
    padding: 1rem;
    min-height: 420px;
}
.pipeline-col-header {
    font-weight: 700;
    font-size: .8rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-muted);
    margin-bottom: .85rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.pipeline-card {
    background: var(--card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding: .85rem;
    margin-bottom: .6rem;
    cursor: pointer;
    transition: var(--transition);
    box-shadow: var(--shadow-sm);
}
.pipeline-card:hover { box-shadow: var(--shadow-md); border-color: var(--brand-primary); }
.pipeline-card .merchant { font-weight: 600; font-size: .88rem; margin-bottom: .2rem; }
.pipeline-card .mrr { font-size: .78rem; color: var(--text-muted); }

/* ── Login ── */
.login-shell {
    min-height: 100vh;
    display: flex;
}
.login-left {
    flex: 0 0 480px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3rem 2.5rem;
    background: var(--card);
    position: relative;
}
.login-card {
    width: 100%;
    max-width: 380px;
}
/* Brand lockup at the top of the sign-in / forgot / reset cards. The card sits on
   white, so it's the dark logo; "Dealer Portal" underneath completes the name. */
.auth-brand { display: block; text-decoration: none; margin-bottom: 2rem; }
.auth-brand img { height: 42px; width: auto; display: block; }
.auth-brand .auth-brand-sub {
    display: block;
    font-size: .78rem;
    font-weight: 600;
    color: var(--text-muted);
    letter-spacing: .06em;
    text-transform: uppercase;
    margin-top: .5rem;
}
.login-version {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 1.25rem;
    text-align: center;
    font-size: .72rem;
    color: var(--text-muted);
    letter-spacing: .02em;
}
.login-right {
    flex: 1;
    background: linear-gradient(145deg, var(--brand-primary-darker) 0%, var(--brand-primary) 60%, var(--brand-accent) 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3rem;
    color: var(--white);
    position: relative;
    overflow: hidden;
}
.login-right::before { content: ''; position: absolute; top: -20%; right: -15%; width: 400px; height: 400px; border-radius: 50%; background: rgba(255,255,255,.05); animation: heroCircle1 18s ease-in-out infinite; }
.login-right::after  { content: ''; position: absolute; bottom: -25%; left: -10%; width: 350px; height: 350px; border-radius: 50%; background: rgba(255,255,255,.04); animation: heroCircle2 24s ease-in-out infinite; }
.login-right-content { position: relative; z-index: 1; max-width: 400px; }
@media(max-width:768px) { .login-right { display: none; } .login-left { flex: 1; } }
/* Single-panel variant (forgot/reset password): just the centered card, no hero. */
.login-shell.login-single .login-left { flex: 1; }

/* ── Status dots ── Named for the Site Health page that first used them; that
   page is gone but the dots are shared vocabulary now — sign-in presence, funnel
   stages, ticket and escalation status. Note .status-offline is red and
   .status-idle is grey, the opposite of what the names suggest. */
.status-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: .4rem; flex-shrink: 0; }
.status-online  { background: var(--success); box-shadow: 0 0 0 3px rgba(5,150,105,.2); }
.status-warning { background: var(--warning); box-shadow: 0 0 0 3px rgba(217,119,6,.2); }
.status-offline { background: var(--danger); box-shadow: 0 0 0 3px rgba(220,38,38,.2); }
.status-idle    { background: #9ca3af; box-shadow: 0 0 0 3px rgba(156,163,175,.2); }

/* ── Active users (admin: who is on the site before a deploy) ── */
.presence-banner {
    display: flex; align-items: flex-start; gap: .9rem;
    padding: 1rem 1.25rem; border-radius: 10px; border: 1px solid;
    margin-bottom: 1.5rem;
}
.presence-banner.clear { background: #ecfdf5; border-color: #a7f3d0; }
.presence-banner.busy  { background: #fffbeb; border-color: #fde68a; }
.presence-banner-icon  { font-size: 1.35rem; line-height: 1.2; }
.presence-banner-title { font-weight: 700; font-size: .95rem; color: var(--text-dark); }
.presence-banner-sub   { font-size: .85rem; color: var(--text-muted); margin-top: .15rem; }

/* The session list groups tabs under the person holding them; only a person with
   more than one tab is clickable, so the pointer never promises nothing. The
   detail cell paints its own background, which is also what keeps .data-table's
   row hover off the expanded panel. */
.presence-person-row { cursor: pointer; }
.presence-person-row:hover { background: var(--surface); }

.presence-caret {
    display: inline-block; margin-right: .35rem;
    font-size: .7rem; color: var(--text-muted);
    transition: transform .15s ease;
}
.presence-caret.open { transform: rotate(90deg); }

.presence-tab-detail { background: var(--surface); }

.presence-tab-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
.presence-tab-table th {
    padding: .25rem .6rem; text-align: left;
    font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-muted);
}
.presence-tab-table td { padding: .4rem .6rem; border-top: 1px solid var(--border-color); }
.presence-tab-table tbody tr:hover { background: transparent; }

/* The heads-up every dealer sees before a deploy. Sticky under the 64px topbar
   so it stays put however far down the page someone has scrolled. */
.deploy-banner {
    position: sticky; top: 64px; z-index: 30;
    display: flex; align-items: flex-start; gap: .8rem;
    padding: .85rem 2rem;
    background: #fef3c7; border-bottom: 1px solid #fcd34d;
}
.deploy-banner-icon  { font-size: 1.15rem; line-height: 1.3; }
.deploy-banner-title { font-weight: 700; font-size: .92rem; color: #78350f; }
.deploy-banner-note  { font-size: .82rem; color: #92400e; margin-top: .1rem; }

/* Admin controls for the same banner, on the Active Users page. */
.deploy-tools { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: flex-end; justify-content: space-between; }
.deploy-tools-copy   { min-width: 220px; flex: 1; }
.deploy-tools-title  { font-weight: 700; font-size: .95rem; }
.deploy-tools-sub    { font-size: .85rem; color: var(--text-muted); margin-top: .15rem; }
.deploy-tools-fields { display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-end; }
.deploy-tools-fields .form-label-custom { margin-bottom: .25rem; }
.deploy-active { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; }
.deploy-active-title { font-weight: 700; font-size: .95rem; color: #92400e; }
.deploy-active-sub   { font-size: .82rem; color: var(--text-muted); margin-top: .15rem; }

@media (max-width: 900px) {
    .deploy-banner { padding: .85rem 1rem; }
}

/* ── Dashboard ── The landing page's own blocks: the funnel→quoted→won strip,
   the merged "needs you today" list, its activity rows, and the service pulse.
   A stat card's .delta is a neutral sub-fact here (a value, a count), so it is
   muted by default; .up/.down still win on specificity where a page means them
   as a trend. */
.stat-card .delta { color: var(--text-muted); }

.journey-head { margin-bottom: 1.25rem; }
.journey-note { display: block; font-size: .8rem; color: var(--text-muted); margin-top: .3rem; }
.journey-steps { display: flex; align-items: stretch; gap: .5rem; flex-wrap: wrap; }
.journey-step {
    flex: 1 1 140px; min-width: 140px; text-align: center;
    padding: 1rem .75rem; background: var(--surface); border-radius: var(--radius);
}
.journey-value { font-size: 1.9rem; font-weight: 800; color: var(--brand-primary); line-height: 1; }
.journey-value.muted { color: var(--text-muted); }
.journey-sub   { font-size: .84rem; font-weight: 700; color: var(--brand-primary); margin-top: .25rem; }
.journey-label { font-size: .78rem; color: var(--text-muted); margin-top: .35rem; }
/* The arrows are decoration between steps; they collapse away once the steps
   wrap, since a sideways arrow above a stacked box reads as nonsense. */
.journey-sep {
    display: flex; align-items: center; color: var(--text-muted);
    font-size: 1.2rem; flex: 0 0 auto;
}
.journey-links { display: flex; gap: 1.25rem; flex-wrap: wrap; margin-top: 1.25rem; }
.journey-links a { font-size: .82rem; color: var(--brand-primary); font-weight: 600; }

.dash-actions { display: flex; flex-direction: column; gap: .6rem; }
.dash-action {
    display: flex; align-items: center; gap: .85rem;
    padding: .7rem .85rem; background: var(--surface); border-radius: var(--radius);
    border-left: 3px solid transparent;
}
.dash-action.urgent { background: var(--brand-primary-light); border-left-color: var(--warning); }
.dash-action-icon { font-size: 1.05rem; flex-shrink: 0; }
.dash-action-body { flex: 1; min-width: 0; }
.dash-action-title  { font-weight: 600; font-size: .89rem; }
.dash-action-reason { font-size: .78rem; color: var(--text-muted); margin-top: .1rem; }
.dash-action.urgent .dash-action-reason { color: var(--warning); font-weight: 700; }
.dash-action-btn { font-size: .78rem; padding: .3rem .8rem; flex-shrink: 0; }

.dash-event { display: flex; gap: 1rem; align-items: start; padding: .85rem 0; border-bottom: 1px solid var(--border-color); }
.dash-event:last-child { border-bottom: none; }
.dash-event-icon {
    width: 32px; height: 32px; border-radius: 50%; background: var(--brand-primary-light);
    display: flex; align-items: center; justify-content: center; font-size: .9rem; flex-shrink: 0;
}

.pulse-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 1rem; }
.pulse-item { text-align: center; padding: .85rem .5rem; background: var(--surface); border-radius: var(--radius); }
.pulse-value { font-size: 1.6rem; font-weight: 800; color: var(--brand-primary); line-height: 1; }
.pulse-value.alert { color: var(--danger); }
.pulse-label { font-size: .76rem; color: var(--text-muted); margin-top: .3rem; }

@media (max-width: 700px) {
    .journey-sep { display: none; }
}

/* ── Scrollbar ── */
.portal-sidebar::-webkit-scrollbar { width: 4px; }
.portal-sidebar::-webkit-scrollbar-track { background: transparent; }
.portal-sidebar::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); border-radius: 4px; }

/* ── Misc ── */
.divider-text { display: flex; align-items: center; gap: 1rem; color: var(--text-muted); font-size: .85rem; margin: 1.25rem 0; }
.divider-text::before, .divider-text::after { content: ''; flex: 1; height: 1px; background: var(--border-color); }

.page-header { margin-bottom: 1.75rem; }
.page-header h2 { margin-bottom: .3rem; }
.page-header p  { color: var(--text-muted); font-size: .9rem; margin: 0; }

/* Release Notes — timeline of user-facing changes */
.release-list { display: flex; flex-direction: column; gap: 1.25rem; }
.release-entry { display: grid; grid-template-columns: 140px 1fr; gap: 1.25rem; align-items: start; }
.release-rail { padding-top: .35rem; text-align: right; }
.release-date { font-weight: 700; font-size: .95rem; color: var(--text-dark); }
.release-version { margin-top: .4rem; display: inline-block; font-size: .72rem; }
.release-card { padding: 1.4rem 1.6rem; }
.release-headline { margin: 0 0 1rem; font-size: 1.02rem; color: var(--brand-primary); }
.release-changes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .85rem; }
.release-changes li { display: flex; align-items: baseline; gap: .75rem; }
.release-tag { flex-shrink: 0; width: 74px; text-align: center; font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; }
.release-text { font-size: .9rem; line-height: 1.5; color: var(--text-dark); }

@media (max-width: 640px) {
    .release-entry { grid-template-columns: 1fr; gap: .6rem; }
    .release-rail { text-align: left; padding-top: 0; display: flex; align-items: center; gap: .6rem; }
    .release-version { margin-top: 0; }
}

.breadcrumb { display: flex; align-items: center; gap: .4rem; font-size: .82rem; color: var(--text-muted); margin-bottom: .75rem; }
.breadcrumb a { color: var(--brand-primary); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }
.breadcrumb-sep { opacity: .5; }

.empty-state { text-align: center; padding: 4rem 2rem; color: var(--text-muted); }
.empty-state .icon { font-size: 3rem; opacity: .3; margin-bottom: 1rem; }
.empty-state h4   { color: var(--text-dark); margin-bottom: .5rem; }

/* ── Quote Builder — shared layout ── */
.qb-layout { display: grid; grid-template-columns: 1fr 320px; gap: 1.5rem; align-items: start; }
.qb-main { min-width: 0; }
/* Offset by the 64px sticky topbar so the card parks just below it — otherwise
   its top border scrolls up behind the topbar and gets clipped. */
.qb-summary { position: sticky; top: calc(64px + 0.5rem); }
.qb-summary-row { display: flex; justify-content: space-between; gap: 1rem; padding: .35rem 0; font-size: .88rem; }
.qb-summary-row.total { font-size: .95rem; padding-top: .5rem; }
.qb-summary hr { border: none; border-top: 1px solid var(--border-color); margin: .5rem 0; }
.qb-nav { display: flex; justify-content: space-between; gap: 1rem; margin-top: 1.5rem; }
@media(max-width:992px) { .qb-layout { grid-template-columns: 1fr; } .qb-summary { position: static; } }

/* Condensed sticky "deal heads-up" bar. Hidden on wide screens — the sticky
   sidebar already keeps the deal in view. Below 992px the sidebar drops beneath
   the wizard, so this pins just under the topbar (z-index below the topbar's 50,
   above content) to keep customer, program, and key economics always visible. */
.qbp-stickybar { display: none; }
@media (max-width: 992px) {
    .qbp-stickybar {
        display: flex; align-items: center; justify-content: space-between; gap: .75rem;
        position: sticky; top: 64px; z-index: 40;
        margin: 0 0 1rem; padding: .5rem .85rem;
        background: var(--card); border: 1px solid var(--border-color); border-radius: var(--radius);
        box-shadow: var(--shadow-sm);
    }
    .qbp-stickybar .id { min-width: 0; }
    .qbp-stickybar .id .name { font-size: .92rem; font-weight: 800; color: var(--text-dark); line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .qbp-stickybar .id .line { font-size: .6rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .qbp-stickybar .stats { display: flex; gap: .9rem; flex-shrink: 0; }
    .qbp-stickybar .stat { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; }
    .qbp-stickybar .stat .l { font-size: .56rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted); }
    .qbp-stickybar .stat .v { font-size: .95rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text-dark); }
    .qbp-stickybar .stat.pos .v { color: var(--brand-primary); }
    .qbp-stickybar .stat.neg .v { color: var(--danger); }
}
/* Very narrow: drop the residual chip so the bar never overflows a phone. */
@media (max-width: 400px) {
    .qbp-stickybar .stat:nth-child(3) { display: none; }
}

/* The wizard column is squeezed by two sidebars (portal nav + summary), so the
   viewport breakpoints on .grid-2/3/4 misjudge its real width. Inside the
   builder, grids wrap by CONTAINER width instead: a track wraps to the next
   row the moment it would drop below a usable field/card width, and
   min(…, 100%) keeps them single-column on phones. */
.qb-main .grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr)); }
.qb-main .grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); }
.qb-main .grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr)); }

/* ════════════════════════════════════════════════════════════════════════
   Quote Builder — "precision instrument" workbench.
   Color vocabulary: brand blue = navigation & selection ONLY;
   green / amber / slate = area resolution state ONLY.
   ════════════════════════════════════════════════════════════════════════ */

/* Numeric inputs: right-aligned, lining tabular figures */
.num-input { text-align: right; font-variant-numeric: tabular-nums; }

/* Stepper with live sub-labels */
.qbp-stepper { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.qbp-step {
    display: flex; align-items: center; gap: .55rem; border: 1px solid var(--border-color);
    background: var(--card); border-radius: var(--radius); padding: .45rem .8rem .45rem .55rem;
    cursor: pointer; min-width: 128px; text-align: left; transition: var(--transition);
}
.qbp-step:hover { border-color: var(--brand-primary); }
.qbp-step.active { border-color: var(--brand-primary); background: var(--brand-primary-light); box-shadow: inset 0 0 0 1px var(--brand-primary); }
.qbp-step-num {
    width: 24px; height: 24px; border-radius: 50%; flex: none; display: grid; place-items: center;
    background: var(--surface); border: 1.5px solid var(--border-color);
    font-size: .75rem; font-weight: 700; color: var(--text-muted);
}
.qbp-step.active .qbp-step-num { background: var(--brand-primary); border-color: var(--brand-primary); color: var(--white); }
.qbp-step.done .qbp-step-num { background: #d1fae5; border-color: var(--success); color: var(--success); }
.qbp-step-label { font-size: .82rem; font-weight: 600; line-height: 1.15; }
.qbp-step-sub { font-size: .68rem; color: var(--text-muted); line-height: 1.2; font-variant-numeric: tabular-nums; max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qbp-step.active .qbp-step-sub { color: var(--brand-primary-dark); }

/* Step header inside each card */
.qbp-step-head { display: flex; align-items: baseline; gap: .75rem; border-bottom: 1px solid var(--border-color); padding-bottom: .7rem; margin-bottom: 1.25rem; }
.qbp-step-head h3 { margin: 0; font-size: 1.02rem; font-weight: 700; }
.qbp-step-head p { margin: 0; font-size: .8rem; color: var(--text-muted); }

/* Compact form scale for the workbench */
.qb4 .form-control-custom { padding: .45rem .7rem; font-size: .88rem; }
.qb4 .form-label-custom { font-size: .8rem; margin-bottom: .25rem; }
.qb4 .form-group-custom { margin-bottom: .9rem; }
.qbp-hint { font-size: .72rem; color: var(--text-muted); margin-top: .25rem; line-height: 1.35; }
.qbp-req { font-size: .66rem; font-weight: 700; color: var(--warning); text-transform: uppercase; letter-spacing: .04em; margin-left: .4rem; }
.qbp-input-warn { border-left: 3px solid var(--warning) !important; }

/* Advisory range warning under a payments input (workbook ✔️/⚠️ parity — warn only) */
.qbp-fieldwarn { display: flex; align-items: flex-start; gap: .3rem; font-size: .72rem; font-weight: 500; color: var(--warning); margin-top: .25rem; line-height: 1.35; }
.qbp-fieldwarn svg { flex-shrink: 0; margin-top: .12rem; }

/* Segmented control (Project Type, Rate Type, Processor) */
.qbp-seg { display: inline-flex; border: 1px solid var(--border-color); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.qbp-seg button {
    border: none; background: transparent; padding: .42rem .95rem; font-size: .84rem; font-weight: 600;
    color: var(--text-muted); cursor: pointer; transition: var(--transition); border-right: 1px solid var(--border-color);
}
.qbp-seg button:last-child { border-right: none; }
.qbp-seg button:hover { color: var(--brand-primary); }
.qbp-seg button.on { background: var(--brand-primary); color: var(--white); }

/* Compact Billable / Included / Show-value control on premium-service lines */
.qbp-billseg { display: inline-flex; border: 1px solid var(--border-color); border-radius: 999px; overflow: hidden; background: var(--surface); vertical-align: middle; }
.qbp-billseg button {
    border: none; background: transparent; padding: .12rem .55rem; font-size: .68rem; font-weight: 700;
    color: var(--text-muted); cursor: pointer; transition: var(--transition); border-right: 1px solid var(--border-color);
}
.qbp-billseg button:last-child { border-right: none; }
.qbp-billseg button:hover { color: var(--brand-primary); }
.qbp-billseg button.on { background: var(--brand-primary); color: var(--white); }

/* Existing-customer typeahead */
.qbp-search { position: relative; }
.qbp-search-pop {
    position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
    background: var(--card); border: 1px solid var(--border-color); border-radius: var(--radius);
    box-shadow: var(--shadow-lg); max-height: 330px; overflow-y: auto;
}
.qbp-search-item { display: flex; justify-content: space-between; gap: .75rem; width: 100%; text-align: left; border: none; background: none; padding: .5rem .8rem; cursor: pointer; font-size: .85rem; border-bottom: 1px solid var(--surface); }
.qbp-search-item:hover, .qbp-search-item.hl { background: var(--brand-primary-light); }
.qbp-search-item .meta { font-size: .72rem; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.qbp-search-item .inactive { color: var(--danger); font-size: .68rem; font-weight: 700; }
.qbp-search-item .sub { display: block; font-size: .72rem; color: var(--text-muted); }
/* "Similar deals" benchmark hint (summary sidebar) */
.qbp-benchmark {
    margin-top: .6rem; padding: .5rem .65rem; border: 1px dashed var(--border-color);
    border-radius: var(--radius); background: var(--surface);
    display: flex; flex-direction: column; gap: .15rem;
}
.qbp-benchmark .ttl { font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); }
.qbp-benchmark .ttl .n { font-weight: 600; text-transform: none; letter-spacing: 0; }
.qbp-benchmark .stats { font-size: .74rem; color: var(--text-dark); font-variant-numeric: tabular-nums; }

/* Goal-seek widget (Step 5) */
/* ── Step 5: the "How to use this" panel above Goal Seek ──
   A plain <details> so it works without JS and collapses out of the way; the
   step is already dense, so this must cost nothing when closed. */
.qbp-howto { margin: -.15rem 0 .5rem; }
.qbp-howto > summary {
    cursor: pointer; display: inline-flex; align-items: center; gap: .3rem;
    font-size: .76rem; font-weight: 600; color: var(--brand-primary);
    list-style: none; padding: .15rem 0;
}
.qbp-howto > summary::-webkit-details-marker { display: none; }
.qbp-howto > summary::before { content: "▸"; font-weight: 700; }
.qbp-howto[open] > summary::before { content: "▾"; }
.qbp-howto > summary:hover { text-decoration: underline; }
.qbp-howto-body {
    border: 1px solid var(--border-color); border-left: 3px solid var(--brand-primary);
    border-radius: var(--radius); background: var(--surface);
    padding: .7rem .85rem; margin-top: .4rem; font-size: .8rem; line-height: 1.5;
}
.qbp-howto-body p { margin: 0 0 .5rem; }
.qbp-howto-body ol, .qbp-howto-body ul { margin: 0 0 .55rem; padding-left: 1.15rem; }
.qbp-howto-body li { margin-bottom: .28rem; }
.qbp-howto-body li:last-child { margin-bottom: 0; }
.qbp-howto-body .qbp-howto-sub {
    font-weight: 700; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-muted); margin: .55rem 0 .3rem;
}
.qbp-goalseek { border: 1px solid var(--border-color); border-radius: var(--radius); padding: .8rem .9rem; background: var(--surface); }
.qbp-goalseek-row { display: flex; gap: .55rem; align-items: center; flex-wrap: wrap; }
.qbp-goalseek-result {
    display: flex; gap: .8rem; align-items: center; justify-content: space-between; flex-wrap: wrap;
    margin-top: .7rem; font-size: .82rem; background: var(--card);
    border: 1px solid var(--brand-primary-mid); border-radius: var(--radius); padding: .6rem .8rem;
}
.qbp-goalseek-result.bad { border-color: #fca5a5; color: #b91c1c; }
.qbp-goalseek-result em { color: var(--text-muted); font-style: normal; font-size: .76rem; }

/* ── Step 5: Project Fees (the workbook's MAIN Project Fees panel) ──
   Six narrow numeric columns on a card that the two sidebars already squeeze,
   so the block scrolls horizontally rather than pushing the page wide — the
   same rule the rate grid follows. A row lights up once it carries a fee. */
.qbp-feeblock { margin-top: .9rem; overflow-x: auto; }
.qbp-feeblock-head {
    display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
    font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    color: var(--text-muted); margin-bottom: .3rem;
}
.qbp-feeblock-warn {
    text-transform: none; letter-spacing: 0; font-weight: 600; font-size: .72rem;
    color: var(--warning);
}
/* `table-layout: fixed` with pinned numeric widths is deliberate: each block is
   its own table, and with auto layout the columns landed at different x
   positions per block (the long "Infinity Program Qual. Fee (Monthly)" name
   pushed its row's inputs right), so the three stacked blocks didn't line up. */
.qbp-feetable { width: 100%; border-collapse: collapse; min-width: 660px; table-layout: fixed; }
.qbp-feetable th:nth-child(2), .qbp-feetable td:nth-child(2) { width: 104px; }
.qbp-feetable th:nth-child(3), .qbp-feetable td:nth-child(3) { width: 108px; }
.qbp-feetable th:nth-child(4), .qbp-feetable td:nth-child(4) { width: 96px; }
.qbp-feetable th:nth-child(5), .qbp-feetable td:nth-child(5) { width: 108px; }
.qbp-feetable th:nth-child(6), .qbp-feetable td:nth-child(6) { width: 100px; }
.qbp-feetable th {
    font-size: .66rem; text-transform: uppercase; letter-spacing: .05em;
    color: var(--text-muted); font-weight: 700; text-align: left; padding: .25rem .4rem;
}
.qbp-feetable th.n, .qbp-feetable td.n { text-align: right; }
.qbp-feetable td {
    padding: .3rem .4rem; border-top: 1px solid var(--border-color);
    font-size: .84rem; vertical-align: middle;
}
.qbp-feetable tr.on td { background: var(--brand-primary-light); }
.qbp-feetable td.total { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.qbp-feetable .form-control-custom { width: 100%; max-width: 92px; min-width: 0; display: inline-block; }
.qbp-feetable-mo {
    display: inline-block; margin-left: .35rem; padding: 0 .35rem; border-radius: 999px;
    background: var(--surface); border: 1px solid var(--border-color);
    font-size: .62rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted);
}

/* Deal-template picker rows (Quotes page "From template" dropdown) */
.qbp-tpl-row { display: flex; align-items: stretch; border-bottom: 1px solid var(--surface); }
.qbp-tpl-row:last-child { border-bottom: none; }
.qbp-tpl-start {
    flex: 1; display: flex; flex-direction: column; gap: .1rem; text-align: left;
    border: none; background: none; padding: .55rem .8rem; cursor: pointer;
}
.qbp-tpl-start:hover { background: var(--brand-primary-light); }
.qbp-tpl-start .name { font-weight: 700; font-size: .86rem; }
.qbp-tpl-start .meta { font-size: .72rem; color: var(--text-muted); }
.qbp-tpl-del {
    border: none; background: none; color: var(--text-muted); cursor: pointer;
    padding: 0 .7rem; font-size: .8rem; border-radius: 0;
}
.qbp-tpl-del:hover { color: var(--danger); }
.qbp-tpl-del.arm { color: var(--danger); font-weight: 700; }

/* Step 6 pre-send AI review card */
.qbp-aireview {
    border: 1px solid var(--border-color); border-left: 4px solid var(--brand-primary);
    background: var(--card); border-radius: var(--radius); padding: .85rem 1.05rem; margin-bottom: 1rem;
}
.qbp-aireview-head { display: flex; justify-content: space-between; align-items: center; font-weight: 800; font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-primary-dark); margin-bottom: .5rem; }
.qbp-aireview-head button { border: none; background: none; color: var(--text-muted); cursor: pointer; font-size: .85rem; padding: .1rem .3rem; }
.qbp-aireview ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.qbp-aireview li { display: flex; align-items: flex-start; gap: .55rem; font-size: .85rem; line-height: 1.45; }
.qbp-aireview li a { color: var(--brand-primary); font-weight: 700; font-size: .78rem; cursor: pointer; white-space: nowrap; }
.qbp-aireview .sev { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; margin-top: .32rem; }
.qbp-aireview .sev.high { background: var(--danger); }
.qbp-aireview .sev.medium { background: var(--warning); }
.qbp-aireview .sev.tip { background: var(--brand-primary); }
.qbp-aireview-clean { font-size: .86rem; font-weight: 600; color: var(--success); }
.qbp-aireview-note { font-size: .74rem; color: var(--text-muted); margin-top: .5rem; }

/* Step 6 share card (proposal link) */
.qbp-sharecard {
    border: 1px solid var(--brand-primary-mid); background: var(--brand-primary-light);
    border-radius: var(--radius); padding: .9rem 1.1rem; margin-bottom: 1rem;
}
.qbp-sharecard-head { font-weight: 700; font-size: .88rem; margin-bottom: .55rem; color: var(--brand-primary-dark); }
.qbp-sharecard-row { display: flex; gap: .55rem; align-items: center; flex-wrap: wrap; }
.qbp-sharecard-row input { flex: 1; min-width: 260px; font-size: .8rem; background: var(--card); }
.qbp-sharecard-hint { font-size: .74rem; color: var(--text-muted); margin-top: .5rem; }
.qbp-sharecard-mailrow { display: flex; gap: .55rem; align-items: center; flex-wrap: wrap; margin-top: .7rem; padding-top: .7rem; border-top: 1px dashed var(--brand-primary-mid); }
.qbp-sharecard-mailrow input[type="email"] { flex: 0 1 240px; font-size: .8rem; background: var(--card); }
.qbp-sharecard-mailrow input:not([type="email"]) { flex: 1; min-width: 220px; font-size: .8rem; background: var(--card); }

/* Hosted proposal page (public, /proposal/{token}) */
.proposal-shell { max-width: 960px; margin: 0 auto; padding: 1.5rem 1.25rem 4rem; }
.proposal-topbar {
    display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap;
    padding: .9rem 0 1.1rem; border-bottom: 1px solid var(--border-color); margin-bottom: 1.25rem;
}
.proposal-brand { font-size: 1.35rem; font-weight: 800; color: var(--brand-primary); }
.proposal-sub { font-size: .85rem; color: var(--text-muted); margin-top: .15rem; }
.proposal-banner {
    border-radius: var(--radius); padding: .8rem 1.1rem; font-size: .9rem; font-weight: 600; margin-bottom: 1.25rem;
}
.proposal-banner.accepted { background: #e8f6ee; border: 1px solid #9ed9b5; color: #1e6b40; }
.proposal-banner.stale { background: var(--surface); border: 1px solid var(--border-color); color: var(--text-muted); }
.proposal-body { background: var(--card); }
.proposal-accept {
    margin-top: 1.75rem; border: 1px solid var(--brand-primary-mid); background: var(--brand-primary-light);
    border-radius: var(--radius); padding: 1.25rem 1.5rem;
}
.proposal-accept h3 { margin: 0 0 .3rem; }
.proposal-accept p { font-size: .88rem; color: var(--text-muted); margin-bottom: .9rem; }
.proposal-accept-row { display: flex; gap: .7rem; align-items: flex-start; flex-wrap: wrap; }
.proposal-fineprint { font-size: .72rem !important; margin-top: .7rem; }
.proposal-missing { text-align: center; padding: 5rem 1rem; color: var(--text-muted); }
@media print {
    .proposal-no-print { display: none !important; }
    .proposal-shell { max-width: none; padding: 0; }
}

.qbp-fillchip {
    display: inline-flex; align-items: center; gap: .5rem; margin-top: .5rem;
    background: var(--brand-primary-light); border: 1px solid var(--brand-primary-mid);
    color: var(--brand-primary-dark); border-radius: 999px; padding: .25rem .75rem; font-size: .76rem;
}
.qbp-fillchip button { border: none; background: none; color: var(--brand-primary); font-weight: 700; cursor: pointer; font-size: .76rem; padding: 0; }

/* Payments rate grid.
   `table-layout: fixed` is deliberate: the card is a `container-type: inline-size`
   size container, so it will NOT grow to fit an over-wide table. With auto layout
   the nowrap label column hogged the fixed-width table and the numeric inputs (or
   the whole grid) spilled past the card border while resizing. Fixed layout pins
   the two numeric columns to a share of the card and lets the label column wrap
   into whatever space is left, so the grid always fits the card at any width.
   The numeric columns are a PERCENTAGE (not a fixed width) so the inputs grow to
   the comfortable 108px when the card is roomy and shrink only as far as needed
   (staying readable) when the sidebars squeeze the card. */
.qbp-rate-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.qbp-rate-grid th { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); font-weight: 700; text-align: right; padding: .35rem .5rem; }
.qbp-rate-grid th:first-child { text-align: left; }
.qbp-rate-grid th:not(:first-child), .qbp-rate-grid td.num { width: 33%; }
.qbp-rate-grid td { padding: .25rem .5rem; border-top: 1px solid var(--surface); font-size: .85rem; }
.qbp-rate-grid td:first-child { font-weight: 600; white-space: normal; }
.qbp-rate-grid td.num { text-align: right; }
/* Input fills its (fixed-width) numeric cell, capped at the nice 108px. */
.qbp-rate-grid .form-control-custom { width: 100%; max-width: 108px; min-width: 0; display: inline-block; }
/* The card's width depends on how hard the two sidebars squeeze the wizard
   column, not on the viewport — so the compact treatment is a CONTAINER query
   on the card itself: when it gets narrow, tighten padding and type so more
   room goes to the (fixed-width) numeric columns. */
.qbp-rate-card { container-type: inline-size; }
@container (max-width: 430px) {
    .qbp-rate-grid td { padding: .25rem .3rem; }
    .qbp-rate-grid td:first-child { font-size: .78rem; }
    .qbp-rate-grid .form-control-custom { font-size: .8rem; }
}
.qbp-rate-card { border: 1.5px solid var(--border-color); border-radius: var(--radius-lg); padding: 1rem 1.1rem; transition: var(--transition); }
.qbp-rate-card.on { border-color: var(--brand-primary); box-shadow: 0 0 0 3px var(--brand-primary-light); }
.qbp-rate-card.off { opacity: .55; }
.qbp-rate-card h4 { margin: 0 0 .5rem; font-size: .9rem; display: flex; align-items: center; gap: .5rem; }

/* Hint dot with CSS-only popover */
.qbp-hintdot { position: relative; display: inline-grid; place-items: center; width: 15px; height: 15px; line-height: 1; border-radius: 50%; background: var(--surface); border: 1px solid var(--border-color); color: var(--text-muted); font-size: .62rem; font-weight: 700; cursor: help; vertical-align: middle; margin-left: .3rem; }

/* Glossary tooltip (JargonTip) — same visual language as the hint dot, but
   glossary-driven and pinnable: hover previews, click pins (interactive link,
   works on touch). */
.qbp-jargon { position: relative; display: inline-block; vertical-align: middle; margin-left: .3rem; line-height: 1; }
.qbp-jargon-dot {
    display: inline-grid; place-items: center; width: 15px; height: 15px; padding: 0;
    border-radius: 50%; background: var(--surface); border: 1px solid var(--border-color);
    color: var(--text-muted); font-size: .62rem; font-weight: 700; cursor: help; line-height: 1;
}
.qbp-jargon.open .qbp-jargon-dot { background: var(--brand-primary); border-color: var(--brand-primary); color: #fff; }
.qbp-jargon-pop {
    position: absolute; bottom: calc(100% + 7px); left: 50%; transform: translate(-50%, 0);
    width: 260px; max-width: min(260px, calc(100vw - 2rem));
    background: var(--text-dark); color: #f3f4f6; font-size: .72rem; font-weight: 400; line-height: 1.5;
    border-radius: 6px; padding: .6rem .75rem; pointer-events: none;
    z-index: 60; text-align: left;
    /* display:none (not opacity:0) while closed — an invisible absolutely
       positioned box still widens the page's scrollable area on phones. */
    display: none; flex-direction: column; gap: .25rem; text-transform: none; letter-spacing: normal;
    white-space: normal;
}
.qbp-jargon-pop strong { font-size: .74rem; color: #fff; }
.qbp-jargon-pop a { color: #8ecdf3; font-weight: 600; font-size: .7rem; text-decoration: none; }
.qbp-jargon-pop a:hover { text-decoration: underline; }
.qbp-jargon:hover .qbp-jargon-pop { display: flex; }
.qbp-jargon.open .qbp-jargon-pop { display: flex; pointer-events: auto; }
.qbp-jargon-overlay { position: fixed; inset: 0; z-index: 55; }
@media (max-width: 640px) {
    /* Pinned on a phone: a fixed panel instead of an anchored bubble. */
    .qbp-jargon.open .qbp-jargon-pop {
        position: fixed; left: 1rem; right: 1rem; bottom: auto; top: 35%;
        width: auto; max-width: none; transform: none; font-size: .82rem;
    }
}
.qbp-hintdot .pop {
    position: absolute; bottom: calc(100% + 7px); left: 50%; transform: translate(-50%, 0); width: 230px;
    background: var(--text-dark); color: #f3f4f6; font-size: .72rem; font-weight: 400; line-height: 1.45;
    border-radius: 6px; padding: .55rem .7rem; display: none; pointer-events: none; z-index: 40; text-align: left;
    /* Hosts like the rate grid's first column set white-space: nowrap — the
       bubble must wrap regardless, or its text clips at the 230px edge. */
    white-space: normal;
}
.qbp-hintdot:hover .pop { display: block; }

/* ── Products step: rail + area panel ── */
.qbp-products { display: grid; grid-template-columns: 236px minmax(0, 1fr); gap: 1rem; align-items: start; }
.qbp-rail { position: sticky; top: calc(64px + 0.5rem); }
.qbp-rail-card { background: var(--card); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: .85rem; box-shadow: var(--shadow-sm); }
.qbp-rail-meter { text-align: center; padding: .25rem 0 .7rem; border-bottom: 1px solid var(--surface); margin-bottom: .55rem; }
.qbp-rail-meter strong { font-size: 1.7rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.qbp-rail-meter .lbl { display: block; font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 700; }
.qbp-rail-meter .progress-track { margin-top: .5rem; }
.qbp-rail-meter .progress-fill { background: var(--success); }
.qbp-rail-item {
    display: flex; align-items: center; gap: .5rem; width: 100%; border: none; background: none;
    padding: .38rem .45rem; border-radius: 6px; cursor: pointer; font-size: .82rem; color: var(--text-dark); text-align: left;
}
.qbp-rail-item:hover { background: var(--surface); }
.qbp-rail-item.focused { background: var(--brand-primary-light); box-shadow: inset 2px 0 0 var(--brand-primary); }
.qbp-rail-item .nm { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
.qbp-rail-item .meta { font-size: .7rem; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.qbp-rail-item .meta.ok { color: var(--success); font-weight: 600; }

/* Tri-state glyphs */
.qbp-st { width: 17px; height: 17px; flex: none; border-radius: 50%; display: grid; place-items: center; }
.qbp-st svg { width: 11px; height: 11px; }
.qbp-st.ok { background: #d1fae5; color: var(--success); }
.qbp-st.skip { background: #f3f4f6; color: #6b7280; }
.qbp-st.auto { background: #f3f4f6; color: #9ca3af; outline: 1px dashed #d1d5db; }
.qbp-st.warn { background: #fef3c7; color: var(--warning); position: relative; }
.qbp-st.warn::after { content: ''; position: absolute; inset: -3px; border-radius: 50%; border: 1.5px solid #fcd34d; animation: qb4pulse 2s ease-out infinite; }
@keyframes qb4pulse { 0% { opacity: .9; transform: scale(.8); } 70% { opacity: 0; transform: scale(1.15); } 100% { opacity: 0; } }

/* Area panel */
.qbp-area { background: var(--card); border: 1px solid var(--border-color); border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-sm); border-left-width: 3px; transition: border-color .2s ease; }
.qbp-area.is-unresolved { border-left-color: var(--warning); }
.qbp-area.is-confirmed { border-left-color: var(--success); }
.qbp-area.is-skipped, .qbp-area.is-auto { border-left-color: #d1d5db; }
.qbp-area-head { display: flex; align-items: center; gap: .65rem; padding: .8rem 1rem; background: var(--surface); border-bottom: 1px solid var(--border-color); }
.qbp-area-head .ttl { font-weight: 700; font-size: .98rem; }
.qbp-area-head .hint { font-size: .74rem; color: var(--text-muted); }
.qbp-area-head .spacer { flex: 1; }
.qbp-area-sub { font-size: .8rem; font-weight: 600; color: var(--brand-primary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.qbp-pill { font-size: .68rem; font-weight: 700; padding: .18rem .6rem; border-radius: 999px; white-space: nowrap; animation: qb4pop .15s ease-out; }
.qbp-pill.ok { background: #d1fae5; color: #065f46; }
.qbp-pill.skip { background: #f3f4f6; color: #4b5563; }
.qbp-pill.warn { background: #fef3c7; color: #92400e; }
@keyframes qb4pop { from { transform: scale(.9); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* Subgroup headers — sticky while scanning long areas */
.qbp-subhead {
    position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: .6rem;
    padding: .42rem 1rem .3rem; background: var(--card); font-size: .67rem; font-weight: 700;
    letter-spacing: .09em; text-transform: uppercase; color: var(--text-muted);
}
.qbp-subhead::after { content: ''; flex: 1; height: 1px; background: var(--border-color); }

/* Product rows: the name owns the full row width (unit price at the right
   edge); a labeled controls strip appears on a second row only when the
   product is on the quote — so unselected rows stay one compact line and
   descriptions never fight the inputs for space. */
.qbp-row {
    display: grid; grid-template-columns: 46px minmax(0,1fr) auto;
    grid-template-areas: "toggle main unit" "toggle ctrl ctrl";
    align-items: center; column-gap: .4rem; row-gap: .35rem;
    padding: .42rem 1rem .42rem .6rem; min-height: 38px;
    border-top: 1px solid var(--surface); transition: background .12s ease;
}
.qbp-row:hover { background: var(--surface); }
.qbp-row.on { background: var(--brand-primary-light); }
.qbp-row.on:hover { background: var(--brand-primary-mid); }
.qbp-row .qbp-toggle { grid-area: toggle; }
.qbp-row .main { grid-area: main; min-width: 0; }
.qbp-row .nm { font-size: .855rem; font-weight: 500; line-height: 1.25; }
.qbp-row .note { font-size: .7rem; color: var(--text-muted); line-height: 1.25; }
.qbp-row .unit { grid-area: unit; font-size: .8rem; color: var(--text-muted); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.qbp-row .ctrl { grid-area: ctrl; display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .85rem; }
.qbp-row .cfield { display: inline-flex; align-items: center; gap: .35rem; }
.qbp-row .clbl { font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); white-space: nowrap; }
.qbp-row .tot { font-size: .855rem; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; margin-left: auto; }
.qbp-row .tot .mo { font-size: .68rem; color: var(--text-muted); font-weight: 600; }
.qbp-row .form-control-custom { padding: .22rem .45rem; font-size: .8rem; }
.qbp-row .cfield .form-control-custom { width: 78px; }
.qbp-row-override { width: 118px; border-bottom: 2px solid var(--warning) !important; }
.qbp-row-override.set { border-bottom-color: var(--border-color) !important; }

/* Toggle switch (V4 size) */
.qbp-toggle { position: relative; display: inline-block; width: 36px; height: 21px; margin-left: .35rem; }
.qbp-toggle input { opacity: 0; width: 0; height: 0; }
.qbp-toggle .sl { position: absolute; inset: 0; background: #cbd5e1; border-radius: 999px; transition: .15s; cursor: pointer; }
.qbp-toggle .sl::before { content: ''; position: absolute; height: 15px; width: 15px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: .15s; box-shadow: 0 1px 2px rgba(0,0,0,.2); }
.qbp-toggle input:checked + .sl { background: var(--brand-primary); }
.qbp-toggle input:checked + .sl::before { transform: translateX(15px); }
.qbp-toggle input:focus-visible + .sl { outline: 2px solid var(--brand-accent); outline-offset: 2px; }

/* Qty stepper (shared QtyInput component) */
.qty-input { display: inline-flex; align-items: stretch; border: 1px solid var(--border-color); border-radius: 6px; overflow: hidden; background: var(--card); }
.qty-input button { border: none; background: var(--surface); width: 24px; cursor: pointer; font-size: .95rem; line-height: 1; color: var(--brand-primary); }
.qty-input button:hover { background: var(--brand-primary-light); }
.qty-input input { width: 44px; border: none; text-align: center; font-size: .82rem; font-weight: 600; -moz-appearance: textfield; appearance: textfield; font-variant-numeric: tabular-nums; }
.qty-input input::-webkit-outer-spin-button, .qty-input input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-input input:focus { outline: none; background: var(--brand-primary-light); }

/* Area footer actions */
.qbp-area-actions { display: flex; align-items: center; justify-content: flex-end; gap: .7rem; padding: .65rem 1rem; border-top: 1px solid var(--border-color); background: var(--surface); }
.qbp-area-actions .helper { font-size: .74rem; color: var(--text-muted); margin-right: auto; }
.qbp-btn-sm { padding: .38rem .9rem; font-size: .8rem; border-radius: var(--radius); font-weight: 600; cursor: pointer; transition: var(--transition); }
.qbp-btn-confirm { background: var(--success); border: none; color: #fff; }
.qbp-btn-confirm:hover { background: #047857; }
.qbp-btn-ghost { background: transparent; border: 1px solid var(--border-color); color: var(--text-dark); }
.qbp-btn-ghost:hover { border-color: var(--text-muted); }
.qbp-btn-text { background: none; border: none; color: var(--text-muted); font-size: .76rem; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.qbp-btn-text:hover { color: var(--danger); }
.qbp-confirm-strip { display: flex; align-items: center; gap: .6rem; font-size: .8rem; color: var(--danger); }

/* Auto-skipped collapsed bar */
.qbp-autoskip { display: flex; align-items: center; gap: .65rem; border: 1px dashed var(--border-color); border-left: 3px dashed #d1d5db; border-radius: var(--radius-lg); padding: .7rem 1rem; background: var(--surface); color: var(--text-muted); font-size: .82rem; }
.qbp-autoskip .ttl { font-weight: 700; color: var(--text-dark); }
.qbp-autoskip button { margin-left: auto; }

/* Soft nudges */
.qbp-nudges { display: flex; flex-direction: column; gap: .45rem; margin: 1rem 0 0; }
.qbp-nudge { display: flex; align-items: center; gap: .6rem; background: #fffbeb; border: 1px solid #fde68a; color: #92400e; border-radius: var(--radius); padding: .45rem .75rem; font-size: .78rem; }
.qbp-nudge a { color: #92400e; font-weight: 700; cursor: pointer; white-space: nowrap; }
/* "bundled" marker on a product row the program covers at no monthly charge. */
.qbp-bundlepill { font-size: .6rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; background: var(--brand-primary-light); color: var(--brand-primary-dark); border-radius: 999px; padding: .12rem .45rem; margin-left: .35rem; vertical-align: middle; }

.qbp-nudge .act { border: 1px solid #d97706; background: #f59e0b; color: #422006; font-weight: 700; font-size: .72rem; border-radius: 999px; padding: .18rem .6rem; cursor: pointer; white-space: nowrap; transition: var(--transition); }
.qbp-nudge .act:hover { background: #d97706; color: #fff; }
.qbp-nudge .x { margin-left: auto; border: none; background: none; color: #b45309; cursor: pointer; font-size: .9rem; line-height: 1; padding: 0 .15rem; }

/* ── Support step ── */
.qbp-tally { display: flex; flex-direction: column; gap: .35rem; }
.qbp-tally-row { display: grid; grid-template-columns: minmax(0,1fr) 110px 110px 64px; align-items: center; gap: .6rem; padding: .35rem .5rem; border-radius: 6px; }
.qbp-tally-row:hover { background: var(--surface); }
.qbp-tally-row .lbl { font-size: .86rem; font-weight: 500; }
.qbp-tally-row .w { font-size: .74rem; color: var(--text-muted); text-align: right; font-variant-numeric: tabular-nums; }
.qbp-autopill { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; background: var(--brand-primary-light); color: var(--brand-primary-dark); border-radius: 999px; padding: .12rem .5rem; margin-left: .4rem; }
.qbp-autopill.manual { background: #fef3c7; color: #92400e; }
.qbp-formula { font-family: 'Cascadia Code', Consolas, monospace; font-size: .8rem; background: var(--surface); border: 1px solid var(--border-color); border-radius: var(--radius); padding: .6rem .8rem; margin: .9rem 0; font-variant-numeric: tabular-nums; }
.qbp-formula strong { color: var(--brand-primary); }
.qbp-tiers { display: flex; flex-direction: column; gap: .5rem; }
.qbp-tier { display: flex; align-items: center; gap: .8rem; border: 1.5px solid var(--border-color); border-radius: var(--radius-lg); padding: .7rem .95rem; cursor: pointer; background: var(--card); text-align: left; width: 100%; transition: var(--transition); }
.qbp-tier:hover { border-color: var(--brand-primary); }
.qbp-tier.on { border-color: var(--brand-primary); background: var(--brand-primary-light); box-shadow: 0 0 0 3px var(--brand-primary-light); }
.qbp-tier .nm { font-size: .88rem; font-weight: 600; flex: 1; }
.qbp-tier .nm small { display: block; font-weight: 400; font-size: .73rem; color: var(--text-muted); }
.qbp-tier .pr { font-size: .92rem; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.qbp-tier .pr .mo { font-size: .68rem; color: var(--text-muted); }
.qbp-reco { font-size: .64rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; background: var(--success); color: #fff; padding: .14rem .5rem; border-radius: 999px; }

/* ── Funding / radio cards (step 5) ── */
.qbp-choice-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); gap: .7rem; }
.qbp-choice { border: 1.5px solid var(--border-color); border-radius: var(--radius-lg); padding: .85rem 1rem; cursor: pointer; background: var(--card); text-align: left; transition: var(--transition); }
.qbp-choice:hover { border-color: var(--brand-primary); }
.qbp-choice.on { border-color: var(--brand-primary); background: var(--brand-primary-light); box-shadow: 0 0 0 3px var(--brand-primary-light); }
.qbp-choice .t { font-weight: 700; font-size: .88rem; }
.qbp-choice .d { font-size: .74rem; color: var(--text-muted); margin-top: .15rem; line-height: 1.35; }
.qbp-choice .v { margin-top: .5rem; font-size: .78rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── Review step ── */
.qbp-gate { border: 1px solid #fde68a; background: #fffbeb; border-radius: var(--radius-lg); padding: 1rem 1.2rem; margin-bottom: 1rem; }
.qbp-gate h4 { margin: 0 0 .5rem; font-size: .92rem; color: #92400e; }
.qbp-gate ul { margin: 0; padding-left: 1.1rem; font-size: .82rem; color: #92400e; }
.qbp-gate a { font-weight: 700; cursor: pointer; color: #92400e; }
.qbp-draft-wrap { position: relative; }
.qbp-draft-wrap.draft > .qbp-proposal { opacity: .55; pointer-events: none; }
.qbp-ribbon {
    position: absolute; top: 22px; right: -34px; z-index: 6; transform: rotate(38deg);
    background: var(--warning); color: #fff; font-size: .7rem; font-weight: 800; letter-spacing: .25em;
    padding: .3rem 2.6rem; box-shadow: var(--shadow-md);
}
.qbp-proposal { background: var(--card); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: 2rem 2.2rem; box-shadow: var(--shadow-sm); overflow: hidden; position: relative; }
/* Dealer letterhead: the selling dealer's identity leads the document; the
   brand rule under it is the proposal's strongest line. */
.qbp-prop-letterhead { display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: .6rem 1.5rem; border-bottom: 3px solid var(--brand-primary); padding-bottom: .9rem; margin-bottom: 1.3rem; }
.qbp-prop-letterhead .name { font-size: 1.3rem; font-weight: 800; color: var(--brand-primary); line-height: 1.15; }
.qbp-prop-letterhead .tag { font-size: .64rem; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--text-muted); margin-top: .25rem; }
.qbp-prop-letterhead .contact { font-size: .76rem; line-height: 1.55; text-align: right; color: var(--text-dark); }
.qbp-prop-letterhead .contact .rep { color: var(--text-muted); margin-top: .15rem; }
.qbp-prop-head { display: flex; justify-content: space-between; gap: 1.5rem; border-bottom: 1px solid var(--border-color); padding-bottom: 1.1rem; margin-bottom: 1.4rem; }
.qbp-prop-head .eyebrow { font-size: .68rem; font-weight: 800; letter-spacing: .22em; text-transform: uppercase; color: var(--brand-primary); }
.qbp-prop-head h2 { margin: .15rem 0 .4rem; font-size: 1.35rem; }
.qbp-prop-head .blk { font-size: .8rem; line-height: 1.5; color: var(--text-dark); }
.qbp-prop-head .blk .lbl { color: var(--text-muted); }
.qbp-prop-meta { font-size: .8rem; line-height: 1.6; text-align: right; }
.qbp-prop-meta .lbl { color: var(--text-muted); margin-right: .5rem; }
.qbp-prop-area { margin-bottom: 1.1rem; }
.qbp-prop-area h5 { font-size: .72rem; text-transform: uppercase; letter-spacing: .09em; color: var(--text-muted); margin: 0 0 .3rem; display: flex; align-items: center; gap: .6rem; }
.qbp-prop-area h5::after { content: ''; flex: 1; height: 1px; background: var(--border-color); }
.qbp-prop-table { width: 100%; border-collapse: collapse; font-size: .83rem; }
.qbp-prop-table td { padding: .28rem .4rem; border-bottom: 1px solid var(--surface); font-variant-numeric: tabular-nums; }
.qbp-prop-table td.r { text-align: right; white-space: nowrap; }
.qbp-prop-table td.muted { color: var(--text-muted); }
.qbp-prop-table .subtot td { border-bottom: none; font-weight: 700; padding-top: .4rem; }
.qbp-totals { margin-left: auto; max-width: 380px; margin-top: 1rem; }
.qbp-totals .row2 { display: flex; justify-content: space-between; padding: .3rem 0; font-size: .86rem; font-variant-numeric: tabular-nums; }
.qbp-totals .row2.grand { border-top: 2px solid var(--text-dark); margin-top: .3rem; padding-top: .55rem; font-weight: 800; font-size: .98rem; }
.qbp-totals .row2 .neg { color: var(--success); }
.qbp-taxnote { font-size: .72rem; color: var(--text-muted); text-align: right; margin-top: .35rem; }
.qbp-dealer-card { border: 1.5px dashed var(--warning); background: #fffbeb; border-radius: var(--radius-lg); padding: 1rem 1.2rem; margin-top: 1.25rem; }
.qbp-dealer-card .eyebrow { font-size: .66rem; font-weight: 800; letter-spacing: .18em; text-transform: uppercase; color: var(--warning); margin-bottom: .5rem; }
.qbp-sign { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; margin-top: 2.2rem; }
.qbp-sign .line { border-top: 1px solid var(--text-dark); padding-top: .3rem; font-size: .72rem; color: var(--text-muted); }

/* ── Summary sidebar V4 ── */
.qbp-summary-hero { padding: .35rem 0 .8rem; border-bottom: 1px solid var(--border-color); margin-bottom: .65rem; }
.qbp-summary-hero .lbl { font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; color: var(--text-muted); }
.qbp-summary-hero .big { font-size: 1.65rem; font-weight: 800; color: var(--brand-primary); font-variant-numeric: tabular-nums; line-height: 1.2; }
.qbp-summary-hero .big .mo { font-size: .85rem; color: var(--text-muted); font-weight: 600; }
.qbp-summary-hero .second { font-size: .98rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.qbp-summary-hero .breakdown { font-size: .72rem; color: var(--text-muted); }
/* "Your earnings" hero card — the dealer's paycheck, always visible, flashes
   on change like the rest of the summary. Never rendered on customer output. */
.qbp-earnings {
    background: linear-gradient(135deg, var(--brand-primary-dark), var(--brand-primary));
    color: #fff; border-radius: var(--radius); padding: .75rem .9rem; margin-bottom: .75rem;
}
.qbp-earnings.neg { background: linear-gradient(135deg, #7f1d1d, #b91c1c); }
.qbp-earnings .eyebrow { font-size: .64rem; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; opacity: .85; }
.qbp-earnings .big { font-size: 1.55rem; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.25; }
.qbp-earnings .big .mo { font-size: .82rem; font-weight: 600; opacity: .8; }
.qbp-earnings .split { font-size: .74rem; opacity: .9; font-variant-numeric: tabular-nums; }
.qbp-earnings .term { font-size: .82rem; font-weight: 700; margin-top: .35rem; font-variant-numeric: tabular-nums; }
.qbp-earnings .term .dim { font-weight: 400; opacity: .75; font-size: .72rem; }
.qbp-earnings .note { font-size: .72rem; opacity: .85; margin-top: .3rem; font-variant-numeric: tabular-nums; }
.qbp-earnings .warn { font-size: .76rem; font-weight: 700; margin-top: .4rem; }
.qbp-earnings .qbp-val { animation-name: qb4flash-onbrand; }
@keyframes qb4flash-onbrand { from { background: rgba(255, 255, 255, .35); } to { background: transparent; } }

.qbp-checklist { display: flex; flex-direction: column; gap: .15rem; margin-top: .5rem; }
.qbp-check-row { display: flex; align-items: center; gap: .5rem; border: none; background: none; width: 100%; text-align: left; padding: .3rem .35rem; border-radius: 6px; font-size: .8rem; cursor: pointer; color: var(--text-dark); }
.qbp-check-row:hover { background: var(--surface); }
.qbp-check-row.cur { background: var(--brand-primary-light); font-weight: 600; }
.qbp-dots { display: flex; gap: 4px; margin: .15rem 0 .3rem 2.1rem; flex-wrap: wrap; }
.qbp-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border-color); }
.qbp-dot.ok { background: var(--success); }
.qbp-dot.skip { background: #9ca3af; }
.qbp-dot.warn { background: var(--warning); box-shadow: 0 0 0 2px #fef3c7; }
.qbp-val { display: inline-block; border-radius: 3px; animation: qb4flash .7s ease-out; padding: 0 2px; margin: 0 -2px; }
@keyframes qb4flash { from { background: var(--brand-primary-mid); } to { background: transparent; } }
.qbp-summary details { margin-top: .8rem; }
.qbp-summary summary { font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em; color: var(--text-muted); cursor: pointer; }

/* Deal identity line — product · program, then the customer name; mirrors the
   middle of the workbook's blue overview band. */
.qbp-summary-id { padding-bottom: .55rem; }
.qbp-summary-id .line { font-size: .64rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em; color: var(--text-muted); }
.qbp-summary-id .name { font-size: 1rem; font-weight: 800; color: var(--text-dark); line-height: 1.25; }

/* Payments snapshot rows + SaaS-fee-by-area rows (the four quoted rate cells,
   and the per-area recurring fee breakdown). */
.qbp-paysnap-row { display: flex; justify-content: space-between; gap: 1rem; padding: .18rem 0; font-size: .8rem; color: var(--text-dark); font-variant-numeric: tabular-nums; }
.qbp-paysnap-row span:first-child { color: var(--text-muted); }

/* Full deal-economics breakdown on the Step 6 internal card (workbook blue-band
   parity): Investment / Monthly Recurring / SaaS Fees side by side, wrapping to
   one column on narrow screens. */
.qbp-econ-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); gap: 1rem 1.75rem; margin-top: .5rem; }
.qbp-econ-grid .section-label { margin-bottom: .15rem; }
.qbp-econ-foot { font-size: .68rem; color: var(--text-muted); margin-top: .45rem; }

/* Stat readout (step 2) */
.qbp-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr)); gap: .7rem; margin-top: 1.1rem; }
.qbp-stat { background: var(--surface); border: 1px solid var(--border-color); border-radius: var(--radius); padding: .7rem .85rem; }
.qbp-stat .v { font-size: 1.15rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.qbp-stat .v.pos { color: var(--success); }
.qbp-stat .l { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 700; }

/* Responsive. The products rail collapses at 1200px (not 900) because the
   summary sidebar sits beside the wizard down to 992px — in the 992–1200 band
   the main column is far narrower than the viewport suggests, and the rail
   would crush the area panel. Stats and choice cards are container-driven
   (auto-fit) and need no viewport override. */
@media (max-width: 1200px) {
    /* minmax(0,1fr), not 1fr — the rail's intrinsic width must not win. */
    .qbp-products { grid-template-columns: minmax(0, 1fr); }
    .qbp-rail { position: static; }
}
@media (max-width: 480px) {
    /* Phone: tighten the support tally's fixed columns so the row fits. */
    .qbp-tally-row { grid-template-columns: minmax(0,1fr) 84px 92px 56px; gap: .4rem; }
}

/* ── Quote Builder V4 · AI assistant (slide-out drawer + DxAIChat) ── */
.qbp-page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }

.qbp-header-actions { display: flex; gap: .6rem; align-items: center; }

/* Inline-editable Sail Plan title: the h2 text is a button that swaps to an
   input on click. Both inherit the heading's font so nothing shifts. */
.qbp-title-btn {
    background: none; border: none; padding: 0; margin: 0;
    font: inherit; color: inherit; cursor: pointer; text-align: left;
}
.qbp-title-btn:hover { color: var(--brand-primary); }
.qbp-title-pencil {
    font-size: .55em; color: var(--text-muted); margin-left: .35rem;
    vertical-align: middle; transition: color .15s ease;
}
.qbp-title-btn:hover .qbp-title-pencil { color: var(--brand-primary); }
.qbp-title-input {
    font: inherit; color: inherit; background: transparent;
    border: none; border-bottom: 2px solid var(--brand-primary);
    outline: none; padding: 0; min-width: 260px; max-width: 100%;
}
.qbp-title-input::placeholder { color: var(--text-muted); font-weight: 400; }
.qbp-title-warn { color: var(--warning); font-size: .82rem; font-weight: 600; margin: .15rem 0 0; }
/* When the AI Assistant drawer is open it overlays the right side of the page,
   hiding these buttons. On wide screens slide the whole group left of the 420px
   drawer and lift it above the drawer's dimming backdrop so it stays visible and
   clickable while chatting. Below this width the drawer is near/full width, so a
   shift would just push the buttons off-screen — leave them for the rep to reveal
   by closing the drawer. */
@media (min-width: 1200px) {
    .qbp-header-actions { transition: transform .28s cubic-bezier(.4,0,.2,1); }
    .qbp-header-actions.ai-open { transform: translateX(-436px); position: relative; z-index: 1090; }
}

.qbp-ai-trigger { display: inline-flex; align-items: center; gap: .45rem; white-space: nowrap; box-shadow: 0 2px 10px rgba(16,93,137,.28); }
.qbp-ai-spark { font-size: 1rem; line-height: 1; }

.qbp-ai-backdrop {
    position: fixed; inset: 0; background: rgba(15,23,42,.35);
    opacity: 0; visibility: hidden; transition: opacity .25s ease, visibility .25s ease;
    z-index: 1070;
}
.qbp-ai-backdrop.open { opacity: 1; visibility: visible; }

.qbp-ai-drawer {
    position: fixed; top: 0; right: 0; height: 100vh; width: 420px; max-width: 92vw;
    background: var(--card); border-left: 1px solid var(--border-color);
    box-shadow: -8px 0 32px rgba(15,23,42,.18);
    display: flex; flex-direction: column;
    transform: translateX(100%); transition: transform .28s cubic-bezier(.4,0,.2,1);
    z-index: 1080;
}
.qbp-ai-drawer.open { transform: translateX(0); }

.qbp-ai-head {
    display: flex; align-items: center; justify-content: space-between; gap: .75rem;
    padding: 1rem 1.15rem; flex: 0 0 auto;
    background: linear-gradient(135deg, var(--brand-primary), var(--brand-primary-dark));
    color: var(--white);
}
.qbp-ai-title { display: flex; align-items: center; gap: .6rem; }
.qbp-ai-title .qbp-ai-spark { font-size: 1.5rem; }
.qbp-ai-title strong { display: block; font-size: .98rem; }
.qbp-ai-sub { font-size: .76rem; opacity: .85; }
.qbp-ai-close {
    flex: 0 0 auto; background: rgba(255,255,255,.18); border: none; color: var(--white);
    width: 30px; height: 30px; border-radius: 6px; cursor: pointer; font-size: .82rem;
    line-height: 1; transition: background .15s;
}
.qbp-ai-close:hover { background: rgba(255,255,255,.32); }

.qbp-ai-head-actions { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }
.qbp-ai-restart {
    display: inline-flex; align-items: center; gap: .3rem; cursor: pointer;
    background: rgba(255,255,255,.18); border: none; color: var(--white);
    height: 30px; padding: 0 .65rem; border-radius: 6px; font-size: .74rem;
    font-weight: 600; line-height: 1; white-space: nowrap; transition: background .15s;
}
.qbp-ai-restart:hover { background: rgba(255,255,255,.32); }
.qbp-ai-restart-glyph { font-size: 1rem; }

.qbp-ai-preview-note {
    display: flex; align-items: center; gap: .45rem; flex: 0 0 auto;
    padding: .55rem 1.15rem; font-size: .75rem; color: var(--warning);
    background: #fffbeb; border-bottom: 1px solid var(--border-color);
}

/* Voice input bar */
.qbp-ai-voicebar {
    display: flex; align-items: center; gap: .6rem; flex: 0 0 auto; flex-wrap: wrap;
    padding: .5rem 1.15rem; border-bottom: 1px solid var(--border-color); background: var(--card);
}
.qbp-ai-mic {
    display: inline-flex; align-items: center; gap: .4rem; cursor: pointer;
    border: 1px solid var(--brand-primary); background: var(--brand-primary-light);
    color: var(--brand-primary); font: inherit; font-size: .8rem; font-weight: 600;
    padding: .35rem .7rem; border-radius: 999px; transition: background .15s, border-color .15s;
}
.qbp-ai-mic:hover:not(:disabled) { background: var(--brand-primary-mid); }
.qbp-ai-mic:disabled { opacity: .45; cursor: not-allowed; }
.qbp-ai-mic-glyph { font-size: .95rem; line-height: 1; }
.qbp-ai-mic.on {
    background: #fdecec; border-color: var(--danger); color: var(--danger);
    animation: qbp-ai-pulse 1.2s ease-in-out infinite;
}
/* Voice-chat toggle "on" reads as active (brand), distinct from one-shot listening (red). */
.qbp-ai-vc.on {
    background: var(--brand-primary); border-color: var(--brand-primary); color: var(--white);
    animation: qbp-ai-pulse 1.6s ease-in-out infinite;
}
@keyframes qbp-ai-pulse { 0%,100% { opacity: 1; } 50% { opacity: .6; } }
.qbp-ai-spk {
    border: 1px solid var(--border-color); background: var(--card); cursor: pointer;
    font-size: .95rem; line-height: 1; padding: .32rem .55rem; border-radius: 999px;
}
.qbp-ai-spk:hover { background: var(--surface); }
.qbp-ai-voice-status { font-size: .74rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.qbp-ai-voice-err { font-size: .74rem; color: var(--danger); }

/* Voice-chat state pill: Listening / Sending / Thinking / Speaking */
.qbp-ai-vstate {
    display: inline-flex; align-items: center; gap: .35rem;
    font-size: .72rem; font-weight: 600; padding: .22rem .6rem; border-radius: 999px;
    background: var(--brand-primary-light); color: var(--brand-primary); white-space: nowrap;
}
.qbp-ai-vdot {
    width: 7px; height: 7px; border-radius: 50%; background: currentColor;
    animation: qbp-ai-vdot-pulse 1.4s ease-in-out infinite;
}
@keyframes qbp-ai-vdot-pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }
.qbp-ai-vstate.pending { background: #fff7e6; color: #a06a00; }
.qbp-ai-vstate.thinking { background: var(--brand-primary-mid); color: var(--brand-primary-darker); }
.qbp-ai-vstate.thinking .qbp-ai-vdot { animation-duration: .8s; }
.qbp-ai-vstate.speaking { background: var(--brand-primary); color: var(--white); }

/* Floating "voice chat is live" chip — the panel renders it when the hands-free
   conversation keeps running with the drawer closed (deliberate: the rep closes
   the drawer to watch the wizard fill while talking). Fixed bottom-right;
   z-index sits UNDER the drawer/backdrop (1070) so the reopened drawer covers it. */
.qbp-ai-voicechip {
    position: fixed; right: 1rem; bottom: 1rem; z-index: 1060;
    display: flex; align-items: stretch; gap: .5rem;
    background: var(--card); border: 1px solid var(--brand-primary);
    border-radius: 12px; box-shadow: 0 6px 24px rgba(15,23,42,.25);
    padding: .55rem .65rem; max-width: min(360px, calc(100vw - 2rem));
}
.qbp-ai-voicechip-main {
    display: flex; flex-direction: column; align-items: flex-start; gap: .3rem;
    border: none; background: none; padding: 0; cursor: pointer; font: inherit;
    text-align: left; min-width: 0;
}
.qbp-ai-voicechip-head { display: inline-flex; align-items: center; gap: .5rem; }
.qbp-ai-voicechip-label {
    font-size: .78rem; font-weight: 700; color: var(--brand-primary-darker); white-space: nowrap;
}
.qbp-ai-voicechip-interim {
    font-size: .72rem; font-style: italic; color: var(--text-muted);
    max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.qbp-ai-voicechip-end {
    align-self: center; flex-shrink: 0; border: 1px solid var(--border-color);
    background: var(--card); cursor: pointer; font: inherit;
    font-size: .72rem; font-weight: 600; line-height: 1;
    padding: .4rem .55rem; border-radius: 8px; color: var(--danger); white-space: nowrap;
}
.qbp-ai-voicechip-end:hover { background: #fef2f2; border-color: #fca5a5; }

/* Header trigger while the conversation runs with the drawer closed: pulsing
   white dot (qbp-ai-vdot uses currentColor) + a breathing glow. */
.qbp-ai-trigger.listening { animation: qbp-ai-trigger-glow 2s ease-in-out infinite; }
@keyframes qbp-ai-trigger-glow {
    0%, 100% { box-shadow: 0 2px 10px rgba(16,93,137,.28); }
    50%      { box-shadow: 0 2px 18px rgba(16,93,137,.65); }
}

/* Live transcript line under the voice bar */
.qbp-ai-interim {
    flex: 0 0 auto; padding: .35rem 1.15rem; font-size: .76rem; font-style: italic;
    color: var(--text-muted); background: var(--card); border-bottom: 1px solid var(--border-color);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* "Send now" (pending turn) + "Interrupt" (reply playing) actions */
.qbp-ai-sendnow, .qbp-ai-stopspk {
    border: 1px solid var(--border-color); background: var(--card); cursor: pointer;
    font: inherit; font-size: .72rem; font-weight: 600; line-height: 1;
    padding: .32rem .6rem; border-radius: 999px; white-space: nowrap;
    transition: background .15s, border-color .15s, color .15s;
}
.qbp-ai-sendnow { border-color: var(--brand-primary); color: var(--brand-primary); }
.qbp-ai-sendnow:hover { background: var(--brand-primary-light); }
.qbp-ai-stopspk { border-color: var(--danger); color: var(--danger); }
.qbp-ai-stopspk:hover { background: #fdecec; }

/* Upload-recording drop zone + transcription progress */
.qbp-ai-dropzone {
    position: relative; display: flex; align-items: center; justify-content: center; gap: .5rem;
    flex: 0 0 auto; padding: .6rem 1.15rem; font-size: .76rem; color: var(--text-muted);
    background: var(--surface); border-bottom: 1px solid var(--border-color); text-align: center;
    transition: background .12s, color .12s, box-shadow .12s;
}
.qbp-ai-dropzone .qbp-ai-mic-glyph { color: var(--brand-primary); }
.qbp-ai-dropzone strong { color: var(--text-dark); font-weight: 600; }
.qbp-ai-dropzone.drag {
    background: var(--brand-primary-light); color: var(--brand-primary);
    box-shadow: inset 0 0 0 2px var(--brand-primary);
}
.qbp-ai-dropzone.drag strong { color: var(--brand-primary); }
.qbp-ai-dropzone.busy { color: var(--brand-primary); background: var(--brand-primary-light); }
/* Transparent file input overlaying the zone — captures both clicks and drops. */
.qbp-ai-drop-input {
    position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
}
.qbp-ai-drop-input:disabled { cursor: not-allowed; }
/* While transcribing, keep the input mounted (so the file stream stays valid)
   but stop it intercepting clicks. */
.qbp-ai-dropzone.busy .qbp-ai-drop-input { pointer-events: none; }
.qbp-ai-spinner {
    width: 14px; height: 14px; flex: 0 0 auto; border-radius: 50%;
    border: 2px solid var(--brand-primary-mid); border-top-color: var(--brand-primary);
    animation: qbp-ai-spin .7s linear infinite;
}
@keyframes qbp-ai-spin { to { transform: rotate(360deg); } }

/* "Start over" confirm modal */
.qbp-confirm-backdrop {
    position: fixed; inset: 0; z-index: 1100;
    display: flex; align-items: center; justify-content: center; padding: 1.25rem;
    background: rgba(15, 30, 45, .45); backdrop-filter: blur(2px);
    animation: qbp-confirm-fade .15s ease;
}
.qbp-confirm {
    width: min(420px, 100%);
    background: var(--card); border-radius: 16px; padding: 1.75rem 1.6rem 1.4rem;
    box-shadow: 0 24px 64px rgba(15, 30, 45, .3); text-align: center;
    animation: qbp-confirm-pop .2s cubic-bezier(.2, .9, .3, 1.15);
}
.qbp-confirm-icon {
    width: 54px; height: 54px; margin: 0 auto 1rem; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.6rem; color: var(--brand-primary); background: var(--brand-primary-light);
}
.qbp-confirm h4 { margin: 0 0 .45rem; font-size: 1.2rem; font-weight: 700; color: var(--text-dark); }
.qbp-confirm p { margin: 0 0 1.4rem; font-size: .86rem; line-height: 1.5; color: var(--text-muted); }
/* Wider variant for modals with three actions (e.g. unsaved-changes leave prompt)
   so button labels don't wrap. */
.qbp-confirm-wide { width: min(520px, 100%); }
.qbp-confirm-actions { display: flex; gap: .6rem; justify-content: center; }
.qbp-confirm-actions button { white-space: nowrap; }
.qbp-confirm-actions button {
    min-width: 120px; padding: .6rem 1rem; border-radius: 9px;
    font-weight: 600; font-size: .85rem; cursor: pointer;
}
.qbp-confirm-cancel {
    border: 1px solid var(--border-color); background: var(--card); color: var(--text-dark);
    transition: background .15s;
}
.qbp-confirm-cancel:hover { background: var(--surface); }
.qbp-confirm-go {
    border: none; background: var(--danger); color: var(--white); transition: filter .15s;
}
.qbp-confirm-go:hover { filter: brightness(.93); }
@keyframes qbp-confirm-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes qbp-confirm-pop {
    from { opacity: 0; transform: translateY(10px) scale(.96); }
    to { opacity: 1; transform: none; }
}

/* "Read aloud" button under an assistant message */
.qbp-ai-speak {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; margin-top: .45rem; border-radius: 50%;
    border: 1px solid var(--border-color); background: var(--card);
    color: var(--text-muted); font-size: .82rem; line-height: 1; cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.qbp-ai-speak:hover { background: var(--surface); color: var(--brand-primary); }
.qbp-ai-speak.on {
    background: var(--brand-primary); border-color: var(--brand-primary); color: var(--white);
}

.qbp-ai-chat-wrap { flex: 1 1 auto; min-height: 0; display: flex; }
.qbp-ai-chat { flex: 1 1 auto; min-height: 0; width: 100%; height: 100%; }

/* Markdown rendered in an AI reply: keep headings close to body size so an
   <h2> the model emits doesn't tower over the message text. */
.qbp-ai-md :is(h1, h2, h3, h4, h5, h6) {
    font-weight: 600; line-height: 1.3; color: var(--text-dark); margin: .6rem 0 .3rem;
}
.qbp-ai-md h1 { font-size: 1.05rem; }
.qbp-ai-md h2 { font-size: 1rem; }
.qbp-ai-md h3 { font-size: .92rem; }
.qbp-ai-md h4, .qbp-ai-md h5, .qbp-ai-md h6 { font-size: .85rem; }
.qbp-ai-md > :first-child { margin-top: 0; }
.qbp-ai-md > :last-child { margin-bottom: 0; }

.qbp-ai-empty { text-align: center; padding: 1.5rem 1.25rem; color: var(--text-muted); }
.qbp-ai-empty-icon { font-size: 2.2rem; margin-bottom: .5rem; }
.qbp-ai-empty h5 { color: var(--text-dark); margin-bottom: .35rem; font-size: 1rem; }
.qbp-ai-empty p { font-size: .82rem; margin-bottom: 1rem; }
.qbp-ai-examples { display: flex; flex-direction: column; gap: .5rem; }
/* Clickable example prompts: real buttons that send themselves as the first message. */
.qbp-ai-chip {
    display: block; width: 100%; font-family: inherit; cursor: pointer;
    text-align: left; font-size: .78rem; color: var(--brand-primary);
    background: var(--brand-primary-light); border: 1px solid var(--brand-primary-mid);
    border-radius: var(--radius); padding: .5rem .7rem; line-height: 1.35;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.qbp-ai-chip:hover, .qbp-ai-chip:focus-visible {
    border-color: var(--brand-primary);
    box-shadow: 0 1px 4px rgba(16, 93, 137, .18);
}

@media (max-width: 640px) {
    .qbp-ai-drawer { width: 100vw; max-width: 100vw; }

    /* Builder on a phone: the stepper becomes a horizontal scroller (labels
       only), and the header's action buttons wrap instead of overflowing. */
    .qbp-stepper { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: .25rem; }
    .qbp-stepper .qbp-step { flex-shrink: 0; }
    .qbp-step-sub { display: none; }
    .qbp-page-header { flex-wrap: wrap; }
    .qbp-page-header > div:last-child { flex-wrap: wrap; }
    .avatar-dropdown, .notif-dropdown { max-width: calc(100vw - 1.5rem); }
}

/* ── Sales Funnel (/portal/sales-funnel) ──
   Also used by Support Tickets (/portal/support): the controls bar, segmented
   toggles, band rows, clickable row and detail panel are the shared vocabulary
   for a CRM-backed list page, and were simply named after the first page to need
   them. Change them with both pages in mind. */
.funnel-controls {
    display: flex;
    /* flex-direction is explicit because this element is also a .card, and
       Bootstrap's .card sets flex-direction: column — inheriting that turns the
       align-items below into *horizontal* centering and stacks the controls. */
    flex-direction: row;
    align-items: center;
    /* Roomy enough that the "whose deals" and "which deals" groups don't read as
       one control when both have an active (blue) button next to each other. */
    gap: 1.25rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.funnel-search { flex: 1; min-width: 220px; }

/* Shared SearchBox component — an input with a clear (X) button sitting inside
   its right edge. The wrapper carries the flex sizing a bare .funnel-search
   input used to have, since the wrapper is now the flex child. */
.search-box { position: relative; display: flex; }
.search-box.funnel-search { flex: 1; min-width: 220px; }
/* Room for the button so a long search term never runs underneath it. */
.search-box-input { padding-right: 2.3rem; }

.search-box-clear {
    position: absolute;
    top: 50%;
    right: .45rem;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    transition: var(--transition);
}
.search-box-clear:hover { background: var(--surface); color: var(--text-dark); }
.search-box-clear:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 1px; }

/* Segmented toggle, same shape as the builder's billable control. */
.funnel-seg { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }

.funnel-seg button {
    background: var(--card);
    border: none;
    border-right: 1px solid var(--border);
    padding: .45rem .85rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
}

.funnel-seg button:last-child { border-right: none; }
.funnel-seg button:hover:not(:disabled) { background: var(--brand-primary-light); color: var(--brand-primary); }
.funnel-seg button.active { background: var(--brand-primary); color: #fff; }
.funnel-seg button:disabled { cursor: default; opacity: .6; }

/* One stage of the funnel: label, proportional bar, figures. A button so it is
   keyboard-reachable — clicking filters the list below. */
.funnel-row {
    display: grid;
    grid-template-columns: minmax(140px, 1.1fr) minmax(0, 2fr) minmax(150px, auto);
    align-items: center;
    gap: .85rem;
    width: 100%;
    background: none;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: .4rem .5rem;
    text-align: left;
    font: inherit;
    cursor: pointer;
}

.funnel-row:hover { background: var(--surface); }
.funnel-row.active { border-color: var(--brand-primary); background: var(--brand-primary-light); }

.funnel-row-label { font-size: .87rem; font-weight: 600; }
.funnel-pct { color: var(--text-muted); font-weight: 500; margin-left: .35rem; font-size: .8rem; }

.funnel-bar { display: block; height: 12px; background: #f3f4f6; border-radius: 999px; overflow: hidden; }

.funnel-bar-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--brand-primary);
    transition: width .25s ease;
}

.funnel-bar-fill.won { background: var(--success); }
.funnel-bar-fill.lost { background: #9ca3af; }
/* Support Tickets: an escalated status band. */
.funnel-bar-fill.alert { background: var(--danger); }

.funnel-row-figs { font-size: .82rem; font-weight: 600; text-align: right; white-space: nowrap; }

/* Stage badge as an editor. The menu is in-flow rather than absolutely
   positioned: .table-wrapper's overflow-x would clip a floating dropdown (the
   same reason the Sail Plans status menu is in-flow). */
.funnel-stage-btn {
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    padding: .15rem .35rem;
    font: inherit;
    cursor: pointer;
    white-space: nowrap;
    text-align: left;
}

.funnel-stage-btn:hover { border-color: var(--border); background: var(--card); }

.funnel-stage-menu {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .25rem;
    margin-top: .4rem;
    min-width: 170px;
}

.funnel-stage-menu-head {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 700;
    color: var(--text-muted);
}

.funnel-stage-chip {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: .18rem .6rem;
    font-size: .78rem;
    font-weight: 600;
    color: var(--text-dark);
    cursor: pointer;
    text-align: left;
}

.funnel-stage-chip:hover:not(:disabled) {
    border-color: var(--brand-primary);
    background: var(--brand-primary-light);
    color: var(--brand-primary);
}

.funnel-stage-chip:disabled { opacity: .5; cursor: default; }
.funnel-stage-chip.muted { color: var(--text-muted); font-weight: 500; }

.funnel-deal-row { cursor: pointer; }
.funnel-deal-row:hover { background: var(--surface); }

.funnel-detail { background: var(--surface); }

.funnel-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
    gap: .85rem 1.5rem;
}

.funnel-detail-label {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
    font-weight: 700;
    margin-bottom: .15rem;
}

/* ── Support Tickets: merchant tickets vs Sail escalations ── */
.support-tabs { margin-bottom: 1.25rem; }

/* A documented workaround is the most actionable thing on an escalation, so it
   reads as an answer rather than as one more field. */
.escalation-workaround {
    margin-top: .85rem;
    padding: .7rem .85rem;
    background: rgba(5, 150, 105, .07);
    border-left: 3px solid var(--success);
    border-radius: 6px;
}

/* ── Customers: the CRM organization/contact directory ── */

/* One person inside an organization's detail panel — a full-width row that
   jumps to their own record on the Contacts tab. */
.dir-contact-row {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .9rem;
    align-items: baseline;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    border-top: 1px solid var(--border-color);
    padding: .5rem .15rem;
    cursor: pointer;
    font-size: .85rem;
    color: var(--text-dark);
}

.dir-contact-row:hover { background: rgba(16, 93, 137, .07); }

/* An in-page cross-reference (a contact's organization) styled as a link —
   it's a button because it navigates state, not a URL. */
.dir-link {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--brand-primary);
    text-decoration: underline;
    font-size: inherit;
    font-family: inherit;
}

@media (max-width: 700px) {
    /* Bar loses its column before the figures become unreadable. */
    .funnel-row { grid-template-columns: 1fr auto; }
    .funnel-row .funnel-bar { display: none; }
}

/* ── Product catalog editor (/portal/product-catalog) ── */
.catalog-toolbar {
    display: flex;
    gap: .75rem;
    align-items: center;
    flex-wrap: wrap;
}

/* The SearchBox wrapper is the flex child here, so it carries the sizing the
   bare search input used to have inline. */
.catalog-toolbar .search-box { flex: 1; min-width: 220px; }

.catalog-check {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    white-space: nowrap;
}

.catalog-count {
    margin-left: auto;
    font-size: .82rem;
    color: var(--text-muted);
    white-space: nowrap;
}

/* Tighter than the default data-table: 150-odd rows of six columns each, and
   the money inputs already carry their own padding. */
.catalog-table td { padding: .45rem .8rem; vertical-align: middle; }
.catalog-table th { padding: .7rem .8rem; }

.catalog-name { font-weight: 600; font-size: .88rem; line-height: 1.3; }

.catalog-meta {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    margin-top: .15rem;
    font-size: .74rem;
    color: var(--text-muted);
}

.catalog-sku { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.catalog-bill { font-size: .64rem; padding: .1rem .4rem; }

.catalog-money { text-align: right; width: 130px; }
.catalog-money .form-control-custom {
    width: 112px;
    padding: .3rem .5rem;
    font-size: .85rem;
}

.catalog-margin {
    text-align: right;
    font-size: .85rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    white-space: nowrap;
}
.catalog-margin.loss { color: var(--danger); font-weight: 700; }

/* Category separator row inside the table body. */
.catalog-group td {
    background: var(--surface);
    font-size: .74rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-muted);
    padding: .5rem .8rem;
    border-bottom: 1px solid var(--border-color);
}
.catalog-table tbody tr.catalog-group:hover td { background: var(--surface); }

.catalog-group-count {
    margin-left: .4rem;
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    opacity: .7;
}

/* An edited row stays obvious while scrolling a long catalog. */
.catalog-dirty td { background: #fffbeb; }
.catalog-table tbody tr.catalog-dirty:hover td { background: #fef3c7; }
.catalog-dirty td:first-child { box-shadow: inset 3px 0 0 var(--warning); }

/* Save bar pinned to the bottom of the viewport, clear of the off-canvas
   sidebar on desktop so it never sits under it. */
.catalog-savebar {
    position: fixed;
    left: 260px;
    right: 0;
    bottom: 0;
    z-index: 40;
    background: var(--card);
    border-top: 1px solid var(--border-color);
    box-shadow: 0 -4px 16px rgba(0,0,0,.08);
    padding: .75rem 2rem;
}

.catalog-savebar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.catalog-savebar-text { font-size: .9rem; color: var(--text-muted); }

.catalog-history td { padding: .55rem .8rem; font-size: .82rem; }
.catalog-history th { padding: .6rem .8rem; }

@media (max-width: 900px) {
    /* Sidebar is off-canvas below this width, so the bar spans the viewport. */
    .catalog-savebar { left: 0; padding: .75rem 1rem; }
    .catalog-count { margin-left: 0; }
}

/* ── Login log (/portal/login-log) ──
   Reuses the shared .funnel-* list vocabulary for its toolbar; only the bits
   with no equivalent elsewhere live here. */
.loginlog-count {
    margin-left: auto;
    font-size: .82rem;
    color: var(--text-muted);
    white-space: nowrap;
}

.loginlog-table td { padding: .6rem 1rem; vertical-align: top; }

.loginlog-sub { font-size: .78rem; color: var(--text-muted); }

/* Email and IP cells double as filters. Styled as a link rather than a button so
   a table of them doesn't read as a row of controls. */
.loginlog-link {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: .82rem;
    color: var(--brand-primary);
    text-align: left;
    cursor: pointer;
}
.loginlog-link:hover { text-decoration: underline; }
.loginlog-link:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; }

/* Monospace so addresses of different lengths still line up down the column. */
.loginlog-ip { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.loginlog-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 1.25rem;
}

@media (max-width: 900px) {
    .loginlog-count { margin-left: 0; }
}

/* ── Print: the V4 proposal is the only intended print target ── */
@media print {
    .portal-sidebar, .portal-topbar, .deploy-banner, .qbp-stepper, .qb-nav, .qb-summary, .qbp-stickybar,
    .page-header, .qbp-nudges, .qbp-gate, .qbp-ribbon, .qbp-print-hide, .qbp-jargon,
    .qbp-ai-trigger, .qbp-ai-backdrop, .qbp-ai-drawer, .qbp-ai-voicechip,
    #components-reconnect-modal { display: none !important; }
    .portal-main { margin-left: 0 !important; }
    .portal-content { padding: 0 !important; }
    .qb-layout { display: block !important; }
    .qbp-draft-wrap.draft > .qbp-proposal { opacity: 1; }
    .qbp-proposal { box-shadow: none; border: none; border-radius: 0; padding: 0; }
    .qbp-prop-area { break-inside: avoid; }
    .qbp-prop-letterhead, .qbp-prop-head, .qbp-prop-table th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    .qbp-dealer-card { break-inside: avoid; }
}

/* ════════════════════════════════════════════════════════════════════════
   Presentation Mode — the customer-safe shell a rep builds a Sail Plan in
   with the merchant sitting beside them.

   Two rules run through all of it:
   1. QUIET. A loud "private data hidden" treatment tells the customer that
      something is being withheld from them, which reads worse than the
      figures it protects. The rep needs to know at a glance; the customer
      should just see a well-made screen.
   2. BIGGER. The merchant reads this from across a table, not from eighteen
      inches away like the rep. The customer panel runs a step up in size
      from the dealer summary it replaces.
   ════════════════════════════════════════════════════════════════════════ */

/* Topbar state chip. Deliberately understated — a dot and one word. Amber only
   when PAUSED, which is the state the rep genuinely must notice. */
.presenting-chip {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .3rem .5rem .3rem .7rem; border-radius: 999px;
    background: var(--surface); border: 1px solid var(--border-color);
    font-size: .78rem; font-weight: 600; color: var(--text-muted);
}
.presenting-chip .dot {
    width: 7px; height: 7px; border-radius: 50%; background: var(--brand-primary);
    box-shadow: 0 0 0 3px var(--brand-primary-light);
}
.presenting-chip.paused { background: #fffbeb; border-color: #fcd34d; color: #92400e; }
.presenting-chip.paused .dot { background: var(--warning); box-shadow: 0 0 0 3px #fef3c7; animation: pm-pulse 1.8s ease-in-out infinite; }
.presenting-chip .act {
    border: none; background: none; padding: .1rem .35rem; border-radius: 6px; cursor: pointer;
    font-size: .76rem; font-weight: 700; color: var(--brand-primary);
}
.presenting-chip.paused .act { color: #92400e; }
.presenting-chip .act:hover { background: rgba(0, 0, 0, .06); }
@keyframes pm-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
/* The AI drawer is fixed and full-height, so it covers the right end of the
   topbar and makes Exit/Resume unclickable. Slide clear of it exactly the way
   .qbp-header-actions.ai-open already does — same 436px, same z-index lift,
   same ≥1200px gate (below that the drawer is near-full-width, so there is
   nowhere to slide to and the rep closes it instead). */
@media (min-width: 1200px) {
    .presenting-chip.ai-open { transform: translateX(-436px); position: relative; z-index: 1075; transition: transform .18s ease; }
}

/* The header button that enters the mode. Outline, not solid: it sits beside
   Save and is not the primary action on the page. */
.qbp-present-btn { white-space: nowrap; }

/* A step the mode keeps closed. Reads as unavailable rather than broken — it
   still looks like a step, because it still is one. */
.qbp-step.locked { opacity: .55; }
.qbp-step.locked .qbp-step-num { background: var(--surface); color: var(--text-muted); border-color: var(--border-color); }
.qbp-step.locked:hover { opacity: .8; }

/* Rendered in place of Step 5 if anything routes there while presenting. */
.qbp-lockcard { text-align: center; padding: 2.4rem 1.6rem; }
.qbp-lockcard .ic {
    width: 54px; height: 54px; margin: 0 auto 1rem; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.6rem; background: var(--surface);
}
.qbp-lockcard h3 { margin: 0 0 .5rem; font-size: 1.15rem; font-weight: 700; color: var(--text-dark); }
.qbp-lockcard p { margin: 0 auto 1.3rem; max-width: 40ch; font-size: .86rem; line-height: 1.55; color: var(--text-muted); }

/* ── The customer value panel (replaces the dealer Summary sidebar) ── */
.qbp-custpanel { padding: 1.15rem 1.15rem 1rem; }
.qbp-custpanel-id { border-bottom: 1px solid var(--border-color); padding-bottom: .7rem; margin-bottom: .9rem; }
.qbp-custpanel-id .name { font-size: 1.05rem; font-weight: 800; color: var(--text-dark); line-height: 1.25; }
.qbp-custpanel-id .line { font-size: .74rem; color: var(--text-muted); margin-top: .15rem; }

.qbp-custpanel-hero { margin-bottom: 1rem; }
.qbp-custpanel-hero .lbl {
    font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em; color: var(--text-muted);
}
.qbp-custpanel-hero .big {
    font-size: 2.15rem; font-weight: 800; color: var(--brand-primary);
    font-variant-numeric: tabular-nums; line-height: 1.15; letter-spacing: -.02em;
}
.qbp-custpanel-hero .big .mo { font-size: .9rem; color: var(--text-muted); font-weight: 600; letter-spacing: 0; }
.qbp-custpanel-hero .second { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; margin-top: .2rem; }
.qbp-custpanel-hero .second .cap { font-size: .78rem; font-weight: 400; color: var(--text-muted); }
.qbp-custpanel-hero .note { font-size: .72rem; color: var(--text-muted); margin-top: .2rem; }

/* The savings card — the one place in the whole mode that raises its voice,
   because it is the number the merchant most wants and it is entirely theirs. */
.qbp-custpanel-save {
    background: linear-gradient(135deg, #065f46, #059669);
    color: #fff; border-radius: var(--radius); padding: .85rem 1rem; margin-bottom: 1rem;
}
.qbp-custpanel-save .eyebrow { font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em; opacity: .9; }
.qbp-custpanel-save .big { font-size: 1.85rem; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.2; }
.qbp-custpanel-save .big .mo { font-size: .85rem; font-weight: 600; opacity: .85; }
.qbp-custpanel-save .yr { font-size: .8rem; opacity: .92; margin-top: .1rem; font-variant-numeric: tabular-nums; }
.qbp-custpanel-save .qbp-val { animation-name: qb4flash-onbrand; }

.qbp-custpanel-sec {
    font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em;
    color: var(--text-muted); margin: 1.1rem 0 .5rem;
}
.qbp-custpanel-areas { display: flex; flex-direction: column; gap: .1rem; }
.qbp-custpanel-area {
    display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
    padding: .4rem 0; border-bottom: 1px solid var(--surface); font-size: .86rem;
}
.qbp-custpanel-area:last-child { border-bottom: none; }
.qbp-custpanel-area .t { font-weight: 600; color: var(--text-dark); }
.qbp-custpanel-area .t .n { display: block; font-size: .7rem; font-weight: 400; color: var(--text-muted); }
.qbp-custpanel-area .v { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }
.qbp-custpanel-area .v .mo { font-size: .72rem; font-weight: 500; color: var(--text-muted); }
.qbp-custpanel-area .v .ot { display: block; font-size: .74rem; font-weight: 500; color: var(--text-muted); }
.qbp-custpanel-area .v .inc { font-size: .78rem; font-weight: 700; color: var(--success); }

.qbp-custpanel-steps { display: flex; flex-direction: column; gap: .18rem; }
.qbp-custpanel-step {
    display: flex; align-items: center; gap: .55rem;
    font-size: .84rem; color: var(--text-muted); padding: .25rem .3rem; border-radius: 6px;
}
.qbp-custpanel-step .st {
    display: inline-grid; place-items: center; width: 19px; height: 19px; border-radius: 50%;
    background: var(--surface); color: var(--text-muted); font-size: .68rem; font-weight: 800; flex-shrink: 0;
}
.qbp-custpanel-step.done { color: var(--text-dark); }
.qbp-custpanel-step.done .st { background: var(--success); color: #fff; }
.qbp-custpanel-step.cur { background: var(--brand-primary-light); color: var(--brand-primary-dark); font-weight: 700; }
.qbp-custpanel-step.cur .st { background: var(--brand-primary); color: #fff; }

.qbp-custpanel-foot {
    margin-top: 1rem; padding-top: .7rem; border-top: 1px solid var(--border-color);
    font-size: .74rem; color: var(--text-muted);
}

/* ── Unlock modal (PresentationUnlockModal, inside a .qbp-confirm shell) ── */
.pm-pinfields { display: flex; flex-direction: column; gap: .5rem; }
.pm-pin { text-align: center; letter-spacing: .35em; font-size: 1.15rem; font-weight: 700; padding: .6rem; }
/* The PIN field masks itself in CSS because it is a type="text" input, not a
   password one — see the comment in PresentationUnlockModal: a password field
   makes Chrome and Edge offer to "update your saved password" with the PIN in it
   the moment the rep navigates away. Supported by Chrome/Edge/Safari and by
   Firefox 112+; without it the digits would be visible, so do not drop it. */
.pm-pin-masked { -webkit-text-security: disc; }
/* The password fallback is a separate field and the wide PIN tracking looks
   wrong on ordinary text, so it keys off the autocomplete hint only that field
   carries. */
.pm-pin[autocomplete="current-password"] { letter-spacing: normal; font-size: 1rem; text-align: left; }
.pm-pin-solo { width: 100%; }
.pm-error { margin-top: .6rem; font-size: .8rem; font-weight: 600; color: var(--danger); }
.pm-altlink {
    display: block; width: 100%; margin-top: .9rem; padding: 0;
    border: none; background: none; cursor: pointer;
    font-size: .78rem; font-weight: 600; color: var(--brand-primary); text-decoration: underline;
}
.pm-altlink-danger { color: var(--danger); }

@media print {
    .presenting-chip { display: none !important; }
}

/* ── Server Stats: the CPU / memory history charts (inline SVG) ── */
.ss-chart { display: block; width: 100%; height: auto; }
.ss-chart .ss-grid { stroke: var(--border-color); stroke-width: 1; }
.ss-chart .ss-axis-text { fill: var(--text-muted); font-size: 11px; }
.ss-chart .ss-area { fill: var(--brand-primary); opacity: .1; }
.ss-chart .ss-line { fill: none; stroke: var(--brand-primary); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
/* The hover layer: every sample owns an invisible vertical band, and hovering
   the band reveals that sample's crosshair, dot and readout. Pure CSS, so
   mouse movement never round-trips to the server. */
.ss-chart .ss-tip { display: none; pointer-events: none; }
.ss-chart .ss-hit:hover .ss-tip { display: block; }
.ss-chart .ss-cross { stroke: var(--brand-primary); stroke-width: 1; opacity: .45; }
.ss-chart .ss-dot { fill: var(--brand-primary); stroke: var(--card); stroke-width: 2; }
/* paint-order strokes a card-colored halo behind the readout text so it stays
   legible where it crosses the line. */
.ss-chart .ss-tip-val { fill: var(--text-dark); font-size: 12.5px; font-weight: 700; paint-order: stroke; stroke: var(--card); stroke-width: 3px; }
.ss-chart .ss-tip-time { fill: var(--text-muted); font-size: 10.5px; paint-order: stroke; stroke: var(--card); stroke-width: 3px; }

/* ════════════════════════════════════════════════════════════════════════
   Dark theme (portal only).
   PortalLayout puts .theme-dark on .portal-shell when the signed-in user has
   chosen dark mode (avatar menu, saved on their profile), so everything here
   is scoped to the shell and the public site never goes dark. Most of the
   portal flips through the token overrides below; the rules after them patch
   the hard-coded tint/text pairs (amber nudges, green pills, etc.) that no
   token covers. Two deliberate light islands live at the bottom.
   ════════════════════════════════════════════════════════════════════════ */

.portal-shell.theme-dark {
    /* Slightly brighter brand blue: the light-mode #105d89 all but vanishes as
       text on a dark card. --brand-primary-darker is untouched — it anchors the
       sidebar and the hero gradients, which are already dark in both themes. */
    --brand-primary: #2b8ec9;
    --brand-primary-dark: #1a7ab5;
    --brand-primary-light: #14293c;   /* selection/hover tint, now a deep blue */
    --brand-primary-mid: #1d3c57;
    --brand-accent: #4aa3d8;
    --text-dark: #e8edf5;             /* "dark" = the main text color, so it flips */
    --text-muted: #9aa7ba;
    --border-color: #2b3950;
    --surface: #0f1626;               /* page background + inset wells */
    --card: #1a2334;                  /* cards, topbar, dropdowns, drawers */
    --success: #10b981;
    --warning: #f59e0b;
    --danger: #ef4444;
    --shadow-sm: 0 1px 3px rgba(0,0,0,.4), 0 1px 2px rgba(0,0,0,.3);
    --shadow-md: 0 4px 12px rgba(0,0,0,.45);
    --shadow-lg: 0 10px 30px rgba(0,0,0,.55);
    /* Native widgets (selects, date pickers, scrollbars) follow suit. */
    color-scheme: dark;
    background: var(--surface);
    color: var(--text-dark);
}

/* The shell already covers the viewport; this catches the overscroll fringe so
   rubber-banding never flashes white. Matches --surface above. */
body:has(.portal-shell.theme-dark) { background: #0f1626; }

/* Inputs read as inset wells against the card they sit on. */
.portal-shell.theme-dark .form-control-custom { background: var(--surface); }
.portal-shell.theme-dark .form-control-custom:focus { box-shadow: 0 0 0 3px rgba(74,163,216,.25); }
.portal-shell.theme-dark .qty-input input { background: transparent; color: var(--text-dark); }
.portal-shell.theme-dark .qbp-toggle .sl { background: #3b4a63; }

/* Semantic tints: the light theme uses pastel fills (#d1fae5, #fef3c7, …) with
   deep text; dark mode swaps to translucent washes with lightened text. */
.portal-shell.theme-dark .badge-success { background: rgba(16,185,129,.15); color: #34d399; }
.portal-shell.theme-dark .badge-warning { background: rgba(245,158,11,.15); color: #fbbf24; }
.portal-shell.theme-dark .badge-danger  { background: rgba(239,68,68,.15); color: #f87171; }
.portal-shell.theme-dark .badge-gray    { background: #273348; color: #b6c2d4; }
.portal-shell.theme-dark .alert-success { background: rgba(16,185,129,.1); color: #6ee7b7; }
.portal-shell.theme-dark .alert-warning { background: rgba(245,158,11,.1); color: #fcd34d; }
.portal-shell.theme-dark .alert-danger  { background: rgba(239,68,68,.1); color: #fca5a5; }
.portal-shell.theme-dark .alert-info    { color: #9ccdea; }

/* Layout chrome */
.portal-shell.theme-dark .avatar-dropdown-signout:hover { background: rgba(239,68,68,.12); color: #f87171; }
.portal-shell.theme-dark .notif-unread:hover { background: var(--brand-primary-mid); }
.portal-shell.theme-dark .portal-toast { box-shadow: 0 12px 32px rgba(0,0,0,.55); }
.portal-shell.theme-dark .reminder-error { background: rgba(239,68,68,.12); color: #fca5a5; }
.portal-shell.theme-dark .deploy-banner { background: #2a2110; border-bottom-color: #57430e; }
.portal-shell.theme-dark .deploy-banner-title { color: #fcd34d; }
.portal-shell.theme-dark .deploy-banner-note  { color: #e5b567; }
.portal-shell.theme-dark .deploy-active-title { color: #fbbf24; }
.portal-shell.theme-dark .presence-banner.clear { background: rgba(16,185,129,.08); border-color: rgba(16,185,129,.35); }
.portal-shell.theme-dark .presence-banner.busy  { background: rgba(245,158,11,.08); border-color: rgba(245,158,11,.35); }
.portal-shell.theme-dark .escalation-workaround { background: rgba(16,185,129,.1); }
.portal-shell.theme-dark .funnel-bar { background: #273348; }
.portal-shell.theme-dark .catalog-dirty td { background: rgba(245,158,11,.07); }
.portal-shell.theme-dark .catalog-table tbody tr.catalog-dirty:hover td { background: rgba(245,158,11,.12); }

/* Quote Builder state colors (tri-state glyphs, pills, nudges, gates) */
.portal-shell.theme-dark .qbp-step.done .qbp-step-num { background: rgba(16,185,129,.18); }
.portal-shell.theme-dark .qbp-step.active .qbp-step-sub { color: #9ccdea; }
.portal-shell.theme-dark .qbp-goalseek-result.bad { border-color: rgba(239,68,68,.45); color: #fca5a5; }
.portal-shell.theme-dark .qbp-st.ok { background: rgba(16,185,129,.18); }
.portal-shell.theme-dark .qbp-st.skip { background: #273348; color: #9aa7ba; }
.portal-shell.theme-dark .qbp-st.auto { background: #273348; color: #7d8ca3; outline-color: #3b4a63; }
.portal-shell.theme-dark .qbp-st.warn { background: rgba(245,158,11,.18); }
.portal-shell.theme-dark .qbp-st.warn::after { border-color: rgba(245,158,11,.55); }
.portal-shell.theme-dark .qbp-area.is-skipped,
.portal-shell.theme-dark .qbp-area.is-auto { border-left-color: #3b4a63; }
.portal-shell.theme-dark .qbp-autoskip { border-left-color: #3b4a63; }
.portal-shell.theme-dark .qbp-pill.ok { background: rgba(16,185,129,.18); color: #6ee7b7; }
.portal-shell.theme-dark .qbp-pill.skip { background: #273348; color: #b6c2d4; }
.portal-shell.theme-dark .qbp-pill.warn { background: rgba(245,158,11,.18); color: #fcd34d; }
.portal-shell.theme-dark .qbp-dot.warn { box-shadow: 0 0 0 2px rgba(245,158,11,.35); }
/* Presentation Mode. Almost all of it is token-driven and adapts on its own —
   only the two hardcoded palettes need a dark reading: the paused chip's amber
   and the savings card's green (which stays saturated, because it is meant to
   carry in a room where someone is looking at it from across a table). */
.portal-shell.theme-dark .presenting-chip.paused { background: rgba(245,158,11,.1); border-color: rgba(245,158,11,.35); color: #fcd34d; }
.portal-shell.theme-dark .presenting-chip.paused .act { color: #fcd34d; }
.portal-shell.theme-dark .presenting-chip .act:hover { background: rgba(255,255,255,.08); }
.portal-shell.theme-dark .qbp-custpanel-save { background: linear-gradient(135deg, #064e3b, #047857); }
.portal-shell.theme-dark .qbp-custpanel-area { border-bottom-color: #273348; }
.portal-shell.theme-dark .qbp-custpanel-step .st { background: #273348; color: #9aa7ba; }
.portal-shell.theme-dark .qbp-lockcard .ic { background: #273348; }
.portal-shell.theme-dark .qbp-nudge { background: rgba(245,158,11,.08); border-color: rgba(245,158,11,.3); color: #fcd34d; }
.portal-shell.theme-dark .qbp-nudge a { color: #fcd34d; }
.portal-shell.theme-dark .qbp-nudge .x { color: #d9a441; }
.portal-shell.theme-dark .qbp-nudge .act { background: rgba(245,158,11,.9); border-color: rgba(245,158,11,.5); color: #291a04; }
.portal-shell.theme-dark .qbp-nudge .act:hover { background: #fcd34d; color: #291a04; }
.portal-shell.theme-dark .qbp-gate { border-color: rgba(245,158,11,.35); background: rgba(245,158,11,.07); }
.portal-shell.theme-dark .qbp-gate h4,
.portal-shell.theme-dark .qbp-gate ul,
.portal-shell.theme-dark .qbp-gate a { color: #fcd34d; }
.portal-shell.theme-dark .qbp-dealer-card { background: rgba(245,158,11,.07); }
.portal-shell.theme-dark .qbp-autopill { color: #9ccdea; }
.portal-shell.theme-dark .qbp-autopill.manual { background: rgba(245,158,11,.16); color: #fcd34d; }
.portal-shell.theme-dark .qbp-fillchip { color: #9ccdea; }
.portal-shell.theme-dark .qbp-aireview-head { color: #9ccdea; }
.portal-shell.theme-dark .qbp-sharecard-head { color: #9ccdea; }

/* Tooltips keep a raised dark bubble (their light-theme background IS
   --text-dark, which now resolves to near-white — hence the explicit color). */
.portal-shell.theme-dark .qbp-jargon-pop,
.portal-shell.theme-dark .qbp-hintdot .pop { background: #33415a; }

/* AI assistant accents */
.portal-shell.theme-dark .qbp-ai-preview-note { background: rgba(245,158,11,.08); }
.portal-shell.theme-dark .qbp-ai-vstate.pending { background: rgba(245,158,11,.15); color: #fbbf24; }
.portal-shell.theme-dark .qbp-ai-vstate.thinking { color: #bfdcf0; }
.portal-shell.theme-dark .qbp-ai-voicechip-label { color: #9ccdea; }
.portal-shell.theme-dark .qbp-ai-mic.on { background: rgba(239,68,68,.15); }
.portal-shell.theme-dark .qbp-ai-voicechip-end:hover { background: rgba(239,68,68,.12); border-color: rgba(239,68,68,.4); }
.portal-shell.theme-dark .qbp-ai-stopspk:hover { background: rgba(239,68,68,.12); }

/* ── Light islands ──
   Re-declaring the light palette on an element flips it and every descendant
   back to light, because the tokens cascade. */

/* The proposal preview is a picture of a PRINTED document: it stays paper-white
   in dark mode so what the rep sees is exactly what the customer gets. */
.portal-shell.theme-dark .qbp-proposal,
/* DevExpress's DxAIChat ships its own light theme, so the chat area stays a
   coherent light island inside the dark drawer rather than half-flipping. */
.portal-shell.theme-dark .qbp-ai-chat-wrap {
    --card: #ffffff;
    --surface: #f9fafb;
    --text-dark: #1a1a2e;
    --text-muted: #6b7280;
    --border-color: #e5e7eb;
    --brand-primary: #105d89;
    --brand-primary-dark: #0d4a6e;
    --brand-primary-light: #e8f3f9;
    --brand-primary-mid: #d0e8f5;
    --success: #059669;
    --warning: #d97706;
    --danger: #dc2626;
    color-scheme: light;
    background: var(--card);
    color: var(--text-dark);
}
