/* ==========================================================================
   ICON Marketing Hub — global.css
   Vanilla CSS design system (no framework). All custom classes prefixed `mh-`.
   Palette reproduced from the legacy SPA (flask_legacy/static/index.html:19-77):
   near-black primary, blue accent, off-white surfaces, red/yellow/green status.
   ========================================================================== */

/* ---- Design tokens ------------------------------------------------------- */
:root {
    /* Brand */
    --brand-primary: #000101;
    --brand-primary-dark: #000000;
    --brand-accent: #0453cd;
    --brand-accent-light: #356ee7;
    --brand-accent-glow: rgba(4, 83, 205, 0.12);

    /* Surfaces & neutrals */
    --bg: #f8f9fb;
    --surface: #ffffff;
    --surface-2: #f3f4f6;
    --surface-3: #edeef0;
    --border: #e5e7eb;
    --border-strong: #c5c6ca;
    --gray-50: #f9fafb;
    --gray-100: #f3f4f6;
    --gray-200: #e5e7eb;
    --gray-400: #9ca3af;
    --gray-500: #6b7280;
    --gray-600: #4b5563;
    --gray-700: #374151;
    --gray-800: #1f2937;
    --gray-900: #111827;

    /* Status */
    --success: #16a34a;
    --success-bg: #dcfce7;
    --warning: #b45309;
    --warning-bg: #fef9c3;
    --danger: #ba1a1a;
    --danger-bg: #fee2e2;
    --info: #0453cd;
    --info-bg: #e6efff;

    /* KPI band colours. Used at full saturation for the gauge's zoned track — the needle
       is ink, not a band colour, so the zones don't have to be muted to stay clear of it. */
    --kpi-red: #ef4444;
    --kpi-yellow: #f59e0b;
    --kpi-green: #16a34a;

    /* Elevation */
    --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06);
    --shadow: 0 1px 3px rgba(16, 24, 40, 0.10);
    --shadow-md: 0 4px 12px rgba(16, 24, 40, 0.08);
    --shadow-lg: 0 12px 28px rgba(16, 24, 40, 0.12);

    /* Shape & motion */
    --radius-sm: 0.25rem;
    --radius: 0.5rem;
    --radius-lg: 0.75rem;
    --transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);

    /* Typography */
    --font-body: 'Zalando Sans Expanded', system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-display: 'Zalando Sans Expanded', system-ui, -apple-system, "Segoe UI", sans-serif;

    /* Layout */
    --nav-height: 60px;
    --content-max: 1280px;
    --gutter: 1.5rem;
}

/* ---- Reset / base -------------------------------------------------------- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

/* The UA rule for [hidden] is only `display: none`, so any component that sets its
   own display (every .mh-badge, .mh-btn, .mh-tabs…) silently defeats the attribute. */
[hidden] {
    display: none !important;
}

body {
    margin: 0;
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.5;
    color: var(--gray-800);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: 1.2;
    color: var(--gray-900);
    margin: 0 0 0.5rem;
}

h1 {
    font-size: 1.6rem;
}

h2 {
    font-size: 1.25rem;
}

h3 {
    font-size: 1.05rem;
}

p {
    margin: 0 0 0.75rem;
}

a {
    color: var(--brand-accent);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

img {
    max-width: 100%;
    height: auto;
}

/* Uppercase, letter-spaced labels (table headers, field labels, eyebrows) */
.mh-label {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--gray-500);
}

/* ---- Layout -------------------------------------------------------------- */
.mh-page {
    max-width: var(--content-max);
    margin: 0 auto;
    /* Mobile-first: tighter gutters and a shorter top pad, opened up at --bp-nav. */
    padding: 1.25rem 1rem 3rem;
}

.mh-page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.mh-page-header .mh-subtitle {
    color: var(--gray-500);
    margin: 0;
}

.mh-row {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

.mh-grid {
    display: grid;
    gap: 1rem;
}

.mh-grid-2 {
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* ---- Brand lockup -------------------------------------------------------- */
/* Lives in global.css, not auth.css: the landing bar (landing.css) reuses
   .mh-auth-logo, and auth.css is not loaded on that page. */
.mh-auth-logo {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.15rem;
    letter-spacing: 0.02em;
    color: #fff;
    text-decoration: none;
}

.mh-auth-logo:hover {
    text-decoration: none;
    color: inherit;
}

.mh-brand-mark {
    display: block;
    height: 28px;
    width: auto;
    /* mark is portrait — let width follow */
    flex: none;
}

/* ---- Navbar -------------------------------------------------------------- */
.mh-nav {
    position: sticky;
    top: 0;
    z-index: 50;
    background: var(--brand-primary);
    color: #fff;
    height: var(--nav-height);
    display: flex;
    align-items: center;
    padding: 0 0.75rem;
    box-shadow: var(--shadow-md);
}

.mh-nav-inner {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    max-width: var(--content-max);
    margin: 0 auto;
}

.mh-nav-brand {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.05rem;
    color: #fff;
    letter-spacing: 0.02em;
    text-decoration: none;
    white-space: nowrap;
}

.mh-nav-brand:hover {
    text-decoration: none;
}

/* Mobile-first: a full-width panel dropped below the bar, revealed by .is-open from
   the toggle button. The horizontal row is assembled at --bp-nav. */
.mh-nav-links {
    position: absolute;
    top: var(--nav-height);
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    background: var(--brand-primary);
    padding: 0.5rem var(--gutter) 1rem;
    box-shadow: var(--shadow-md);
}

.mh-nav-links.is-open {
    display: flex;
}

.mh-nav-link {
    color: rgba(255, 255, 255, 0.75);
    padding: 0.4rem 0.7rem;
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
    font-weight: 500;
    white-space: nowrap;
    transition: var(--transition);
}

.mh-nav-link:hover {
    color: #fff;
    background: rgba(255, 255, 255, 0.08);
    text-decoration: none;
}

.mh-nav-link.is-active {
    color: #fff;
    background: var(--brand-accent);
}

/* Reminders-due plus unverified-tag count, on the CRM link. Amber rather than red: these are
   accounts waiting on a decision, not a failure — red here would compete with the overdue dots
   that genuinely do mean someone is behind.
   This is the ONLY .mh-nav-badge rule. A second one lived at the foot of this file, styling it red
   for a Messages nav item that migration 088 deleted; being later it won the cascade, so the badge
   rendered red and contradicted the reasoning directly above it. */
.mh-nav-badge {
    display: inline-block;
    min-width: 1.15rem;
    margin-left: 0.35rem;
    padding: 0 0.3rem;
    border-radius: 999px;
    background: var(--kpi-yellow);
    color: #1f2937;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.15rem;
    text-align: center;
    vertical-align: middle;
}

/* admin panel shortcut — sits left of the account pill (admins only). Hidden on
   phones, where the collapsed nav already carries the admin links. */
.mh-nav-admin {
    display: none;
    align-items: center;
    flex: none;
    padding: 0.35rem 0.75rem;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 999px;
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    transition: var(--transition);
}

.mh-nav-admin:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.5);
    color: #fff;
    text-decoration: none;
}

/* account dropdown */
.mh-nav-account {
    position: relative;
}

.mh-nav-avatar {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
    border: none;
    padding: 0.35rem 0.6rem;
    border-radius: 999px;
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
}

