/* ==========================================================================
   base.css
   Global reset, body background, tokens, and shared layout primitives.
   Edit here for page-wide base styles and CSS variables only.
   ========================================================================== */

html {
    min-height: 100vh;
    height: auto;
    margin: 0;
    padding: 0;
}

/* Mobile horizontal-overflow guard. The marketing/sales pages have decorative
   body::after glow pseudo-elements anchored with right: -35vw / width: 85vw
   that extend past the viewport right edge by ~35vw, plus the occasional wide
   inline element (long headline, embedded code, CTA pair). At ≤480px wide the
   resulting 24-32px horizontal scrollbar is a documented audit finding.
   `overflow-x: clip` is the modern mobile-safety fix — it clips overflow
   without creating a scrolling container, so position: sticky (the sales
   header) keeps working. Excludes .tenant-site so tenant pages keep their
   own overflow story. */
@media (max-width: 480px) {
    html:not(.tenant-site),
    body:not(.tenant-site) {
        overflow-x: clip;
    }
}

:root {
    /* ── Typography + layout primitives ── */
    --font-sans: 'DM Sans', system-ui, sans-serif;
    --font-heading: 'Montserrat', 'DM Sans', sans-serif;
    --platform-content-width: 1200px;

    /* ── Brand ── */
    --primary-blue: #0070B9;
    --primary-green: #56C045;

    /* ── Blue scale (brand-aligned, surfaces include nav, dark headers, links) ── */
    --blue-300: #86b7fe;            /* focus ring / light accent */
    --blue-500: #3778c2;            /* mid-blue (admin / table headers) */
    --blue-600: #1b3f73;            /* dark blue (nav, dark headers) — historical token */
    --blue-700: #215183;            /* deeper navy variant */
    --blue-800: #163766;            /* deeper navy variant */
    --blue-900: #082a47;            /* near-black navy */
    --blue-mid-600: #2b66a5;        /* secondary mid-blue (sales hero) */
    --blue-mid-700: #2b6ca3;        /* tighter sales-hero variant */
    --blue-mid-800: #2974d2;        /* link accent */
    --blue-deep-700: #295382;       /* hover-state dark blue */
    --blue-slate-500: #48607d;      /* blue-gray slate */
    --blue-slate-600: #5a6e82;      /* slightly darker blue-gray */

    /* ── Gray scale ── */
    --gray-50:  #f8f9fa;
    --gray-100: #f3f4f6;
    --gray-150: #f1f3f6;
    --gray-200: #e9ecef;
    --gray-300: #dee2e6;
    --gray-350: #d4d9e1;
    --gray-400: #ced4da;
    --gray-450: #aaaaaa;            /* legacy alias for #aaa hexes */
    --gray-500: #6c757d;
    --gray-550: #6b7280;            /* tailwind gray-500 — historically --light-text */
    --gray-560: #6a7689;            /* one-off blue-gray */
    --gray-570: #69707d;            /* navbar muted */
    --gray-580: #7d8592;            /* slate-mid */
    --gray-600: #4b5563;            /* tailwind gray-600 */
    --gray-650: #555555;            /* legacy alias for #555 hexes */
    --gray-700: #374151;            /* tailwind gray-700 */
    --gray-800: #1d2129;            /* near-black body text */
    --gray-850: #181a20;            /* darker variant */
    --gray-900: #0f172a;            /* near-black */

    /* ── Neutrals ── */
    --white: #ffffff;
    --black: #000000;
    --light-border: #e1e4e8;        /* historical alias for divider gray */

    /* ── Red scale (errors, danger buttons, alert backgrounds) ── */
    --red-50:  #fef2f2;
    --red-100: #fdeaea;
    --red-150: #fde1e0;
    --red-200: #fbe9ea;
    --red-300: #d9534f;
    --red-400: #c0392b;
    --red-500: #b13026;
    --red-600: #dc2626;             /* historical token (tailwind red-600) */
    --red-700: #b02a37;
    --red-750: #c9302c;
    --red-800: #b71c1c;
    --red-900: #991b1b;
    --red-950: #9f2331;
    --red-onfill-text: #a73322;     /* high-contrast red text on red-tinted bg */

    /* ── Green scale ── */
    --green-50:  #eaf7ee;
    --green-100: #c8ecd5;
    --green-200: #b2e8cc;
    --green-300: #7ee8a4;
    --green-400: #20a067;
    --green-500: #3fa83a;
    --green-550: #3aaa1e;
    --green-600: #1b7a3a;
    --green-700: #146644;
    --green-800: #065f46;
    --green-onfill-text: #1a6e2e;   /* high-contrast green text on green-tinted bg */

    /* ── Gold / amber (warnings, premium accents) ── */
    --gold-100: #fff3cd;
    --gold-150: #fff4cf;
    --gold-200: #fde68a;
    --gold-300: #f2e0a4;
    --gold-400: #e3b21a;
    --gold-500: #e9b83a;
    --gold-600: #c8a84b;
    --gold-700: #b8982f;
    --gold-800: #8a6a00;
    --gold-900: #6a4a00;

    /* ── Teal accent ── */
    --teal-300: #e3f7fa;
    --teal-500: #119da4;
    --teal-700: #06b6d4;

    /* ── Data-viz categorical accents (charts/dashboards) ──
       One vibrance family anchored on the two brand colors: every member is a
       saturated mid-tone matched to the green/blue pair so multi-series charts
       read as one design system, not a grab-bag of leftovers. The *-bright
       variants are the luminous dark-surface (navy) flips. Exposed app-wide as
       the --chart-1..6 series palette below (the shared chart-engine.js reads
       those via getComputedStyle); these MUST stay plain colors (or aliases to
       plain colors) — getComputedStyle cannot resolve color-mix. */
    --viz-green:         var(--primary-green);
    --viz-blue:          var(--primary-blue);
    --viz-amber:         #f0b429;
    --viz-cyan:          #00b8d9;
    --viz-violet:        #8b5cf6;
    --viz-coral:         #ee5a52;
    --viz-green-bright:  #67d455;
    --viz-blue-bright:   #4da6ff;
    --viz-amber-bright:  #ffc94d;
    --viz-cyan-bright:   #2fd4ee;
    --viz-violet-bright: #a78bfa;
    --viz-coral-bright:  #ff7a70;

    /* The app chart series palette — the domain-neutral tokens the shared
       chart-engine.js reads for its 6 categorical series colors (light here;
       the dark-surface flip to the *-bright twins is in body.theme-dark below).
       COMMONALITY_AUDIT cycle 5 promoted these out of the CRM domain so any
       surface can chart with one contract. Plain-color aliases only. */
    --chart-1: var(--viz-green);
    --chart-2: var(--viz-blue);
    --chart-3: var(--viz-amber);
    --chart-4: var(--viz-cyan);
    --chart-5: var(--viz-violet);
    --chart-6: var(--viz-coral);

    /* ── Light tint backgrounds (alert/info/hero panels) ── */
    --bg-tint-blue:   #f3f8fc;
    --bg-tint-blue-2: #e6eef7;
    --bg-tint-blue-3: #dde9f5;
    --bg-tint-green:  #e6f6ea;
    --bg-tint-amber:  #fff8e6;
    --bg-tint-teal:   #e3f7fa;
    --bg-tint-gray:   #f7fafb;

    /* ── Slate (blue-tinted gray family) ── */
    --slate-300:  #c2d3e9;
    --slate-400:  #c6d4e7;
    --slate-450:  #d8dde6;
    --slate-460:  #d6dee8;
    --slate-470:  #dbe9ff;
    --slate-500:  #64748b;
    --slate-550:  #4a5a55;
    --slate-700:  #2f4159;
    --slate-800:  #274a5e;
    --slate-900:  #2b3c50;

    /* ── Additional blue shades (mid + deep variants) ── */
    --blue-bright-500: #2563eb;          /* vivid focus blue */
    --blue-005:        #005fa0;          /* hover darken */
    --blue-mid-650:    #2b6cb0;          /* sales hero variant */
    --blue-deep-680:   #1a3e66;
    --blue-deep-690:   #1a4065;

    /* ── Additional green shades ── */
    --green-450:  #4aaa3a;
    --green-470:  #2d7a1e;
    --green-480:  #2f7a26;
    --green-490:  #2b7d2a;
    --green-495:  #1a7a4a;
    --green-soft: #2b4a33;                /* dark muted green */
    --green-100b: #bfe4c8;
    --green-90b:  #d1fae5;
    --green-95b:  #e5f4e9;
    --green-97b:  #e6f9ec;
    --green-98b:  #e8f6ec;
    --green-99b:  #f0f7f0;
    --green-99c:  #f0fbf5;
    --green-99d:  #f6f9f5;
    --green-99e:  #f8fcf3;

    /* ── Additional red / pink shades ── */
    --red-pink-1: #f3c7c7;
    --red-pink-2: #f5c2c2;
    --red-pink-3: #fca5a5;
    --red-pink-4: #fff5f5;
    --red-650:    #b91c1c;
    --red-740:    #9a3b3b;
    --red-760:    #ba2342;
    --red-770:    #7a0e0e;

    /* ── Additional gold / amber shades ── */
    --gold-130: #fdecea;                  /* warm pink-cream */
    --gold-160: #fef3c7;
    --gold-180: #fff7dc;
    --gold-190: #fefce8;
    --gold-250: #f1d68b;
    --gold-350: #f7d36a;
    --gold-450: #d99100;
    --gold-460: #d39d24;
    --gold-650: #664d03;
    --gold-660: #713f12;
    --gold-680: #8a6c1e;
    --gold-720: #92400e;

    /* ── Light backgrounds (paper / mist tints — off-whites used across pages) ── */
    --bg-paper-1: #f5f6f8;
    --bg-paper-2: #f4f6f8;
    --bg-paper-3: #f6f7f9;
    --bg-paper-4: #f5f7fb;
    --bg-paper-5: #eef1f5;
    --bg-paper-6: #f0f4f8;
    --bg-paper-7: #f1f3f6;
    --bg-paper-8: #f2f4f9;
    --bg-paper-9: #f4f4f5;
    --bg-paper-10: #fafbfc;
    --bg-paper-11: #fbfcfe;
    --bg-paper-12: #f9fbfe;
    --bg-paper-13: #fafcff;
    --bg-paper-14: #fafafa;
    --bg-paper-15: #e0e0e0;
    --bg-paper-16: #e2e8f0;
    --bg-paper-17: #f8fafc;
    --bg-paper-18: #f7fafc;
    --bg-paper-19: #f7faff;
    --bg-paper-20: #f6faff;
    --bg-paper-21: #f7f9fc;
    --bg-paper-22: #f0f6ff;
    --bg-paper-23: #f2f8ff;
    --bg-paper-24: #eef4ff;

    /* ── Additional dark backgrounds / muted ── */
    --gray-440: #bbbbbb;                  /* mid-light gray (close to --gray-450) */
    --gray-420: #dddddd;
    --gray-810: #232533;
    --gray-820: #232539;
    --gray-830: #24262d;

    --slate-580: #7d8f9e;

    /* ── Misc accent (yellow caution dot) ── */
    --yellow-500: #f5f12c;

    /* ── Historical aliases (existing references — keep working) ── */
    --light-text: var(--gray-550);  /* alias of --gray-550 for back-compat */

    /* ── TABLE COLUMN-WIDTH LADDER (solution-wide) ────────────────────────
       ONE definition for every data-table surface (.admin-table, .crm-table,
       .client-table). Units are intentionally intrinsic (ch / rem) so columns
       scale with font-size and stay readable at every viewport. NO PX VALUES.

       CENTRALIZED 2026-08-05. These were defined VERBATIM TWICE
       (Admin/modules/_tokens.css + ClientApp/client-base.css) and hardcoded a
       third time as literals in Crm/modules/_data.css — and had already DRIFTED
       (actions was 14rem on admin/client, 13rem on CRM). client-base.css's own
       comment recorded the intent: "duplicate until harmonization is complete,
       then evaluate centralization" (2026-05-28). This is that centralization.
       CRM's actions column moves 13rem -> 14rem to match the majority.

       The VALUES are shared; the APPLICATION mechanism stays per-domain and
       must: .admin-table is a flex column whose thead/rows are their own
       `display: table` context, so <colgroup> cannot reach it and admin pins
       width on the th AND td; .crm-table and .client-table are real tables
       where <col class="..."> works. */
    --col-checkbox: 3ch;     /* checkbox cells (cell-checkbox) */
    --col-id:       7ch;     /* short numeric ID columns */
    --col-status:   10ch;    /* status badge/text columns — width hint, content can expand */
    --col-actions:  14rem;   /* one or two action buttons inline + buffer */
    --col-image:    14rem;   /* thumbnail preview cells */
    --col-state:    9ch;     /* state-code columns incl. the dual "OH / FL" render */
    --col-phone:    17ch;    /* formatted US phone incl. " (DNC)" suffix room */
    --col-datetime: 17ch;    /* nowrap local-time datetime columns ("2026-07-27 14:08" + gutter) */
    --col-identity: 30ch;    /* identity cell: name + one muted meta line — the CRM list
                                convention (owner de-cram ruling 2026-08-18; first adopter
                                Kinetic Inbox under .crm-table.is-fixed). Sized so a typical
                                "First Last" stays one line and the meta line
                                ("Mortgage · Score 4 · Received 2026-08-07 · Activity —")
                                wraps once at most. */
    /* THE ALIGNMENT SPINE — the ONE width the header, the app shell (rail + content) and the
       footer all share, so those three read as a single aligned stack. Every surface consumed
       `--platform-content-width` directly before 2026-08-07; they now consume THIS, which
       defaults to it, so a surface can widen the WHOLE spine coherently instead of widening one
       band and leaving the others behind. Owner ruling 2026-08-07: "I'm not willing to have the
       table go outside of the navigation row… either the header and footer need to become a
       wider width to accommodate the wider table and the rail so that it all looks coherent, or
       the table is going to have to scroll… It has to be professional looking across the board."
       Consumers: .topnav-container (layout.css), .platform-footer-inner (below),
       .app-shell:has(.app-rail) (app-shell.css). */
    --platform-spine-w: var(--platform-content-width);

    /* READABILITY FLOOR for a data table. Below this its wrapper SCROLLS rather than crushing
       columns, and the admin data-page shell derives its own max-width from this + the rail +
       the shell gap (layout/app-shell.css), so where the viewport allows the table fits exactly
       and nothing scrolls. DERIVED, not a round number: the widest admin table (/Admin/Users,
       10 columns) pins checkbox 3ch + actions 14rem + datetime 17ch + two status 10ch = ~544px;
       its remaining columns need ~289px for the longest real email measured on prod, ~120px
       each for first/last name, ~90px status and ~70px count = ~848px. 544 + 848 = 1392px,
       which is 87rem at the 16px root. */
    --table-min-w:  87rem;
    --col-email:    14ch;    /* the Catalog's ONE merged Email column (critic ruling 2026-08-05):
                                header "Email" fits; cells ellipsize by design with the full
                                both-addresses tooltip; kept lean so the unpinned identity
                                columns keep their share. */
}

