/* ==========================================================================
   v3-visual.css - depth and craft layered on top of v2.css structure.

   Deliberately EXCLUDED, because 2026 field data shows these hurt real
   service-business sites: WebGL/3D (a single Spline scene ships 800KB-2MB
   of JS runtime and collapses Core Web Vitals on mobile), kinetic
   typography (fights screen readers and crawlers, causes layout shift),
   and heavy glassmorphism (15-30% FPS drops on real devices).

   Everything here is CSS-only: no JavaScript, no extra network requests,
   and it degrades to the plain v2 layout where unsupported.
   ========================================================================== */

/* --- Extra tokens -------------------------------------------------------- */
:root {
    --grain-opacity: 0.035;
    --mesh-1: rgba(212, 58, 18, 0.16);
    --mesh-2: rgba(0, 35, 89, 0.14);
    --mesh-3: rgba(249, 87, 51, 0.10);
    --hairline: rgba(16, 24, 40, 0.11);
}

/* Give the hero headline room to breathe beside the form card. */
@media (min-width: 1025px) {
    .hero__grid { grid-template-columns: 1.1fr 0.9fr; gap: var(--sp-24); }
}

:root[data-theme="dark"] {
    --grain-opacity: 0.05;
    --mesh-1: rgba(255, 107, 66, 0.16);
    --mesh-2: rgba(56, 120, 220, 0.14);
    --mesh-3: rgba(255, 135, 99, 0.08);
    --hairline: rgba(255, 255, 255, 0.07);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --grain-opacity: 0.05;
        --mesh-1: rgba(255, 107, 66, 0.16);
        --mesh-2: rgba(56, 120, 220, 0.14);
        --mesh-3: rgba(255, 135, 99, 0.08);
        --hairline: rgba(255, 255, 255, 0.07);
    }
}

/* --- Film grain ----------------------------------------------------------
   Inline SVG fractal noise: a few hundred bytes, zero network requests,
   works in both themes. Restores the tactile quality a flat page lacks. */
body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 9999;
    pointer-events: none;
    opacity: var(--grain-opacity);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --- Hero: gradient mesh + drafting grid --------------------------------- */
.hero {
    position: relative;
    isolation: isolate;
    overflow: hidden;
}

/* Static radial blooms. No animated blur, so no per-frame paint cost. */
.hero::before {
    content: "";
    position: absolute;
    inset: -30% -10% auto;
    height: 150%;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(38rem 30rem at 12% 8%, var(--mesh-1), transparent 68%),
        radial-gradient(34rem 26rem at 88% 14%, var(--mesh-2), transparent 66%),
        radial-gradient(28rem 24rem at 62% 84%, var(--mesh-3), transparent 70%);
}

/* Fine grid that fades out - a drafting-table substrate under the type. */
.hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        linear-gradient(to right, var(--hairline) 1px, transparent 1px),
        linear-gradient(to bottom, var(--hairline) 1px, transparent 1px);
    background-size: 72px 72px;
    -webkit-mask-image: radial-gradient(75% 65% at 30% 20%, #000 20%, transparent 78%);
    mask-image: radial-gradient(75% 65% at 30% 20%, #000 20%, transparent 78%);
}

/* Oversized editorial headline. Type is the primary visual device: free,
   accessible and crawlable, unlike a 3D scene. */
.hero h1 {
    font-size: clamp(2.6rem, 1.5rem + 5.2vw, 5.2rem);
    font-weight: 700;
    letter-spacing: -0.045em;
    line-height: 0.98;
}

/* The phrase carrying the promise, set apart with no extra markup. */
.hero h1 em {
    font-style: normal;
    background: linear-gradient(100deg, var(--accent), var(--accent-bright) 70%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .hero h1 em { color: var(--accent); }
}

/* --- Eyebrow becomes a pill -------------------------------------------- */
.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    margin-bottom: var(--sp-6);
    padding: var(--sp-1) var(--sp-3);
    border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--accent) 9%, transparent);
}

.eyebrow::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
}

/* --- Quote card: raised with a light inner edge ------------------------- */
.quote-card {
    border-color: color-mix(in srgb, var(--text) 10%, transparent);
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.6) inset,
        var(--shadow-md),
        0 40px 80px -32px rgba(16, 24, 40, 0.18);
}

:root[data-theme="dark"] .quote-card {
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06) inset, var(--shadow-md);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .quote-card {
        box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06) inset, var(--shadow-md);
    }
}

/* --- Section rhythm ----------------------------------------------------- */
.section { position: relative; }

/* Anchor targets must clear the sticky header, or in-page nav links land
   with the heading hidden behind it. */
:where(#services, #work, #reviews, #faq, #blog, #quote) {
    scroll-margin-top: calc(var(--header-h) + var(--sp-6));
}

.section--subtle {
    border-block: 1px solid var(--border);
    background:
        radial-gradient(50rem 24rem at 82% -10%, var(--mesh-3), transparent 70%),
        var(--bg-subtle);
}

h2 {
    font-size: clamp(1.9rem, 1.4rem + 2.1vw, 3rem);
    letter-spacing: -0.035em;
}