.mh-nav-avatar .mh-avatar-circle {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--brand-accent);
    display: grid;
    place-items: center;
    font-size: 0.75rem;
    font-weight: 700;
}

.mh-nav-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    background: var(--surface);
    color: var(--gray-800);
    min-width: 200px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 0.35rem;
    display: none;
}

.mh-nav-menu.is-open {
    display: block;
}

.mh-nav-menu a,
.mh-nav-menu button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.5rem 0.65rem;
    border-radius: var(--radius-sm);
    color: var(--gray-700);
    font-size: 0.9rem;
    background: none;
    border: none;
    cursor: pointer;
    font: inherit;
}

.mh-nav-menu a:hover,
.mh-nav-menu button:hover {
    background: var(--surface-2);
    text-decoration: none;
}

.mh-nav-menu .mh-menu-divider {
    height: 1px;
    background: var(--border);
    margin: 0.35rem 0;
}

/* mobile toggle */
.mh-nav-toggle {
    display: block;
    background: none;
    border: none;
    color: #fff;
    font-size: 1.4rem;
    cursor: pointer;
    padding: 0.25rem;
}

/* Phones only: the wordmark and username are dropped so the mark, toggle and account
   pill are never squeezed. Restored at --bp-nav-tight. */
.mh-nav-brand-text {
    display: none;
}

.mh-nav-avatar-name {
    display: none;
}

/* ---- Cards --------------------------------------------------------------- */
.mh-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.25rem;
    box-shadow: var(--shadow-sm);
}

.mh-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.mh-card-title {
    font-size: 1rem;
    font-weight: 600;
    margin: 0;
}

/* ---- Stat tiles ----------------------------------------------------------
   Plain summary tiles (invoices.php rep totals). The dashboard's own KPIs use the
   percentile arc gauges below, not these — hence no colour-band modifiers here. */
.mh-kpi {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--border-strong);
    border-radius: var(--radius-lg);
    padding: 1rem 1.1rem;
    box-shadow: var(--shadow-sm);
    transition: var(--transition);
}

.mh-kpi:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

.mh-kpi-head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.mh-kpi-value {
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--gray-900);
    margin: 0.35rem 0 0.15rem;
}

/* Accent rail marks the one tile that is the headline number in a row of tiles. */
.mh-kpi.is-primary {
    border-left-color: var(--brand-accent);
}

/* Caption under a value: which invoice, which period. Truncates rather than
   wrapping, so a long vendor name can't make one tile taller than its neighbours. */
.mh-kpi-sub {
    font-size: 0.75rem;
    color: var(--gray-500);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- KPI arc gauge panel (shared: sales.php, dashboard.php) --------------
   Percentile-banded 180deg arcs. Geometry is drawn by assets/js/gauges.js;
   ranking and band assignment happen server-side in mh_percentile.php.
   The panel is one card and one visual object -- the gauges deliberately carry
   no card chrome of their own, so they read as a single row of readings
   rather than a set of nested boxes. */
.mh-gauge-panel {
    margin: 0 0 1.5rem;
}

/* The title row carries the gap down to the readings itself — the explanatory note
   that used to sit between them and space them apart is gone. */
.mh-gauge-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.mh-gauge-bar-left {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

/* Week picker sizes to its longest option instead of stretching to the card. */
.mh-gauge-week {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.mh-gauge-week .mh-select {
    width: auto;
    font-size: 0.85rem;
    padding: 0.35rem 0.55rem;
}

.mh-gauge-grid {
    display: grid;
    /* Mobile-first: one gauge per row. Columns are added upward — a lone 200px arc
       stranded beside empty space on a phone-width card reads as a layout fault. */
    grid-template-columns: 1fr;
    gap: 1.25rem;
    align-items: start;
}

.mh-gauge {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    min-width: 0;
}

.mh-gauge-label {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--gray-500);
    min-height: 0;
}

/* The arc's box. Its aspect ratio is fixed because Chart.js can't resolve partial
   doughnut geometry against `height: auto` with maintainAspectRatio: false. A
   half-circle only fills the top half of a circle's box, so this is roughly 2:1 --
   wider than the 220/168 the 270deg arc needed -- with the surplus over 2:1 being
   the headroom the boundary numbers are drawn into. */
.mh-gauge-plot {
    position: relative;
    width: 100%;
    max-width: 220px;
    aspect-ratio: 220 / 132;
    margin: 0.2rem auto 0;
}

.mh-gauge-plot canvas {
    display: block;
    width: 100%;
    height: 100%;
}

/* Hero reading, sitting inside the arc. A half-circle's centre is down at its
   diameter rather than in the middle of the box, so the number is anchored to the
   bottom of the plot instead of centred in it -- centring would float it up toward
   the crown of the arc and leave the well beneath it empty. Tabular figures so the
   number doesn't shuffle sideways as you page through weeks. */
.mh-gauge-value {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: end center;
    padding: 0 14% 12%;
    font-family: var(--font-display);
    font-size: 1.8rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--gray-900);
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}

.mh-gauge-value.is-long {
    font-size: 1.45rem;
}

/* Scale row below the arc. These used to be absolutely positioned over the canvas
   at bottom:13%, which is exactly where the arc's terminals land — the labels were
   drawn underneath the coloured stroke. Keeping them in flow under the plot makes
   the collision structurally impossible, and matters more at 180° than it did at
   270°, since a half-circle's ends now sit at the very bottom corners of the box. */
.mh-gauge-scale {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    max-width: 220px;
    margin: 0.15rem auto 0;
}

.mh-gauge-bound {
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--gray-400);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.mh-gauge-rule {
    flex: 1;
    height: 1px;
    background: var(--border);
}

/* Band verdict as a pill. This is the accessibility mitigation for a
   red/yellow/green scale, which is NOT colourblind-safe on its own — red and
   green separate by only ~4 ΔE under simulated deuteranopia, and yellow clears
   just 2.04:1 against the page. The glyph and the words carry the verdict;
   never reduce this to colour alone. */
.mh-gauge-band {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin: 0.65rem 0 0;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    white-space: nowrap;
    background: var(--surface-3);
    color: var(--gray-600);
}

.mh-gauge-meta {
    font-size: 0.68rem;
    line-height: 1.45;
    color: var(--gray-400);
    margin: 0.3rem 0 0;
}

/* ---- Band colours -------------------------------------------------------- */
.mh-gauge.is-red .mh-gauge-band {
    background: var(--danger-bg);
    color: var(--danger);
}

.mh-gauge.is-yellow .mh-gauge-band {
    background: var(--warning-bg);
    color: var(--warning);
}

.mh-gauge.is-green .mh-gauge-band {
    background: var(--success-bg);
    color: var(--success);
}

/* Not enough completed weeks to rank against — show the value, withhold the verdict. */
.mh-gauge.is-none .mh-gauge-band {
    background: var(--surface-3);
    color: var(--gray-500);
}

.mh-gauge-empty {
    grid-column: 1 / -1;
    padding: 1.5rem 0;
    font-size: 0.85rem;
    color: var(--gray-500);
}

/* Two-up as soon as there is room for it, then let the track count float once the
   viewport can hold three 200px gauges. */
@media (min-width: 461px) {
    .mh-gauge-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 1.5rem 1rem;
    }

    /* Reserve two lines so a wrapped label cannot shift the arcs out of line with each
       other — only matters once gauges sit side by side. */
    .mh-gauge-label {
        min-height: 2.1em;
    }
}