/* Dark-surface flip for the app chart series palette — the *-bright twins on
   body.theme-dark (COMMONALITY_AUDIT cycle 5). ONLY the --chart-* series tokens
   flip here; platform-chrome dark styling remains a separate, per-domain concern. */
body.theme-dark {
    --chart-1: var(--viz-green-bright);
    --chart-2: var(--viz-blue-bright);
    --chart-3: var(--viz-amber-bright);
    --chart-4: var(--viz-cyan-bright);
    --chart-5: var(--viz-violet-bright);
    --chart-6: var(--viz-coral-bright);
}

body {
    min-height: 100vh;
    height: auto;
    margin: 0;
    padding: 0;
    background-color: white;
    background-attachment: fixed;
    position: relative;
    font-family: var(--font-sans);
    line-height: 1.65;
}

a {
    color: var(--primary-blue);
    text-decoration: none;
}

/* No global a:hover underline. The previous rule (a 1990s-era reset) produced
   pervasive hover-underlines on buttons-styled-as-anchors, nav items, and
   data-table row links — every anchor in the app. Specific contexts that
   genuinely want hover-underline (body-text inline links, the sales legal
   strip) carry their own targeted rule. Components that style anchors as
   buttons (.client-btn, .edit-button, .cta-button) keep `text-decoration:
   none` from their own base rule. */