/* --- Services: lead panel + equal card row ------------------------------ */
@media (min-width: 900px) {
    /* Lead service gets a full-width two-column panel; the remaining three
       sit as equal cards below it. The previous 3x2 bento left a hole
       bottom-right because card 4 spanned two columns with nothing beside
       it, and the two stacked cards never summed to the tall card's height. */
    .feature-lead {
        display: grid;
        grid-template-columns: 1.15fr 0.85fr;
        gap: var(--sp-12);
        align-items: center;
        padding: var(--sp-12);
    }

    .feature-lead .card__icon { width: 56px; height: 56px; }
    .feature-lead h3 { font-size: var(--step-2); }
    .feature-lead p  { max-width: 48ch; font-size: var(--step-0); }

    /* Exactly three, so the row always fills. auto-fit would drop to two
       columns at some widths and leave a gap again. */
    .card-grid--3 { grid-template-columns: repeat(3, 1fr); }
}

.feature-lead { margin-bottom: var(--sp-6); }

/* The included-list sits in its own bordered field inside the lead panel. */
.feature-lead .card__list {
    margin: 0;
    padding: var(--sp-6);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--accent) 4%, transparent);
}

@media (max-width: 899px) {
    .feature-lead { display: block; }
    .feature-lead .card__list { margin-top: var(--sp-6); }
}

.card {
    position: relative;
    overflow: hidden;
    background:
        linear-gradient(180deg, color-mix(in srgb, var(--text) 2.5%, transparent), transparent 42%),
        var(--bg-raised);
}

/* Accent hairline wiping in on hover - one small deliberate gesture. */
.card::after {
    content: "";
    position: absolute;
    inset: auto 0 0;
    height: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent-bright));
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.card:hover::after { transform: scaleX(1); }

.card__icon {
    border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
    background:
        linear-gradient(140deg,
            color-mix(in srgb, var(--accent) 18%, transparent),
            color-mix(in srgb, var(--accent) 6%, transparent));
}

/* --- Card feature list + inline CTA ------------------------------------- */
.card__list {
    display: grid;
    gap: var(--sp-3);
    margin: var(--sp-6) 0 0;
    padding: 0;
    list-style: none;
}

.card__list li {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    color: var(--text);
    font-size: var(--step--1);
    line-height: 1.45;
}

.card__list svg {
    flex: none;
    margin-top: 3px;
    color: var(--accent);
}

.card__cta {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 24px;
    margin-top: var(--sp-6);
    color: var(--accent);
    font-size: var(--step--1);
    font-weight: 650;
}

.card__cta svg { transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1); }
.card:hover .card__cta svg { transform: translateX(3px); }

@media (prefers-reduced-motion: reduce) {
    .card:hover .card__cta svg { transform: none; }
}