@media (min-width: 901px) {
    .mh-gauge-grid {
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    }
}

/* ---- Normalization curve (assets/js/normalization.js) --------------------
   The percentile distribution the arcs above are banded against, drawn once with a
   marker per KPI. Deliberately shares the gauge panel's chrome classes
   (.mh-gauge-bar, .mh-gauge-empty) — it is the same instrument family
   and should not grow a second set of near-identical rules. Only what is genuinely
   curve-specific lives here. */
.mh-curve-panel {
    margin: 0 0 1.5rem;
}

/* Below ~560px the viewBox would scale the axis and band labels down to unreadable, so
   the curve scrolls horizontally instead of shrinking — the same trade .mh-table-wrap
   makes. */
.mh-curve-plot {
    position: relative;
    /* positioning context for the hover tooltip */
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
}

.mh-curve-svg {
    display: block;
    width: 100%;
    min-width: 560px;
    height: auto;
    /* Text inside an SVG doesn't inherit the page font through the shadow of the
       viewport scaling — set it explicitly so the numbers match the rest of the card. */
    font-family: var(--font-body);
}

/* Curve silhouette and the line the bands stand on. */
.mh-curve-line {
    fill: none;
    stroke: var(--gray-800);
    stroke-width: 1.75;
    stroke-linejoin: round;
}

.mh-curve-axis {
    stroke: var(--border-strong);
    stroke-width: 1.25;
}

/* Band divider. Surface-coloured, exactly like the 1.5px gap the gauge draws between
   two adjacent zones. */
.mh-curve-divider {
    stroke: var(--surface);
    stroke-width: 2;
}

.mh-curve-median {
    stroke: var(--gray-800);
    stroke-width: 1.25;
    stroke-dasharray: 2 4;
    opacity: 0.5;
}

.mh-curve-tick {
    stroke: var(--border-strong);
    stroke-width: 1.25;
}

.mh-curve-tick-text {
    fill: var(--gray-500);
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.mh-curve-band-text {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.02em;
}


/* Mark: needle from the axis to a dot on the curve, both haloed in the surface colour so
   they read against a fully saturated band fill. */
.mh-curve-needle-halo {
    stroke: var(--surface);
    stroke-width: 5;
    stroke-linecap: round;
}

.mh-curve-needle {
    stroke: var(--gray-900);
    stroke-width: 2;
    stroke-linecap: round;
}

.mh-curve-knob-halo {
    fill: var(--surface);
}

.mh-curve-knob {
    fill: var(--gray-900);
}

/* Band-coloured ring on the dot — the mark's own statement of the verdict, for the case
   where it lands close enough to a divider that its position can't settle the question.
   Stroke colour is set inline from the payload's band. */
.mh-curve-knob-ring {
    fill: var(--surface);
    stroke-width: 2;
    transition: stroke-width var(--transition);
}

/* Transparent pointer target, larger than the dot it covers. The dot is 12 user units
   across, which is a few real pixels once the viewBox is scaled down on a narrow card. */
.mh-curve-hit {
    fill: transparent;
    cursor: pointer;
}

/* Hovered mark thickens its ring rather than growing — SVG geometry properties like `r`
   aren't animatable in every browser this has to run in. */
.mh-curve-marker.is-hover .mh-curve-knob-ring {
    stroke-width: 3.5;
}

/* Rank on demand, so the exact percentile doesn't have to be printed on the chart.
   Positioned in the plot's content box by normalization.js, which is why the container
   is the positioning context and the bubble ignores the pointer. */
.mh-curve-tip {
    position: absolute;
    z-index: 2;
    transform: translate(-50%, -100%);
    padding: 0.35rem 0.6rem;
    border-radius: var(--radius-sm);
    background: var(--gray-900);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    box-shadow: var(--shadow-md);
    pointer-events: none;
}

/* KPI names on the axis. Rotated -45° in the SVG, which is what lets five of them sit at
   percentile spacings that would overlap horizontally. */
.mh-curve-name {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0;
}

/* Drawn only when a name had to be nudged clear of its neighbour, to tie it back to the
   mark it belongs to. */
.mh-curve-leader {
    fill: none;
    stroke: var(--gray-400);
    stroke-width: 1;
}

/* ---- Buttons ------------------------------------------------------------- */
.mh-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    padding: 0.5rem 0.95rem;
    border-radius: var(--radius);
    border: 1px solid transparent;
    cursor: pointer;
    transition: var(--transition);
    text-decoration: none;
    line-height: 1.2;
}

.mh-btn:hover {
    text-decoration: none;
}

.mh-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.mh-btn-primary {
    background: var(--brand-primary);
    color: #fff;
}

.mh-btn-primary:hover {
    background: var(--brand-primary-dark);
    color: #fff;
}

.mh-btn-accent {
    background: var(--brand-accent);
    color: #fff;
}

.mh-btn-accent:hover {
    background: var(--brand-accent-light);
    color: #fff;
}

.mh-btn-secondary {
    background: var(--surface);
    color: var(--gray-700);
    border-color: var(--border-strong);
}

.mh-btn-secondary:hover {
    background: var(--surface-2);
}

.mh-btn-danger {
    background: var(--danger);
    color: #fff;
}

.mh-btn-danger:hover {
    background: #9a1414;
    color: #fff;
}

.mh-btn-ghost {
    background: none;
    color: var(--gray-600);
}

.mh-btn-ghost:hover {
    background: var(--surface-2);
}

/* 44px floor is a touch-target minimum, so it applies mobile-first and is dropped on
   pointer-width screens where the compact size is the point. */
.mh-btn-sm {
    padding: 0.3rem 0.6rem;
    font-size: 0.8rem;
    min-height: 44px;
}

.mh-btn-block {
    width: 100%;
}

/* ---- Badges -------------------------------------------------------------- */
.mh-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    background: var(--surface-3);
    color: var(--gray-600);
}

.mh-badge.is-success {
    background: var(--success-bg);
    color: var(--success);
}

.mh-badge.is-warning {
    background: var(--warning-bg);
    color: var(--warning);
}

.mh-badge.is-danger {
    background: var(--danger-bg);
    color: var(--danger);
}

.mh-badge.is-info {
    background: var(--info-bg);
    color: var(--info);
}

/* ── Shared cadence vocabulary (emitted by usersc/includes/mh_ui.php) ────────
   These live in global.css, not in a page stylesheet, because the helpers that emit them —
   mh_stage_badge(), mh_lifecycle_badge(), mh_status_cell() — are in a GLOBAL include and are
   rendered by the accounts list, the account detail view, the dashboard health cards and the CRM
   page. While they sat in the per-page sheet (then accounts.css, now insights.css), every page
   that wanted a stage badge had to load the
   accounts stylesheet to get one (dashboard.php still sets $mh_page_css = 'accounts' for exactly
   that reason), which meant a new page could not use a shared helper without inheriting an
   unrelated stylesheet. A red dot has to mean the same thing everywhere; so does where it is
   defined. */