h1, h2, h3, h4, h5, h6 {
    text-wrap: balance;
}

/* Clickable table rows — solution-wide single source (admin, CRM, client all
   load base.css). Paired with the global data-row-href handler in
   Shared/platform-interactions.js; the whole <tr class="clickable-row"> navigates. */
/* NO hover rule here. `.admin-table tr:hover td` already exists in
   Admin/modules/_components.css, and adding a `.clickable-row:hover > td` background in
   base.css would be a SECOND appearance rule for the same cells reached through a
   different context — the C1 contextual-override shape Gate-CssNoOverride exists to
   forbid, and a duplicate for admin regardless. The real defect here was never the
   missing hover cue: a <tr> with a JS click handler is not keyboard-focusable and not
   announced as a link, so the fix is the REAL ANCHOR now carried by each list's identity
   cell (Users/Index, Tenants/Index). Keep the mouse cue where the surface already owns it. */
.clickable-row {
    cursor: pointer;
}

/* A rendered UTC instant is ONE value and must never break mid-value — the same fact
   `.cell-date` asserts at the column level, stated where it is actually true: on the
   element the ONE universal partial emits (Pages/Shared/_LocalTime.cshtml:27), which
   js/Shared/local-time.js then rewrites in place. Declared here rather than as a
   column classification because that classification is INFERRED from cell text and is
   therefore data-dependent: MEASURED 2026-08-17, a Last-pull cell reading
   "2026-08-17 14:08 UTC batch 4711" returns NaN from Date.parse, so the same column is
   `cell-date` on one tenant and `cell-text` on the next (table-engine.js:81,100). */