/* --- Marquee ------------------------------------------------------------ */
.marquee {
    position: relative;
    overflow: hidden;
    margin-top: var(--sp-8);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.marquee__track {
    display: flex;
    align-items: center;
    width: max-content;
    gap: var(--sp-16);
    animation: marquee 34s linear infinite;
}

.marquee img {
    width: auto;
    max-height: 38px;
    opacity: 0.68;
    transition: opacity 0.2s ease;
}

.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee img:hover { opacity: 1; }

:root[data-theme="dark"] .marquee img { filter: brightness(0) invert(1); opacity: 0.55; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .marquee img { filter: brightness(0) invert(1); opacity: 0.55; }
}

@keyframes marquee {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

/* --- Work cards --------------------------------------------------------- */
.work {
    position: relative;
    transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.45s ease;
}

.work:hover { transform: translateY(-5px); box-shadow: var(--shadow-md); }

.work__shot { transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
.work:hover .work__shot { transform: scale(1.03); }

/* Interim slot: the client's logo centred on a branded panel until a real
   screenshot replaces it. Reads as a deliberate brand card, not a broken
   image. The logos are 174x50 strips, so they are never stretched. */
.work__logo {
    display: grid;
    place-items: center;
    aspect-ratio: 16 / 10;
    padding: var(--sp-8);
    border-bottom: 1px solid var(--border);
    background:
        radial-gradient(20rem 12rem at 50% 0%, color-mix(in srgb, var(--accent) 11%, transparent), transparent 72%),
        linear-gradient(165deg, color-mix(in srgb, var(--text) 5%, transparent), transparent 58%),
        var(--bg-subtle);
}

.work__logo img {
    width: auto;
    max-width: min(74%, 190px);
    max-height: 46px;
    object-fit: contain;
    transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

.work:hover .work__logo img { transform: scale(1.05); }

/* Client marks are dark artwork on transparent: on the dark theme they need
   a light chip to stay legible, matching the testimonial treatment. */
:root[data-theme="dark"] .work__logo img {
    padding: 10px 14px;
    border-radius: 8px;
    background: #fff;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .work__logo img {
        padding: 10px 14px;
        border-radius: 8px;
        background: #fff;
    }
}

@media (prefers-reduced-motion: reduce) {
    .work:hover .work__logo img { transform: none; }
}

/* These are real projects awaiting a screenshot, not invented entries, so
   the empty slot reads as a quiet branded panel rather than a warning. */
.work__placeholder {
    border-bottom: 1px solid var(--border);
    background:
        radial-gradient(16rem 10rem at 30% 0%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%),
        linear-gradient(160deg, color-mix(in srgb, var(--text) 4%, transparent), transparent 62%),
        var(--bg-subtle);
}

.work__link {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 24px;
    color: var(--accent);
    font-size: var(--step--1);
    font-weight: 600;
}

.work__link:hover { color: var(--accent-hover); text-decoration: underline; }

/* --- Testimonials ------------------------------------------------------- */
.quote { position: relative; overflow: hidden; }

.quote::before {
    content: "\201C";
    position: absolute;
    top: -2.8rem;
    right: 0.7rem;
    color: color-mix(in srgb, var(--accent) 16%, transparent);
    font-size: 11rem;
    line-height: 1;
    pointer-events: none;
}

.quote blockquote { position: relative; font-size: var(--step-1); line-height: 1.5; }

/* Client logos are dark-on-transparent artwork, so they muddy against the
   dark card. Sit them on a light chip that works in both themes rather
   than inverting brand colours. */
.quote__by img {
    padding: 5px 7px;
    border-radius: 6px;
    background: #fff;
}

/* --- CTA band ----------------------------------------------------------- */
.cta {
    position: relative;
    isolation: isolate;
    overflow: hidden;
}

/* Keep this band dark in BOTH themes. v2 paints it with --bg-inverse,
   which flips to near-white in dark mode and washes the mesh out; a
   committed dark panel reads as a deliberate full-bleed moment instead. */
.cta {
    background: #10151f;
    color: #fff;
}

.cta p { color: rgba(255, 255, 255, 0.72); }

.cta::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(30rem 22rem at 8% 12%, rgba(212, 58, 18, 0.42), transparent 66%),
        radial-gradient(26rem 20rem at 92% 88%, rgba(56, 120, 220, 0.3), transparent 66%);
}

/* v2 flips these for dark mode; override so they stay light-on-dark. */
.cta .btn--on-dark,
:root[data-theme="dark"] .cta .btn--on-dark {
    border-color: rgba(255, 255, 255, 0.38);
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cta .btn--on-dark {
        border-color: rgba(255, 255, 255, 0.38);
        background: rgba(255, 255, 255, 0.08);
        color: #fff;
    }
}

.cta h2 { font-size: clamp(1.7rem, 1.3rem + 1.5vw, 2.4rem); letter-spacing: -0.03em; }

/* --- Stat band ---------------------------------------------------------- */
.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--sp-4);
}

.stat {
    padding: var(--sp-8);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background:
        linear-gradient(180deg, color-mix(in srgb, var(--text) 2.5%, transparent), transparent 40%),
        var(--bg-raised);
}

.stat__num {
    display: block;
    font-size: clamp(2rem, 1.5rem + 1.6vw, 2.9rem);
    font-weight: 700;
    letter-spacing: -0.04em;
    line-height: 1;
    background: linear-gradient(100deg, var(--accent), var(--accent-bright) 75%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .stat__num { color: var(--accent); }
}

.stat__label {
    display: block;
    margin-top: var(--sp-3);
    color: var(--text-muted);
    font-size: var(--step--1);
}

/* --- Scroll-driven reveal ----------------------------------------------
   Native CSS scroll-driven animation: no JS, no IntersectionObserver.
   Unsupported browsers render the final state; reduced-motion opts out. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        .reveal {
            animation: reveal-in linear both;
            animation-timeline: view();
            animation-range: entry 5% cover 24%;
        }
    }
}

@keyframes reveal-in {
    from { opacity: 0; transform: translateY(26px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* --- Header ------------------------------------------------------------- */
.site-header { border-bottom-color: var(--hairline); }
.brand img { width: 148px; }

/* --- Responsive --------------------------------------------------------- */
@media (max-width: 640px) {
    .hero::after { background-size: 48px 48px; }
    .quote::before { font-size: 7rem; top: -1.7rem; }
    .marquee__track { gap: var(--sp-12); animation-duration: 26s; }
    .stat { padding: var(--sp-6); }

    /* Keep the logo legible: shrink the header CTA and let the brand hold
       its size rather than being squeezed by a full-width button. */
    .brand img { width: 116px; }

    .header-actions { gap: var(--sp-2); }

    .header-actions .btn--primary {
        padding-inline: var(--sp-4);
        font-size: var(--step--1);
    }

    .site-header__inner { gap: var(--sp-3); }
}

@media (max-width: 420px) {
    /* At the narrowest widths the hamburger plus phone/WhatsApp floats are
       enough - drop the header button so the logo stays readable. */
    .header-actions .btn--primary { display: none; }
}

/* --- Motion opt-out for everything above -------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .marquee__track { animation: none; }
    .work:hover,
    .work:hover .work__shot { transform: none; }
    .card:hover::after { transform: scaleX(1); transition: none; }
    .reveal { animation: none; opacity: 1; transform: none; }
}