/* ── Cadence stage badges ────────────────────────────────────────────────────
   Seven stages, coloured by what they mean rather than by position in the list:
   healthy relationships read green, work-in-progress blue, and the two that need
   attention (at-risk, dormant) read red and grey. */
.mh-stage {
    display: inline-block;
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
    border: 1px solid transparent;
}

.mh-stage.is-newProspect {
    background: var(--info-bg);
    color: var(--info);
    border-color: rgba(4, 83, 205, .18);
}

.mh-stage.is-activeProposal {
    background: var(--warning-bg);
    color: var(--warning);
    border-color: rgba(180, 83, 9, .18);
}

.mh-stage.is-onboarding {
    background: #e0f2fe;
    color: #0369a1;
    border-color: rgba(3, 105, 161, .18);
}

.mh-stage.is-activeClient {
    background: var(--success-bg);
    color: var(--success);
    border-color: rgba(22, 163, 74, .18);
}

.mh-stage.is-strategicAccount {
    background: #ede9fe;
    color: #6d28d9;
    border-color: rgba(109, 40, 217, .18);
}

.mh-stage.is-atRisk {
    background: var(--danger-bg);
    color: var(--danger);
    border-color: rgba(186, 26, 26, .2);
}

.mh-stage.is-dormant {
    background: var(--surface-3);
    color: var(--gray-600);
    border-color: var(--border);
}

/* ── Cadence status dot ──────────────────────────────────────────────────────
   Reuses the KPI band colours so a red dot here means the same thing as a red
   band on the dashboard gauges. */
.mh-dot {
    display: inline-block;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    margin-right: 0.4rem;
    vertical-align: middle;
    flex: 0 0 auto;
}

.mh-dot.is-current {
    background: var(--kpi-green);
}

.mh-dot.is-approaching {
    background: var(--kpi-yellow);
}

.mh-dot.is-overdue {
    background: var(--kpi-red);
}

.mh-dot.is-unknown {
    background: var(--gray-400);
}

/* Hollow, not filled: a tagged account is out of the traffic light altogether, and giving it a
   solid colour would put it back on the same scale as current/due-soon/overdue. */
.mh-dot.is-paused {
    background: transparent;
    box-shadow: inset 0 0 0 2px var(--border-strong);
}

.mh-status-cell {
    display: flex;
    align-items: center;
    white-space: nowrap;
}

/* ── Lifecycle tags ──────────────────────────────────────────────────────────
   A separate component from .mh-stage on purpose: the two answer different questions
   ("how often do we call" vs "do we call at all") and can appear on the same row. Squared
   corners and an uppercase label keep them visually distinct from the pill-shaped stage
   badge beside them, so nobody reads one as a replacement for the other. */
.mh-life {
    display: inline-block;
    padding: 0.2rem 0.5rem;
    border-radius: var(--radius-sm);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    border: 1px solid transparent;
}

.mh-life.is-dead {
    background: var(--surface-3);
    color: var(--gray-600);
    border-color: var(--border-strong);
}

.mh-life.is-submitted {
    background: var(--info-bg);
    color: var(--info);
    border-color: rgba(4, 83, 205, .18);
}

.mh-life.is-won {
    background: var(--success-bg);
    color: var(--success);
    border-color: rgba(22, 163, 74, .18);
}

/* The "nobody has checked this yet" marker, carried on the badge so a row states its own
   verification state wherever it appears — not only on the Notifications page. */
.mh-life-unverified {
    margin-left: 0.3rem;
    color: var(--warning);
    font-size: 0.9rem;
    line-height: 0;
}


/* ---- Tables -------------------------------------------------------------- */
.mh-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
}

.mh-table {
    width: 100%;
    border-collapse: collapse;
    min-width: 640px;
}

.mh-table th {
    text-align: left;
    padding: 0.65rem 0.85rem;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--gray-500);
    background: var(--gray-50);
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.mh-table td {
    padding: 0.7rem 0.85rem;
    border-bottom: 1px solid var(--surface-2);
    font-size: 0.9rem;
}

.mh-table tbody tr:last-child td {
    border-bottom: none;
}

.mh-table tbody tr:hover {
    background: var(--gray-50);
}

/* Click-to-sort headers (wired globally in app.js) */
.mh-table th.mh-sortable {
    cursor: pointer;
    user-select: none;
    padding-right: 1.35rem;
    position: relative;
}

.mh-table th.mh-sortable:hover {
    color: var(--gray-700, #374151);
}

.mh-table th.mh-sortable .mh-sort-ind {
    position: absolute;
    right: 0.55rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.7rem;
    line-height: 1;
    opacity: 0.35;
}

.mh-table th.mh-sort-asc .mh-sort-ind,
.mh-table th.mh-sort-desc .mh-sort-ind {
    opacity: 1;
    color: var(--brand-primary, #0453cd);
}

/* Server-sorted headers (paginated tables): the <th> is a link that reloads with
   ?sort=&dir= so the sort spans every page, not just the visible one. */
.mh-table th.mh-th-sort {
    padding: 0;
}

.mh-table th.mh-th-sort a {
    display: block;
    padding: 0.65rem 0.85rem;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
}

.mh-table th.mh-th-sort a:hover {
    color: var(--gray-700, #374151);
}

.mh-table th.mh-th-sort.is-sorted a {
    color: var(--brand-primary, #0453cd);
}

/* ---- Tabs (pill toggle) -------------------------------------------------- */
.mh-tabs {
    display: inline-flex;
    background: var(--surface-3);
    border-radius: 999px;
    padding: 0.2rem;
    gap: 0.15rem;
    max-width: 100%;
    overflow-x: auto;
}

.mh-tab {
    border: none;
    background: none;
    cursor: pointer;
    font: inherit;
    padding: 0.35rem 0.85rem;
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--gray-600);
    white-space: nowrap;
    flex-shrink: 0;
}

.mh-tab.is-active {
    background: var(--surface);
    color: var(--gray-900);
    box-shadow: var(--shadow-sm);
}

/* ---- Forms --------------------------------------------------------------- */
.mh-field {
    margin-bottom: 1rem;
}

.mh-field>label {
    display: block;
    margin-bottom: 0.3rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--gray-700);
}

.mh-input,
.mh-select,
.mh-textarea {
    /* max(16px, …) mobile-first: iOS zooms the viewport on focusing any control under
       16px, and the zoom is not undone on blur. Relaxed to the design size at --bp-nav. */
    width: 100%;
    font: inherit;
    font-size: max(16px, 0.9rem);
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--gray-800);
    transition: var(--transition);
}

.mh-input:focus,
.mh-select:focus,
.mh-textarea:focus {
    outline: none;
    border-color: var(--brand-accent);
    box-shadow: 0 0 0 3px var(--brand-accent-glow);
}

.mh-textarea {
    min-height: 90px;
    resize: vertical;
}

/* toggle switch (capability on/off) */
.mh-switch {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
}

.mh-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.mh-switch .mh-slider {
    position: absolute;
    inset: 0;
    cursor: pointer;
    background: var(--border-strong);
    border-radius: 999px;
    transition: var(--transition);
}

.mh-switch .mh-slider::before {
    content: "";
    position: absolute;
    height: 16px;
    width: 16px;
    left: 3px;
    top: 3px;
    background: #fff;
    border-radius: 50%;
    transition: var(--transition);
}

.mh-switch input:checked+.mh-slider {
    background: var(--brand-accent);
}

.mh-switch input:checked+.mh-slider::before {
    transform: translateX(18px);
}

/* ---- Alerts (usError/usSuccess/usMessage) -------------------------------- */
.mh-alert {
    padding: 0.7rem 1rem;
    border-radius: var(--radius);
    font-size: 0.9rem;
    margin-bottom: 1rem;
    border: 1px solid transparent;
}

.mh-alert-success {
    background: var(--success-bg);
    color: var(--success);
    border-color: #bbf7d0;
}

.mh-alert-error {
    background: var(--danger-bg);
    color: var(--danger);
    border-color: #fecaca;
}

.mh-alert-info {
    background: var(--info-bg);
    color: var(--info);
    border-color: #cfe0ff;
}

/* ---- Modal --------------------------------------------------------------- */
.mh-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(17, 24, 39, 0.5);
    backdrop-filter: blur(2px);
    display: none;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    z-index: 100;
}

