@media (min-width: 900px) { .audit { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.8fr); } }
/* ==========================================================================
   VEE Agency — Design System
   Light theme. Brand tokens sampled from the real logo.
   ========================================================================== */

/* 0. Fonts
   Self-hosted, Latin subset, variable. One file per family covers every weight
   the design uses: 70 KB in total, from this origin, with no third-party
   request and no visitor IP sent anywhere before consent.
   See static/fonts/README.md for provenance and licensing.
   ========================================================================== */

@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/inter-latin-var.260c81a4759b.woff2") format("woff2");
}

@font-face {
    font-family: "Space Grotesk";
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url("../fonts/space-grotesk-latin-var.87c506d88b9f.woff2") format("woff2");
}

/* 1. Tokens
   ========================================================================== */

:root {
    color-scheme: light;

    /* Surfaces. Off-white rather than pure #FFF: a pure-white page under a
       bright screen is the other half of the eye-strain problem. */
    --bg: #FBFAF9;
    --surface: #FFFFFF;
    --surface-2: #F4F2F0;
    --surface-3: #EAE7E4;
    --border: rgba(23, 20, 18, 0.10);
    --border-strong: rgba(23, 20, 18, 0.18);
    /* Boundaries of interactive controls — inputs, secondary buttons, carousel
       dots. WCAG 1.4.11 requires 3:1 for anything needed to identify a control,
       which --border and --border-strong (1.23:1 and 1.47:1) do not meet. Kept
       separate so decorative card borders can stay quiet. */
    --border-control: rgba(23, 20, 18, 0.50);   /* 3.45:1 on white, 3.31:1 on --bg */
    --border-brand: rgba(200, 62, 20, 0.45);

    /* Brand — sampled from initial assets/VEE AGENCY LOGO.png.
       --brand-ink is a darkened orange used wherever the brand colour carries
       text or a thin line, because #E64A1A on white is only 3.3:1. */
    --brand: #E64A1A;
    --brand-ink: #B83A12;      /* brand as text on light: 5.5:1 */
    --brand-btn: #C83E14;      /* brand as a button fill: white on it is 5.05:1 */
    --brand-btn-hover: #A83310;
    --brand-hover: #A83310;    /* brand text on hover: 6.68:1 on the page background */
    --brand-deep: #880000;
    --brand-soft: rgba(230, 74, 26, 0.10);
    --brand-glow: rgba(230, 74, 26, 0.28);

    --demo-brand: #176b87;
    --demo-brand-dark: #0d3b4c;
    --demo-brand-light: #7dc4d3;
    --demo-brand-text: #fff;
    --demo-color: #176b87;

    /* Text — all AA or better against --bg */
    --text: #171412;
    --text-muted: #57514C;
    --text-dim: #6E6862;

    /* Status */
    --whatsapp: #25D366;
    --whatsapp-hover: #1FBF5B;
    --error: #C2281F;
    --success: #1B7F44;

    /* Layout */
    --container: 1280px;
    --container-narrow: 820px;
    --pad-x: clamp(1.15rem, 4vw, 2.5rem);
    --section-y: clamp(3rem, 6vw, 4.5rem);

    /* Radius */
    --r-sm: 8px;
    --r: 14px;
    --r-lg: 16px;
    --r-pill: 999px;

    /* Elevation — light themes need real shadow to separate surfaces, where a
       dark theme could lean on borders alone. */
    --shadow: 0 1px 2px rgba(23, 20, 18, 0.06), 0 2px 8px rgba(23, 20, 18, 0.04);
    --shadow-lg: 0 18px 40px -16px rgba(23, 20, 18, 0.22), 0 2px 8px rgba(23, 20, 18, 0.06);

    /* Motion */
    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
    --dur: 0.35s;

    /* Two rings, because no single colour clears 3:1 on both the light page and
       the dark band. The white halo carries the indicator on dark surfaces
       (18.8:1) and the brand ring carries it on light ones (4.85:1), so one of
       the two is always clearly visible. */
    --focus-ring: 0 0 0 2px #FFFFFF, 0 0 0 5px var(--brand-btn);

    /* Shared tick mark for "included" lists. */
    /* Included markers are green and uncircled (owner, 2026-09-17). The peach
       circle read as decorative; green reads as "yes" without being explained,
       and dropping the disc removes the pastel look it came with. */
    --tick-ink: #15803D;                          /* 4.9:1 on white */
    --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2315803D' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.5 12.8l5 5L19.5 6.6'/%3E%3C/svg%3E");

    --header-h: 74px;

    --font-display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
    --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif;
}

/* 2. Reset & base
   ========================================================================== */

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

* { margin: 0; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--header-h) + 1.5rem);
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden;
}

/* Ambient brand wash — sits behind everything, never over content. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(60rem 40rem at 78% -8%, rgba(230, 74, 26, 0.07), transparent 62%),
        radial-gradient(48rem 36rem at 4% 8%, rgba(136, 0, 0, 0.05), transparent 60%);
}

img, svg, video { display: block; max-width: 100%; height: auto; }

a { color: inherit; text-decoration: none; }

button, input, select, textarea { font: inherit; color: inherit; }

button { cursor: pointer; background: none; border: 0; }

ul, ol { padding: 0; list-style: none; }

:focus-visible {
    /* Transparent outline rather than none: forced-colors mode discards
       box-shadow but repaints a transparent outline in the system colour, so
       the indicator survives High Contrast instead of disappearing. */
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: var(--focus-ring);
    border-radius: var(--r-sm);
}

::selection { background: var(--brand-btn); color: #fff; }

/* 3. Typography
   ========================================================================== */

h1, h2, h3, h4, h5 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.025em;
    text-wrap: balance;
}

h1 { font-size: clamp(2.5rem, 1.6rem + 4.4vw, 4.75rem); }
h2 { font-size: clamp(1.75rem, 1.3rem + 2vw, 2.6rem); }
h3 { font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.6rem); line-height: 1.2; }
h4 { font-size: clamp(1.08rem, 1rem + 0.4vw, 1.25rem); line-height: 1.3; }

p { text-wrap: pretty; }

.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-family: var(--font-display);
    font-size: 0.775rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brand-ink);
}

.eyebrow::before {
    content: "";
    width: 1.75rem;
    height: 1px;
    background: currentColor;
    opacity: 0.7;
}

.lead {
    font-size: clamp(1rem, 0.95rem + 0.35vw, 1.15rem);
    line-height: 1.55;
    color: var(--text-muted);
    max-width: 62ch;
}

.muted { color: var(--text-muted); }
.dim { color: var(--text-dim); font-size: 0.9rem; }

.text-brand { color: var(--brand-ink); }

/* Gradient accent for a single emphasised phrase in a headline. */
.accent {
    color: var(--brand-ink);
}

/* 4. Layout
   ========================================================================== */

.container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--pad-x);
}

.container--narrow { max-width: var(--container-narrow); }

.section { padding-block: clamp(2.75rem, 5vw, 4.5rem); position: relative; }
.section--tight { padding-block: clamp(2.25rem, 4vw, 3.5rem); }

.section-head { max-width: 72ch; margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }
.section-head h2 { margin-block: 0.5rem 0.6rem; }
.section-head .lead { max-width: 62ch; line-height: 1.5; }

.section-head--split {
    max-width: none;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem 2rem;
}

.section-head--split > div { max-width: 62ch; }

.grid { display: grid; gap: clamp(1rem, 2vw, 1.5rem); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }

/* Flex rather than grid, so a row that does not fill centres instead of
   leaving a hole. Seven services in three columns is 3 + 3 + 1, and as a grid
   that last card sat hard left with two thirds of the row empty — the dead
   space the owner called out. Items still share a row equally, because they
   grow from a common basis. */
.grid--3 {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}

.grid--3 > * {
    flex: 1 1 18.5rem;
    /* Without a ceiling, a lone trailing card stretches the full width and
       stops looking like the cards above it. */
    max-width: min(100%, 26rem);
}

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

.divider { height: 1px; background: var(--border); border: 0; }

.skip-link {
    position: absolute;
    left: 1rem;
    top: -100%;
    z-index: 200;
    padding: 0.75rem 1.25rem;
    background: var(--brand-btn);
    color: #fff;
    font-weight: 600;
    border-radius: var(--r-sm);
    transition: top 0.2s var(--ease);
}

.skip-link:focus { top: 1rem; }

/* Staging notice. Deliberately unstyled-looking and impossible to miss. */
.staging-flag {
    position: relative;
    z-index: 101;
    padding: 0.5rem var(--pad-x);
    background: repeating-linear-gradient(
        45deg, #FFF1E6, #FFF1E6 12px, #FFE7D6 12px, #FFE7D6 24px
    );
    border-bottom: 1px solid var(--border-brand);
    color: #7A3410;
    font-size: 0.82rem;
    text-align: center;
}

.staging-flag strong {
    color: var(--brand-ink);
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* 5. Buttons
   ========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.55rem;
    min-height: 46px;
    padding: 0.7rem 1.55rem;
    border-radius: var(--r-pill);
    font-family: var(--font-display);
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    text-align: center;
    border: 1px solid transparent;
    transition: transform var(--dur) var(--ease),
                background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                color var(--dur) var(--ease);
}

.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--primary {
    background: var(--brand-btn);
    color: #FFFFFF;
    box-shadow: 0 8px 26px -10px var(--brand-glow);
}

.btn--primary:hover {
    background: var(--brand-btn-hover);
    box-shadow: 0 14px 34px -10px var(--brand-glow);
}

.btn--secondary {
    background: rgba(23, 20, 18, 0.035);
    border-color: var(--border-control);
    color: var(--text);
}

.btn--secondary:hover {
    background: rgba(23, 20, 18, 0.07);
    border-color: var(--border-brand);
}

.btn--whatsapp {
    background: var(--whatsapp);
    color: #062814;
}

.btn--whatsapp:hover { background: var(--whatsapp-hover); }

.btn--ghost {
    padding-inline: 0;
    min-height: 0;
    color: var(--brand-ink);
    border-radius: var(--r-sm);
}

.btn--ghost:hover { color: var(--brand-hover); transform: none; }
.btn--ghost .btn__arrow { transition: transform var(--dur) var(--ease); }
.btn--ghost:hover .btn__arrow { transform: translateX(4px); }

.btn--lg { min-height: 54px; padding-inline: 2rem; font-size: 1.02rem; }
.btn--sm { min-height: 40px; padding: 0.45rem 1.1rem; font-size: 0.875rem; }
.btn--block { width: 100%; }

.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem;
    align-items: center;
}

/* 6. Cards
   ========================================================================== */

.card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    padding: clamp(1.2rem, 2.2vw, 1.65rem);
    background: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(244,242,240,0.9) 100%);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: 0 1px 2px rgba(23, 20, 18, 0.05), 0 12px 28px -22px rgba(23, 20, 18, 0.18);
    transition: transform var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

.card:hover,
.card:focus-within {
    transform: translateY(-4px);
    border-color: var(--border-brand);
    box-shadow: 0 18px 38px -22px rgba(23, 20, 18, 0.24), 0 2px 8px rgba(23, 20, 18, 0.06);
}

.card h3 a { transition: color var(--dur) var(--ease); }
.card:hover h3 a { color: var(--brand-ink); }

.card__heading {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
}

.card__heading h2,
.card__heading h3 {
    min-width: 0;
    margin: 0;
    overflow-wrap: anywhere;
}

/* Whole-card click target that keeps the real link accessible. */
.card__link::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
}

.card__meta {
    margin-top: auto;
    padding-top: 0.9rem;
    font-size: 0.875rem;
    color: var(--text-dim);
}

.card__price {
    font-family: var(--font-display);
    font-weight: 600;
    color: var(--brand-ink);
}

.card__icon {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    margin-bottom: 0;
    color: var(--brand-ink);
    flex-shrink: 0;
}

.card__icon-svg { width: 2.15rem; height: 2.15rem; }
.card__icon-image { width: 2.15rem; height: 2.15rem; object-fit: contain; }