time[data-local-time] {
    white-space: nowrap;
}

    /* Decorative blue/green gradient glows for marketing/sales chrome. Excluded
       on tenant sites (their own theme owns the bg) AND on admin pages (the
       admin shell needs a clean readable background; the gradients were bleeding
       through the dashboard card at desktop widths — round-1 audit found this
       sub-4 on every admin page). Admin opts in to its own admin-page bg via
       Admin/modules/_base.css. */

    body:not(.tenant-site):not(.admin-page)::before {
        content: "";
        position: fixed;
        pointer-events: none;
        z-index: 0;
        transition: opacity 0.7s;
        mix-blend-mode: lighten;
        top: 30vh;
        left: -35vw;
        width: 110vw;
        height: 80vh;
        background: radial-gradient(circle at 20% 70%, color-mix(in srgb, var(--primary-blue) 11%, transparent) 0, transparent 85%);
        opacity: 0.85;
        filter: blur(8px);
        animation: bgGlow1 9s ease-in-out infinite alternate;
    }

    body:not(.tenant-site):not(.admin-page)::after {
        content: "";
        position: fixed;
        pointer-events: none;
        z-index: 0;
        transition: opacity 0.7s;
        mix-blend-mode: lighten;
        top: -18vh;
        right: -35vw;
        width: 85vw;
        height: 70vh;
        background: radial-gradient(circle at 80% 30%, color-mix(in srgb, var(--primary-green) 7%, transparent) 0, transparent 80%);
        opacity: 0.75;
        filter: blur(10px);
        animation: bgGlow2 8s ease-in-out infinite alternate;
    }