.mh-modal-overlay.is-open {
    display: flex;
}

.mh-modal {
    background: var(--surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 480px;
    max-height: 90vh;
    overflow-y: auto;
    padding: 1.5rem;
}

.mh-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
}

/* ---- Empty / coming-soon state ------------------------------------------- */
.mh-empty {
    text-align: center;
    padding: 3.5rem 1.5rem;
    background: var(--surface);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-lg);
    color: var(--gray-500);
}

.mh-empty h2 {
    color: var(--gray-700);
}

/* ---- Rendered Markdown prose --------------------------------------------- */
/* Shared by documentation.php (the docs reader) and dashboard.php's AI trend
   summary. Scoped under .mh-markdown so it can never leak into app chrome. */
.mh-markdown {
    color: var(--gray-700);
    font-size: 0.92rem;
    line-height: 1.65;
}

.mh-markdown>*:first-child {
    margin-top: 0;
}

.mh-markdown>*:last-child {
    margin-bottom: 0;
}

.mh-markdown h1,
.mh-markdown h2,
.mh-markdown h3,
.mh-markdown h4 {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: 1.25;
    color: var(--gray-900);
    margin: 1.85rem 0 0.6rem;
}

.mh-markdown h1 {
    font-size: 1.4rem;
}

/* h2 rules off the section — these documents are long and need the scan anchors. */
.mh-markdown h2 {
    font-size: 1.15rem;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--border);
}

.mh-markdown h3 {
    font-size: 1rem;
}

.mh-markdown h4 {
    font-size: 0.9rem;
    color: var(--gray-700);
}

.mh-markdown p {
    margin: 0 0 0.9rem;
}

.mh-markdown ul,
.mh-markdown ol {
    margin: 0 0 0.9rem;
    padding-left: 1.35rem;
}

.mh-markdown li {
    margin-bottom: 0.3rem;
}

.mh-markdown li>ul,
.mh-markdown li>ol {
    margin: 0.3rem 0 0;
}

.mh-markdown a {
    color: var(--brand-accent);
}

.mh-markdown strong {
    color: var(--gray-900);
    font-weight: 600;
}

.mh-markdown hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 1.75rem 0;
}

/* Inline code: tinted so it separates from prose without a border. */
.mh-markdown code {
    background: var(--surface-2);
    color: var(--gray-800);
    padding: 0.1rem 0.35rem;
    border-radius: var(--radius-sm);
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.86em;
}

/* Fenced blocks scroll on their own rather than widening the page. */
.mh-markdown pre {
    background: var(--gray-900);
    border-radius: var(--radius);
    padding: 0.9rem 1rem;
    margin: 0 0 1.1rem;
    overflow-x: auto;
    font-size: 0.82rem;
    line-height: 1.55;
}

.mh-markdown pre code {
    background: none;
    color: #e5e7eb;
    padding: 0;
    font-size: inherit;
}

/* Tables: the docs lean on these heavily, so they get the app's table styling
   plus their own horizontal scroll (no .mh-table-wrap in generated markup). */
.mh-markdown table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1.1rem;
    font-size: 0.85rem;
    display: block;
    overflow-x: auto;
}

.mh-markdown th,
.mh-markdown td {
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--border);
    text-align: left;
    vertical-align: top;
}

.mh-markdown th {
    background: var(--gray-50);
    color: var(--gray-600);
    font-weight: 600;
    white-space: nowrap;
}

/* Blockquotes carry the "> **Note:**" callouts throughout the docs. */
.mh-markdown blockquote {
    margin: 0 0 1.1rem;
    padding: 0.7rem 1rem;
    border-left: 3px solid var(--brand-accent);
    background: var(--surface-2);
    border-radius: 0 var(--radius) var(--radius) 0;
    color: var(--gray-600);
}

.mh-markdown blockquote>*:last-child {
    margin-bottom: 0;
}