.card__icon--seo { color: #B83A12; }
.card__icon--aeo { color: #8A5A12; }
.card__icon--geo { color: #276749; }

/* Service cards: the name leads, the price and the way in sit on one baseline
   at the foot so a row of them scans cleanly. */
.card--service { gap: 0.5rem; }

.card--service .card__tagline {
    font-family: var(--font-display);
    font-size: 0.92rem;
    font-weight: 500;
    color: var(--brand-ink);
}

.card--service .card__meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    border-top: 1px solid var(--border);
    margin-top: auto;
}

.card__go {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    border: 1px solid var(--border-control);
    color: var(--text-muted);
    flex-shrink: 0;
    transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
                border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.card--service:hover .card__go {
    background: var(--brand-btn);
    border-color: var(--brand-btn);
    color: #FFFFFF;
    transform: translateX(3px);
}

.card--flat { background: var(--surface); }

/* 7. Header
   One rounded island, after `pinterest/vid 11.mp4`: the logo, the links and the
   call to action all live inside a single bar, with the CTA inset against its
   right end rather than floating beside it. The header element itself carries
   no background — it is only the sticky frame the bar sits in.
   ========================================================================== */

.header {
    position: sticky;
    top: 0;
    z-index: 100;
    padding-block: 0.6rem;
    background: transparent;
}

/* A floating bar leaves the page visible around it, and text sliding through
   those gaps — the gutters either side, the sliver above — read as a mess.
   This softens the strip the bar sits in once the page has moved.

   It blurs rather than tints, because the same strip is over a dark video on
   the home page and over off-white everywhere else, and blur is right for
   both. The mask fades it out downwards so there is no edge to notice. */
.header::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: calc(100% + 0.75rem);
    backdrop-filter: blur(7px);
    -webkit-backdrop-filter: blur(7px);
    mask-image: linear-gradient(180deg, #000 58%, transparent);
    -webkit-mask-image: linear-gradient(180deg, #000 58%, transparent);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease);
}

.header.is-scrolled::before { opacity: 1; }

/* Above the strip, so the bar is never the thing being blurred. */
.header > .container { position: relative; z-index: 1; }

.header__bar {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    min-height: calc(var(--header-h) - 1.2rem);
    /* Tight on the right so the CTA reads as set into the bar's end, roomier
       on the left where the wordmark needs air. */
    padding: 0.4rem 0.45rem 0.4rem 1rem;
    border: 1px solid rgba(23, 20, 18, 0.10);
    border-radius: 999px;
    background: rgba(251, 250, 249, 0.82);
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    box-shadow: 0 1px 2px rgba(23, 20, 18, 0.04), 0 8px 24px -12px rgba(23, 20, 18, 0.18);
    transition: background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

/* Once the page has moved, the bar is floating over content rather than over
   the top of the page, so it earns a firmer edge. */
.header.is-scrolled .header__bar {
    background: rgba(251, 250, 249, 0.94);
    border-color: rgba(23, 20, 18, 0.14);
    box-shadow: 0 2px 4px rgba(23, 20, 18, 0.05), 0 14px 34px -14px rgba(23, 20, 18, 0.26);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.12rem;
    letter-spacing: -0.02em;
    flex-shrink: 0;
}

.brand img { width: 34px; height: 34px; }
.brand span { white-space: nowrap; }

.nav {
    display: none;
    margin-inline: auto;
    gap: 0.15rem;
}

.nav a {
    position: relative;
    display: inline-block;
    /* Without nowrap, "Web Development" and "Areas We Serve" broke over two or
       three lines at every width below 1280 and left the header three times
       its proper height. */
    white-space: nowrap;
    padding: 0.5rem clamp(0.35rem, 0.8vw, 0.7rem);
    font-size: clamp(0.82rem, 0.9vw, 0.925rem);
    font-weight: 500;
    color: var(--text-muted);
    border-radius: var(--r-sm);
    transition: color var(--dur) var(--ease);
}

.nav a:hover { color: var(--text); }

.nav a::after {
    content: "";
    position: absolute;
    left: 0.7rem;
    right: 0.7rem;
    bottom: 0.25rem;
    height: 2px;
    border-radius: 2px;
    background: var(--brand);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur) var(--ease);
}

.nav a:hover::after,
.nav a[aria-current="page"]::after { transform: scaleX(1); }

.nav a[aria-current="page"] { color: var(--text); }

.header__actions {
    display: none;
    align-items: center;
    gap: 0.6rem;
    margin-left: auto;
}

/* Mobile menu toggle */
.menu-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-left: auto;
    border: 0;
    border-radius: 0;
    color: var(--brand-ink);
    transition: color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.menu-toggle:hover { color: var(--brand-btn-hover); transform: scale(1.06); }

.menu-toggle__bars {
    position: relative;
    width: 18px;
    height: 2px;
    background: currentColor;
    border-radius: 2px;
    transition: background-color 0.2s var(--ease);
}

.menu-toggle__bars::before,
.menu-toggle__bars::after {
    content: "";
    position: absolute;
    left: 0;
    width: 18px;
    height: 2px;
    background: currentColor;
    border-radius: 2px;
    transition: transform var(--dur) var(--ease);
}

.menu-toggle__bars::before { top: -6px; }
.menu-toggle__bars::after { top: 6px; }

.menu-toggle[aria-expanded="true"] .menu-toggle__bars { background: transparent; }
.menu-toggle[aria-expanded="true"] .menu-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] .menu-toggle__bars::after { transform: translateY(-6px) rotate(-45deg); }

/* Mobile overlay */
.mobile-nav {
    position: fixed;
    inset: var(--header-h) 0 0;
    z-index: 99;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 2rem var(--pad-x) 2.5rem;
    /* Fully opaque: at 0.97 the page behind it still showed through. */
    background: var(--bg);
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity var(--dur) var(--ease),
                transform var(--dur) var(--ease),
                visibility var(--dur);
}

.mobile-nav.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.mobile-nav__links { display: flex; flex-direction: column; }

.mobile-nav__links a {
    padding: 0.95rem 0;
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    border-bottom: 1px solid var(--border);
    transition: color var(--dur) var(--ease), padding-left var(--dur) var(--ease);
}

.mobile-nav__links a:hover,
.mobile-nav__links a[aria-current="page"] { color: var(--brand-ink); padding-left: 0.5rem; }

.mobile-nav__actions { display: grid; gap: 0.75rem; }

.mobile-nav__contact {
    display: grid;
    gap: 0.35rem;
    font-size: 0.925rem;
    color: var(--text-muted);
}

.mobile-nav__contact a:hover { color: var(--brand-ink); }

body.nav-open { overflow: hidden; }

/* The bar is translucent by design, but not while it caps an open menu — the
   overlay behind it is opaque, so a see-through bar showed the seam. */
body.nav-open .header__bar {
    background: var(--bg);
    border-color: var(--border);
}

/* 8. Hero
   Full-bleed video with the copy over it, after the NVIDIA reference. The media
   panel is the hero — not a thumbnail beside it — so it takes the full width
   and most of the viewport.
   ========================================================================== */

.hero {
    position: relative;
    isolation: isolate;
    display: flex;
    align-items: flex-end;
    min-height: min(88vh, 46rem);
    /* Pulled up the height of the header so the video runs behind the floating
       bar, then padded back out so the copy still clears it. The bar has its
       own background; this is what makes it read as sitting on the media
       rather than as a strip above it. */
    margin-top: calc(var(--header-h) * -1);
    padding-block: calc(var(--header-h) + clamp(2rem, 8vh, 4.5rem)) clamp(1.5rem, 4vh, 2.5rem);
    background: #141110;
    overflow: hidden;
}

.hero__media {
    position: absolute;
    inset: 0;
    z-index: -2;
}

.hero__slides { position: absolute; inset: 0; }

.hero__slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    visibility: hidden;
    /* `visibility: hidden` alone still left these as paint candidates: the
       second clip, fetched ahead so the slide change is not waiting on the
       network, registered as a new largest contentful paint the moment it
       decoded — a couple of pixels "bigger" than the poster already on screen,
       which pushed the home page's LCP from 240ms to 2.7 seconds even though
       nothing a visitor could see had changed.

       `content-visibility: hidden` takes them out of rendering entirely. The
       clip still loads and is ready to play; it just cannot be mistaken for
       something the visitor is looking at. */
    content-visibility: hidden;
    transition: opacity 1.1s var(--ease), visibility 1.1s;
}

.hero__slide.is-active {
    opacity: 1;
    visibility: visible;
    content-visibility: visible;
}

/* Without JS every slide is shown; the first one wins by stacking order, and
   nothing is hidden behind a script that never ran. */
html:not(.js) .hero__slide { opacity: 1; visibility: visible; }

/* The video fills the panel. `cover` crops rather than letterboxes, and the two
   renditions are cut so that whatever cover throws away was never the subject —
   see `tools/build_hero_video.py`. The background matches the clips' own
   near-black, so the moment before the first frame paints is not a flash.

   The CSS stand-in that used to live here, its drift animation and the grid
   overlay that gave it texture are all gone: there is real footage now, and a
   grid over it reads as a defect rather than a surface. */
.hero__video {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;

    /* The poster, as a background rather than the `poster` attribute. The right
       still depends on which framing the screen gets, and CSS can answer that
       before any script runs — the attribute cannot. It is also what shows for
       the whole visit on a browser that cannot decode either file.

       The two URLs arrive as custom properties on the element itself; the
       aspect-ratio query below must stay in step with `heroFraming()` in
       main.js, or the still and the video disagree about the crop. */
    background-color: #0A0908;
    background-image: var(--poster-wide);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

@media (max-aspect-ratio: 5/4) {
    .hero__video { background-image: var(--poster-tall); }
}

/* Text over footage is only legible with something between them, and footage
   needs a lighter hand than the flat panel did — the clips are near-black
   already, so this only has to protect the copy column and the bottom edge
   where the captions and controls sit. */
.hero__scrim {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(100deg, rgba(10, 9, 8, 0.95) 0%, rgba(10, 9, 8, 0.90) 30%, rgba(10, 9, 8, 0.62) 50%, rgba(10, 9, 8, 0.16) 78%, rgba(10, 9, 8, 0.02) 100%),
        linear-gradient(0deg, rgba(10, 9, 8, 0.80) 0%, transparent 32%);
}

/* On a phone the copy runs the full width, so the horizontal gradient has
   nothing to protect and the vertical one has to do the whole job. */
@media (max-width: 767px) {
    .hero__scrim {
        background:
            linear-gradient(180deg, rgba(10, 9, 8, 0.88) 0%, rgba(10, 9, 8, 0.52) 26%, rgba(10, 9, 8, 0.48) 52%, rgba(10, 9, 8, 0.92) 100%);
    }
}

.hero__inner {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: clamp(2rem, 6vh, 3.5rem);
    width: 100%;
}

/* Same reason as the caption: the last control must not end up underneath the
   floating action button. */
.hero__inner .slider-controls { padding-right: 4.5rem; }

@media (max-width: 767px) {
    /* Back-to-top is hidden on phones, so only the right side needs clearing. */
    .hero__inner .slider-controls { padding-right: 3.5rem; }
}

.hero__copy { max-width: 46rem; }

.hero__copy .eyebrow { color: #FF8A5B; }

.hero__copy h1 {
    margin-block: 0.9rem 1.1rem;
    color: #FFFFFF;
    font-size: clamp(2.6rem, 1.5rem + 5vw, 5rem);
}

.hero__copy .lead {
    color: rgba(255, 255, 255, 0.82);
    max-width: 40rem;
}

.hero__copy .btn-row { margin-top: 2rem; }

.hero__trust {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 1.75rem;
    margin-top: 2rem;
    font-size: 0.9rem;
    color: rgba(255, 255, 255, 0.72);
}

.hero__trust li { display: flex; align-items: center; gap: 0.5rem; }

.hero__trust li::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--brand);
    flex-shrink: 0;
}

/* The hero foot: the sliding service row, in the page flow rather than pinned
   to a corner, so it reads the same on a phone as on a desktop and can never
   end up under the floating WhatsApp button. */
.hero__foot {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    width: 100%;
}

.hero__services {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    padding-top: 0.9rem;
    border-top: 1px solid rgba(255, 255, 255, 0.18);
}

/* The window. `mask-image` fades both ends instead of letting names appear and
   vanish at a hard edge — the row reads as something passing through rather
   than a list being clipped. */
.hero__ticker {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 4.5rem, #000 calc(100% - 4.5rem), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 4.5rem, #000 calc(100% - 4.5rem), transparent);
}

/* The list, twice. The animation travels exactly one copy's width, measured in
   main.js and written here as --ticker-distance, so the second copy is where
   the first was at the moment it loops and there is no jump. The duration
   comes from that same measurement, so the speed does not change when a
   service is added in Admin. */
.hero__ticker-track {
    display: flex;
    align-items: center;
    gap: 2.4rem;
    width: max-content;
    margin: 0;
    padding: 0;
    list-style: none;
    will-change: transform;
    animation: hero-ticker var(--ticker-duration, 42s) linear infinite;
    animation-play-state: paused;
}

.hero__services.is-in-view:not(.is-paused) .hero__ticker-track {
    animation-play-state: running;
}

@keyframes hero-ticker {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(calc(var(--ticker-distance, 50%) * -1), 0, 0); }
}

/* Reaching for a link should not mean chasing it. */
.hero__services:hover .hero__ticker-track,
.hero__services:focus-within .hero__ticker-track,
.hero__services.is-paused .hero__ticker-track {
    animation-play-state: paused;
}

.hero__ticker-track a {
    display: inline-block;
    padding: 0.15rem 0;
    font-family: var(--font-display);
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
    color: rgba(255, 255, 255, 0.88);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color 0.35s var(--ease), border-color 0.35s var(--ease), text-shadow 0.35s var(--ease);
}

.hero__ticker-track a:hover,
.hero__ticker-track a:focus,
.hero__ticker-track a:focus-visible {
    color: #FF8A5B;
    border-bottom-color: #FF8A5B;
    text-shadow: 0 0 16px rgba(255, 138, 91, 0.32);
}

.hero__ticker-track a.is-revealed {
    color: #FF8A5B;
    text-shadow: 0 0 12px rgba(255, 138, 91, 0.2);
}

.hero__ticker-pause {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 2.1rem;
    height: 2.1rem;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    cursor: pointer;
    transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.hero__ticker-pause:hover { background: rgba(255, 255, 255, 0.2); }
.hero__ticker-pause .slider__play-icon { display: none; }
.hero__services.is-paused .hero__ticker-pause .slider__pause-icon { display: none; }
.hero__services.is-paused .hero__ticker-pause .slider__play-icon { display: block; }

/* Asked for less movement: the row does not move. It becomes an ordinary
   wrapped list of services, which is the same information standing still.
   The duplicate copy is hidden so nothing reads twice. */
@media (prefers-reduced-motion: reduce) {
    .hero__ticker {
        -webkit-mask-image: none;
        mask-image: none;
    }

    .hero__ticker-track {
        flex-wrap: wrap;
        width: auto;
        gap: 0.5rem 1.5rem;
        animation: none;
    }

    .hero__ticker-track li[aria-hidden="true"] { display: none; }
    .hero__ticker-pause { display: none; }
}

/* A secondary button that has to sit on media rather than on the page. */
.btn--onmedia {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.5);
    color: #FFFFFF;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.btn--onmedia:hover {
    background: rgba(255, 255, 255, 0.22);
    border-color: #FFFFFF;
}

@media (max-width: 599px) {
    /* The video needs real height to be worth loading, but not so much that the
       first section below the fold becomes invisible. `svh` rather than `vh`
       so a mobile browser's collapsing address bar does not crop it. */
    .hero {
        min-height: min(80svh, 38rem);
        padding-block: calc(var(--header-h) + 1.25rem) 1.75rem;
    }
    .hero__copy .eyebrow { font-size: 0.72rem; }
    .hero__copy h1 { font-size: clamp(2.25rem, 9vw, 3rem); }
    .hero__copy .lead { max-width: 28ch; font-size: 0.98rem; }
    .hero__copy .btn-row { gap: 0.55rem; flex-wrap: nowrap; }
    .hero__copy .btn--lg {
        flex: 1 1 0;
        min-height: 44px;
        padding: 0.55rem 0.7rem;
        font-size: 0.78rem;
        white-space: nowrap;
    }
    .hero__trust { display: none; }

    /* A narrower window needs a narrower fade, or the ends eat the names. */
    .hero__ticker {
        -webkit-mask-image: linear-gradient(90deg, transparent, #000 2rem, #000 calc(100% - 2rem), transparent);
        mask-image: linear-gradient(90deg, transparent, #000 2rem, #000 calc(100% - 2rem), transparent);
    }
    .hero__ticker-track { gap: 1.45rem; }
    .hero__ticker-track a { font-size: 0.8rem; }
    .hero__ticker-pause { width: 1.9rem; height: 1.9rem; }

    /* Tightened so the service row lands above the fold on a 844px-tall phone
       rather than half a row below it. */
    .hero__inner { gap: 1.25rem; }
    .hero__copy .btn-row { margin-top: 1.2rem; }
    .hero__copy h1 { margin-block: 0.55rem 0.7rem; }
    .hero__services { padding-top: 0.65rem; }
}

/* 8b. Services showcase
   Nvidia-style: one service at a time, sliding. Built on native scroll-snap so
   it works by swipe or scroll with no JavaScript; the buttons and auto-advance
   are enhancements.
   ========================================================================== */

.showcase { position: relative; }

.showcase__viewport {
    display: flex;
    gap: clamp(1rem, 2vw, 1.5rem);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    /* Align the first card with the page container, and let the last one scroll
       fully into place. */
    padding: 0.5rem var(--pad-x) 1.25rem;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.showcase__viewport::-webkit-scrollbar { display: none; }
.showcase__viewport:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-lg); }

.showcase__item {
    position: relative;
    flex: 0 0 min(100%, 34rem);
    scroll-snap-align: center;
    display: flex;
    gap: clamp(1rem, 2.5vw, 1.75rem);
    padding: clamp(1.5rem, 3vw, 2.25rem);
    background: linear-gradient(140deg, rgba(255,255,255,0.98) 0%, rgba(244,242,240,0.96) 100%);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: 0 1px 2px rgba(23, 20, 18, 0.05), 0 16px 32px -24px rgba(23, 20, 18, 0.2);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.showcase__item:hover,
.showcase__item:focus-within {
    border-color: var(--border-brand);
    box-shadow: 0 18px 38px -22px rgba(23, 20, 18, 0.22), 0 2px 8px rgba(23, 20, 18, 0.06);
    transform: translateY(-2px);
}

.showcase__icon {
    display: grid;
    place-items: center;
    width: 3.25rem;
    height: 3.25rem;
    flex-shrink: 0;
    color: var(--brand-ink);
}

.showcase__icon .card__icon-svg,
.showcase__icon .card__icon-image { width: 3.1rem; height: 3.1rem; }

.showcase__body { display: flex; flex-direction: column; gap: 0.45rem; min-width: 0; }

.showcase__count {
    font-family: var(--font-display);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    color: var(--brand-ink);
}

.showcase__count span { color: var(--text-dim); }

.showcase__tagline { color: var(--text-dim); font-size: 0.9rem; }

.showcase__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem 1rem;
    margin-top: auto;
    padding-top: 1rem;
}




@media (max-width: 560px) {
    .showcase__item {
        flex-direction: column;
        align-items: flex-start;
    }

    .showcase__icon {
        position: absolute;
        top: 1.25rem;
        right: 1.25rem;
        width: 2.35rem;
        height: 2.35rem;
    }

    .showcase__icon .card__icon-svg,
    .showcase__icon .card__icon-image { width: 2.35rem; height: 2.35rem; }

    .showcase__body {
        display: grid;
        width: 100%;
        grid-template-columns: auto minmax(0, 1fr);
        column-gap: 0.65rem;
        row-gap: 0.45rem;
    }

    .showcase__count { grid-column: 1; grid-row: 1; margin-top: 0.2rem; }
    .showcase__body h3 {
        grid-column: 2;
        grid-row: 1;
        padding-right: 2.85rem;
    }
    .showcase__tagline,
    .showcase__body > .muted,
    .showcase__foot { grid-column: 1 / -1; }
}

/* 8h. Slider controls
   One control bar, shared by the hero carousel and the services showcase.
   Previous on the left, position in the middle, next on the right — and the
   middle never wraps, whatever the item count, which a row of fixed-width dots
   could not promise.
   ========================================================================== */

.slider-controls {
    display: flex;
    align-items: center;
    gap: clamp(0.5rem, 2vw, 0.9rem);
    width: 100%;
}

.slider__arrow {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border: 1px solid var(--border-control);
    border-radius: 50%;
    color: var(--text-muted);
    transition: border-color var(--dur) var(--ease),
                color var(--dur) var(--ease),
                background-color var(--dur) var(--ease);
}

.slider__arrow:hover {
    border-color: var(--border-brand);
    color: var(--brand-ink);
    background: var(--brand-soft);
}

/* Discrete segments — one per slide, each clickable. flex:1 means they share
   whatever space is left between the arrows instead of overflowing it. */
.slider__track {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex: 1 1 auto;
    min-width: 0;
}

.slider__seg {
    flex: 1 1 0;
    min-width: 0;
    height: 24px;
    padding: 0;
    background: none;
    position: relative;
}

.slider__seg::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    top: 50%;
    transform: translateY(-50%);
    height: 3px;
    border-radius: 2px;
    background: var(--border-control);
    transition: background-color var(--dur) var(--ease), height var(--dur) var(--ease);
}

.slider__seg[aria-current="true"]::after { background: var(--brand-btn); height: 4px; }
.slider__seg:hover::after { background: var(--text-dim); }
.slider__seg[aria-current="true"]:hover::after { background: var(--brand-btn); }

/* Continuous track — for the scroll-snap showcase, where the item count is
   open-ended and position matters more than direct access. */
.slider__progress {
    flex: 1 1 auto;
    min-width: 0;
    height: 3px;
    border-radius: 2px;
    background: var(--border-control);
    overflow: hidden;
}

.slider__progress-fill {
    display: block;
    height: 100%;
    width: 20%;
    border-radius: 2px;
    background: var(--brand-btn);
    transition: width 0.3s var(--ease), transform 0.3s var(--ease);
}

.slider__pause {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border: 1px solid var(--border-control);
    border-radius: 50%;
    color: var(--text-dim);
    transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease),
                background-color var(--dur) var(--ease);
}