@keyframes bgGlow1 {
    0% {
        opacity: 0.90;
        filter: blur(0px);
    }

    60% {
        opacity: 1;
        filter: blur(5px);
    }

    100% {
        opacity: 0.8;
        filter: blur(2px);
    }
}

@keyframes bgGlow2 {
    0% {
        opacity: 0.80;
        filter: blur(0px);
    }

    60% {
        opacity: 1;
        filter: blur(7px);
    }

    100% {
        opacity: 0.7;
        filter: blur(2.5px);
    }
}

.main-content {
    min-height: 1px;
    padding-bottom: 0;
}

.page-container {
    max-width: var(--platform-content-width);
    width: 100%;
    margin: 0 auto;
    padding: 0.75rem 1rem 1rem;
    box-sizing: border-box;
}

.status-banner-rail {
    /* Centers the universal _StatusBanners on the crm/client surfaces in the SAME content column the
       page body uses. Every crm/client page wraps its content in .client-page-inner (1200px, 0.9rem
       gutter, no mobile override); the banner rail is a SIBLING above it, so it MIRRORS that geometry
       EXACTLY to share the same left/right edges (admin gets the equivalent via .admin-page-inner).
       The banner supplies its own vertical spacing. Follows the shared spine so it stays aligned
       with the header on admin, where the spine is wider. */
    max-width: var(--platform-spine-w);
    margin-inline: auto;
    padding-inline: 0.9rem;
    box-sizing: border-box;
}