/* ---- Toast --------------------------------------------------------------- */
.mh-toast-wrap {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.mh-toast {
    background: var(--gray-900);
    color: #fff;
    padding: 0.7rem 1rem;
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    font-size: 0.88rem;
}

.mh-toast.is-success {
    background: var(--success);
}

.mh-toast.is-error {
    background: var(--danger);
}

/* ---- Footer -------------------------------------------------------------- */
.mh-foot {
    text-align: center;
    padding: 2rem 1rem;
    color: var(--gray-400);
    font-size: 0.8rem;
}

/* ---- Admin tool tiles (settings.php recovery/update actions) -------------- */
/* Surface stays white so the secondary buttons inside keep a visible hover state
   (--btn-secondary hover is --surface-2); the border does the separating. */
.mh-tool {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.1rem;
}

.mh-tool-title {
    font-size: 0.95rem;
    font-weight: 600;
    margin: 0 0 0.4rem;
}

.mh-tool p {
    margin: 0 0 0.9rem;
    font-size: 0.85rem;
    line-height: 1.5;
}

/* ---- Filter bar ---------------------------------------------------------- */
/* Mobile-first: controls stack full width; the single row is assembled at --bp-filter. */
.mh-filter-bar {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.mh-filter-bar-item {
    flex: none;
    width: 100%;
}

.mh-filter-bar-actions {
    display: flex;
    flex: none;
    width: 100%;
    gap: 0.5rem;
    flex-wrap: wrap;
}

@media (min-width: 641px) {
    .mh-filter-bar {
        flex-direction: row;
        align-items: flex-end;
    }

    .mh-filter-bar .mh-filter-bar-item {
        flex: 1;
        width: auto;
        min-width: 220px;
    }

    /* Pushed to the trailing edge, away from the fields it acts on. `flex: initial`
       releases the stacked layout's `flex: none` so the group can shrink in the row. */
    .mh-filter-bar .mh-filter-bar-actions {
        flex: initial;
        width: auto;
        margin-left: auto;
    }
}

/* ---- Utilities ----------------------------------------------------------- */
.mh-muted {
    color: var(--gray-500);
}

.mh-mt-0 {
    margin-top: 0;
}

.mh-mt-15 {
    margin-top: 1.5rem;
}

.mh-mb-1 {
    margin-bottom: 1rem;
}

.mh-mb-15 {
    margin-bottom: 1.5rem;
}

.mh-section {
    margin-top: 1.5rem;
}

.mh-stack>*+* {
    margin-top: 1.5rem;
}

.mh-flex {
    display: flex;
}

.mh-flex-end {
    display: flex;
    justify-content: flex-end;
}

.mh-items-center {
    align-items: center;
}

.mh-gap {
    gap: 0.75rem;
}

.mh-gap-sm {
    gap: 0.5rem;
}

/* Modal sizing */
.mh-modal-lg {
    max-width: 620px;
}


/* ---- Responsive ----------------------------------------------------------
   Mobile-first per CLAUDE.md: every rule above is the phone layout, and these blocks
   add capability as the viewport grows. Widths are +1px against the old max-width
   values so the breakpoint boundaries land exactly where they did before. */

/* ② Small phones keep only the mark; the wordmark and username return once there is
   room for them beside the toggle and account pill. */
@media (min-width: 421px) {
    .mh-nav-brand-text {
        display: inline;
    }

    .mh-nav-avatar-name {
        display: inline;
    }
}

/* Nav has room for a horizontal row: retire the toggle and unpack the links. */
@media (min-width: 821px) {
    .mh-nav-toggle {
        display: none;
    }

    .mh-nav-links {
        position: static;
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 0.25rem;
        flex: 1;
        background: none;
        padding: 0;
        box-shadow: none;
    }

    .mh-nav-admin {
        display: inline-flex;
    }

    .mh-nav {
        padding: 0 var(--gutter);
    }

    .mh-nav-inner {
        gap: 1.25rem;
    }

    .mh-page {
        padding: 2rem var(--gutter) 4rem;
    }

    /* ① Past the iOS zoom threshold — a mouse pointer wants the design size. */
    .mh-input,
    .mh-select,
    .mh-textarea {
        font-size: 0.9rem;
    }

    /* ④ Touch-target floor no longer applies to a pointer-driven viewport. */
    .mh-btn-sm {
        min-height: 0;
    }
}

/* ==========================================================================
   Scroll-tied reveal — cards fade and lift as they come into view.
   Driven by assets/js/scroll_reveal.js, which ADDS .mh-reveal at runtime. That
   direction matters: with JS off, unsupported, or under prefers-reduced-motion
   the class is never added, so every card renders visible and nothing is
   hidden behind a script. Never author .mh-reveal into markup.
   ========================================================================== */
.mh-reveal {
    opacity: 0;
    transform: translateY(14px);
    /* Slightly slower out than the standard transition — an entrance should settle,
       not snap. Opacity leads so the card is legible before it stops moving. */
    transition: opacity 0.45s ease-out, transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
    /* Hint the compositor for the two properties actually animating. */
    will-change: opacity, transform;
}

.mh-reveal.is-revealed {
    opacity: 1;
    transform: none;
}

/* Once it has arrived, stop claiming compositor resources for it. */
.mh-reveal.is-revealed {
    will-change: auto;
}

/* Belt and braces: if the OS asks for reduced motion, neutralise the reveal even
   if a stale .mh-reveal is somehow present. */
@media (prefers-reduced-motion: reduce) {
    .mh-reveal {
        opacity: 1;
        transform: none;
        transition: none;
        will-change: auto;
    }
}

/* ==========================================================================
   Utilities — generated, replacing 300 inline style= attributes.
   CLAUDE.md: "HTML: Semantic tags; no inline styles (use classes)", and CSP's
   style-src cannot drop 'unsafe-inline' while any style attribute survives (a
   nonce covers <style> elements, never attributes).
/* ---- Pipeline Pass-Through -----------------------------------------------
   Centred funnel; stages narrow top→bottom with ↓ pass-through rates. */
.mh-funnel {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0.5rem 0 0.75rem;
}

.mh-funnel-stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 54px;
    padding: 0.4rem 1.25rem;
    border-radius: 8px;
    color: #fff;
    text-align: center;
    box-shadow: var(--shadow-md);
    /* Width AND opacity: the scroll scrub grows each pipe from nothing, and padding alone would
       leave a visible 2.5rem stub at width 0 without the fade. Eased out so a pipe settles into
       place instead of stopping dead. */
    transition: width 0.45s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease-out;
}

/* ---- Scroll-scrubbed pipeline --------------------------------------------
   A pipe not yet reached by the scroll (or already scrolled back past) sits collapsed. Set by
   assets/js/scroll_reveal.js via renderPipeline() — never authored into markup, so with JS off
   every stage renders at full width. */
.mh-funnel-stage.is-pending {
    opacity: 0;
    box-shadow: none;
}

.mh-funnel-arrow,
.mh-funnel-stage-types {
    transition: opacity 0.3s ease-out;
}

/* The arrow and the type caption belong to their pipe and fade with it. They keep their space
   (visibility/opacity, not display) so the funnel's height doesn't jump as pipes come and go —
   a reflow on every scroll tick would make the whole card judder. */
.mh-funnel-arrow.is-pending,
.mh-funnel-stage-types.is-pending {
    opacity: 0;
}

.mh-funnel-stage-label {
    font-weight: 700;
    font-size: 0.92rem;
    line-height: 1.2;
}

.mh-funnel-stage-count {
    font-size: 0.75rem;
    opacity: 0.95;
}

.mh-funnel-stage-types {
    font-size: 0.68rem;
    line-height: 1.3;
    color: var(--gray-500);
    text-align: center;
    max-width: 82%;
    margin: 0.2rem 0 0.4rem;
}

.mh-funnel-arrow {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--gray-600);
}

/* ---- Funnel: not-yet-reached stages ---------------------------------------
   On the account page the funnel shows ONE account's journey, so stages it has not reached yet
   must read as "not there yet" rather than as absent. They keep the stage's own colour but as an
   outline plus a soft halo of the same hue, so the shape of the whole pipeline stays legible and
   the eye can still see how far there is left to go.

   Colour is per stage and therefore set from JS as a DOM property (see assets/js/account.js) —
   the same approach the sales funnel already uses for its fills, which keeps style-src strict
   because a style ATTRIBUTE would force 'unsafe-inline' for the whole origin.

   Not distinguished by colour alone: the fill is removed, the border appears, the weight drops
   and the label is prefixed in markup, so the state survives greyscale and colour-blindness. */
.mh-funnel-stage.is-unreached {
    /* No !important needed: account.js only assigns .style.background to pipes the account HAS
       reached, so an unreached pipe has no inline background to override. */
    background: transparent;
    border: 2px dashed currentColor;
    box-shadow: none;
    font-weight: 500;
}

.mh-funnel-stage.is-unreached .mh-funnel-stage-count {
    opacity: 0.75;
}

/* The halo. A second, softer ring outside the dashed border so an unreached pipe still reads as
   part of the funnel rather than a hole in it. Drawn with box-shadow rather than outline so it
   follows the border radius. */
.mh-funnel-stage.is-unreached.has-halo {
    box-shadow: 0 0 0 3px var(--mh-halo, transparent), 0 0 12px 2px var(--mh-halo, transparent);
}

/* The stage an account is currently sitting at: filled, and lifted so it reads as "you are here"
   without relying on the badge text alone. */
.mh-funnel-stage.is-current-stage {
    box-shadow: var(--shadow-lg);
    position: relative;
}