.slider__pause:hover { border-color: var(--border-brand); color: var(--brand-ink); }
.slider__pause .slider__play-icon { display: none; }
.slider__pause.is-paused .slider__pause-icon { display: none; }
.slider__pause.is-paused .slider__play-icon { display: block; }

.slider__pause.is-paused {
    background: var(--brand-soft);
    border-color: var(--border-brand);
    color: var(--brand-ink);
}

/* Sitting on the dark hero media rather than on the page. */
.slider-controls--onmedia .slider__arrow,
.slider-controls--onmedia .slider__pause {
    border-color: rgba(255, 255, 255, 0.45);
    color: rgba(255, 255, 255, 0.9);
}

.slider-controls--onmedia .slider__arrow:hover,
.slider-controls--onmedia .slider__pause:hover {
    background: rgba(255, 255, 255, 0.16);
    border-color: #FFFFFF;
    color: #FFFFFF;
}

.slider-controls--onmedia .slider__seg::after { background: rgba(255, 255, 255, 0.45); }
.slider-controls--onmedia .slider__seg[aria-current="true"]::after { background: #FFFFFF; }
.slider-controls--onmedia .slider__seg:hover::after { background: rgba(255, 255, 255, 0.75); }

.showcase__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.25rem;
    margin-top: 1.1rem;
}

.showcase__position {
    flex: 0 0 auto;
    font-family: var(--font-display);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: var(--text-dim);
}

@media (max-width: 560px) {
    .slider__arrow, .slider__pause { width: 40px; height: 40px; }
}

/* 8c. Section rhythm
   A page where every section is "heading + grid" reads as one long block.
   These give the page a cadence: tinted, dark, and bordered bands alternate
   with the default so the eye gets a beat between ideas.
   ========================================================================== */

.section--tinted {
    background: linear-gradient(180deg, var(--surface-2), var(--bg));
    border-block: 1px solid var(--border);
}

/* A dark band inverts the page for one idea. Everything inside it has to opt
   out of the page tokens, which is why the colours here are explicit. */