@media (max-width: 600px) {
    .page-container {
        max-width: 100%;
        padding: 0.75rem 0.5rem 1rem 0.5rem;
    }
}

body.tenant-site main > .page-container {
    max-width: 100%;
    padding: 0;
}

body.tenant-site .tenant-theme {
    padding: 0;
}

/* ==========================================================================
   Platform Footer (non-tenant, non-admin pages)
   ========================================================================== */
.platform-footer {
    color: var(--gray-500);
    padding: 1rem 0;
}

/* Print: chrome never prints (platform-wide; each piece hidden in its owning
   sheet). This sheet owns the footer, the status-banner rail, the cookie
   banner, and the fixed decorative background gradients (fixed elements
   re-paint on EVERY printed page in some engines). */
@media print {
    .platform-footer,
    .status-banner-rail,
    .cookie-banner,
    body:not(.tenant-site):not(.admin-page)::before,
    body:not(.tenant-site):not(.admin-page)::after {
        display: none;
    }
}

/* ADMIN WIDENS THE WHOLE SPINE, not just its tables. The nav/IA overhaul (2026-07-27) put a
   236px rail INSIDE the 1200px spine, so admin tables silently lost 21% of their width and
   /Admin/Users crushed every email into 59px (measured on prod 2026-08-07). Recovering that by
   widening only the content band would push it past the header and footer — the incoherence the
   owner rejected on sight in July, and rejected again here. So the spine itself widens on admin:
   header, rail, content and footer move together and stay one aligned stack.

   DERIVED, and applied to EVERY admin page rather than only the table-dominated ones: a spine
   that changed width as you moved between /Admin/Users and /Admin/Users/Edit would be its own
   kind of sloppy. Value = the table's readability floor + the rail + the shell gap, so a data
   table fits exactly inside the spine where the viewport allows, and scrolls within its own card
   where it does not — never outside the navigation row. */
