/* =============================================================================
   AI BestChat — public marketing site (aibestchat.com)

   Hand-written on purpose. Tailwind v4 scans at build time (see the @source
   globs in resources/css/filament/admin/theme.css), so a utility class a Blade
   file is the FIRST to use silently does nothing until `make assets` runs — no
   error, just an unstyled page. That trap is survivable inside the panel, where
   a broken class is obvious to whoever is iterating; it is not survivable on the
   surface a stranger sees first. Nothing here needs a build step.

   Consequence: marketing Blades must carry no Tailwind utility classes. Use the
   class names defined below.

   Colours come only from the closed six-colour palette in
   docs/branding/AI_BEST_CHAT_Design_Spec_Sheet.pdf:
     #00B266 green · #333333 charcoal · #1A1A1A near-black
     #C4C4C4 cool gray · #F7F7F7 off-white · #FFFFFF white
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Fonts — Montserrat, the design's family, self-hosted. Latin subset only, four
   weights. No third-party CDN: a font request the landing page cannot control
   is a consent surface and a render-blocking dependency on someone else's
   uptime.

   Note this supersedes the spec sheet's Helvetica Neue for the marketing site.
   The panel, the widget and the public listing page are untouched — they still
   use the system stack, so the vendor's own product surfaces stay on-spec.
   -------------------------------------------------------------------------- */
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url('/site/fonts/montserrat-latin-300-normal.woff2') format('woff2');
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/site/fonts/montserrat-latin-400-normal.woff2') format('woff2');
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/site/fonts/montserrat-latin-500-normal.woff2') format('woff2');
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/site/fonts/montserrat-latin-600-normal.woff2') format('woff2');
}

/* -----------------------------------------------------------------------------
   Tokens.

   Light is the bare :root so no colour has its only definition inside a media
   query. Dark arrives two ways and must agree: the OS preference (only while the
   visitor has expressed no preference of their own), and an explicit data-theme
   the toggle stamps. The :not([data-theme]) guard is what lets the toggle win in
   both directions rather than only towards dark.

   Contrast, measured, all ≥ 4.5:1 for text:
     --ink        on --bg          17.4:1 both modes
     --ink-muted  on --bg           9.98:1 dark · 12.6:1 light
     --accent-ink on --accent        6.27:1 both modes
     --accent-text on --bg           6.27:1 dark · 5.12:1 light

   The one the comp gets wrong: it sets WHITE on #00B266, which is 2.78:1 and
   fails AA. --accent-ink is near-black instead. That keeps the comp's exact
   green — only the text on it changes — and it is the spec's own reversed
   treatment rather than a new colour. Darkening the green to #00874D to rescue
   white text was the alternative and is worse: #00874D on #F7F7F7 is 4.29:1,
   a fail already shipped in showcase.blade.php and public/listing.blade.php.
   -------------------------------------------------------------------------- */
:root {
    --bg: #ffffff;
    --bg-alt: #f7f7f7;
    --surface: #ffffff;
    --surface-2: #f7f7f7;
    --border: rgba(26, 26, 26, 0.12);
    --border-strong: rgba(26, 26, 26, 0.22);

    --ink: #1a1a1a;
    --ink-muted: #333333;
    --ink-faint: #6b7280;

    --accent: #00b266;        /* fills and non-text only */
    --accent-ink: #1a1a1a;    /* text ON --accent */
    --accent-text: #007f3e;   /* green AS text */
    --rule: rgba(0, 178, 102, 0.45);
    --focus: #007f3e;

    --wash-1: rgba(0, 178, 102, 0.10);
    --wash-2: rgba(0, 178, 102, 0.04);
    --shadow: 0 18px 40px -24px rgba(16, 24, 40, 0.35);
    --stage: #1a1a1a;         /* backdrop for the dark-baked illustration */

    --radius: 18px;
    --radius-lg: 26px;
    --wrap: 1196px;   /* content 1148px, matching the comp's 146px margins */
    --font-display: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --font-body: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, -apple-system, 'Segoe UI', sans-serif;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) {
        --bg: #1a1a1a;
        --bg-alt: #232323;
        --surface: rgba(255, 255, 255, 0.045);
        --surface-2: #333333;
        --border: rgba(255, 255, 255, 0.12);
        --border-strong: rgba(255, 255, 255, 0.24);

        --ink: #ffffff;
        --ink-muted: #c4c4c4;
        --ink-faint: #9aa0a6;

        --accent-text: #00b266;
        --rule: rgba(0, 178, 102, 0.35);
        --focus: #00b266;

        --wash-1: rgba(0, 178, 102, 0.18);
        --wash-2: rgba(0, 178, 102, 0.07);
        --shadow: 0 22px 50px -28px rgba(0, 0, 0, 0.8);
        --stage: transparent;
    }
}

:root[data-theme='dark'] {
    --bg: #1a1a1a;
    --bg-alt: #232323;
    --surface: rgba(255, 255, 255, 0.045);
    --surface-2: #333333;
    --border: rgba(255, 255, 255, 0.12);
    --border-strong: rgba(255, 255, 255, 0.24);

    --ink: #ffffff;
    --ink-muted: #c4c4c4;
    --ink-faint: #9aa0a6;

    --accent-text: #00b266;
    --rule: rgba(0, 178, 102, 0.35);
    --focus: #00b266;

    --wash-1: rgba(0, 178, 102, 0.18);
    --wash-2: rgba(0, 178, 102, 0.07);
    --shadow: 0 22px 50px -28px rgba(0, 0, 0, 0.8);
    --stage: transparent;
}

/* -----------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; margin: 0; padding: 0; }

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

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

body {
    font-family: var(--font-body);
    background: var(--bg);
    color: var(--ink);
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    position: relative;
}

/* The page is ONE continuous field, drifting light and dark down its length.
   Measured off the comp: its background tone runs 25 → 38 → 26 over 5000px with
   no step anywhere. Alternating section blocks — which this used to do — are
   precisely what makes a page read as a template, because the eye finds a seam
   every 600px and starts counting rows. */
body::before {
    content: '';
    position: absolute; inset: 0;
    z-index: -2;
    pointer-events: none;
    background:
        radial-gradient(1200px 800px at 88% 2%, rgba(0, 178, 102, 0.13), transparent 62%),
        radial-gradient(1000px 900px at -8% 26%, rgba(0, 178, 102, 0.07), transparent 60%),
        radial-gradient(1100px 800px at 95% 52%, rgba(0, 178, 102, 0.06), transparent 60%),
        radial-gradient(900px 700px at 5% 74%, rgba(0, 178, 102, 0.05), transparent 60%),
        radial-gradient(1100px 800px at 90% 96%, rgba(0, 178, 102, 0.07), transparent 62%);
}