Deliberately LAST in this file. An inline style outranks every selector,
so a single-class utility replacing one must at least win against the component rules above it — equal specificity,
so source order decides.==========================================================================*/
.mh-u-ai-center {
    align-items: center;
}

.mh-u-ai-flex-end {
    align-items: flex-end;
}

.mh-u-ai-flex-start {
    align-items: flex-start;
}

.mh-u-ai-start {
    align-items: start;
}

.mh-u-as-center {
    align-self: center;
}

.mh-u-bb-1px-solid-var-gray-200 {
    border-bottom: 1px solid var(--gray-200);
}

.mh-u-bd-1px-solid-var-border {
    border: 1px solid var(--border);
}

.mh-u-bd-none {
    border: none;
}

.mh-u-bg-fff {
    background: #fff;
}

.mh-u-bg-gray-900 {
    background: var(--gray-900);
}

.mh-u-bg-none {
    background: none;
}

.mh-u-bg-surface-2 {
    background: var(--surface-2);
}

.mh-u-bl-4px-solid-var-success {
    border-left: 4px solid var(--success);
}

.mh-u-br-35rem {
    border-radius: .35rem;
}

.mh-u-br-50pct {
    border-radius: 50%;
}

.mh-u-br-radius {
    border-radius: var(--radius);
}

.mh-u-bt-1px-solid-var-border {
    border-top: 1px solid var(--border);
}

.mh-u-bt-1px-solid-var-gray-200 {
    border-top: 1px solid var(--gray-200);
}

.mh-u-c-0a66c2 {
    color: #0A66C2;
}

.mh-u-c-22c55e {
    color: #22c55e;
}

.mh-u-c-6b7280 {
    color: #6b7280;
}

.mh-u-c-brand-accent {
    color: var(--brand-accent);
}

.mh-u-c-danger {
    color: var(--danger);
}

.mh-u-c-fg {
    color: var(--fg);
}

.mh-u-c-gray-400 {
    color: var(--gray-400);
}

.mh-u-c-gray-500 {
    color: var(--gray-500);
}

.mh-u-c-muted {
    color: var(--muted);
}

.mh-u-c-success {
    color: var(--success);
}

.mh-u-cur-pointer {
    cursor: pointer;
}

.mh-u-d-block {
    display: block;
}

.mh-u-d-flex {
    display: flex;
}

.mh-u-d-inline {
    display: inline;
}

.mh-u-d-inline-block {
    display: inline-block;
}

.mh-u-d-inline-flex {
    display: inline-flex;
}

.mh-u-d-none {
    display: none;
}

.mh-u-ff-monospace {
    font-family: monospace;
}

.mh-u-ff-ui-monospace-consolas-monospace {
    font-family: ui-monospace, Consolas, monospace;
}

.mh-u-flex-1 {
    flex: 1;
}

.mh-u-flex-2 {
    flex: 2;
}

.mh-u-fs-0-72rem {
    font-size: 0.72rem;
}

.mh-u-fs-0-75rem {
    font-size: 0.75rem;
}

.mh-u-fs-0-78rem {
    font-size: 0.78rem;
}

.mh-u-fs-0-82rem {
    font-size: 0.82rem;
}

.mh-u-fs-0-85rem {
    font-size: 0.85rem;
}

.mh-u-fs-0-8rem {
    font-size: 0.8rem;
}

.mh-u-fs-0-95rem {
    font-size: 0.95rem;
}

.mh-u-fs-0-9rem {
    font-size: 0.9rem;
}

.mh-u-fs-1-1rem {
    font-size: 1.1rem;
}

.mh-u-fs-2-5rem {
    font-size: 2.5rem;
}

.mh-u-fs-8rem {
    font-size: .8rem;
}

.mh-u-fs-9rem {
    font-size: .9rem;
}

.mh-u-fw-wrap {
    flex-wrap: wrap;
}

.mh-u-fwt-400 {
    font-weight: 400;
}

.mh-u-fwt-600 {
    font-weight: 600;
}

.mh-u-gap-0-3rem {
    gap: 0.3rem;
}

.mh-u-gap-0-4rem {
    gap: 0.4rem;
}

.mh-u-gap-0-5rem {
    gap: 0.5rem;
}

.mh-u-gap-0-6rem {
    gap: 0.6rem;
}

.mh-u-gap-0-75rem {
    gap: 0.75rem;
}

.mh-u-gap-1rem {
    gap: 1rem;
}

.mh-u-gtc-minmax-0-240px-1fr {
    grid-template-columns: minmax(0, 240px) 1fr;
}

.mh-u-h-120px {
    height: 120px;
}

.mh-u-h-220px {
    height: 220px;
}

.mh-u-h-260px {
    height: 260px;
}

.mh-u-h-300px {
    height: 300px;
}

.mh-u-h-360px {
    height: 360px;
}

.mh-u-h-96px {
    height: 96px;
}

.mh-u-jc-center {
    justify-content: center;
}

.mh-u-jc-flex-end {
    justify-content: flex-end;
}

.mh-u-jc-space-between {
    justify-content: space-between;
}

.mh-u-lh-1 {
    line-height: 1;
}

.mh-u-ls-0-1em {
    letter-spacing: 0.1em;
}

.mh-u-ls-0-3em {
    letter-spacing: 0.3em;
}

.mh-u-m-0 {
    margin: 0;
}

.mh-u-m-0-0-0-35rem {
    margin: 0 0 0.35rem;
}

.mh-u-m-0-0-0-5rem {
    margin: 0 0 0.5rem;
}

.mh-u-m-0-0-0-75rem {
    margin: 0 0 0.75rem;
}

.mh-u-m-0-0-1-25rem {
    margin: 0 0 1.25rem;
}

.mh-u-m-0-25rem-auto-0-75rem {
    margin: 0.25rem auto 0.75rem;
}

.mh-u-m-1rem-0 {
    margin: 1rem 0;
}

.mh-u-m-25rem-0-1rem {
    margin: .25rem 0 1rem;
}

.mh-u-maxh-320px {
    max-height: 320px;
}

.mh-u-maxh-360px {
    max-height: 360px;
}

.mh-u-maxh-380px {
    max-height: 380px;
}

.mh-u-maxh-400px {
    max-height: 400px;
}

.mh-u-maxh-60vh {
    max-height: 60vh;
}

.mh-u-maxw-280px {
    max-width: 280px;
}

.mh-u-maxw-440px {
    max-width: 440px;
}

.mh-u-maxw-460px {
    max-width: 460px;
}

.mh-u-maxw-580px {
    max-width: 580px;
}

.mh-u-maxw-620px {
    max-width: 620px;
}

.mh-u-maxw-680px {
    max-width: 680px;
}

.mh-u-maxw-760px {
    max-width: 760px;
}

.mh-u-maxw-900px {
    max-width: 900px;
}

.mh-u-mb-0 {
    margin-bottom: 0;
}

.mh-u-mb-0-25rem {
    margin-bottom: 0.25rem;
}

.mh-u-mb-0-5rem {
    margin-bottom: 0.5rem;
}

.mh-u-mb-0-75rem {
    margin-bottom: 0.75rem;
}

.mh-u-mb-1-25rem {
    margin-bottom: 1.25rem;
}