body.admin-page {
    --platform-spine-w: calc(var(--table-min-w) + var(--app-rail-w) + var(--app-shell-gap));
}

/* The CRM app widens the spine the SAME derived way (owner de-cram ruling 2026-08-18,
   the admin exemplar above copied exactly). --crm-table-min-w is the 6-column CRM list
   readability floor, DERIVED: identity 30ch (~240px) + status 10ch (~80px) + responsible
   ~10rem (the broker select + Set inline, 160px) + geography ~12ch ("Lawrenceville, GA",
   ~110px) + phone 17ch (~136px) + actions 14rem (224px) + six cells' padding (~110px)
   ≈ 1060px = 67rem at the 16px root. Where the viewport allows, an is-fixed CRM table
   fits exactly inside the spine; where it does not, the table scrolls within its own
   wrapper — never outside the navigation row (the 2026-08-07 owner ruling above). */
body.crm-app {
    --crm-table-min-w: 67rem;
    /* Unlike the admin twin above, the side paddings are ADDED: the app shell is
       border-box, so its --app-shell-pad-side comes out of the content column —
       without this term the table sits exactly 2x pad wider than its wrapper and
       clips the Actions column (measured 2026-08-18 at 1440: ~32px cut). */
    --platform-spine-w: calc(var(--crm-table-min-w) + var(--app-rail-w) + var(--app-shell-gap) + (var(--app-shell-pad-side) * 2));
}

.platform-footer-inner {
    max-width: var(--platform-spine-w);
    width: 100%;
    /* border-box so width:100% absorbs the 1rem side padding — without it the
       footer was 100%+2rem and overflowed the viewport by 32px at tablet (768),
       the shared cause of horizontal-overflow on every client/onboarding/marketing
       page (admin hides the footer, which is why only those domains showed it). */
    box-sizing: border-box;
    margin: 0 auto;
    padding: 1rem 1rem 0;
    border-top: 1px solid var(--gray-300);
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
}

.platform-footer-nav {
    display: flex;
    /* MEASURED 2026-08-07: without this the row was 472px inside a 375px viewport, so
       "Do Not Sell or Share My Information" collapsed to a ~60px 4-line column, "Data
       Deletion Instructions" ran off the right edge, and "Manage cookies" was not
       visible AT ALL. The page itself does not scroll horizontally, so the overflow was
       UNREACHABLE — these are compliance links. Its sibling `.sales-legal-footer-nav`
       below has always wrapped; COMMONALITY cycle 19 shared the legal-links PARTIAL
       between the two surfaces but left each wrapper's layout per-surface, and the two
       silently diverged. Sharing markup without sharing the layout contract is what
       produced this. */
    flex-wrap: wrap;
    align-items: center;
}

.platform-footer-link {
    color: var(--gray-500);
    text-decoration: none;
    padding: 0 0.5rem;
}

.platform-footer-link:hover {
    color: var(--blue-deep-700);
}

.platform-footer-sep {
    color: var(--gray-500);
    padding: 0 0.25rem;
}

/* ==========================================================================
   Sales legal footer — thin compliance strip rendered by _SalesLayout BELOW
   the page's own `.sales-footer`. Color-matched to the brand-blue page
   footer above (sales-base.css `.sales-footer` uses `var(--primary-blue)`)
   so the two read as one continuous footer band, with a subtle top divider
   marking the legal row. Privacy + Terms + Manage-cookies on every sales
   page without per-page edits.
   ========================================================================== */