.section--dark {
    background:
        radial-gradient(70% 90% at 80% 0%, rgba(230, 74, 26, 0.22), transparent 60%),
        radial-gradient(60% 80% at 0% 100%, rgba(136, 0, 0, 0.28), transparent 60%),
        #141110;
    color: #FFFFFF;
}

.section--dark h2,
.section--dark h3 { color: #FFFFFF; }
.section--dark .lead,
.section--dark p { color: rgba(255, 255, 255, 0.76); }
.section--dark .eyebrow { color: #FF8A5B; }
.section--dark .dim { color: rgba(255, 255, 255, 0.55); }

.section--dark .btn--secondary {
    background: rgba(255, 255, 255, 0.06);
    /* 3.82:1 against the band — the button's only visual boundary. */
    border-color: rgba(255, 255, 255, 0.40);
    color: #FFFFFF;
}

.section--dark .btn--secondary:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.38);
}

/* A number that labels the section, editorial-style. */
.section-index {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5rem;
    font-family: var(--font-display);
    font-size: 0.76rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 0.9rem;
}

.section-index b {
    color: var(--brand-ink);
    font-weight: 700;
}

.section--dark .section-index { color: rgba(255, 255, 255, 0.5); }
.section--dark .section-index b { color: #FF8A5B; }

/* 8d. Problem statement
   ========================================================================== */


.problem__item {
    display: grid;
    gap: 0.55rem;
}

.problem__item h3 {
    font-size: clamp(1.1rem, 1rem + 0.5vw, 1.3rem);
    line-height: 1.3;
}
.problem__item p { color: var(--text-muted); font-size: 0.95rem; }

.problem__turn {
    margin-top: clamp(2rem, 4vw, 3rem);
    padding: clamp(1.4rem, 3vw, 2rem);
    border-left: 3px solid var(--brand-btn);
    background: var(--surface);
    border-radius: 0 var(--r) var(--r) 0;
    box-shadow: var(--shadow);
    margin-inline: auto;
}

.problem__turn p {
    font-family: var(--font-display);
    font-size: clamp(1.1rem, 1rem + 0.7vw, 1.45rem);
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.015em;
}

/* 8d-ii. Before / after contrast
   Two panels side by side, so "what usually happens" and "how we do it" read as
   an argument rather than four paragraphs in a row.
   ========================================================================== */

.compare {
    display: grid;
    gap: clamp(1rem, 2.5vw, 1.75rem);
}

@media (min-width: 820px) {
    .compare { grid-template-columns: repeat(2, 1fr); }
}

.compare__side {
    display: grid;
    align-content: start;
    gap: 0.9rem;
    padding: clamp(1.5rem, 3vw, 2.25rem);
    border-radius: var(--r-lg);
    background: var(--surface);
    border: 1px solid var(--border);
}

.compare__side p { color: var(--text-muted); }

.compare__label {
    font-family: var(--font-display);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.compare__side--ours {
    background: linear-gradient(160deg, rgba(230, 74, 26, 0.07), var(--surface) 60%);
    border-color: var(--border-brand);
}

.compare__side--ours .compare__label { color: var(--brand-ink); }
.compare__side--ours p { color: var(--text); }

/* 8e. Growth system
   ========================================================================== */

.system { display: grid; gap: 1px; background: rgba(255, 255, 255, 0.20); border-radius: var(--r-lg); overflow: hidden; }

.system__step {
    display: grid;
    /* Steps stretch to equal height in the row. Without this, the spare space
       is absorbed by the auto rows, silently inflating the number and heading
       of whichever step has the shortest paragraph. */
    align-content: start;
    gap: 0.5rem;
    padding: clamp(1.4rem, 2.5vw, 1.9rem);
    background: rgba(20, 17, 16, 0.75);
    transition: background-color var(--dur) var(--ease);
}

.system__step:hover { background: rgba(38, 31, 28, 0.9); }

.system__step p.system__num {
    font-family: var(--font-display);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    color: #FF8A5B;
}

@keyframes process-number-glow-5 {
    0%, 20% {
        color: #FFB49A;
        text-shadow: 0 0 0 rgba(255, 138, 91, 0);
        transform: scale(1);
    }
    10% {
        color: #FFFFFF;
        text-shadow: 0 0 18px rgba(255, 138, 91, 0.9);
        transform: scale(1.08);
    }
    20.01%, 100% {
        color: #FF8A5B;
        text-shadow: 0 0 0 rgba(255, 138, 91, 0);
        transform: scale(1);
    }
}

html.js-motion .system.is-visible .system__step p.system__num {
    animation: process-number-glow-5 15s var(--ease-in-out) infinite;
}

html.js-motion .system.is-visible .system__step:nth-child(2) p.system__num { animation-delay: 3s; }
html.js-motion .system.is-visible .system__step:nth-child(3) p.system__num { animation-delay: 6s; }
html.js-motion .system.is-visible .system__step:nth-child(4) p.system__num { animation-delay: 9s; }
html.js-motion .system.is-visible .system__step:nth-child(5) p.system__num { animation-delay: 12s; }

.system__step h3 {
    font-size: clamp(1.02rem, 0.95rem + 0.4vw, 1.18rem);
    line-height: 1.3;
    min-height: 2.6em;
}
.system__step p { font-size: 0.9rem; }

.system__note {
    margin-top: 1.5rem;
    font-size: 0.95rem;
    max-width: 66ch;
    margin-inline: auto;
    text-align: center;
}

@media (min-width: 720px) { .system { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .system { grid-template-columns: repeat(5, 1fr); } }

/* 8e. Horizontal Scroll Row (Sliders)
   ========================================================================== */
/* These elements carry `grid--3 scroll-row` together, and the two rule sets
   disagree about display and wrapping. Same specificity, so source order
   decides — and .scroll-row is declared after .grid--3, which is why none of
   this needs !important. Keep it below .grid--3 if the file is ever
   reorganised. */
.scroll-row {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: clamp(1rem, 2vw, 1.5rem);
    padding-bottom: 1.5rem;
    padding-top: 0.8rem;
    margin-inline: calc(var(--pad-x) * -1);
    padding-inline: var(--pad-x);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.scroll-row::-webkit-scrollbar { display: none; }

.scroll-row > * {
    scroll-snap-align: center;
    flex: 0 0 calc(85vw - var(--pad-x));
    max-width: 360px;
}

.scroll-row[data-showcase] > * { scroll-snap-align: center; }

@media (min-width: 768px) {
    .scroll-row:not(.scroll-row--always) {
        margin-inline: 0;
        padding-inline: 0;
        padding-bottom: 0;
        overflow-x: visible;
        flex-wrap: wrap;
    }
    .scroll-row:not(.scroll-row--always) > * {
        max-width: none;
        flex: 1 1 18.5rem; /* Restore grid--3 / default wrap behaviour */
    }
    .scroll-row--always {
        display: flex;
        overflow-x: auto;
        padding-bottom: 1.5rem;
        margin-inline: calc(var(--pad-x) * -1);
        padding-inline: var(--pad-x);
    }
    .scroll-row--always > * {
        flex: 0 0 380px;
    }
}

@media (max-width: 767px) {
    .section { padding-block: 2.2rem; }
    .section--tight { padding-block: 1.8rem; }
    .section-head { margin-bottom: 1.25rem; }
    .section-head h2 { margin-block: 0.35rem 0.45rem; }
    .scroll-row { justify-content: flex-start; }
    .scroll-row[data-showcase] > * { flex-basis: calc(85vw - var(--pad-x)); }

}

/* 8f. Audit explainer
   ========================================================================== */

.audit {
    display: grid;
    gap: clamp(1.25rem, 3vw, 2rem);
    align-items: start;
}

@media (min-width: 900px) { .audit { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.6fr); } }

.audit__panel {
    padding: clamp(1.25rem, 3vw, 2.25rem);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
}

.audit__list { display: grid; gap: clamp(0.75rem, 2vw, 1rem); counter-reset: audit-item; }

.audit__list li {
    display: grid;
    grid-template-columns: clamp(1.4rem, 4vw, 1.6rem) 1fr;
    gap: clamp(0.6rem, 2vw, 0.85rem);
    align-items: start;
}

.audit__list li::before {
    content: counter(audit-item);
    counter-increment: audit-item;
    display: grid;
    place-items: center;
    width: clamp(1.4rem, 4vw, 1.6rem);
    height: clamp(1.4rem, 4vw, 1.6rem);
    border-radius: 50%;
    background: var(--brand-soft);
    border: 1px solid var(--border-brand);
    color: var(--brand-ink);
    font-family: var(--font-display);
    font-size: clamp(0.7rem, 2vw, 0.76rem);
    font-weight: 700;
}

.audit__list--process li { position: relative; }

.audit__list--process li:not(:last-child)::after {
    content: "";
    position: absolute;
    left: clamp(0.7rem, 2vw, 0.8rem);
    top: clamp(1.4rem, 4vw, 1.6rem);
    bottom: clamp(-1rem, -2vw, -0.75rem);
    width: 2px;
    background: linear-gradient(var(--border-brand), var(--border));
}

html.js-motion .audit__panel .audit__list--process li::before {
    animation: process-number-glow-4 12s var(--ease-in-out) infinite;
}

html.js-motion .audit__panel .audit__list--process li:nth-child(2)::before { animation-delay: 3s; }
html.js-motion .audit__panel .audit__list--process li:nth-child(3)::before { animation-delay: 6s; }
html.js-motion .audit__panel .audit__list--process li:nth-child(4)::before { animation-delay: 9s; }


.audit__list strong { display: block; font-size: clamp(0.95rem, 2vw, 1rem); }
.audit__list span { color: var(--text-muted); font-size: clamp(0.85rem, 2vw, 0.92rem); line-height: 1.45; }

.audit__assurance {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--border);
    font-size: 0.9rem;
    color: var(--text-muted);
}

.audit__actions {
    width: 100%;
    flex-wrap: nowrap;
    gap: 0.65rem;
}

.audit__actions .btn {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: 0.8rem;
    font-size: 0.88rem;
    white-space: nowrap;
}

.home-website-actions { flex-wrap: nowrap; }

/* 8g. Trust reasons
   ========================================================================== */


/* 9. Marquee / logo-free trust strip
   ========================================================================== */

.strip {
    border-block: 1px solid var(--border);
    background: rgba(23, 20, 18, 0.025);
    padding-block: clamp(1.75rem, 3vw, 2.5rem);
}

.strip__grid {
    display: grid;
    gap: clamp(1.25rem, 3vw, 2rem);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}

.strip__item h3 {
    font-size: clamp(1.6rem, 1.3rem + 1.2vw, 2.1rem);
    color: var(--brand-ink);
    margin-bottom: 0.2rem;
}

.strip__item p { font-size: 0.9rem; color: var(--text-muted); }

/* 10. Steps / process
   A progression, not four stacked paragraphs. The numbers sit on a connecting
   rail so the order is the first thing you read, and the rail runs horizontally
   once there is room for the steps to sit side by side.
   ========================================================================== */

.steps { counter-reset: step; display: grid; gap: 1.75rem; }

.step {
    position: relative;
    padding-left: 3.75rem;
    counter-increment: step;
}

.step::before {
    content: counter(step, decimal-leading-zero);
    position: absolute;
    left: 0;
    top: 0;
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    background: var(--surface);
    border: 1.5px solid var(--border-brand);
    font-family: var(--font-display);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--brand-ink);
    z-index: 1;
}

/* The rail. Vertical while the steps are stacked. */
.step::after {
    content: "";
    position: absolute;
    left: 1.25rem;
    top: 2.5rem;
    bottom: -1.75rem;
    width: 2px;
    background: linear-gradient(var(--border-brand), var(--border));
}

.step:last-child::after { display: none; }

@keyframes process-number-glow-4 {
    0%, 25% {
        color: #FFFFFF;
        background: var(--brand-btn);
        border-color: var(--brand-btn);
        box-shadow: 0 0 0 0 rgba(200, 62, 20, 0), 0 0 0 rgba(200, 62, 20, 0);
        transform: scale(1);
    }
    12% {
        box-shadow: 0 0 0 7px rgba(200, 62, 20, 0.10), 0 0 24px rgba(200, 62, 20, 0.42);
        transform: scale(1.06);
    }
    25.01%, 100% {
        color: var(--brand-ink);
        background: var(--surface);
        border-color: var(--border-brand);
        box-shadow: 0 0 0 0 rgba(200, 62, 20, 0), 0 0 0 rgba(200, 62, 20, 0);
        transform: scale(1);
    }
}

html.js-motion .steps.is-visible .step::before {
    animation: process-number-glow-4 12s var(--ease-in-out) infinite;
}

html.js-motion .steps.is-visible .step:nth-child(2)::before { animation-delay: 3s; }
html.js-motion .steps.is-visible .step:nth-child(3)::before { animation-delay: 6s; }
html.js-motion .steps.is-visible .step:nth-child(4)::before { animation-delay: 9s; }

.step h3 { margin-bottom: 0.35rem; font-size: clamp(1.1rem, 1rem + 0.5vw, 1.3rem); }
.step p { color: var(--text-muted); font-size: 0.95rem; }

@media (min-width: 900px) {
    .steps { grid-template-columns: repeat(4, 1fr); gap: 2rem; }

    .step { padding-left: 0; padding-top: 3.75rem; }
    .step::before { top: 0; left: 0; }

    /* Horizontal rail, running from each number to the next. */
    .step::after {
        left: 2.5rem;
        top: 1.25rem;
        bottom: auto;
        right: -2rem;
        width: auto;
        height: 2px;
        background: linear-gradient(90deg, var(--border-brand), var(--border));
    }
}

/* 11. Packages
   ========================================================================== */

.package {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: clamp(1.4rem, 2.2vw, 1.85rem);
    background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    transition: transform var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

.package:hover {
    transform: translateY(-4px);
    border-color: var(--border-brand);
    box-shadow: var(--shadow-lg);
}

.package.is-highlighted {
    border-color: var(--border-brand);
    background:
        radial-gradient(90% 55% at 50% 0%, rgba(230, 74, 26, 0.16), transparent 70%),
        linear-gradient(180deg, var(--surface-2) 0%, var(--surface) 100%);
}

.package__badge {
    position: absolute;
    top: -0.7rem;
    left: clamp(1.6rem, 2.5vw, 2.1rem);
    padding: 0.28rem 0.8rem;
    border-radius: var(--r-pill);
    background: var(--brand-btn);
    color: #fff;
    font-family: var(--font-display);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.package__price {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.4rem;
    font-family: var(--font-display);
}

.package__amount {
    font-size: clamp(1.85rem, 1.5rem + 1.4vw, 2.4rem);
    font-weight: 700;
    letter-spacing: -0.03em;
}

.package__period { color: var(--text-dim); font-size: 0.95rem; }

.package__was {
    color: var(--text-dim);
    font-size: 0.95rem;
    text-decoration: line-through;
}

.package__features { display: grid; gap: 0.6rem; font-size: 0.925rem; }

.package__features li {
    display: grid;
    grid-template-columns: 1.15rem 1fr;
    gap: 0.6rem;
    align-items: start;
    color: var(--text-muted);
}

/* A tick, not colour alone, carries the "included" meaning. */
.package__features li::before {
    content: "";
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.22rem;
    background-image: var(--tick);
    background-size: 100% 100%;
    background-position: center;
    background-repeat: no-repeat;
}

.package__ideal {
    padding-top: 0.9rem;
    border-top: 1px solid var(--border);
    font-size: 0.875rem;
    color: var(--text-dim);
}

.package .btn { margin-top: auto; }

/* 11b. Package highlight (home page)
   A compact version of the package card. The full inclusions live on the
   packages page, so this only carries enough to make the comparison.
   ========================================================================== */

.highlight {
    /* position: relative is load-bearing — the badge is absolutely positioned
       and would otherwise escape to the nearest positioned ancestor. */
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: clamp(1.2rem, 2.2vw, 1.6rem);
    background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    transition: transform var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

.highlight:hover {
    transform: translateY(-4px);
    border-color: var(--border-brand);
    box-shadow: var(--shadow-lg);
}

.highlight.is-highlighted {
    border-color: var(--border-brand);
    background:
        radial-gradient(90% 55% at 50% 0%, rgba(230, 74, 26, 0.10), transparent 70%),
        linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
}

.highlight__points { display: grid; gap: 0.5rem; font-size: 0.9rem; }

.highlight__points li {
    display: grid;
    grid-template-columns: 1.05rem 1fr;
    gap: 0.55rem;
    align-items: start;
    color: var(--text-muted);
}

.highlight__points li::before {
    content: "";
    width: 1.05rem;
    height: 1.05rem;
    margin-top: 0.22rem;
    background-image: var(--tick);
    background-size: 100% 100%;
    background-position: center;
    background-repeat: no-repeat;
}

.highlight__more {
    color: var(--brand-ink);
    font-weight: 500;
}

/* No tick on the "+N more" line: it is a pointer to the packages page, not an
   inclusion in its own right. Qualified by the list rather than shouting with
   !important — `.highlight__points li::before` is the rule being overridden,
   and a bare `.highlight__more::before` is less specific than it, not more. */
.highlight__points li.highlight__more::before {
    background: none;
}

.highlight .btn { margin-top: auto; }

@media (hover: none) {
    .highlight:hover { transform: none; }
}

/* 12. Website-type mockups
   ========================================================================== */

.mockups { display: grid; gap: clamp(1.25rem, 3vw, 2rem); }

@media (max-width: 639px) {
    .mockups { margin-top: 2rem; padding-top: 0; gap: 1rem; }
}

.mockup {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    overflow: hidden;
    transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
    text-decoration: none;
    color: inherit;
}

.mockup:hover { transform: translateY(-7px); border-color: var(--border-brand); box-shadow: var(--shadow-lg); }

.mockup__chrome {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.6rem 0.85rem;
    background: var(--surface-3);
    border-bottom: 1px solid var(--border);
}

.mockup__dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: #C9C3BD;
}

.mockup__url {
    margin-left: 0.5rem;
    padding: 0.15rem 0.6rem;
    border-radius: var(--r-pill);
    background: #FFFFFF;
    border: 1px solid var(--border);
    font-size: 0.7rem;
    color: var(--text-dim);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
}

/* The "screen" is a pure-CSS skeleton — deliberately abstract so it is never
   mistaken for a real client site. */
.mockup__screen {
    display: grid;
    gap: 6px;
    padding: 12px;
    background: #F7F5F3;
    aspect-ratio: 16 / 11;
    grid-auto-rows: minmax(0, auto);
    transition: background-color var(--dur) var(--ease);
}

.mockup--portfolio .mockup__screen { background: linear-gradient(145deg, #f4e9df, #e1d5cd); }
.mockup--ecommerce .mockup__screen { background: linear-gradient(145deg, #eef0eb, #e4ddd4); }
.mockup--booking .mockup__screen { background: linear-gradient(145deg, #e8e9e1, #d9ddd3); }

.mockup__bar {
    background: #D9D4CF;
    border-radius: 4px;
    height: 10px;
}

.mockup__bar--brand { background: linear-gradient(90deg, var(--brand-deep), var(--brand)); }

.mockup--portfolio { --demo-color: #176b87; }
.mockup--ecommerce { --demo-color: #2f5d62; }
.mockup--booking { --demo-color: #2e6f95; }

.mockup.mockup--link:hover { border-color: var(--demo-color); box-shadow: 0 10px 30px -10px rgba(0,0,0,0.08); }
.mockup--link .mockup__eyebrow { color: var(--demo-color); }
.mockup--link .mockup__bar--brand { background: var(--demo-color); }
.mockup--link .mockup__cta:hover { color: var(--demo-color); }
.mockup__bar--wide { height: 16px; }
.mockup__bar--half { width: 55%; }
.mockup__bar--third { width: 34%; }

.mockup__block {
    background: linear-gradient(140deg, #E6E2DE, #EFECE9);
    border-radius: 6px;
    min-height: 40px;
}

.mockup__block--hero {
    min-height: 78px;
    background:
        radial-gradient(60% 80% at 75% 30%, rgba(230, 74, 26, 0.22), transparent 70%),
        linear-gradient(140deg, #E9E5E1, #DCD7D2);
    display: grid;
    align-content: center;
    gap: 6px;
    padding: 12px;
}

.mockup__row { display: grid; gap: 6px; }
.mockup__row--2 { grid-template-columns: repeat(2, 1fr); }
.mockup__row--3 { grid-template-columns: repeat(3, 1fr); }
.mockup__row--4 { grid-template-columns: repeat(4, 1fr); }
.mockup__row--sidebar { grid-template-columns: 1fr 2fr; }

.mockup__tile {
    background: linear-gradient(140deg, #E6E2DE, #EFECE9);
    border-radius: 5px;
    min-height: 46px;
    display: grid;
    align-content: end;
    gap: 4px;
    padding: 6px;
}

.mockup__tile .mockup__bar { height: 6px; }

.mockup__body {
    padding: clamp(1.1rem, 2vw, 1.5rem);
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    background: var(--surface);
}

.mockup__eyebrow { color: var(--brand-ink); font-family: var(--font-display); font-size: .7rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.mockup__body h3 { font-size: 1.2rem; }
.mockup__body p { font-size: 0.92rem; color: var(--text-muted); }

.mockup__features {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.2rem;
}

.mockup__features li {
    padding: 0.25rem 0.65rem;
    border-radius: var(--r-pill);
    background: rgba(23, 20, 18, 0.04);
    border: 1px solid var(--border);
    font-size: 0.775rem;
    color: var(--text-muted);
}

/* Each card is a self-identification moment — "that's my business". Without a
   CTA the visitor has to scroll back up to act on it. */
.mockup__cta { margin-top: auto; padding-top: 1rem; align-self: flex-start; }

/* Honesty label — required, never removed. */
.mockups__note {
    margin-top: 1.25rem;
    padding: 0.85rem 1.1rem;
    border: 1px dashed var(--border-strong);
    border-radius: var(--r);
    font-size: 0.875rem;
    color: var(--text-dim);
}

@media (max-width: 767px) {
    .audit__actions { gap: 0.5rem; }
    .audit__actions .btn {
        min-height: 44px;
        padding: 0.55rem 0.45rem;
        font-size: 0.74rem;
        white-space: nowrap;
    }

    .audit__assurance,
    .mockups__note {
        border-top: 0;
        border-inline-start: 2px solid var(--border-brand);
        border-radius: 0;
        padding: 0.15rem 0 0.15rem 0.85rem;
    }

    .audit__assurance { margin-top: 1rem; }

    .home-website-actions { gap: 0.5rem; }
    .home-website-actions .btn {
        min-height: 40px;
        padding: 0.55rem 0.7rem;
        font-size: 0.75rem;
    }
}

@media (max-width: 639px) {
    .mockups { margin-inline: 0; padding-inline: 0; overflow-x: auto; grid-auto-flow: column; grid-auto-columns: minmax(18rem, 86vw); scroll-snap-type: x mandatory; scroll-padding-inline: 0; scrollbar-width: none; padding-bottom: 1rem; }
    .mockups::-webkit-scrollbar { display: none; }
    .mockup { scroll-snap-align: start; scroll-snap-stop: always; }
}

/* 12b. Map embed
   A placeholder until the visitor asks for it. See the comment in
   `contact.html`: the iframe is a third-party request and the Cookie Policy
   promises none are made before consent.
   ========================================================================== */

.map-embed {
    position: relative;
    border-radius: var(--r-sm);
    overflow: hidden;
    box-shadow: var(--shadow);
    background: var(--surface-2, var(--surface));
}

.map-embed__load {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    width: 100%;
    min-height: 250px;
    padding: 1.5rem 1.25rem;
    border: 1px dashed var(--border-strong);
    border-radius: var(--r-sm);
    color: var(--text-muted);
    text-align: center;
    /* A faint street-grid hint, so the panel reads as a map before it is one
       without pretending to show a location it has not loaded. */
    background-image:
        linear-gradient(var(--border) 1px, transparent 1px),
        linear-gradient(90deg, var(--border) 1px, transparent 1px);
    background-size: 34px 34px;
    transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.map-embed__load:hover {
    border-color: var(--border-brand);
    background-color: var(--brand-soft);
    color: var(--text);
}

.map-embed__label {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 1rem;
    color: var(--text);
}

.map-embed__note {
    max-width: 22rem;
    font-size: 0.8rem;
    line-height: 1.4;
}

.map-embed.is-loaded { border: 0; }

/* 13. Forms
   ========================================================================== */

.form { display: grid; gap: 1.15rem; }

.form__row {
    display: grid;
    gap: 1.15rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    align-items: start;
}

.field { display: grid; gap: 0.4rem; }

.field label {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-muted);
}

.field input,
.field select,
.field textarea {
    width: 100%;
    min-height: 48px;
    padding: 0.75rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border-control);
    border-radius: var(--r);
    color: var(--text);
    transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.field textarea { min-height: 9rem; resize: vertical; }

.field input::placeholder,
.field textarea::placeholder { color: var(--text-dim); }

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--border-brand); }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
    border-color: var(--brand-btn);
    background: var(--surface-2);
}

.field select {
    appearance: none;
    height: 54px;
    min-height: 54px;
    line-height: 1.4;
    background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                      linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
    background-position: calc(100% - 1.15rem) 1.35rem, calc(100% - 0.8rem) 1.35rem;
    background-size: 6px 6px;
    background-repeat: no-repeat;
    padding-right: 2.5rem;
}

.field__error, .errorlist {
    display: grid;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--error);
}

.field__help { font-size: 0.8rem; color: var(--text-dim); }

/* The reassurance line under the submit button. Given real weight rather than
   help-text grey: it is answering the last hesitation before someone commits. */
.form__assurance {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin-top: 1rem;
    padding: 0.85rem 1rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--text-muted);
}

.form__assurance .cell-tick { flex-shrink: 0; margin-top: 0.1rem; }
.form__assurance strong { color: var(--text); }
.form__assurance a { color: var(--brand-ink); text-decoration: underline; text-underline-offset: 3px; }

.field--invalid input,
.field--invalid select,
.field--invalid textarea { border-color: var(--error); }

/* The honeypot must stay in the DOM but out of sight and out of the tab order. */
.honeypot {
    position: absolute !important;
    left: -9999px;
    opacity: 0;
    pointer-events: none;
}

.form__errors {
    display: grid;
    gap: 0.5rem;
    padding: 1rem 1.15rem;
    border: 1px solid var(--error);
    border-left-width: 3px;
    border-radius: var(--r);
    background: rgba(194, 40, 31, 0.07);
    color: var(--error);
    font-size: 0.9rem;
}

.form__errors:focus-visible { box-shadow: var(--focus-ring); }

.form__errors ul { display: grid; gap: 0.3rem; padding-left: 1.1rem; }
.form__errors li { list-style: disc; }

.form__errors a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* Marks a field the browser itself has judged invalid, only after the visitor
   has interacted with it — so an untouched required field is not scolded. */
.field input:user-invalid,
.field select:user-invalid,
.field textarea:user-invalid { border-color: var(--error); }

.messages {
    display: grid;
    gap: 0.6rem;
    max-width: var(--container);
    margin: 1.25rem auto 0;
    padding-inline: var(--pad-x);
}

.messages li {
    padding: 0.85rem 1.15rem;
    border-radius: var(--r);
    border: 1px solid var(--border-strong);
    background: var(--surface);
    font-size: 0.925rem;
}

.messages li.success { border-color: rgba(27, 127, 68, 0.45); color: var(--success); }
.messages li.error { border-color: var(--error); color: var(--error); }

/* 14. Prose (blog body, legal pages)
   ========================================================================== */

.prose { max-width: 70ch; }
.prose > * + * { margin-top: 1.15rem; }

.prose h2 {
    font-size: clamp(1.5rem, 1.25rem + 1.1vw, 2rem);
    margin-top: 2.75rem;
    padding-top: 0.25rem;
}

.prose h3 { margin-top: 2rem; }
.prose p, .prose li { color: #2E2A27; }

.prose a {
    color: var(--brand-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.prose a:hover { color: var(--brand-hover); }

.prose ul, .prose ol { display: grid; gap: 0.55rem; padding-left: 1.3rem; }
.prose ul li { list-style: disc; }
.prose ol li { list-style: decimal; }

.prose blockquote {
    padding: 1rem 1.4rem;
    border-left: 3px solid var(--brand);
    background: var(--surface);
    border-radius: 0 var(--r) var(--r) 0;
    color: var(--text-muted);
}

.prose strong { color: var(--text); }

.prose code {
    padding: 0.15em 0.4em;
    background: var(--surface-3);
    border-radius: 4px;
    font-size: 0.9em;
}

/* Tables — cookie disclosures, package comparison, Markdown blog tables.
   Wrapped in .table-wrap so a wide table scrolls inside its own box instead of
   forcing the whole page sideways on a phone. */
.table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
}

.table-wrap:focus-visible { box-shadow: var(--focus-ring); }

.table {
    width: 100%;
    min-width: 34rem;
    border-collapse: collapse;
    font-size: 0.9rem;
    text-align: left;
}

.table th,
.table td {
    padding: 0.7rem 0.95rem;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}

.table thead th {
    position: sticky;
    top: 0;
    background: var(--surface-2);
    font-family: var(--font-display);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
    border-bottom: 1px solid var(--border-strong);
}

.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--surface-2); }
.table code { white-space: nowrap; }

/* Comparison table — what changes between package tiers. Built so a row can
   be read across on a phone without losing which column is which. */
.table--compare { min-width: 46rem; }

.table--compare thead th:not(:first-child),
.table--compare tbody td { text-align: center; }

.table--compare thead th { vertical-align: bottom; }

.table--compare thead th.is-highlighted {
    background: var(--brand-soft);
    color: var(--brand-ink);
    border-bottom-color: var(--border-brand);
}

.table--compare tbody th {
    font-family: var(--font-body);
    font-size: 0.925rem;
    font-weight: 600;
    color: var(--text);
    min-width: 14rem;
}

.table__price {
    display: block;
    margin-top: 0.3rem;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    text-transform: none;
    color: var(--text);
}

.table__price span { font-weight: 500; color: var(--text-dim); }

.table__note {
    display: block;
    margin-top: 0.2rem;
    font-weight: 400;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-dim);
}

/* The tick is the meaning, so it is a shape rather than colour alone. */
.cell-tick {
    display: inline-block;
    width: 1.05rem;
    height: 1.05rem;
    vertical-align: -0.16rem;
    background-image: var(--tick);
    background-size: 100% 100%;
    background-position: center;
    background-repeat: no-repeat;
}

.cell-yes {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.85rem;
    text-align: left;
    color: var(--text-muted);
}

.cell-no { color: var(--text-dim); font-size: 1.1rem; }

/* Only shown where the table actually overflows. Without it a cut-off table
   reads as broken rather than scrollable. */
.table-hint {
    display: none;
    margin-bottom: 0.6rem;
    font-size: 0.8rem;
    color: var(--text-dim);
}

@media (max-width: 800px) {
    .table-hint { display: block; }
}

/* The prose column is narrow for reading; a table needs the full measure. */
.prose .table-wrap { max-width: none; }

/* 15. Article / blog
   ========================================================================== */

.article-head { max-width: 70ch; }
.article-head h1 { margin-block: 0.9rem 1rem; font-size: clamp(2rem, 1.5rem + 2.5vw, 3.25rem); }

.article-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1.15rem;
    font-size: 0.9rem;
    color: var(--text-dim);
}

/* The author's face beside their name. Round, small and cropped to a square
   from whatever portrait was uploaded — object-fit rather than a fixed ratio,
   because a phone photo is usually 3:4 and squashing a face is worse than
   cropping one. */
.byline {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
}

.byline__face {
    flex: 0 0 auto;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    object-fit: cover;
    object-position: center 20%;
    border: 1px solid var(--border);
    background: var(--surface-2);
}

.quick-answer {
    max-width: 70ch;
    margin-block: 2rem;
    padding: clamp(1.15rem, 2.5vw, 1.6rem);
    border: 1px solid var(--border-brand);
    border-radius: var(--r-lg);
    background: radial-gradient(90% 100% at 0% 0%, rgba(230, 74, 26, 0.10), transparent 70%), var(--surface);
}

.quick-answer h2 { font-size: 1.05rem; margin-bottom: 0.5rem; color: var(--brand-ink); }
.quick-answer p { color: #2E2A27; }

.tag-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.tag {
    padding: 0.32rem 0.85rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--border);
    background: rgba(23, 20, 18, 0.035);
    font-size: 0.82rem;
    color: var(--text-muted);
    transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

a.tag:hover { border-color: var(--border-brand); color: var(--brand-ink); }

/* FAQ */
.faq { 
    display: flex; 
    flex-direction: column;
    max-width: 80ch; 
    margin-inline: auto; 
}

.faq__item {
    border-bottom: 1px solid var(--border);
}

.faq__item:first-child {
    border-top: 1px solid var(--border);
}

.faq__item summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.5rem 0;
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 700;
    cursor: pointer;
    list-style: none;
    color: var(--text);
}

.faq__item summary::-webkit-details-marker { display: none; }

/* The question inside a disclosure is a real heading, so a screen-reader user
   can pull up the list of questions and jump between them instead of tabbing
   through every control on the page to find one. It is styled to nothing:
   the summary already carries the type, and an h3's own size and margins
   would break the row the summary lays out. Semantics only. */
.faq__q {
    /* flex: 1 and min-width: 0 are not cosmetic. The summary is a flex row and
       the question used to be anonymous text in it; an element child instead
       takes flex-basis from its own content and will not grow into the space
       left over, so every question wrapped a word or two early at 390px.
       Compared against the previous build at 390 and 1440, open and shut. */
    flex: 1;
    min-width: 0;
    margin: 0;
    font: inherit;
    letter-spacing: inherit;
    color: inherit;
    text-transform: none;
    /* Headings balance their lines; anonymous text does not. Left alone, every
       question re-broke — "Is advertising spend / included in the price?"
       instead of filling the first line — which is a typographic change, and
       this was meant to be a semantic one. Balance is worth considering for
       these on its own merits; it should not arrive as a side effect. */
    text-wrap: inherit;
}


.faq__item summary::after {
    content: "↓";
    flex-shrink: 0;
    font-size: 1.1rem;
    color: var(--brand-ink);
    transition: transform var(--dur) var(--ease);
}

.faq__item[open] summary::after { transform: rotate(180deg); }

.faq__item > div { padding: 0 0 1.5rem 0; color: var(--text-muted); line-height: 1.6; }

.faq__item[open] summary { color: var(--brand-ink); }

/* 16. Pagination
   ========================================================================== */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 3rem;
    font-size: 0.925rem;
}

.pagination a {
    padding: 0.55rem 1.15rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-pill);
    transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.pagination a:hover { border-color: var(--border-brand); color: var(--brand-ink); }
.pagination span { color: var(--text-dim); }

/* 17. CTA band
   ========================================================================== */

.cta-band {
    position: relative;
    padding: clamp(2.2rem, 5vw, 3.75rem) clamp(1.35rem, 3.5vw, 3rem);
    border: 1px solid var(--border-brand);
    border-radius: var(--r-lg);
    overflow: hidden;
    background:
        radial-gradient(80% 120% at 85% 10%, rgba(230, 74, 26, 0.18), transparent 65%),
        radial-gradient(70% 100% at 10% 90%, rgba(136, 0, 0, 0.16), transparent 65%),
        linear-gradient(180deg, rgba(255,255,255,0.96) 0%, rgba(244,242,240,0.94) 100%);
    box-shadow: 0 18px 32px -26px rgba(23, 20, 18, 0.22);
}

.cta-band h2 { max-width: 20ch; margin-bottom: 0.9rem; }
.cta-band p { max-width: 54ch; margin-bottom: 1.85rem; color: var(--text-muted); }

/* 17b. Page header
   ========================================================================== */

.page-head {
    position: relative;
    padding-block: clamp(2.75rem, 6vw, 4.5rem) clamp(2rem, 4vw, 3rem);
    border-bottom: 1px solid var(--border);
}

.page-head h1 {
    margin-block: 0.9rem 1rem;
    font-size: clamp(2.35rem, 1.6rem + 2.9vw, 3.9rem);
}
.page-head__chosen { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 1rem; }

.page-head__price {
    font-size: clamp(1.15rem, 1rem + 0.8vw, 1.6rem);
    font-weight: 600;
    color: var(--brand-ink);
    letter-spacing: -0.01em;
}

.page-head__price span { font-weight: 500; color: var(--text-dim); }

.page-head .lead { margin-bottom: 0; }
.page-head .btn-row { margin-top: 1.85rem; }

/* A page head that fills the width instead of leaving half the screen blank.
   The heading keeps a readable measure; the space beside it carries something
   worth reading rather than nothing at all. */
.page-head__grid {
    display: grid;
    gap: clamp(2rem, 5vw, 4rem);
    align-items: end;
}

@media (min-width: 900px) {
    .page-head__grid { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); }
    .page-head__grid > :only-child { grid-column: 1 / -1; }
}

.page-head__aside {
    display: grid;
    gap: 1rem;
    padding: clamp(1.4rem, 2.5vw, 2rem);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
}

.page-head__figure {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-family: var(--font-display);
    font-size: clamp(1.9rem, 1.4rem + 2vw, 2.9rem);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1;
}

.page-head__figure small {
    font-size: 0.9rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--text-dim);
}

.page-head__aside .checklist li { font-size: 0.925rem; }

.breadcrumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1.1rem;
    font-size: 0.85rem;
    color: var(--text-dim);
}

.breadcrumbs a:hover { color: var(--brand-ink); }
.breadcrumbs li:not(:last-child)::after { content: " /"; color: var(--text-dim); }

/* Two-column body used by contact and service detail. */
.split {
    display: grid;
    gap: clamp(2rem, 4vw, 3.5rem);
    align-items: start;
}

@media (min-width: 900px) {
    .split { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
    .split--reverse > :first-child { order: 2; }
}

/* A card beside its own explanation, rather than one card stretched across a
   three-column grid. */
.split--card { align-items: start; }

@media (min-width: 900px) {
    .split--card { grid-template-columns: minmax(0, 24rem) minmax(0, 1fr); }
}

.split--card .prose h3 { margin-bottom: 0.6rem; }

.panel {
    padding: clamp(1.4rem, 2.5vw, 2rem);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
}

.panel h2, .panel h3 { font-size: 1.15rem; margin-bottom: 0.9rem; }

.checklist { display: grid; gap: 0.7rem; }

.checklist li {
    display: grid;
    grid-template-columns: 1.15rem 1fr;
    gap: 0.65rem;
    align-items: start;
    color: var(--text-muted);
}

.checklist li::before {
    content: "";
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.22rem;
    background-image: var(--tick);
    background-size: 100% 100%;
    background-position: center;
    background-repeat: no-repeat;
}

/* A checklist that spreads across the width instead of running down one narrow
   column with the rest of the row empty. */
.checklist--grid {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: 1rem 2rem;
}

.checklist--grid li { font-size: 1rem; color: var(--text); }

.contact-methods { display: grid; gap: 0.75rem; }

/* Direct children only — nested spans are the value, not another row. */
.contact-methods > a,
.contact-methods > span,
.contact-methods > li > a {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.8rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: rgba(23, 20, 18, 0.025);
    font-size: 0.95rem;
    transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.contact-methods > a:hover,
.contact-methods > li > a:hover { border-color: var(--border-brand); background: var(--brand-soft); }
.contact-methods strong { font-family: var(--font-display); }

.empty-note {
    padding: clamp(1.5rem, 3vw, 2.25rem);
    border: 1px dashed var(--border-strong);
    border-radius: var(--r-lg);
    color: var(--text-muted);
    text-align: center;
}

/* 18. Footer
   ========================================================================== */

.footer {
    /* No top margin: every page's final section already carries bottom padding,
       and stacking the two left a dead band above the footer. */
    border-top: 1px solid var(--border);
    background: linear-gradient(180deg, rgba(23, 20, 18, 0.025), transparent);
}

.footer__top {
    display: grid;
    gap: clamp(2rem, 4vw, 3rem);
    padding-block: clamp(3rem, 6vw, 4.5rem);
    grid-template-columns: 1fr 1fr;
}

.footer__brand { }
@media (max-width: 639px) { .footer__top { grid-template-columns: 1fr; } }
.footer__brand .brand { margin-bottom: 1rem; }
.footer__brand p { color: var(--text-muted); font-size: 0.925rem; }

.footer__heading {
    /* Semantically h2 — sibling of the page's sections, not a child of the
       last one — but sized as the small footer label it looks like. */
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 1rem;
}

.footer__links { display: grid; gap: 0.2rem; font-size: 0.925rem; }

/* Padded to a real tap target. These measured 18px tall — below the 24px
   minimum, and they are exactly the links someone taps on a phone. */
.footer__links a {
    display: inline-flex;
    align-items: center;
    min-height: 26px;
    padding-block: 0.2rem;
    color: var(--text-muted);
    transition: color var(--dur) var(--ease);
}
.footer__links a:hover { color: var(--brand-ink); }

.footer__contact { display: grid; gap: 0.6rem; font-size: 0.925rem; }

.footer__contact a {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    /* A real tap target: these measured 18px tall. */
    min-height: 26px;
    padding-block: 0.15rem;
    color: var(--text-muted);
    transition: color var(--dur) var(--ease);
}

.footer__contact a:hover { color: var(--brand-ink); }

.footer__contact-icon {
    width: 1.25rem;
    height: 1.25rem;
    flex: 0 0 1.25rem;
    object-fit: contain;
    mix-blend-mode: multiply;
}

.footer__location {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.footer__social { display: flex; gap: 0.6rem; margin-top: 1.15rem; }

.footer__social a {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    color: var(--text-muted);
    transition: border-color var(--dur) var(--ease),
                color var(--dur) var(--ease),
                background-color var(--dur) var(--ease);
    animation: footer-social-glow 4.5s ease-in-out infinite;
}

.footer__social-icon {
    width: 1.35rem;
    height: 1.35rem;
    object-fit: contain;
    mix-blend-mode: multiply;
}

@keyframes footer-social-glow {
    0%, 100% { box-shadow: 0 0 0 0 rgba(200, 62, 20, 0); }
    50% { box-shadow: 0 0 0 4px rgba(200, 62, 20, 0.08); }
}

@media (prefers-reduced-motion: reduce) {
    .footer__social a { animation: none; }
}

.footer__social a.social-wa { color: #25D366; border-color: rgba(37, 211, 102, 0.4); }
.footer__social a.social-wa:hover { background: rgba(37, 211, 102, 0.1); border-color: #25D366; }

.footer__social a.social-fb { color: #1877F2; border-color: rgba(24, 119, 242, 0.4); }
.footer__social a.social-fb:hover { background: rgba(24, 119, 242, 0.1); border-color: #1877F2; }

.footer__social a.social-ig { border-color: rgba(221, 42, 123, 0.4); }
.footer__social a.social-ig svg { stroke: url(#ig-grad); }
.footer__social a.social-ig:hover { background: rgba(221, 42, 123, 0.05); border-color: #DD2A7B; }

.newsletter { display: grid; gap: 0.6rem; }

.newsletter__form {
    display: flex;
    gap: 0.5rem;
    /* Wraps, and must. The email field and the button cannot sit side by side
       below about 414px, and `nowrap` here pushed the whole document 78px wider
       than a 320px phone — horizontal scroll on every page on the site, since
       the footer is on every page. */
    flex-wrap: wrap;
}

.newsletter__form input {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 46px;
    padding: 0.6rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-pill);
    color: var(--text);
    transition: border-color var(--dur) var(--ease);
}

.newsletter__form > .btn {
    flex: 0 0 auto;
    white-space: nowrap;
}

.newsletter__form input:hover,
.newsletter__form input:focus-visible { border-color: var(--border-brand); }

.newsletter__status { font-size: 0.85rem; min-height: 1.2rem; }
.newsletter__status.is-success { color: var(--success); }
.newsletter__status.is-error { color: var(--error); }

.footer__bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
    padding-block: 1.5rem;
    border-top: 1px solid var(--border);
    font-size: 0.85rem;
    color: var(--text-dim);
}

.footer__legal { display: flex; flex-wrap: wrap; gap: 0.6rem 1.25rem; }

.footer__legal a,
.footer__cookie-settings {
    display: inline-flex;
    align-items: center;
    min-height: 26px;
    padding: 0;
    color: inherit;
    font-size: inherit;
    text-align: left;
    border-radius: var(--r-sm);
    transition: color var(--dur) var(--ease);
}

.footer__legal a:hover { color: var(--brand-ink); }

/* 19. Floating controls
   ========================================================================== */

/* Two separate corners. They were previously stacked in one column, which put
   the back-to-top button right on top of the WhatsApp button. */
.floats {
    position: fixed;
    right: clamp(0.9rem, 3vw, 1.75rem);
    /* Plus the iOS home-indicator inset, or these sit partly underneath it. */
    bottom: calc(clamp(0.9rem, 3vw, 1.75rem) + env(safe-area-inset-bottom, 0px));
    z-index: 90;
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    align-items: flex-end;
}

.floats--left {
    right: auto;
    left: clamp(0.9rem, 3vw, 1.75rem);
    align-items: flex-start;
}

.float-btn {
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    box-shadow: var(--shadow-lg);
    transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

/* On a phone the reading column is the whole screen, so a button floating over
   the left of it sits squarely on the words. Back-to-top is the one that can
   go: phones already have their own way back up (tapping the status bar on
   iOS, and the address bar on Android), whereas the WhatsApp button is the
   conversion path and earns its place. */
@media (max-width: 767px) {
    .floats--left { display: none; }
    .float-btn { width: 48px; height: 48px; }
}

.float-btn:hover { transform: translateY(-3px) scale(1.04); }

.float-btn--whatsapp { background: var(--whatsapp); color: #062814; }
.float-btn--whatsapp:hover { background: var(--whatsapp-hover); }

.float-btn--top {
    background: var(--surface-2);
    border: 1px solid var(--border-control);
    color: var(--text-muted);
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
}

.float-btn--top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }
.float-btn--top:hover { color: var(--brand-ink); border-color: var(--border-brand); }

/* 19b. Cookie banner
   ========================================================================== */

.cookie-banner {
    position: fixed;
    left: clamp(0.75rem, 3vw, 1.5rem);
    right: clamp(0.75rem, 3vw, 1.5rem);
    bottom: calc(clamp(0.75rem, 3vw, 1.5rem) + env(safe-area-inset-bottom, 0px));
    z-index: 95;
    max-width: 62rem;
    margin-inline: auto;
    padding: clamp(1.1rem, 2.5vw, 1.5rem);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transform: translateY(12px);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.cookie-banner.is-open { opacity: 1; transform: none; }

.cookie-banner__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem 1.75rem;
}

.cookie-banner__inner > div:first-child { flex: 1 1 22rem; }

.cookie-banner__title {
    font-size: 1rem;
    margin-bottom: 0.3rem;
}

.cookie-banner__text {
    font-size: 0.9rem;
    color: var(--text-muted);
    max-width: 62ch;
}

.cookie-banner__text a {
    color: var(--brand-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* Equal visual weight: declining must not be made harder than accepting. */
.cookie-banner__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    flex-shrink: 0;
}

.cookie-banner__actions .btn { flex: 1 1 auto; }

/* The floating controls lift clear of the banner rather than colliding with it.
   The offset is the banner's measured height, published by JS as a custom
   property — a fixed value cannot work, because the banner grows as its text
   wraps and its buttons stack on narrow screens. */
body.cookie-banner-open .floats,
body.cookie-banner-open .floats--left {
    bottom: calc(
        clamp(0.9rem, 3vw, 1.75rem) + env(safe-area-inset-bottom, 0px)
        + var(--cookie-banner-height, 0px) + 0.75rem
    );
}

.footer__cookie-settings:hover { color: var(--brand-ink); }

@media (max-width: 560px) {
    .cookie-banner__actions { width: 100%; }
}

/* 20. Preloader
   ========================================================================== */

.preloader {
    position: fixed;
    inset: 0;
    z-index: 999;
    display: grid;
    place-items: center;
    gap: 1rem;
    background: var(--bg);
    transition: opacity 0.45s var(--ease), visibility 0.45s;
    /* CSS clears the preloader on its own timer. If main.js never loads, the
       site still becomes usable — JS only ever dismisses it *earlier*.
       The delay used to be 1.8s, which meant up to 2.25s of covered viewport
       on every page view, not just the first. */
    animation: preloader-out 0.4s var(--ease) 0.6s forwards;
}

.preloader.is-done { opacity: 0; visibility: hidden; pointer-events: none; }

/* With JS disabled the inline class swap never runs, so skip the splash. */
html.no-js .preloader { display: none; }

/* Already seen in this tab. A splash on every internal navigation is a cost
   paid repeatedly for an impression that only lands once. */
html.preloader-seen .preloader { display: none; }

@keyframes preloader-out {
    to { opacity: 0; visibility: hidden; pointer-events: none; }
}

.preloader__mark {
    width: 64px;
    height: 64px;
    animation: pulse 1.4s var(--ease-in-out) infinite;
}

.preloader__bar {
    width: 140px;
    height: 2px;
    border-radius: 2px;
    background: var(--border-strong);
    overflow: hidden;
}

.preloader__bar::after {
    content: "";
    display: block;
    width: 40%;
    height: 100%;
    background: var(--brand);
    animation: sweep 1.1s var(--ease-in-out) infinite;
}

@keyframes pulse {
    0%, 100% { opacity: 0.55; transform: scale(0.96); }
    50% { opacity: 1; transform: scale(1); }
}

@keyframes sweep {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(350%); }
}

/* 21. Motion
   The vocabulary the whole site moves in, taken from `pinterest/vid 1.mp4`:
   things arrive by rising and settling — a short lift plus a touch of scale,
   never a slide across or a spin. Groups arrive in sequence rather than all at
   once, which is what makes a row of cards read as a row rather than a flash.

   Only armed once JS confirms it can un-arm them, so a JS failure can never
   leave content permanently invisible. Everything here animates `opacity` and
   `transform` only: both are composited, so a mid-range phone does not have to
   re-lay-out the page on every frame.
   ========================================================================== */

html.js-motion .reveal {
    opacity: 0;
    transform: translateY(40px);
    transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
    will-change: opacity, transform;
}

html.js-motion .reveal.is-visible {
    opacity: 1;
    transform: none;
    will-change: auto;
}

html.js-motion .reveal-group > * {
    opacity: 0;
    transform: translateY(40px);
    transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

html.js-motion .reveal-group.is-visible > * { opacity: 1; transform: none; }

html.js-motion .reveal-group.is-visible > *:nth-child(1) { transition-delay: 0ms; }
html.js-motion .reveal-group.is-visible > *:nth-child(2) { transition-delay: 80ms; }
html.js-motion .reveal-group.is-visible > *:nth-child(3) { transition-delay: 160ms; }
html.js-motion .reveal-group.is-visible > *:nth-child(4) { transition-delay: 240ms; }
html.js-motion .reveal-group.is-visible > *:nth-child(5) { transition-delay: 320ms; }
html.js-motion .reveal-group.is-visible > *:nth-child(n+6) { transition-delay: 400ms; }

/* Headings and subheadings keep the long, unhurried rise they had before the
   performance pass (owner, 2026-09-22). Everything else stays quick: it was
   the section bodies and card grids that felt sluggish, not the headings.
   `.section-head` is the eyebrow + h2 + lead block that opens almost every
   section, and `.page-head` is the h1 area, so between them this is exactly
   what reads as "the heading".

   The two halves of the transition are deliberately different lengths. A 2.5s
   *fade* is what pushed largest-contentful-paint to 2.7s — the browser will
   not count text it can barely see — so the opacity settles in 0.8s and the
   travel keeps the full 2.5s. The motion is the slow part; the text is
   readable almost immediately. Measured, not assumed: LCP stayed under 300ms
   on every page with this in place. */
html.js-motion .reveal.section-head,
html.js-motion .reveal.page-head,
html.js-motion .reveal-group > .section-head,
html.js-motion .reveal-group > .page-head {
    transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
                transform 2.5s cubic-bezier(0.16, 1, 0.3, 1);
}

/* A phone shows one card at a time, so a long stagger becomes a wait rather
   than a rhythm — and the travel has to be shorter because the whole viewport
   is shorter. */
@media (max-width: 767px) {
    html.js-motion .reveal { transform: translateY(24px); }
    html.js-motion .reveal-group > * { transform: translateY(24px); }
    html.js-motion .reveal-group.is-visible > *:nth-child(2) { transition-delay: 60ms; }
    html.js-motion .reveal-group.is-visible > *:nth-child(3) { transition-delay: 120ms; }
    html.js-motion .reveal-group.is-visible > *:nth-child(4) { transition-delay: 180ms; }
    html.js-motion .reveal-group.is-visible > *:nth-child(n+5) { transition-delay: 240ms; }

    .hero__copy h1 {
        font-size: clamp(2.15rem, 9vw, 2.8rem);
        line-height: 1.02;
    }

    .hero__copy .lead {
        max-width: 31rem;
        font-size: 0.98rem;
        line-height: 1.45;
    }

    .hero__copy .btn-row { gap: 0.6rem; }
    .hero__copy .btn { min-height: 46px; padding-inline: 1.15rem; font-size: 0.88rem; }
    .hero__trust { max-width: 28rem; }
}

/* Keep the completed state authoritative after the mobile offset rules above. */
html.js-motion .reveal.is-visible,
html.js-motion .reveal-group.is-visible > * {
    opacity: 1 !important;
    transform: none !important;
}

html.js-motion .reveal:not(.is-visible) {
    opacity: 0 !important;
    transform: translateY(40px) !important;
    transition: none !important;
}

html.js-motion .reveal-group:not(.is-visible) > * {
    opacity: 0 !important;
    transform: translateY(40px) !important;
    transition: none !important;
}

@media (max-width: 767px) {
    html.js-motion .reveal:not(.is-visible),
    html.js-motion .reveal-group:not(.is-visible) > * {
        transform: translateY(24px) !important;
    }
}

/* A heading whose words rise on their own does not also rise as a block — one
   motion, not two stacked on top of each other. The attribute is added by
   main.js, so with no JS the heading animates as an ordinary group child. */
html.js-motion .reveal-group > [data-words] {
    opacity: 1;
    transform: none;
    transition: none;
}

/* Headline reveal, after `pinterest/vid 11.mp4`: each word rises out from
   behind the line above it, a beat apart. main.js wraps the words; without it
   the heading is an ordinary heading, which is the point.

   The mask is `.word`, so `.word__i` needs somewhere to hide. The padding and
   the matching negative margin give descenders room inside the mask without
   changing where the line sits. */
html.js-motion [data-words] .word {
    display: inline-block;
    overflow: hidden;
    padding-bottom: 0.14em;
    margin-bottom: -0.14em;
    vertical-align: bottom;
}

html.js-motion [data-words] .word + .word { margin-left: 0.22em; }
html.js-motion [data-words] > .accent,
html.js-motion [data-words] .accent { margin-left: 0.22em; }

html.js-motion [data-words] .word__i {
    display: inline-block;
    opacity: 0;
    transform: translateY(115%);
    /* As above: a long travel, a short fade. The word is legible early and
       keeps rising into place, which is the effect the owner asked to keep. */
    transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
                transform 2.5s cubic-bezier(0.16, 1, 0.3, 1);
}

html.js-motion [data-words] .accent .word__i {
    background: linear-gradient(100deg, var(--brand-ink) 5%, #E04A18 95%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

html.js-motion [data-words].is-visible .word__i {
    opacity: 1;
    transform: none;
}

/* 22. Responsive
   ========================================================================== */

@media (min-width: 640px) {
    .mockups { grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr)); }
}

@media (min-width: 900px) {
}

/* Back down to 1000 now the bar carries five links instead of eight. Measured,
   not guessed: the row needs about 900px, so this leaves headroom.
   Keep in step with the matchMedia query in main.js. */
@media (min-width: 1000px) {
    .nav { display: flex; }
    .header__actions { display: flex; }
    .menu-toggle { display: none; }
    .mobile-nav { display: none; }
    .footer__top { grid-template-columns: 1.6fr 1fr 1fr 1.4fr; }
}

/* Desktop-only pointer enhancements. */
@media (hover: none) {
    .card:hover, .package:hover, .mockup:hover { transform: none; }
}

/* 23. Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    .steps .step::before,
    .system .system__step p.system__num { animation: none !important; }

    /* Skip the splash entirely rather than snapping it away. */
    .preloader { display: none; }

    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    html.js-motion .reveal,
    html.js-motion .reveal-group > *,
    html.js-motion [data-words] .word__i {
        opacity: 1 !important;
        transform: none !important;
    }

    .btn:hover, .card:hover, .package:hover,
    .mockup:hover, .float-btn:hover { transform: none; }
}

/* 23b. Forced colours (Windows High Contrast)
   The system repaints text and backgrounds with its own palette but honours
   `color: transparent`, so gradient-clipped text simply disappears — taking
   the emphasised half of three H1s with it.
   ========================================================================== */

@media (forced-colors: active) {
    .accent,
    .accent .word__i {
        background: none;
        -webkit-text-fill-color: currentColor;
        color: CanvasText;
    }

    /* Decorative fills carry meaning in these components, and forced colours
       flattens them, so give each a real border to keep the shape readable. */
    .cell-tick,
    .checklist li::before { border: 1px solid CanvasText; }

    .slider__seg[aria-current="true"]::after { outline: 2px solid Highlight; }
    .slider__progress-fill { outline: 1px solid Highlight; }

    /* box-shadow is dropped entirely; the outline underneath is what survives. */
    :focus-visible { outline: 3px solid Highlight; outline-offset: 2px; }
}

/* 23c. Small-screen legibility floor
   Placed last on purpose. These override component rules defined earlier in
   the sheet, and at equal specificity the later rule wins — the first attempt
   sat next to the hero and was quietly beaten by every later definition.
   ========================================================================== */

@media (max-width: 767px) {
    .eyebrow { font-size: 0.82rem; }
    .section-index { font-size: 0.8rem; }
    .section-index b { font-size: 0.85rem; }
    .compare__label { font-size: 0.8rem; }
    .mockup__url { font-size: 0.75rem; }
    .mockup__features li { font-size: 0.82rem; }
    .showcase__count { font-size: 0.8rem; }
    .table__note { font-size: 0.82rem; }
    .dim { font-size: 0.875rem; }
    .system__num { font-size: 0.85rem; }
    .mockup__url { font-size: 0.78rem; }

    /* Keep compact cards content-led on small screens instead of reserving
       large internal cushions for the tallest card in a row. */
    .card,
    .highlight,
    .package,
    .mockup__body {
        padding: 1rem;
    }

    .card { gap: 0.5rem; }
    .highlight { gap: 0.55rem; }
    .highlight .btn,
    .package .btn,
    .card__meta { margin-top: 0; }

    .cta-band {
        padding: 1.5rem 1.1rem;
    }

    .cta-band p { margin-bottom: 1.1rem; }

    .page-head .btn-row { gap: 0.55rem; }
    .page-head .btn--lg {
        min-height: 44px;
        padding: 0.55rem 1rem;
        font-size: 0.88rem;
        white-space: nowrap;
    }

    .page-head__figure {
        font-size: 2rem;
    }

    .page-head__figure small {
        font-size: 0.85rem;
    }

    /* Give the process copy more room in the two audit sections. */
    .audit__panel {
        padding: 1rem 0.75rem 1rem 0.6rem;
    }

    .audit__list li {
        grid-template-columns: 1.35rem 1fr;
        gap: 0.45rem;
    }

    .audit__list li::before {
        width: 1.35rem;
        height: 1.35rem;
    }

    .audit__list--process li:not(:last-child)::after {
        left: 0.675rem;
        top: 1.35rem;
    }
}

/* 24. Print
   ========================================================================== */

@media print {
    body { background: #fff; color: #000; }
    body::before, .header, .footer, .floats, .preloader, .carousel__controls { display: none !important; }
    .prose a::after { content: " (" attr(href) ")"; font-size: 0.85em; }
}


/* Author Headshot */
.page-head__author {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
}
/* A portrait cropped to a circle. `object-position` biases the crop upward
   because a headshot taken on a phone puts the face in the top half of the
   frame and dead-centring it crops the forehead while keeping the shirt.
   20% suits a normal portrait; it is not tuned to one particular photo. */
.author-headshot {
    width: 84px;
    height: 84px;
    border-radius: 50%;
    object-fit: cover;
    object-position: center 20%;
    border: 2px solid var(--surface);
    box-shadow: var(--shadow);
}