.mh-u-mb-1-5rem {
    margin-bottom: 1.5rem;
}

.mh-u-mb-1rem {
    margin-bottom: 1rem;
}

.mh-u-minh-160px {
    min-height: 160px;
}

.mh-u-minh-180px {
    min-height: 180px;
}

.mh-u-minh-220px {
    min-height: 220px;
}

.mh-u-minw-120px {
    min-width: 120px;
}

.mh-u-minw-130px {
    min-width: 130px;
}

.mh-u-minw-140px {
    min-width: 140px;
}

.mh-u-minw-150px {
    min-width: 150px;
}

.mh-u-minw-160px {
    min-width: 160px;
}

.mh-u-minw-180px {
    min-width: 180px;
}

.mh-u-minw-190px {
    min-width: 190px;
}

.mh-u-minw-220px {
    min-width: 220px;
}

.mh-u-minw-240px {
    min-width: 240px;
}

.mh-u-minw-320px {
    min-width: 320px;
}

.mh-u-ml-0-35rem {
    margin-left: 0.35rem;
}

.mh-u-ml-25rem {
    margin-left: .25rem;
}

.mh-u-ml-auto {
    margin-left: auto;
}

.mh-u-mt-0-15rem {
    margin-top: 0.15rem;
}

.mh-u-mt-0-25rem {
    margin-top: 0.25rem;
}

.mh-u-mt-0-2rem {
    margin-top: 0.2rem;
}

.mh-u-mt-0-35rem {
    margin-top: 0.35rem;
}

.mh-u-mt-0-5rem {
    margin-top: 0.5rem;
}

.mh-u-mt-0-6rem {
    margin-top: 0.6rem;
}

.mh-u-mt-0-75rem {
    margin-top: 0.75rem;
}

.mh-u-mt-1-25rem {
    margin-top: 1.25rem;
}

.mh-u-mt-1-5rem {
    margin-top: 1.5rem;
}

.mh-u-mt-1rem {
    margin-top: 1rem;
}

.mh-u-of-cover {
    object-fit: cover;
}

.mh-u-ov-hidden {
    overflow: hidden;
}

.mh-u-oy-auto {
    overflow-y: auto;
}

.mh-u-p-0 {
    padding: 0;
}

.mh-u-p-0-15rem {
    padding: 0 .15rem;
}

.mh-u-p-0-6rem-0 {
    padding: 0.6rem 0;
}

.mh-u-p-1-5rem {
    padding: 1.5rem;
}

.mh-u-p-1rem {
    padding: 1rem;
}

.mh-u-p-2-5rem {
    padding: 2.5rem;
}

.mh-u-p-2rem {
    padding: 2rem;
}

.mh-u-p-55rem-1rem {
    padding: .55rem 1rem;
}

.mh-u-pos-absolute {
    position: absolute;
}

.mh-u-pos-relative {
    position: relative;
}

.mh-u-pr-2-5rem {
    padding-right: 2.5rem;
}

.mh-u-pt-1rem {
    padding-top: 1rem;
}

.mh-u-right-0-5rem {
    right: 0.5rem;
}

.mh-u-ta-center {
    text-align: center;
}

.mh-u-ta-right {
    text-align: right;
}

.mh-u-td-none {
    text-decoration: none;
}

.mh-u-tf-translatey-50pct {
    transform: translateY(-50%);
}

.mh-u-to-ellipsis {
    text-overflow: ellipsis;
}

.mh-u-top-50pct {
    top: 50%;
}

.mh-u-tt-uppercase {
    text-transform: uppercase;
}

.mh-u-va-bottom {
    vertical-align: bottom;
}

.mh-u-w-120px {
    width: 120px;
}

.mh-u-w-130px {
    width: 130px;
}

.mh-u-w-180px {
    width: 180px;
}

.mh-u-w-200px {
    width: 200px;
}

.mh-u-w-220px {
    width: 220px;
}

.mh-u-w-34px {
    width: 34px;
}

.mh-u-w-40px {
    width: 40px;
}

.mh-u-w-90px {
    width: 90px;
}

.mh-u-w-95pct {
    width: 95%;
}

.mh-u-w-96px {
    width: 96px;
}

.mh-u-ws-nowrap {
    white-space: nowrap;
}

.mh-u-ws-pre-wrap {
    white-space: pre-wrap;
}

.mh-u-c-red {
    color: red;
}

.mh-u-op-45 {
    opacity: .45;
}

.mh-u-pe-none {
    pointer-events: none;
}

/* SVG paint utilities for assets/js/normalization.js, which builds its shapes as
   markup strings. A style attribute forces style-src unsafe-inline regardless of who
   wrote it, and a nonce cannot cover attributes — so the closed set of band/text/ring
   custom properties gets a class each. Names must match fillClass()/strokeClass(). */
.mh-u-fill-kpi-red {
    fill: var(--kpi-red);
}

.mh-u-fill-kpi-yellow {
    fill: var(--kpi-yellow);
}

.mh-u-fill-kpi-green {
    fill: var(--kpi-green);
}

.mh-u-fill-danger {
    fill: var(--danger);
}

.mh-u-fill-warning {
    fill: var(--warning);
}

.mh-u-fill-success {
    fill: var(--success);
}

.mh-u-fill-gray-600 {
    fill: var(--gray-600);
}

.mh-u-fill-border-strong {
    fill: var(--border-strong);
}

.mh-u-fill-surface-3 {
    fill: var(--surface-3);
}

.mh-u-stroke-kpi-red {
    stroke: var(--kpi-red);
}

.mh-u-stroke-kpi-yellow {
    stroke: var(--kpi-yellow);
}

.mh-u-stroke-kpi-green {
    stroke: var(--kpi-green);
}

.mh-u-stroke-border-strong {
    stroke: var(--border-strong);
}

/* Glassmorphism & Modern UI Extensions */
.mh-card-glass {
    background: rgba(255, 255, 255, 0.85);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(229, 231, 235, 0.8);
    box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.05);
    border-radius: var(--radius-lg);
    transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.mh-hover-lift:hover {
    transform: translateY(-2px);
    box-shadow: 0 12px 28px rgba(16, 24, 40, 0.12);
}

.mh-icon-inline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    vertical-align: middle;
    width: 1.15em;
    height: 1.15em;
    stroke-width: 2;
    margin-right: 0.35rem;
}

/* The mobile bottom dock lives entirely in mh_mobile.css.
   A second implementation used to sit here behind @media (max-width: 768px): a dark dock
   (rgba(11,15,23,.95)) with its own .mh-mobile-dock-item rules. It was dead in practice —
   mh_mobile.css loads after this file and restates .mh-mobile-dock/.mh-mobile-dock-item
   unconditionally, so its light-theme values won on source order and the dark ones never
   painted. Worse, the two disagreed about the breakpoint (768px here, 1024px there), so
   between 769px and 1024px the dock showed with no item styling at all. Removed rather than
   reconciled: one dock, one stylesheet, one breakpoint. */

.mh-u-flex-col {
    flex-direction: column;
}

.mh-u-w-100 {
    width: 100%;
}

.mh-u-h-48px {
    height: 48px;
}

.mh-u-fwt-700 {
    font-weight: 700;
}