.sales-legal-footer {
    background: var(--primary-blue);
    color: rgba(255, 255, 255, 0.55);
    padding: 0.55rem 0;
    font-size: 0.78rem;
    border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.sales-legal-footer-inner {
    max-width: var(--platform-content-width);
    width: 100%;
    /* border-box so the 1.5rem side padding sits INSIDE width:100% — without it
       this strip was 100%+3rem and overflowed the viewport by ~33-48px at tablet
       (768) on every sales page (same class of bug already fixed on the platform
       + legal footers). Verified live 2026-05-26: it was the sole element wider
       than the 768 viewport. */
    box-sizing: border-box;
    margin: 0 auto;
    padding: 0 1.5rem;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
}

.sales-legal-footer-nav {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.sales-legal-footer-nav a {
    color: rgba(255, 255, 255, 0.6);
    text-decoration: none;
    padding: 0 0.5rem;
}

.sales-legal-footer-nav a:hover {
    color: var(--white);
}

.sales-legal-footer-sep {
    color: rgba(255, 255, 255, 0.25);
    padding: 0 0.1rem;
}

/* ==========================================================================
   Cookie consent banner — shown on first visit when the visitor is in a
   regulated region (EU/EEA, California). Renders nothing once a consent
   cookie is set. Bottom-fixed strip; not modal-blocking so users can keep
   reading; styled subtle but visible.

   Tokens only: `var(--blue-600)` (dark-blue band per base.css token table)
   for the background, white-tint `rgba(255,255,255,X)` pattern for body text
   matching `sales-base.css` `.sales-footer-copy` / `.sales-footer-links a`,
   `var(--primary-blue)` / `var(--primary-green)` for the primary button.
   ========================================================================== */
.cookie-banner {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9999;
    background: var(--blue-600);
    color: rgba(255, 255, 255, 0.9);
    border-top: 1px solid var(--primary-blue);
    padding: 0.85rem 1rem;
    box-shadow: 0 -6px 20px color-mix(in srgb, var(--blue-600) 30%, transparent);
}

.cookie-banner.is-hidden {
    display: none;
}

.cookie-banner-inner {
    max-width: var(--platform-content-width);
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.cookie-banner-text {
    flex: 1 1 360px;
    font-size: 0.9rem;
    line-height: 1.4;
    min-width: 0;
}

.cookie-banner-text strong {
    color: rgba(255, 255, 255, 1);
}

.cookie-banner-text a {
    color: rgba(255, 255, 255, 0.85);
}

.cookie-banner-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.cookie-banner-btn {
    appearance: none;
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 0.45rem 0.95rem;
    font: 500 0.85rem/1 var(--font-sans);
    cursor: pointer;
    transition: background-color 0.12s ease-in-out, border-color 0.12s ease-in-out;
}

.cookie-banner-btn--ghost {
    background: transparent;
    color: rgba(255, 255, 255, 0.9);
    border-color: rgba(255, 255, 255, 0.3);
}

.cookie-banner-btn--ghost:hover {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.55);
}

.cookie-banner-btn--primary {
    background: var(--primary-blue);
    color: rgba(255, 255, 255, 1);
    border-color: var(--primary-blue);
}

.cookie-banner-btn--primary:hover {
    /* Accept-all inherits white ink from .cookie-banner-btn; on --primary-green that is 2.33:1.
       Consent controls are the last place a user should have to squint, so the ink swaps with
       the fill — same principle as .cta-button--accent in marketing.css. */
    background: var(--primary-green);
    color: var(--black);
    border-color: var(--primary-green);
}

/* 1x1 tracking-pixel noscript fallback (Meta Pixel + similar). */
.pixel-noscript {
    display: none;
}

/* Invisible reCAPTCHA mount point (_RecaptchaInvisible partial). The widget renders no inline
   box — Google appends its own fixed-position badge + challenge overlay to <body>; the mount
   only carries the sitekey for grecaptcha.render/execute, so it is hidden. */
.recaptcha-invisible {
    display: none;
}

/* ==========================================================================
   Platform Alert (error messages outside admin/client context)
   ========================================================================== */
.platform-alert-wrap {
    margin-top: 1rem;
}

.platform-alert {
    padding: 0.75rem 1.25rem;
    border-radius: 8px;
    background: var(--red-150);
    color: var(--red-onfill-text);
    font-weight: 500;
}