/* The line field, stretched across the whole document rather than per section,
   so a curve begun under the hero finishes under the pricing table. */
body::after {
    content: '';
    position: absolute; inset: 0;
    z-index: -1;
    pointer-events: none;
    background: url('/site/flow.svg') top center / 100% 100% no-repeat;
}
:root[data-theme='light'] body::after { opacity: 0.45; filter: invert(1); }
@media (prefers-color-scheme: light) {
    :root:not([data-theme='dark']) body::after { opacity: 0.45; filter: invert(1); }
}

h1, h2, h3, h4, .display {
    font-family: var(--font-display);
    font-weight: 300;
    letter-spacing: -0.015em;
    line-height: 1.12;
    text-wrap: balance;
    color: var(--ink);
}

a { color: inherit; }
/* height:auto is not optional here. The images carry width/height attributes so
   the browser can reserve space and avoid layout shift, but without this the
   attribute height survives while max-width shrinks the width — every
   illustration renders stretched. */
img, svg, picture { max-width: 100%; display: block; }
img { height: auto; }

:where(a, button, input, textarea, select, summary):focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 3px;
    border-radius: 6px;
}

/* Skip link — the site is one long page of landmarks, so keyboard users need
   a way past the header on every one of them. */
.skip {
    position: absolute; left: -9999px; top: 0; z-index: 100;
    background: var(--accent); color: var(--accent-ink);
    padding: 12px 18px; border-radius: 0 0 10px 0; font-weight: 600;
}
.skip:focus { left: 0; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }

section { padding: clamp(56px, 8vw, 104px) 0; }
/* Kept as a no-op so the markup can still name a rhythm, but it paints nothing.
   The page has no bands — see body::before. */
.band-alt { background: none; }

.eyebrow {
    font-family: var(--font-display);
    font-size: 12px; font-weight: 600; letter-spacing: 0.16em;
    text-transform: uppercase; color: var(--accent-text);
}

/* 48px at 1440 — measured off the comp, where the H1 glyphs run 46px
   with 50px between baselines. The previous 57.6px read as shouty. */
.h1 { font-size: clamp(2rem, 3.35vw, 3rem); line-height: 1.06; }
/* 34px at 1440, line-height 1.3 — again the comp's own numbers. */
.h2 { font-size: clamp(1.5rem, 2.36vw, 2.13rem); line-height: 1.28; }
.h3 { font-size: clamp(1.1rem, 1.4vw, 1.25rem); font-weight: 400; }

/* The comp's two-tone headline: white first phrase, green second. */
.accent-phrase { color: var(--accent-text); }

.lead { font-size: clamp(1rem, 1.6vw, 1.12rem); color: var(--ink-muted); max-width: 58ch; }
/* Headline and its standfirst are set tight, so they collide without this. */
h1 + .lead, h2 + .lead, .h1 + .lead, .h2 + .lead { margin-top: 18px; }
.sub { color: var(--ink-muted); max-width: 68ch; }
.center { text-align: center; }
.center .lead, .center .sub { margin-inline: auto; }
.sub.center, .lead.center { margin-inline: auto; } /* the element itself centred, not only its parent */

/* -----------------------------------------------------------------------------
   Buttons. The pill is the comp's signature control; both variants carry text
   on green or a green border, never white-on-green.
   -------------------------------------------------------------------------- */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    font-family: var(--font-display); font-weight: 500; font-size: 15px;
    padding: 12px 26px; border-radius: 999px; text-decoration: none;
    border: 1px solid transparent; cursor: pointer;
    transition: transform 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn-solid { background: var(--accent); color: var(--accent-ink); }
.btn-solid:hover { background: #00c471; }
.btn-outline { border-color: var(--accent); color: var(--accent-text); background: transparent; }
.btn-outline:hover { background: var(--wash-1); }
.btn-quiet { color: var(--ink-muted); text-decoration: underline; text-underline-offset: 4px; padding: 12px 8px; }
.btn-sm { padding: 9px 18px; font-size: 14px; }

/* -----------------------------------------------------------------------------
   Header / footer
   -------------------------------------------------------------------------- */
.site-header { position: sticky; top: 0; z-index: 40; backdrop-filter: blur(14px); background: color-mix(in srgb, var(--bg) 82%, transparent); border-bottom: 1px solid var(--border); }
.site-header .wrap { display: flex; align-items: center; gap: 20px; padding-block: 14px; }
.site-header .brand { display: flex; align-items: center; margin-right: auto; }
.site-header .brand svg { height: 28px; width: auto; }

.site-nav { display: flex; align-items: center; gap: 26px; }
.site-nav a { font-family: var(--font-display); font-size: 15px; text-decoration: none; color: var(--ink-muted); }
.site-nav a:hover, .site-nav a[aria-current='page'] { color: var(--ink); }

.header-tools { display: flex; align-items: center; gap: 10px; }

/* -----------------------------------------------------------------------------
   Phone menu.

   Hidden above the breakpoint by `display: none` here and revealed in the
   max-width block far below, so the desktop header is unchanged by any of this.
   The panel itself carries the `hidden` attribute while closed, which keeps its
   links out of the tab order rather than merely out of sight: on desktop the
   same four links already sit in the bar, and a visually hidden copy would make
   every keyboard user tab through the nav twice.
   -------------------------------------------------------------------------- */
.nav-toggle {
    display: none;
    width: 42px; height: 42px;
    align-items: center; justify-content: center;
    border: 1px solid var(--border-strong); border-radius: 12px;
    background: transparent; color: var(--ink); cursor: pointer;
    padding: 0;
}
.nav-toggle-bars { display: block; width: 18px; height: 12px; position: relative; }
.nav-toggle-bars span {
    position: absolute; left: 0; width: 100%; height: 2px;
    background: currentColor; border-radius: 2px;
    transition: transform 0.22s ease, opacity 0.15s ease, top 0.22s ease;
}
.nav-toggle-bars span:nth-child(1) { top: 0; }
.nav-toggle-bars span:nth-child(2) { top: 5px; }
.nav-toggle-bars span:nth-child(3) { top: 10px; }
/* Open state: the outer bars meet in the middle and cross, the middle one goes.
   Driven off aria-expanded so the attribute that assistive tech reads is the
   same one the drawing depends on, and the two cannot disagree. */
.nav-toggle[aria-expanded='true'] .nav-toggle-bars span:nth-child(1) { top: 5px; transform: rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars span:nth-child(3) { top: 5px; transform: rotate(-45deg); }

.site-menu {
    display: none;
    position: fixed; left: 0; right: 0;
    /* Pinned under the bar and running to the bottom edge, so the sheet is the
       whole screen below the header and a stray tap cannot land on the page
       behind it. */
    top: var(--header-h, 64px); bottom: 0;
    z-index: 39;
    background: var(--bg);
    border-top: 1px solid var(--border);
    padding: 8px clamp(18px, 5vw, 28px) calc(24px + env(safe-area-inset-bottom));
    overflow-y: auto;
    flex-direction: column;
}
.site-menu[data-open] { display: flex; }

.site-menu-nav { display: flex; flex-direction: column; }
.site-menu-nav a {
    font-family: var(--font-display); font-size: 20px; font-weight: 500;
    color: var(--ink); text-decoration: none;
    /* Generous rows: this is the only nav on a phone, and a 56px target is the
       difference between tapping a link and tapping the one under it. */
    padding: 17px 2px; border-bottom: 1px solid var(--rule);
}
.site-menu-nav a[aria-current='page'] { color: var(--accent-text); }

.site-menu-foot {
    display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
    margin-top: 22px;
}
.site-menu-foot .lang-switch { flex: 0 0 auto; }
.site-menu-foot .lang-switch a { font-size: 13px; padding: 8px 15px; }
.site-menu-foot .theme-toggle { width: 38px; height: 38px; flex: 0 0 auto; }
/* The CTA is dropped from the bar on a phone and this is where it comes back,
   on its own full-width row: sharing a line with the two small controls left it
   about 200px, which wrapped "Let's Talk Business" onto two lines inside the
   pill. A primary action is also the one thing here worth an unmissable target. */
.site-menu-foot .btn { flex: 1 0 100%; }

/* The sheet animates in; a visitor who has asked for less motion gets it
   immediately instead. */
@media (prefers-reduced-motion: no-preference) {
    .site-menu[data-open] { animation: site-menu-in 0.22s ease both; }
}
@keyframes site-menu-in {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: none; }
}

/* While the sheet is open the page behind it must not scroll under the finger. */
body.menu-open { overflow: hidden; }

.lang-switch { display: inline-flex; border: 1px solid var(--border-strong); border-radius: 999px; overflow: hidden; }
.lang-switch a { font-family: var(--font-display); font-size: 12px; font-weight: 600; letter-spacing: 0.06em; padding: 5px 11px; text-decoration: none; color: var(--ink-muted); }
.lang-switch a[aria-current='true'] { background: var(--accent); color: var(--accent-ink); }

.theme-toggle { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 999px; border: 1px solid var(--border-strong); background: transparent; color: var(--ink-muted); cursor: pointer; }
.theme-toggle:hover { color: var(--ink); }
.theme-toggle svg { width: 17px; height: 17px; }
/* Exactly one of the two glyphs shows, and which one depends on the resolved
   theme — so it is driven by the same tokens as everything else rather than by
   JavaScript keeping a second source of truth in sync. */
.theme-toggle .i-sun { display: none; }
:root[data-theme='dark'] .theme-toggle .i-sun { display: block; }
:root[data-theme='dark'] .theme-toggle .i-moon { display: none; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .theme-toggle .i-sun { display: block; }
    :root:not([data-theme='light']) .theme-toggle .i-moon { display: none; }
}

.site-footer { border-top: 1px solid var(--border); padding: 52px 0 34px; background: var(--bg-alt); }
.footer-grid { display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: 34px; }
.footer-grid h4 { font-size: 13px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 12px; }
.footer-grid ul { list-style: none; display: grid; gap: 9px; }
.footer-grid a { font-size: 14.5px; color: var(--ink-muted); text-decoration: none; }
.footer-grid a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.footer-brand svg { height: 26px; width: auto; margin-bottom: 14px; }
.footer-tagline { font-size: 14px; color: var(--ink-muted); max-width: 34ch; }
.footer-legal { margin-top: 38px; padding-top: 22px; border-top: 1px solid var(--border); display: flex; flex-wrap: wrap; gap: 14px; justify-content: space-between; font-size: 13px; color: var(--ink-faint); }
/* The phone number is the only link on this row. Left alone it would take the
   browser's default underline in the row's faint grey, which reads as damage
   rather than as a link; this is the footer's own link treatment, one size down. */
.footer-legal a { color: var(--ink-muted); text-decoration: none; }
.footer-legal a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

/* -----------------------------------------------------------------------------
   Surfaces
   -------------------------------------------------------------------------- */
/* Panels, not boxes.
   The comp's surfaces are a barely-there lift off the page — no drawn frame and
   no drop shadow. A visible border plus a shadow turns a flowing page into a
   grid of cards, which is the "no boxes / fluidity" note. What is left is a
   faint fill and a hairline that only resolves at the edge of the eye. */
/* A lit surface, not a container.
   The comp's cards have NO stroke: a directional gradient, brighter at the
   top-left and falling away to nothing at the bottom-right, with a 28px radius.
   The hairline that used to be here is the single biggest reason the page read
   as a template — a drawn rectangle announces itself as a box, whereas a
   gradient just catches light. */
.card {
    background:
        linear-gradient(152deg,
            rgba(255, 255, 255, 0.085) 0%,
            rgba(255, 255, 255, 0.032) 42%,
            rgba(255, 255, 255, 0.012) 100%);
    border: 0;
    border-radius: 28px;
    padding: 34px;
    /* One hair of light along the top edge, the way a real surface catches it. */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.055);
}
:root[data-theme='light'] .card,
:root:not([data-theme='dark']) .card {
    background: linear-gradient(152deg, rgba(26, 26, 26, 0.055) 0%, rgba(26, 26, 26, 0.018) 45%, rgba(26, 26, 26, 0.008) 100%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .card {
        background: linear-gradient(152deg, rgba(255, 255, 255, 0.085) 0%, rgba(255, 255, 255, 0.032) 42%, rgba(255, 255, 255, 0.012) 100%);
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.055);
    }
}

/* No stage any more. The illustration ships with its black keyed to
   transparency, so the glow sits directly on the page and blends — which is
   what the comp shows and what a framed black rectangle was not. In light mode
   a soft radial pool stands in for the dark field the artwork was drawn on,
   rather than a hard panel with edges. */
.stage {
    padding: clamp(8px, 1.6vw, 20px);
    /* Light mode only: the artwork is a glow drawn for a dark field, so it needs
       one. A soft-cornered dark screen reads as the product's own surface; the
       radial fade this replaced read as a smudge. */
    background: #1a1a1a;
    border-radius: var(--radius-lg);
}
:root[data-theme='dark'] .stage { background: none; padding: 0; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .stage { background: none; padding: 0; }
}

.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 64px); align-items: center; }
/* Alternating sides, so a run of sections does not read as one long column.
   Deliberately scoped to the two-column width: the swap is a property of the
   side-by-side layout, and it used to be set inline, which meant it survived
   the collapse to one column and left half the sections showing their icon
   above the heading and half below. */
@media (min-width: 861px) {
    .split-swap > .split-copy { order: 2; }
    .split-swap > .split-panel { order: 1; }
}

/* -----------------------------------------------------------------------------
   Icons — white line art on alpha, painted with currentColor through a mask so
   one file serves both themes and any accent. An <img> would need a second,
   dark-ink copy of all seven for light mode.
   -------------------------------------------------------------------------- */
.icn {
    display: block; width: 56px; height: 56px;
    background-color: currentColor;
    -webkit-mask-position: center; mask-position: center;
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.icn-lg { width: 84px; height: 84px; }
.icn-always-on     { -webkit-mask-image: url('/site/icons/always-on.png');     mask-image: url('/site/icons/always-on.png'); }
.icn-lead-ready    { -webkit-mask-image: url('/site/icons/lead-ready.png');    mask-image: url('/site/icons/lead-ready.png'); }
.icn-brand-aligned { -webkit-mask-image: url('/site/icons/brand-aligned.png'); mask-image: url('/site/icons/brand-aligned.png'); }
.icn-understands   { -webkit-mask-image: url('/site/icons/understands.png');   mask-image: url('/site/icons/understands.png'); }
.icn-qualifies     { -webkit-mask-image: url('/site/icons/qualifies.png');     mask-image: url('/site/icons/qualifies.png'); }
.icn-captures      { -webkit-mask-image: url('/site/icons/captures.png');      mask-image: url('/site/icons/captures.png'); }
.icn-connects      { -webkit-mask-image: url('/site/icons/connects.png');      mask-image: url('/site/icons/connects.png'); }

/* Circular tile behind an icon, as in the comp's three feature cards. */
/* 112px in the comp, and a glass disc rather than an outlined chip: a faint
   radial fill with one ring of light, no drawn border. */
.icn-tile {
    width: 112px; height: 112px; border-radius: 999px; flex: none;
    display: grid; place-items: center;
    color: var(--ink-muted);
    background: radial-gradient(120% 120% at 30% 22%, rgba(255, 255, 255, 0.075), rgba(255, 255, 255, 0.012) 70%);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
}

/* -----------------------------------------------------------------------------
   Chat panel — rebuilt in markup, not shipped as an image.

   The comps bake lorem into every panel; French runs ~20% longer and would need
   a second image set; and the bubbles need --accent-ink rather than the comp's
   failing white. Real markup also means the copy is selectable, translatable,
   and checkable by the contrast pass.
   -------------------------------------------------------------------------- */
.chat {
    background: var(--surface-2);
    border-radius: var(--radius-lg);
    padding: clamp(16px, 2.4vw, 26px);
    display: grid; gap: 14px;
}
:root[data-theme='dark'] .chat,
:root:not([data-theme='light']) .chat { background: linear-gradient(160deg, #19251d 0%, #1a1a1a 62%); }
@media (prefers-color-scheme: light) {
    :root:not([data-theme='dark']) .chat { background: var(--surface-2); }
}

/* Measured off the comp at 1440: both bubbles are a fixed 419px in a 992px
   column (42%), 20px radius on every corner — no tail — 26px/29px padding,
   12px type on a 15px line, and the avatar's top edge level with the bubble's
   rather than its bottom.

   Text is WHITE on both, as drawn. Note that white on #00B266 is 2.78:1 and
   below the WCAG AA floor of 4.5:1; it is the design's colour and it is used
   here deliberately. --accent-ink (near-black, 6.27:1) is still what buttons
   use, so only these bubbles carry the exception. */
.msg { display: flex; align-items: flex-start; gap: 15px; max-width: 49%; }
.msg-in { margin-right: auto; }
.msg-out { margin-left: auto; flex-direction: row-reverse; }
.msg .bubble { padding: 26px 29px; border-radius: 20px; font-size: 12px; line-height: 15px; }
.msg-in .bubble { background: #2e2e2e; color: #ffffff; }
.msg-out .bubble { background: var(--accent); color: #ffffff; }
.msg .who {
    width: 50px; height: 50px; border-radius: 999px; flex: none; overflow: hidden;
    background: var(--surface-2); display: grid; place-items: center;
    /* The light ring the comp draws around every avatar. */
    box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.55);
}
.msg .who img, .msg .who > picture { width: 100%; height: 100%; object-fit: cover; }
.msg .who svg { width: 21px; height: auto; }
/* The assistant's disc is a touch lighter than the bubble beside it, with the
   lockup inset — measured off the comp. */
.msg-in .who { background: #3a3a3a; }
/* Higher specificity than the photo rule above, which forces avatars to fill
   the disc with object-fit:cover — applied to the lockup it blew it up and
   cropped it to a fragment. */
.msg .who .who-mark { width: 32px; height: auto; object-fit: contain; }

/* The comp's citation chips — the product's whole argument, so they are part of
   the mockup rather than decoration on it. */
.cites { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.cite { font-size: 10.5px; font-weight: 600; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--rule); color: var(--accent-text); background: var(--wash-2); }
.msg-out .cite { border-color: rgba(255, 255, 255, 0.42); color: #ffffff; background: rgba(255, 255, 255, 0.18); }

/* -----------------------------------------------------------------------------
   The hero conversation, played rather than shown finished
   -----------------------------------------------------------------------------
   The panel is the product demonstration, so it demonstrates: a question
   arriving late at night, an answer with its sources, a showing booked in the
   thread. Telling the same four claims on rotating banner frames would hide
   three of them at any moment and read as a template site.

   Two rules hold this to something that costs nothing:

   1. Only opacity and transform animate. Every bubble occupies its finished
      size from first paint, so the tallest block above the fold never reflows.
      No layout shift, and nothing here is on the LCP path (the hero figure is,
      and it stays eager).
   2. Timings come from --step on each turn, so adding a turn or translating the
      copy keeps the rhythm without touching this file.

   The composer is deliberately NOT part of the sequence. It is the one element
   that tells a visitor this is a thing you type into, and animating it in last
   meant the panel read as a recording for the first few seconds. It is present
   from the first frame; the conversation arrives around it.
   -------------------------------------------------------------------------- */

/* A clock reading above the conversation. This is the 24-hour claim made by
   showing it, which is the argument the whole product rests on. */
.chat-time {
    /* .chat is a grid, so the horizontal axis is justify-self. align-self here
       centred it vertically and let it stretch the full width as a bar. */
    justify-self: center; font-size: 11.5px; letter-spacing: .04em; color: var(--ink-muted);
    background: var(--wash-2); border: 1px solid var(--rule); border-radius: 999px;
    padding: 3px 12px; margin-bottom: 4px;
}

.chat-play .msg { opacity: 0; animation: chat-turn-in .5s ease-out forwards; animation-delay: calc(var(--step) * 1.15s); }
.chat-play .chat-time { opacity: 0; animation: chat-turn-in .5s ease-out forwards; }

/* The assistant pauses before answering, the visitor does not: a typed message
   appears whole. The dots sit inside the finished bubble and cross-fade with
   its text, so the pause costs no height. */
.chat-play .msg-in .bubble-body { opacity: 0; animation: chat-body-in .35s ease-out forwards; animation-delay: calc(var(--step) * 1.15s + .62s); }
.chat-play .msg-in .dots { animation: chat-dots-out .2s ease-in forwards; animation-delay: calc(var(--step) * 1.15s + .58s); }

.dots { position: absolute; display: inline-flex; gap: 4px; align-items: center; height: 1.2em; }
.dots i { width: 5px; height: 5px; border-radius: 999px; background: currentColor; opacity: .45; animation: chat-dot 1s infinite ease-in-out; }
.dots i:nth-child(2) { animation-delay: .15s; }
.dots i:nth-child(3) { animation-delay: .3s; }
.msg-in .bubble { position: relative; }

@keyframes chat-turn-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
@keyframes chat-body-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes chat-dots-out { to { opacity: 0; visibility: hidden; } }
@keyframes chat-dot { 0%, 100% { transform: translateY(0); opacity: .35; } 50% { transform: translateY(-3px); opacity: .9; } }

/* Someone who has asked their system for less motion is not asking for a
   slower version of it. The conversation is simply already finished. */
@media (prefers-reduced-motion: reduce) {
    .chat-play .msg,
    .chat-play .chat-time,
    .chat-play .msg-in .bubble-body { opacity: 1; animation: none; transform: none; }
    .chat-play .dots { display: none; }
    .dots i { animation: none; }
}

.chat-input { display: flex; align-items: center; gap: 16px; border: 1px solid var(--rule); border-radius: 999px; padding: 14px 14px 14px 30px; color: #ffffff; font-size: 17px; margin-top: 4px; }
/* The placeholder is the comp's mid-grey, matched by measurement. It reads as
   unfilled rather than typed, which is the whole point of the mock. Safe to sit
   below the muted-ink token here and only here: this panel is a picture of the
   product (aria-hidden), not a control anyone types into — the real widget's
   placeholder is a live form field and keeps its legible colour. */
.chat-input > span { color: #7f7f7f; }
.chat-input .send { margin-left: auto; width: 52px; height: 52px; border-radius: 999px; background: var(--accent); color: #ffffff; display: grid; place-items: center; flex: none; }
/* No rotation: the stand-in was a horizontal paper plane tilted to fake the
   comp's angle. The real glyph is drawn at its designed angle, so rotating it
   now would tilt it twice. Height is auto because the artwork is 50x44. */
.chat-input .send svg { width: 22px; height: auto; }

/* -----------------------------------------------------------------------------
   Four-column feature row, with the comp's green divider rules
   -------------------------------------------------------------------------- */
.pillars { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; margin-top: 46px; }
.pillar { padding: 0 26px; text-align: center; display: grid; justify-items: center; gap: 12px; border-left: 1px solid var(--rule); }
.pillar:first-child { border-left: 0; }
.pillar h3 { color: var(--accent-text); font-size: 1.18rem; font-weight: 400; }
.pillar p { font-size: 14.5px; color: var(--ink-muted); }

/* Feature cards with the icon beside the heading, as in the comp */
.feature-card { display: flex; gap: 34px; align-items: flex-start; }
.feature-card h3 { margin-bottom: 6px; }
.feature-card .rule { width: 55px; height: 2px; background: var(--accent); margin: 10px 0 16px; }
.feature-card p { font-size: 14.5px; color: var(--ink-muted); }

/* -----------------------------------------------------------------------------
   Testimonials. Captioned as illustrative — these are scenarios, not customer
   quotes, and a marketing site whose product promise is "cite, don't invent"
   cannot present invented attributions as real ones.
   -------------------------------------------------------------------------- */
.quotes { display: grid; gap: 18px; }
.quote-row { display: flex; align-items: center; gap: 16px; }
.quote-row img, .quote-row > picture { width: 66px; height: 66px; border-radius: 999px; flex: none; overflow: hidden; }
.quote-row .bubble { background: var(--accent); color: #ffffff; padding: 18px 22px; border-radius: 20px; font-size: 15px; }
.quote-row .bubble cite { display: block; font-style: normal; font-weight: 600; font-size: 13px; margin-top: 7px; opacity: 0.9; }
.illustrative { font-size: 12.5px; color: var(--ink-faint); margin-top: 18px; }

/* -----------------------------------------------------------------------------
   Proof stats
   -------------------------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.stat { text-align: center; }
.stat .n { font-family: var(--font-display); font-size: clamp(1.8rem, 3.4vw, 2.6rem); font-weight: 500; color: var(--accent-text); font-variant-numeric: tabular-nums; }
.stat .l { font-size: 13px; color: var(--ink-muted); }

/* -----------------------------------------------------------------------------
   Pricing
   -------------------------------------------------------------------------- */
.plans { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; align-items: start; }
.plan { display: flex; flex-direction: column; gap: 14px; height: 100%; }
.plan .name { font-family: var(--font-display); font-size: 1.3rem; }
/* Sized to hold the longest label priceLabel() produces on ONE line in a
   quarter-width card — measured against Montserrat, which is appreciably wider
   than the face this was first set for. Bigger looks better in isolation and
   wraps "CAD" onto its own line, which reads as a layout bug. */
.plan .price { font-family: var(--font-display); font-size: clamp(1.05rem, 1.46vw, 1.31rem); font-weight: 500; color: var(--ink); }
.plan .price .per { font-size: 0.9rem; color: var(--ink-muted); }
/* A quoted tier occupies the price row and must carry its weight — at caption
   size the card read as though the price had failed to load. Font is inherited
   from .price rather than restated, so the two cannot drift.

   It is a link, and colour alone is a poor affordance, so it carries an arrow
   that moves on hover. */
.plan .price .quoted {
    display: inline-flex; align-items: center; gap: 10px;
    color: var(--accent-text);
    text-decoration: none;
}
.plan .price .quoted span { transition: transform 0.16s ease; }
.plan .price .quoted:hover { text-decoration: underline; text-underline-offset: 5px; }
.plan .price .quoted:hover span { transform: translateX(4px); }
.plan .trial { font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent-text); }
.plan ul { list-style: none; display: grid; gap: 9px; font-size: 14px; color: var(--ink-muted); }
.plan li { display: flex; gap: 9px; align-items: flex-start; }
.plan li::before { content: ''; width: 15px; height: 15px; flex: none; margin-top: 4px; background-color: var(--accent-text); -webkit-mask: var(--tick) center/contain no-repeat; mask: var(--tick) center/contain no-repeat; }
.plan .btn { margin-top: auto; }
:root { --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M7.6 14.2 3.8 10.4l1.4-1.4 2.4 2.4 7-7 1.4 1.4z'/%3E%3C/svg%3E"); }

.cmp { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.cmp th, .cmp td { padding: 13px 14px; border-bottom: 1px solid var(--border); text-align: left; }
.cmp th { font-family: var(--font-display); font-weight: 500; color: var(--ink); }
.cmp td { color: var(--ink-muted); }
.cmp .yes { color: var(--accent-text); font-weight: 600; }
.cmp .no { color: var(--ink-faint); }
.table-scroll { overflow-x: auto; }

/* -----------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */
.form { display: grid; gap: 18px; max-width: 620px; }
.field { display: grid; gap: 7px; }
.field label { font-size: 14px; font-weight: 600; color: var(--ink); }
.field .hint { font-size: 12.5px; color: var(--ink-faint); }
.field input, .field select, .field textarea {
    font: inherit; font-size: 15px; color: var(--ink);
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: 12px; padding: 12px 14px; width: 100%;
}
.field textarea { min-height: 150px; resize: vertical; }
.field .err { font-size: 13px; color: #b3261e; }
:root[data-theme='dark'] .field .err,
:root:not([data-theme='light']) .field .err { color: #ffb4ab; }
.field-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

/* SMS consent. The checkbox has to opt out of `.field input { width: 100% }`,
   which otherwise draws it as a band across the form rather than a box to tick,
   and the label out of `.field label`'s 600 weight — this is a sentence the
   visitor reads and agrees to, not a heading over a field. */
.consent-field { gap: 4px; }
.consent-field .consent {
    display: flex; align-items: flex-start; gap: 9px;
    font-size: 14px; font-weight: 400; line-height: 1.4; cursor: pointer;
}
.consent-field .consent input[type='checkbox'] {
    width: auto; flex: 0 0 auto; margin: 2px 0 0; padding: 0;
    accent-color: var(--brand, #00B266);
}

/* The honeypot. Off-screen rather than display:none — some bots skip hidden
   fields, and the point is that they fill it in. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.notice { border: 1px solid var(--rule); background: var(--wash-2); border-radius: var(--radius); padding: 18px 22px; }

/* -----------------------------------------------------------------------------
   Long-form (legal) documents
   -------------------------------------------------------------------------- */
.prose { max-width: 74ch; }
.prose h2 { font-size: 1.5rem; margin: 38px 0 12px; }
.prose h3 { font-size: 1.15rem; margin: 26px 0 8px; }
.prose p, .prose li { color: var(--ink-muted); font-size: 15.5px; }
.prose p { margin-bottom: 14px; }
.prose ul, .prose ol { margin: 0 0 14px 22px; display: grid; gap: 7px; }
.prose a { color: var(--accent-text); }

/* -----------------------------------------------------------------------------
   Responsive. French runs ~20% longer than English, so the four-column pillar
   row and the plan grid drop to two columns earlier than the visual would
   otherwise need.
   -------------------------------------------------------------------------- */
@media (max-width: 1040px) {
    .grid-4, .pillars, .plans, .stats { grid-template-columns: repeat(2, 1fr); }
    .pillar { border-left: 0; padding: 22px 16px; }
    .pillar:nth-child(even) { border-left: 1px solid var(--rule); }
    /* Two rows now, so the row divider has to be drawn as well. Without it the
       four columns read as one list that happens to have wrapped, and the grid
       the divider rules exist to describe is gone. */
    .pillar:nth-child(n + 3) { border-top: 1px solid var(--rule); }
    .footer-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 860px) {
    .split, .grid-3 { grid-template-columns: 1fr; }
    /* Stacked, every section reads the same way round: the icon names what the
       section is about, then the narrative explains it. */
    .split > .split-panel { order: 1; }
    .split > .split-copy { order: 2; }
    .field-2 { grid-template-columns: 1fr; }

    /* The avatar stays beside its bubble at every width. Stacking it — which is
       what this used to do — turned three scenarios into six unrelated blocks
       and cost a screen of height to do it. */
    .quote-row { gap: 12px; }
    .quote-row img, .quote-row > picture { width: 46px; height: 46px; }
    .quote-row .bubble { font-size: 13.5px; padding: 14px 16px; }
}

/* The three claims stay on one row on a phone, which is how the design shows
   them: they are a single glance, and stacking put three screens of scrolling
   between the headline and the argument that follows it. Scoped to .card-row
   rather than every .grid-3 — the Solutions market cards carry real phrases
   ("Property Management") that would wrap to four lines in a third of 390px. */
@media (max-width: 860px) {
    /* minmax(0, …), not a bare 1fr: a grid track's default floor is its
       min-content width, so the one card whose body contains "opportunities"
       claimed 102px of 320 and left the other two at 76px each. Three cards
       that are visibly not the same width read as a bug, not as a set. */
    .card-row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
    /* No width left to put the icon beside the heading, so it goes above it. */
    .card-row .feature-card {
        flex-direction: column; align-items: center; text-align: center;
        gap: 12px; padding: 18px 10px; border-radius: 20px;
    }
    .card-row .icn-tile { width: 56px; height: 56px; }
    .card-row .icn { width: 30px; height: 30px; }
    /* Two lines reserved whether or not the title needs them. "Brand Aligned"
       wraps and the other two do not, which left the three green rules under
       them sitting at three different heights — the one detail that makes a row
       of cards read as misaligned rather than as a set. */
    .card-row .feature-card h3 { font-size: 14px; margin-bottom: 0; min-height: 2.4em; }
    .card-row .feature-card .rule { width: 34px; margin: 8px auto; }
    /* A third of 320px is 76px, which is narrower than "opportunities" is long.
       Hyphenate rather than let one word decide the layout — html carries a
       lang, so the browser knows where the breaks go. break-word is the floor
       for a locale with no hyphenation dictionary loaded. */
    .card-row .feature-card p {
        font-size: 11.5px; line-height: 1.45;
        hyphens: auto; -webkit-hyphens: auto; overflow-wrap: break-word;
    }
}

/* The header switches to the menu button earlier than the rest of the layout
   reflows, and 960 is measured rather than picked: between about 861 and 955 the
   nav and the tools still fit side by side, but only by wrapping "Let's Talk
   Business" onto two lines inside its pill, which pushes the bar from 72px to
   95px. The bar drops to the logo and the button; everything it drops is in the
   sheet, so nothing gets less reachable by being hidden. */
@media (max-width: 960px) {
    .site-nav,
    .site-header .header-tools { display: none; }
    .nav-toggle { display: inline-flex; }
}

@media (max-width: 560px) {
    .grid-4, .plans, .stats, .footer-grid { grid-template-columns: 1fr; }
    /* .pillars deliberately stays two-up. The other grids here hold a price or
       a column of links, which are readable in a single file; these four are a
       sequence — understands → qualifies → captures → connects — and the 2x2
       with its divider rules is what says so. One column says only "four
       things". Shrink the type instead. */
    .pillars { margin-top: 30px; }
    .pillar { padding: 20px 10px; gap: 8px; }
    .pillar .icn-lg { width: 58px; height: 58px; }
    .pillar h3 { font-size: 1rem; }
    .pillar p { font-size: 13px; }
    .msg { max-width: 96%; }
    body { background-attachment: scroll; }

    /* The icon tile, a 34px gap and the text no longer fit side by side on the
       narrowest phones, and the card was pushing the whole page into sideways
       scroll. Tighten the gap rather than stack: the tile is what makes these
       three cards scannable. */
    .feature-card { gap: 16px; }
    /* The rule that used to hide the header CTA here is gone: header-tools is
       hidden wholesale from 860px down, and the CTA now has a full-width row of
       its own in the menu sheet instead of being dropped. */
}

/* -----------------------------------------------------------------------------
   Preview banner. Only rendered when SitePreview has authorised drafts for this
   request. Loud on purpose — a preview that reads as the live site is how an
   unpublished headline gets reported as a regression. Amber rather than the
   brand green so it cannot be mistaken for part of the design.
   -------------------------------------------------------------------------- */
.preview-banner {
    position: sticky; top: 0; z-index: 60;
    display: flex; align-items: center; justify-content: center; gap: 14px;
    flex-wrap: wrap;
    background: #ffb703; color: #1a1a1a;      /* 11.6:1 — outside the brand palette by design */
    font-family: var(--font-display); font-size: 14px; font-weight: 500;
    padding: 9px 18px; text-align: center;
}
.preview-banner a { color: #1a1a1a; font-weight: 600; text-underline-offset: 3px; }

/* =============================================================================
   Band 1 — the product itself, at the top of the page.

   This is the comp's opening frame and it is deliberate: a visitor sees the
   thing working before they read a claim about it. It is full-bleed, not a
   section with a card in it — the chat area runs to the right edge of the
   viewport, the figure sits in the left column, and the flowing line texture
   crosses behind both. Nothing here is drawn with a frame.
   ============================================================================= */
.stage-band {
    position: relative;
    overflow: hidden;
    padding: 0;
    /* Reads as the first screen without pinning to an exact viewport height,
       which would clip the conversation on a short laptop. */
    min-height: min(86vh, 760px);
    display: grid;
    grid-template-columns: 322px 1fr;

    /* Dark in BOTH themes, by re-declaring the tokens locally rather than by
       hard-coding colours on each child.

       The band is a product stage: everything in it — the figure, the funnel's
       sibling illustration, the conversation — is dark artwork, and on a white
       page the figure renders as a black rectangle no amount of feathering
       rescues. The comp only ever shows this band dark, so light mode gets a
       dark opening band and a light document below it, which is a deliberate
       and common pairing rather than a theme that failed to apply. */
    --bg: #1a1a1a;
    --bg-alt: #232323;
    --surface: rgba(255, 255, 255, 0.045);
    --surface-2: #333333;
    --border: rgba(255, 255, 255, 0.12);
    --border-strong: rgba(255, 255, 255, 0.24);
    --ink: #ffffff;
    --ink-muted: #c4c4c4;
    --ink-faint: #9aa0a6;
    --accent-text: #00b266;
    --rule: rgba(0, 178, 102, 0.35);
    --focus: #00b266;

    color: var(--ink);
    background:
        radial-gradient(120% 90% at 85% 10%, rgba(0, 178, 102, 0.10), transparent 60%),
        var(--bg);
}

/* The line texture. Behind everything, ignores pointer events, and scales with
   the band rather than tiling — a repeated pattern would read as wallpaper. */
.stage-band::before {
    content: '';
    position: absolute; inset: 0;
    background: url('/site/flow.svg') center / cover no-repeat;
    opacity: 0.9;
    pointer-events: none;
}

/* The left column of the opening band: the figure, sitting on the floor of the
   band. The comp also puts a product rail here (New Chat / Archived / Library /
   Recents); it is deliberately not built, because those are not features of this
   product and a nav-like list naming things that do not exist is a support
   ticket waiting to happen. */
.stage-aside {
    position: relative;
    display: flex; flex-direction: column; justify-content: flex-end;
    padding-bottom: 0;
}
.stage-figure { width: 100%; height: auto; display: block; }

.stage-chat {
    position: relative;
    display: flex; flex-direction: column; justify-content: center;
    gap: 14px;
    padding: clamp(24px, 4vw, 56px) clamp(20px, 4vw, 60px);
    min-width: 0;
}
/* The chat sits on its own faint field with one rounded corner, exactly as in
   the comp — a shape, not a container with a border. */
.stage-chat::before {
    content: '';
    position: absolute; inset: 0 -100vw 0 0;
    background: linear-gradient(120deg, rgba(0, 178, 102, 0.07), transparent 55%);
    border-top-left-radius: 28px;
    pointer-events: none;
}
.stage-chat > * { position: relative; }

@media (max-width: 900px) {
    /* One column, and the two halves swap roles rather than one being dropped:
       the conversation first, the figure under it. It used to be display:none,
       on the reasoning that a left column no longer exists at this width — but
       the design's own phone layout stacks them, and hiding it opened the page
       with a panel of text where there is meant to be a person. */
    .stage-band { grid-template-columns: 1fr; min-height: 0; }
    .stage-aside { order: 2; align-items: center; }
    .stage-figure { width: min(300px, 74%); }
    .stage-chat { order: 1; padding: clamp(18px, 5vw, 30px) 16px; margin: 14px 12px 0; }
    /* Stacked, the conversation is no longer a field bleeding off the right
       edge — it is a panel with four sides, which is how the phone comp draws
       it. The desktop rule runs it to -100vw precisely because it has no right
       edge there. */
    .stage-chat::before {
        inset: 0;
        border-radius: 24px;
        background: linear-gradient(150deg, rgba(0, 178, 102, 0.10), rgba(255, 255, 255, 0.02) 62%);
    }
}

/* Inside the opening band the conversation has no panel of its own — the
   bubbles sit directly on the field, as in the comp. The .chat surface exists
   for the mid-page instances, where a panel is what separates the mockup from
   the copy beside it. */
/* `:root` prefix only to win specificity against the themed `.chat` rules
   above — those are `:root[data-theme] .chat` and out-rank a plain descendant
   selector, which left a panel visible behind the conversation in light mode. */
:root .stage-chat .chat { background: none; padding: 0; gap: 26px; }
.stage-chat .chat-input { margin-top: 43px; }
.stage-chat .msg { max-width: 49%; }

.chat-input .plus { width: 26px; height: 26px; flex: none; color: #ffffff; }

/* The hero band gives the illustration more width than the copy, as the comp
   does — there it spans roughly 720px against the headline's 515. An even
   1fr/1fr split shrinks the artwork to something that reads as a thumbnail
   floating beside the text rather than as the other half of the composition. */
.split-media { grid-template-columns: 1fr 1.32fr; }
.split-media .stage { padding: 0; }
.split-media .stage img { width: 100%; }

@media (max-width: 860px) {
    .split-media { grid-template-columns: 1fr; }
}

/* =============================================================================
   The composed figure blocks (comp bands 5 and 7).

   A green plate, the figure standing against it, and the conversation
   overlapping the figure's near side. The chat is real markup rather than part
   of the picture, so the words stay translatable and contrast-checkable — the
   composition is the design's, the content is not baked in.
   ============================================================================= */
.figure-compo {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    min-height: 460px;
}
.figure-compo-person {
    width: min(340px, 46%);
    height: auto;
    position: relative;
    z-index: 1;
}
.figure-compo .chat.figure-compo-chat {
    position: absolute;
    left: 0; top: 50%;
    transform: translateY(-52%);
    width: min(62%, 420px);
    z-index: 2;
    /* A real surface here, unlike the opening band: it sits over a photograph
       and needs to separate from it. */
    background: linear-gradient(158deg, rgba(38, 46, 42, 0.97), rgba(26, 26, 26, 0.97));
    backdrop-filter: blur(6px);
    border-radius: 22px;
    padding: 20px;
    gap: 10px;
}
.figure-compo .figure-compo-chat .msg { max-width: 92%; }
.figure-compo .figure-compo-chat .bubble { font-size: 13px; padding: 10px 13px; }
.figure-compo .figure-compo-chat .cite { font-size: 10.5px; padding: 2px 7px; }
.figure-compo .figure-compo-chat .chat-input { padding: 7px 7px 7px 14px; font-size: 12.5px; }
.figure-compo .figure-compo-chat .chat-input .send { width: 28px; height: 28px; }
.figure-compo .figure-compo-chat .chat-input .send svg { width: 12px; }
.figure-compo .figure-compo-chat .who { width: 26px; height: 26px; }

/* The closing band mirrors it: figure left, conversation right.
   Percentages without a pixel cap on purpose — the two must sum to more than
   100% so the panel actually overlaps the figure. Capped widths left a gap
   between them at wide viewports, which reads as two things placed near each
   other rather than one composition. */
.figure-compo-wide { justify-content: flex-start; min-height: 560px; }
.figure-compo-wide .figure-compo-person { width: 46%; max-width: 500px; }
.figure-compo-wide .chat.figure-compo-chat { left: auto; right: 0; width: 60%; max-width: 640px; }

/* Band 6: scenarios left, argument right — the comp's proportions. */
.split-quotes { grid-template-columns: 1fr 0.95fr; align-items: center; }
.split-quotes .quotes { gap: 22px; }
.split-figure { align-items: center; }

@media (max-width: 900px) {
    .figure-compo, .figure-compo-wide { flex-direction: column; min-height: 0; gap: 20px; }
    .figure-compo-person, .figure-compo-wide .figure-compo-person { width: min(300px, 70%); }
    .figure-compo .chat.figure-compo-chat,
    .figure-compo-wide .chat.figure-compo-chat {
        position: static; transform: none; width: 100%;
    }
    .split-quotes { grid-template-columns: 1fr; }
    /* The claim, then what it looks like. Source order puts the scenarios first
       because that is the desktop composition — they are its left column — and
       collapsed to one column that opened the section with three quotes before
       a reader has been given any reason to care what they are. */
    .split-quotes > .quotes { order: 2; margin-top: 6px; }
}

/* Light mode only: the composed blocks carry dark artwork (a figure cropped
   from a dark field, plus a conversation designed for one), so they get their
   own dark plate rather than bleeding a grey haze onto a white page. Same
   reasoning as the hero illustration and the opening band — the comp only ever
   specifies these dark, and a mode it never described should not be allowed to
   half-apply. In dark mode the plate is invisible and nothing changes. */
:root[data-theme='light'] .figure-compo,
:root:not([data-theme='dark']) .figure-compo {
    background: #1a1a1a;
    border-radius: var(--radius-lg);
    padding: clamp(16px, 2.4vw, 34px);
    --ink: #ffffff;
    --ink-muted: #c4c4c4;
    --ink-faint: #9aa0a6;
    --surface-2: #333333;
    --bg-alt: #232323;
    --accent-text: #00b266;
    --rule: rgba(0, 178, 102, 0.35);
    color: var(--ink);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .figure-compo {
        background: none; padding: 0;
    }
}

/* About-page marks. A card containing only an icon stretches to the row height
   and reads as an empty box waiting for content; this gives it a deliberate
   shape and centres the mark in it. */
.about-mark {
    display: grid;
    place-items: center;
    min-height: 240px;
    padding: 0;
}
.about-mark .icn { width: 96px; height: 96px; color: var(--ink-muted); }

/* The comp's 42% bubble is a 1440 proportion. Below the split it would leave
   two words per line, so the conversation goes near-full-width — and the
   .stage-chat override above out-specifies the generic .msg rule, so it has to
   be restated at the same specificity here. */
@media (max-width: 900px) {
    .stage-chat .msg { max-width: 90%; }
    .stage-chat .chat-input { margin-top: 26px; font-size: 15px; padding: 10px 10px 10px 20px; }
    .stage-chat .chat-input .send { width: 42px; height: 42px; }
    .msg .bubble { padding: 18px 20px; }
    .msg .who { width: 40px; height: 40px; }
    .msg .who .who-mark { width: 26px; }
}
