/* ═══════════════════════════════════════════════════════════════════════════════
   Nextekk Studio — global stylesheet

   Every color, type size, radius, and component spec below comes from
   Nextekk Brand Guidelines Edition 1.0 (May 2026), in assets/brand/.
   Do not introduce a value that is not in that document without recording the
   decision in CLAUDE.md §21.

   Mobile-first. Authored top-down: fonts → tokens → reset → layout → components
   → utilities. One stylesheet, no framework, no build step.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* ─── Fonts ────────────────────────────────────────────────────────────────────
   Inter is the brand workhorse (guide §04). Self-hosted per house style §2.2 —
   never a Google Fonts link. JetBrains Mono is also specified for code and
   tech-stack chips but we have no woff2 for it yet, so --font-mono falls back to
   the system mono stack. See CLAUDE.md §18.
   ──────────────────────────────────────────────────────────────────────────── */

@font-face {
    font-family: "Inter";
    src: url("/assets/fonts/inter/inter-latin-400.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Inter";
    src: url("/assets/fonts/inter/inter-latin-500.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Inter";
    src: url("/assets/fonts/inter/inter-latin-600.woff2") format("woff2");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Inter";
    src: url("/assets/fonts/inter/inter-latin-700.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Inter";
    src: url("/assets/fonts/inter/inter-latin-800.woff2") format("woff2");
    font-weight: 800;
    font-style: normal;
    font-display: swap;
}

/* ─── Design tokens ─────────────────────────────────────────────────────────── */

:root {
    /* ─── PALETTE LAYER (--p-*) ────────────────────────────────────────────────
       The ONLY variables a palette overrides. Everything below maps onto these,
       so adding a palette means writing one [data-palette] block — never touching
       a component rule. Defaults here are the Nextekk brand values (guide §03).
       ──────────────────────────────────────────────────────────────────────── */
    --p-dark:        #0E1A3D;   /* wordmark navy — dark bands, primary CTA fill */
    --p-dark-2:      #16234E;   /* hover for dark surfaces */
    --p-dark-soft:   #E8ECF6;   /* pale tint of the dark hue */
    --p-accent:      #2553EB;   /* the electric blue X — links, focus, secondary CTA */
    --p-accent-2:    #1B40C4;
    --p-accent-soft: #E5EBFE;
    --p-spark:       #06B6D4;   /* AI signal only (guide §03) */
    --p-spark-soft:  #CFFAFE;
    --p-ink:         #0F172A;
    --p-ink-soft:    #334155;
    --p-muted:       #64748B;
    --p-line:        #E2E8F0;
    --p-surface:     #FFFFFF;
    --p-surface-alt: #F8FAFC;
    --p-on-dark:     #cdd6e6;   /* body text on a dark band */
    --p-on-dark-accent: #8FA8F5; /* the palette's signature, legible ON the dark band */

    /* BRAND — mapped from the palette layer. In the `brand` palette these hold the
       literal guide values; in an alternate palette they hold the analogous role. */
    --brand-navy:   var(--p-dark);
    --brand-navy-2: var(--p-dark-2);
    --brand-soft:   var(--p-dark-soft);

    --accent:      var(--p-accent);
    --accent-2:    var(--p-accent-2);
    --accent-soft: var(--p-accent-soft);

    --spark:      var(--p-spark);
    --spark-soft: var(--p-spark-soft);

    --ink:         var(--p-ink);
    --ink-soft:    var(--p-ink-soft);
    --muted:       var(--p-muted);
    --line:        var(--p-line);
    --surface:     var(--p-surface);
    --surface-alt: var(--p-surface-alt);
    --on-dark:        var(--p-on-dark);
    --on-dark-accent: var(--p-on-dark-accent);

    /* STATUS — fixed across palettes; these carry meaning, not style. */
    --success: #10B981;
    --warning: #F59E0B;
    --danger:  #DC2626;

    /* ─── FORMAT LAYER (--f-*) ─────────────────────────────────────────────────
       The ONLY variables a format overrides. Defaults are the `split` format.
       ──────────────────────────────────────────────────────────────────────── */
    --f-radius-control: 8px;
    --f-radius-card:    12px;
    --f-border-width:   1px;
    --f-card-shadow:    var(--shadow-sm);
    --f-card-shadow-hover: var(--shadow);
    --f-heading-transform: none;
    --f-heading-spacing:   -0.02em;
    --f-heading-weight:    700;
    --f-eyebrow-family:    var(--font-body);
    --f-section-y:         clamp(48px, 8vw, 96px);
    --f-card-padding:      24px;

    /* Semantic aliases */
    --color-bg:          var(--surface);
    --color-bg-alt:      var(--surface-alt);
    --color-bg-dark:     var(--brand-navy);
    --color-text:        var(--ink);
    --color-text-muted:  var(--ink-soft);
    --color-text-invert: #ffffff;
    --color-border:      var(--line);
    --color-focus:       var(--accent);

    /* Typography — the scale in guide §04 */
    --font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Calibri, sans-serif;
    --font-mono: ui-monospace, "Cascadia Code", "Segoe UI Mono", Menlo, monospace; /* TODO: JetBrains Mono */

    --size-hero:    clamp(2.6rem, 1.6rem + 3.6vw, 4.5rem);  /* 56–72px, 800 */
    --size-h1:      clamp(2rem, 1.6rem + 1.5vw, 2.75rem);   /* 44px, 700 */
    --size-h2:      clamp(1.6rem, 1.35rem + 1vw, 2rem);     /* 32px, 700 */
    --size-h3:      1.375rem;                               /* 22px, 700 */
    --size-lead:    1.25rem;                                /* 20px, 400 */
    --size-body:    1.0625rem;                              /* 17px, 400 */
    --size-eyebrow: 0.8125rem;                              /* 13px, 600 */
    --size-mono:    0.9375rem;                              /* 15px, 400 */
    --size-small:   0.875rem;                               /* 14px — form labels */

    /* Radii — 8px controls, 12px cards in the brand format (guide §07); the format
       layer overrides them. */
    --radius-sm:   4px;
    --radius:      var(--f-radius-control);
    --radius-lg:   var(--f-radius-card);
    --radius-pill: 999px;

    /* Soft layered card shadow (guide §07) */
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
    --shadow:    0 1px 2px rgba(15, 23, 42, 0.04), 0 4px 12px rgba(15, 23, 42, 0.06);
    --shadow-lg: 0 2px 4px rgba(15, 23, 42, 0.05), 0 12px 32px rgba(15, 23, 42, 0.10);

    /* Layout */
    --container:        1200px;
    --container-narrow: 760px;
    --header-h:         186px;  /* grown to carry the two-line lockup at 158px */
    --header-h-mobile:  120px;
    --gutter:           24px;
    --section-y:        var(--f-section-y);
    --edge: max(var(--gutter), calc((100vw - var(--container)) / 2 + var(--gutter)));
}

/* ─── Reset & base ─────────────────────────────────────────────────────────── */

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

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

body {
    margin: 0;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-body);
    font-size: var(--size-body);
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
    line-height: 1.15;
    margin: 0 0 0.5em;
    font-weight: var(--f-heading-weight);
    letter-spacing: var(--f-heading-spacing);
    text-transform: var(--f-heading-transform);
    color: var(--ink);
}

h1 { font-size: var(--size-h1); }
h2 { font-size: var(--size-h2); }
h3 { font-size: var(--size-h3); }
h4 { font-size: 1.125rem; }

p, ul, ol { margin: 0 0 1em; }

/* Guide §03: body links are accent blue, no underline, underline on hover. */
a {
    color: var(--accent);
    text-decoration: none;
}

a:hover {
    color: var(--accent-2);
    text-decoration: underline;
    text-underline-offset: 3px;
}

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

/* Guide §07: 2px accent blue outline at 2px offset. */
:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

code, kbd, samp, .mono {
    font-family: var(--font-mono);
    font-size: var(--size-mono);
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 200;
    padding: 12px 20px;
    background: var(--brand-navy);
    color: #fff;
    border-radius: 0 0 var(--radius) 0;
}

.skip-link:focus { left: 0; }

/* ─── Layout ───────────────────────────────────────────────────────────────── */

.container {
    max-width: var(--container);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

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

section { padding: var(--section-y) 0; }

/* Guide §07: alternate surface and surface-alt for rhythm down the page. */
.bg-alt { background: var(--surface-alt); }

/* Dark feature band — guide §07 says once per page max. */
.bg-dark {
    background: var(--brand-navy);
    color: #fff;
}

.bg-dark h1, .bg-dark h2, .bg-dark h3 { color: #fff; }
.bg-dark a { color: #fff; }

.grid {
    display: grid;
    gap: clamp(16px, 3vw, 32px);
}

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

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

@media (min-width: 640px) and (max-width: 899px) {
    .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

/* ─── Typography helpers ───────────────────────────────────────────────────── */

/* Guide §04: eyebrow is 13px / 600. All-caps is allowed here and nowhere else. */
.eyebrow {
    display: inline-block;
    font-family: var(--f-eyebrow-family);
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 12px;
}

.bg-dark .eyebrow { color: var(--spark); }

.section-heading {
    max-width: 46ch;
    margin-bottom: clamp(28px, 4vw, 48px);
}

.section-heading p {
    color: var(--ink-soft);
    font-size: var(--size-lead);
    margin: 0;
}

.bg-dark .section-heading p { color: #cdd6e6; }

.lede {
    font-size: var(--size-lead);
    color: var(--ink-soft);
}

.hero-title { font-size: var(--size-hero); font-weight: 800; }

/* ─── Buttons (guide §07) ──────────────────────────────────────────────────── */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 24px;
    border: 2px solid transparent;
    border-radius: var(--radius);
    font: inherit;
    font-weight: 600;
    line-height: 1.25;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

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

/* Primary — brand navy background, white text. Hover: navy 2. */
.btn-primary {
    background: var(--brand-navy);
    border-color: var(--brand-navy);
    color: #fff;
}

.btn-primary:hover {
    background: var(--brand-navy-2);
    border-color: var(--brand-navy-2);
    color: #fff;
}

/* Secondary — white background, 2px accent border, accent text.
   Hover: accent background, white text. */
.btn-secondary {
    background: var(--surface);
    border-color: var(--accent);
    color: var(--accent);
}

.btn-secondary:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

/* Tertiary — transparent, accent text, underline on hover. */
.btn-tertiary {
    background: transparent;
    border-color: transparent;
    color: var(--accent);
    padding-left: 0;
    padding-right: 0;
}

.btn-tertiary:hover {
    color: var(--accent-2);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* On dark — primary becomes white with navy text; secondary is transparent
   with a white border. */
.btn-on-dark {
    background: #fff;
    border-color: #fff;
    color: var(--brand-navy);
}

.btn-on-dark:hover {
    background: var(--brand-soft);
    border-color: var(--brand-soft);
    color: var(--brand-navy);
}

/* `.bg-dark a { color: #fff }` outranks the class above, so inside a dark BAND this
   button rendered white-on-white — an invisible label. It never showed on the homepage
   because there the button sits in .split-panel--create, not .bg-dark. Restate it at
   matching specificity rather than weakening the blanket link rule. */
.bg-dark .btn-on-dark,
.bg-dark .btn-on-dark:hover { color: var(--brand-navy); }

.btn-on-dark-outline {
    background: transparent;
    border-color: rgba(255, 255, 255, 0.6);
    color: #fff;
}

.btn-on-dark-outline:hover {
    background: rgba(255, 255, 255, 0.1);
    border-color: #fff;
    color: #fff;
}

.btn-lg {
    padding: 15px 30px;
    font-size: var(--size-lead);
}

.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* ─── Header & nav (guide §07) ─────────────────────────────────────────────────
   White, 72px desktop / 56px mobile. Logo left, links center, Contact CTA right.
   Active link: 2px accent underline at 4px offset. On scroll: shrink to 56px and
   add a 1px bottom border.
   ──────────────────────────────────────────────────────────────────────────── */

.site-header {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--surface);
    border-bottom: 1px solid transparent;
    transition: border-color 0.2s ease;
}

.site-header.is-scrolled { border-bottom-color: var(--line); }

.header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    min-height: var(--header-h-mobile);
    transition: min-height 0.2s ease;
}

@media (min-width: 960px) {
    .header-inner { min-height: var(--header-h); }
    .site-header.is-scrolled .header-inner { min-height: var(--header-h-mobile); }
}

/* ─── Brand lockup ────────────────────────────────────────────────────────────
   The wordmark is an IMAGE ASSET, never CSS text — guide §02 forbids typesetting
   "Nextekk" and treating it as a logo. The "Studio" suffix beside it is INTERIM
   until a real Nextekk Studio lockup exists (CLAUDE.md §16.4).
   Guide §02 minimums: 28px on web for the wordmark.
   ──────────────────────────────────────────────────────────────────────────── */

.brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    flex: 0 0 auto;
}

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

.brand-logo {
    display: block;
    /* 90px + 75%. The lockup is two lines of art, wordmark over graffiti, so it
       needs real height to read. The header grows to match. */
    height: 158px;
    width: auto;
}

@media (max-width: 600px) {
    .brand-logo { height: 96px; }
}

.site-header.is-scrolled .brand-logo { height: 108px; }

/* Interim "Studio" suffix. Divided from the wordmark by a hairline so it reads as
   an addition to the logo, not part of the artwork. */
.brand-suffix {
    font-size: 1.0625rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--ink-soft);
    padding-left: 10px;
    border-left: 1px solid var(--line);
    line-height: 1;
}

.brand--footer .brand-suffix {
    color: #cdd6e6;
    border-left-color: rgba(255, 255, 255, 0.25);
}

.nav { display: none; }

.nav-list {
    display: flex;
    align-items: center;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.nav-link {
    display: block;
    padding: 8px 14px;
    color: var(--ink);
    font-weight: 500;
    text-decoration: none;
    border-radius: var(--radius);
}

.nav-link:hover {
    color: var(--accent);
    text-decoration: none;
}

.nav-link[aria-current="page"] {
    color: var(--accent);
    text-decoration: underline;
    text-decoration-color: var(--accent);
    text-decoration-thickness: 2px;
    text-underline-offset: 4px;
}

.nav-item { position: relative; }

.nav-submenu {
    margin: 0;
    padding: 8px;
    list-style: none;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font: inherit;
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
}

.nav-toggle-bars {
    display: block;
    width: 18px;
    height: 2px;
    background: var(--ink);
    position: relative;
}

.nav-toggle-bars::before,
.nav-toggle-bars::after {
    content: "";
    position: absolute;
    left: 0;
    width: 18px;
    height: 2px;
    background: var(--ink);
}

.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after  { top: 6px; }

.mobile-nav {
    border-top: 1px solid var(--line);
    background: var(--surface);
}

.mobile-nav[hidden] { display: none; }

.mobile-nav-list {
    margin: 0;
    padding: 12px 0 20px;
    list-style: none;
}

.mobile-nav-link {
    display: block;
    padding: 12px var(--gutter);
    color: var(--ink);
    font-weight: 500;
    text-decoration: none;
    border-bottom: 1px solid var(--line);
}

.mobile-nav-link--sub {
    padding-left: calc(var(--gutter) + 20px);
    font-weight: 400;
    color: var(--ink-soft);
}

@media (min-width: 960px) {
    .nav { display: block; }
    .nav-toggle, .mobile-nav { display: none; }

    .nav-submenu {
        position: absolute;
        top: 100%;
        left: 0;
        min-width: 250px;
        opacity: 0;
        visibility: hidden;
        transform: translateY(-6px);
        transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
    }

    .nav-item:hover > .nav-submenu,
    .nav-item:focus-within > .nav-submenu {
        opacity: 1;
        visibility: visible;
        transform: translateY(0);
    }

    .nav-submenu .nav-link { padding: 10px 12px; }
}

/* ─── Split hero ───────────────────────────────────────────────────────────────
   Blake's proposal, awaiting sign-off (CLAUDE.md §16.5). The two halves separate
   by VALUE, SCALE and TEXTURE — not by a foreign hue. Guide §03 reserves spark
   cyan for genuinely AI-related content, so an off-brand accent for the
   "creative" side is not available. See CLAUDE.md §21.2.
   ──────────────────────────────────────────────────────────────────────────── */

.split-hero {
    padding: 0;
    border-bottom: 1px solid var(--line);
}

.split-hero-intro {
    padding: clamp(40px, 7vw, 80px) 0 clamp(28px, 4vw, 44px);
    text-align: center;
}

.split-hero-intro h1 { margin-bottom: 0.35em; }

.split-hero-intro .lede {
    max-width: 54ch;
    margin: 0 auto;
}

/* ─── Animated Studio lockup (NextekkStudio-zg5) ───────────────────────────────
   8 seconds, plays once: NEXTEKK types on sharp, then STUDIO paints on in spray
   script with drips. An alpha channel costs 6.6x in animated WebP (1267KB vs
   191KB), so each variant is flattened onto a solid ground and the ground is
   removed with a blend mode instead — multiply drops white on light palettes,
   screen drops black on dark ones. That keeps ONE asset working across all of
   the §22 palettes rather than one render per background colour.
   ──────────────────────────────────────────────────────────────────────────── */

.studio-lockup-anim {
    width: 100%;
    max-width: 400px;
    aspect-ratio: 480 / 366;
    margin: 0 auto clamp(4px, 1.5vw, 16px);
    background-image: url('/assets/brand/studio-lockup-anim-light.webp');
    background-position: center;
    background-repeat: no-repeat;
    background-size: contain;
    mix-blend-mode: multiply;
}

[data-palette="midnight"] .studio-lockup-anim,
[data-custom-mode="dark"] .studio-lockup-anim {
    background-image: url('/assets/brand/studio-lockup-anim-dark.webp');
    mix-blend-mode: screen;
}

/* Reduced motion gets the settled final frame, never the write-on. */
@media (prefers-reduced-motion: reduce) {
    .studio-lockup-anim {
        background-image: url('/assets/brand/studio-lockup-still-light.png');
    }

    [data-palette="midnight"] .studio-lockup-anim,
    [data-custom-mode="dark"] .studio-lockup-anim {
        background-image: url('/assets/brand/studio-lockup-still-dark.png');
    }
}

/* ─── One lockup per viewport ──────────────────────────────────────────────────
   On the homepage the hero owns the lockup, so the header's static one is hidden
   while the page sits at the top. It comes back the moment .is-scrolled lands, so
   a sticky header is never left without a brand. Visibility, not display, so the
   link keeps its box and the nav does not reflow.
   `.no-js` keeps it visible: without JS nothing ever adds .is-scrolled, and a
   permanently brandless header is the worse failure.
   ──────────────────────────────────────────────────────────────────────────── */

.js .has-hero-lockup .site-header:not(.is-scrolled) .brand {
    visibility: hidden;
}

.has-hero-lockup .brand {
    transition: visibility 0.2s ease;
}

.split-grid {
    display: grid;
    gap: 0;
}

.split-panel {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 14px;
    min-height: clamp(340px, 52vh, 480px);
    padding: clamp(28px, 4vw, 48px);
    position: relative;
    overflow: hidden;
}

.split-panel h2 { margin-bottom: 0.25em; }

.split-panel p { margin: 0; max-width: 38ch; }

.split-panel-label {
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* Utilitarian half — light, gridded, restrained. */
.split-panel--util {
    color: var(--ink);
    background-color: var(--surface-alt);
    background-image:
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: 32px 32px, 32px 32px;
    background-position: -1px -1px;
}

.split-panel--util .split-panel-label { color: var(--accent); }
.split-panel--util p { color: var(--ink-soft); }

/* Expressive half — the page's one dark feature band (guide §07). Contrast comes
   from inverted value and a larger, tighter headline. */
.split-panel--create {
    background: var(--brand-navy);
    color: #fff;
}

.split-panel--create::before {
    content: "";
    position: absolute;
    inset: auto -25% -45% auto;
    width: 70%;
    aspect-ratio: 1;
    background: radial-gradient(circle at 32% 32%, var(--accent), transparent 60%);
    opacity: 0.5;
    pointer-events: none;
}

.split-panel--create > * { position: relative; }

.split-panel--create h2 {
    color: #fff;
    font-size: var(--size-h1);
    letter-spacing: -0.03em;
}

.split-panel--create .split-panel-label { color: var(--on-dark-accent); }
.split-panel--create p { color: #cdd6e6; }

.split-panel-list {
    margin: 4px 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.split-panel-list li {
    font-size: var(--size-small);
    padding: 5px 12px;
    border: 1px solid currentColor;
    border-radius: var(--radius-pill);
    opacity: 0.8;
}

@media (min-width: 900px) {
    .split-grid { grid-template-columns: 1fr 1fr; }
    .split-panel--util { border-right: 1px solid var(--line); }
    .split-panel--util   { padding-left: var(--edge); }
    .split-panel--create { padding-right: var(--edge); }
}

/* ─── Cards (guide §07) ────────────────────────────────────────────────────────
   White background, 1px line border, 12px radius, 24px padding, soft layered
   shadow. Hover: shadow lifts, border tints accent blue at 30% alpha.
   ──────────────────────────────────────────────────────────────────────────── */

.card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: var(--f-card-padding);
    background: var(--surface);
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--f-card-shadow);
    transition: box-shadow 0.18s ease, border-color 0.18s ease;
}

.card:hover {
    box-shadow: var(--f-card-shadow-hover);
    border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}

.card h3 { margin-bottom: 0; }

.card p { color: var(--ink-soft); margin: 0; }

.card-link {
    margin-top: auto;
    padding-top: 6px;
    font-weight: 600;
    color: var(--accent);
}

.card-link::after { content: " →"; }

/* Industry pill — brand-soft background, brand navy text (guide §07). */
.pill-industry {
    align-self: flex-start;
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 4px 10px;
    border-radius: var(--radius-lg);
    background: var(--brand-soft);
    color: var(--brand-navy);
}

/* ─── Price cards ─────────────────────────────────────────────────────────────
   MoonFu's packaging logic (CLAUDE.md §17.1) rendered in Nextekk's system. When
   deliverables are empty the card shows a skeleton — an unfinished tier must
   LOOK unfinished.
   ──────────────────────────────────────────────────────────────────────────── */

.price-card {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: var(--f-card-padding);
    background: var(--surface);
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--f-card-shadow);
}

.price-card--featured {
    border-color: var(--accent);
    border-width: 2px;
    box-shadow: var(--shadow);
}

.price-card-flag {
    align-self: flex-start;
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 4px 10px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent-2);
}

.price-card-flag--placeholder {
    visibility: hidden;
    background: none;
}

.price-card-name {
    font-size: var(--size-h3);
    font-weight: 700;
    margin: 0;
    color: var(--ink);
}

.price-card-amount {
    font-size: var(--size-h2);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.03em;
    /* --ink, not --brand-navy: ink inverts with the palette, so the price stays
       legible on a dark canvas. In the brand palette the two are near-identical. */
    color: var(--ink);
}

.price-card-cadence {
    font-size: var(--size-small);
    color: var(--muted);
    margin-top: 4px;
}

.price-card-compare {
    text-decoration: line-through;
    color: var(--muted);
    font-size: var(--size-lead);
    margin-left: 8px;
}

.price-card-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 9px;
    font-size: var(--size-small);
    color: var(--ink-soft);
}

.price-card-list li {
    display: grid;
    grid-template-columns: 18px 1fr;
    gap: 8px;
    align-items: start;
}

.price-card-list li::before {
    content: "✓";
    color: var(--accent);
    font-weight: 700;
}

.price-card .btn-row { margin-top: auto; }

/* Skeleton for tiers whose deliverables aren't written yet. */
.price-skeleton {
    display: grid;
    gap: 10px;
    padding: 14px;
    border: 1px dashed var(--line);
    /* Fixed 8px, not var(--radius): the Soft format sets controls to 999px and the
       skeleton box turns into a blob. */
    border-radius: 8px;
    background: var(--surface-alt);
}

.price-skeleton-note {
    font-size: var(--size-small);
    font-weight: 600;
    color: var(--danger);
}

.price-skeleton-bar {
    height: 9px;
    border-radius: var(--radius-pill);
    background: var(--line);
}

.price-skeleton-bar:nth-child(2) { width: 92%; }
.price-skeleton-bar:nth-child(3) { width: 78%; }
.price-skeleton-bar:nth-child(4) { width: 85%; }
.price-skeleton-bar:nth-child(5) { width: 64%; }

/* ─── Forms (guide §07) ────────────────────────────────────────────────────── */

.field { display: grid; gap: 6px; margin-bottom: 18px; }

.field label {
    font-size: var(--size-small);
    font-weight: 500;
    color: var(--ink-soft);
}

.field input,
.field textarea,
.field select {
    padding: 12px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font: inherit;
    color: var(--ink);
}

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

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

.field-error {
    font-size: var(--size-small);
    color: var(--danger);
}

/* ─── Build notice (non-production only) ───────────────────────────────────── */

.build-notice {
    background: var(--brand-navy);
    color: #fff;
    font-size: var(--size-small);
    padding: 10px 0;
    border-bottom: 2px solid var(--warning);
}

.build-notice strong { color: var(--warning); }

/* ─── CTA band & footer ────────────────────────────────────────────────────── */

.footer-cta-band {
    background: var(--surface-alt);
    border-top: 1px solid var(--line);
    text-align: center;
}

.footer-cta-band p {
    color: var(--ink-soft);
    max-width: 52ch;
    margin: 0 auto 24px;
}

.footer-cta-band .btn-row { justify-content: center; }

.site-footer {
    background: var(--brand-navy);
    color: #cdd6e6;
    padding: clamp(40px, 6vw, 64px) 0 28px;
    font-size: var(--size-small);
}

.footer-grid {
    display: grid;
    gap: 32px;
    margin-bottom: 36px;
}

.site-footer h3 {
    font-size: var(--size-body);
    color: #fff;
    margin-bottom: 14px;
}

.footer-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 9px;
}

.site-footer a { color: #cdd6e6; }
.site-footer a:hover { color: #fff; }

.footer-blurb {
    margin-top: 14px;
    max-width: 34ch;
}

.footer-heading-spaced { margin-top: 24px; }

.footer-bottom {
    border-top: 1px solid rgba(255, 255, 255, 0.14);
    padding-top: 20px;
    display: flex;
    flex-wrap: wrap;
    gap: 10px 24px;
    justify-content: space-between;
}

@media (min-width: 700px) {
    .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; }
}

/* ─── Utilities ────────────────────────────────────────────────────────────── */

.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ─── Reveal on scroll ─────────────────────────────────────────────────────────
   Gated on the no-js → js flip so content stays visible without JS.
   ──────────────────────────────────────────────────────────────────────────── */

.js .reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.5s ease, transform 0.5s ease;
}

.js .reveal.is-visible {
    opacity: 1;
    transform: none;
}

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

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

    .js .reveal { opacity: 1; transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   PALETTES — each block overrides ONLY the --p-* layer.

   `brand` is the default declared in :root and is the sole on-brand palette
   (Nextekk Brand Guidelines Ed. 1.0). The three alternates are Studio exploration
   per CLAUDE.md §22 — deliberately off the Nextekk guide. The Nextekk logo is
   never recolored in any of them.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* ─── Midnight — dark mode. Deep indigo, electric blue, cyan. ───────────────── */

[data-palette="midnight"] {
    --p-dark:        #060A16;   /* darker than the canvas, so bands recede */
    --p-dark-2:      #10182E;
    --p-dark-soft:   #1B2540;
    --p-accent:      #4B7BFF;
    --p-accent-2:    #6E97FF;
    --p-accent-soft: #17224A;
    --p-spark:       #22D3EE;
    --p-spark-soft:  #0E3444;
    --p-ink:         #EEF2FB;   /* inverted — "ink" is light on this canvas */
    --p-ink-soft:    #B4C0DC;
    --p-muted:       #8494B8;
    --p-line:        #232D48;
    --p-surface:     #0B1020;
    --p-surface-alt: #151C33;
    --p-on-dark:     #B4C0DC;
    --p-on-dark-accent: #22D3EE;   /* cyan */
}

/* ─── Acid — bone paper, near-black type, acid lime, violet second. ─────────── */

[data-palette="acid"] {
    --p-dark:        #14150F;
    --p-dark-2:      #23251A;
    --p-dark-soft:   #E7E9D5;
    --p-accent:      #5B4BD6;   /* violet carries links — lime fails contrast as text */
    --p-accent-2:    #4536B8;
    --p-accent-soft: #E9E6FB;
    --p-spark:       #C6F53D;   /* lime is a FILL and highlight, never body text */
    --p-spark-soft:  #EEF9C9;
    --p-ink:         #14150F;
    --p-ink-soft:    #3F4235;
    --p-muted:       #6B6F5D;
    --p-line:        #DAD8C4;
    --p-surface:     #F5F3E7;
    --p-surface-alt: #EDEADA;
    --p-on-dark:     #DDE0CC;
    --p-on-dark-accent: #C6F53D;   /* the acid lime finally lands somewhere */
}

/* ─── Ember — warm cream, terracotta, plum. Editorial and quiet. ───────────── */

[data-palette="ember"] {
    --p-dark:        #2C1A17;
    --p-dark-2:      #3D2620;
    --p-dark-soft:   #F3E4DC;
    --p-accent:      #B8431F;   /* darkened from the swatch so links pass AA on cream */
    --p-accent-2:    #903317;
    --p-accent-soft: #FAE6DD;
    --p-spark:       #7B3F5E;   /* plum takes the spark slot in this palette */
    --p-spark-soft:  #F4E3EC;
    --p-ink:         #2C1A17;
    --p-ink-soft:    #58423C;
    --p-muted:       #857069;
    --p-line:        #E7DACE;
    --p-surface:     #FBF6EF;
    --p-surface-alt: #F5EDE3;
    --p-on-dark:     #E8D5CC;
    --p-on-dark-accent: #E8A87C;   /* warm terracotta tint */
}

/* Dark-chrome palettes: the header sits on a dark canvas, so swap to the
   white-knockout lockup. Swapping between the two sanctioned variants is allowed;
   recoloring the artwork is not (brand guide §02). */
[data-palette="midnight"] .brand-logo--dark  { display: none; }
[data-palette="midnight"] .brand-logo--light { display: block; }

/* On a dark canvas a dark-filled primary CTA disappears — it ends up darker than
   the page it sits on. Accent carries the primary action instead. Format rules come
   later in this file, so Brutal still overrides this where it applies. */
/* Dark text on the accent, NOT white. Midnight's accent (#4B7BFF) is a light blue —
   white on it measures ~3.4:1, under AA for a 17px label. Against the canvas
   (#0B1020) it clears 5:1 and reads as a deliberate high-contrast CTA. */
[data-palette="midnight"] .btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
}

[data-palette="midnight"] .btn-primary:hover {
    background: var(--accent-2);
    border-color: var(--accent-2);
    color: var(--surface);
}

/* The switcher trigger has the same problem. */
[data-palette="midnight"] .theme-switcher-toggle {
    background: var(--accent);
    color: var(--surface);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   FORMATS — each block overrides ONLY the --f-* layer, plus the few structural
   rules a variable cannot express.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* ─── Editorial — one column, oversized type, hairline rules, air. ──────────── */

[data-format="editorial"] {
    --f-radius-control: 4px;
    --f-radius-card:    0px;
    --f-border-width:   0px;
    --f-card-shadow:       none;
    --f-card-shadow-hover: none;
    --f-heading-spacing:   -0.035em;
    --f-heading-weight:    800;
    --f-section-y:         clamp(64px, 11vw, 140px);
    --f-card-padding:      0px;
}

/* Cards become rule-separated entries rather than boxes. */
[data-format="editorial"] .card,
[data-format="editorial"] .price-card {
    border-top: 1px solid var(--line);
    padding-top: 22px;
    background: transparent;
}

[data-format="editorial"] .split-grid { grid-template-columns: 1fr; }

[data-format="editorial"] .split-panel {
    min-height: 0;
    padding: clamp(40px, 6vw, 72px) var(--edge);
    border-right: 0;
}

[data-format="editorial"] .split-panel--util { background-image: none; }
[data-format="editorial"] .split-panel p { max-width: 60ch; }

[data-format="editorial"] .split-hero-intro h1 {
    font-size: var(--size-hero);
    font-weight: 800;
}

/* ─── Brutal — square, heavy borders, uppercase, flat. ─────────────────────── */

[data-format="brutal"] {
    --f-radius-control: 0px;
    --f-radius-card:    0px;
    --f-border-width:   2px;
    --f-card-shadow:       none;
    --f-card-shadow-hover: none;
    --f-heading-transform: uppercase;
    --f-heading-spacing:   -0.01em;
    --f-heading-weight:    800;
    --f-eyebrow-family:    var(--font-mono);
    --f-card-padding:      20px;
}

[data-format="brutal"] .card,
[data-format="brutal"] .price-card {
    border-color: var(--ink);
}

[data-format="brutal"] .card:hover {
    border-color: var(--ink);
    box-shadow: 6px 6px 0 var(--accent);
}

[data-format="brutal"] .btn { border-width: 2px; }

/* Brutal fills the primary button with --ink, which INVERTS per palette, so the
   label must invert with it — otherwise a light ink on a dark palette gives white
   text on a white button. Caught in Midnight + Brutal. */
[data-format="brutal"] .btn-primary {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--surface);
}
[data-format="brutal"] .btn-secondary { border-color: var(--ink); color: var(--ink); }

[data-format="brutal"] .btn-secondary:hover {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--surface);
}

[data-format="brutal"] .split-panel-list li { border-radius: 0; }
[data-format="brutal"] .price-card--featured { border-width: 3px; }

[data-format="brutal"] .pill-industry,
[data-format="brutal"] .price-card-flag { border-radius: 0; }

/* ─── Soft — big radii, deep shadows, borderless, airy. ────────────────────── */

[data-format="soft"] {
    --f-radius-control: 999px;
    --f-radius-card:    24px;
    --f-border-width:   0px;
    --f-card-shadow:       var(--shadow);
    --f-card-shadow-hover: var(--shadow-lg);
    --f-heading-spacing:   -0.025em;
    --f-heading-weight:    700;
    --f-section-y:         clamp(56px, 9vw, 112px);
    --f-card-padding:      30px;
}

[data-format="soft"] .split-panel--util { background-image: none; }

[data-format="soft"] .split-grid {
    gap: 20px;
    padding: 0 var(--gutter) var(--gutter);
}

[data-format="soft"] .split-panel {
    border-radius: 24px;
    border-right: 0;
    padding: clamp(28px, 4vw, 48px);
}

[data-format="soft"] .split-hero { border-bottom: 0; }

[data-format="soft"] .price-card--featured {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   THEME SWITCHER — the client-facing mix-and-match control.
   Progressive enhancement: hidden without JS, since it cannot work.
   ═══════════════════════════════════════════════════════════════════════════════ */

.no-js .theme-switcher { display: none; }

.theme-switcher {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 120;
    font-size: var(--size-small);
}

.theme-switcher-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 11px 16px;
    background: var(--brand-navy);
    color: #fff;
    border: 0;
    border-radius: var(--radius-pill);
    font: inherit;
    font-weight: 600;
    box-shadow: var(--shadow-lg);
    cursor: pointer;
}

.theme-switcher-dots { display: inline-flex; gap: 3px; }

.theme-switcher-dots span {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    display: block;
}

.theme-switcher-panel {
    position: absolute;
    right: 0;
    bottom: calc(100% + 10px);
    width: min(340px, calc(100vw - 32px));
    max-height: min(70vh, 560px);
    overflow-y: auto;
    padding: 18px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    box-shadow: var(--shadow-lg);
}

.theme-switcher-panel[hidden] { display: none; }

.theme-switcher-panel h2 {
    font-size: var(--size-body);
    margin: 0 0 4px;
    text-transform: none;
}

.theme-switcher-intro {
    color: var(--muted);
    margin: 0 0 16px;
    font-size: var(--size-small);
}

.theme-group { margin-bottom: 18px; }

.theme-group:last-of-type { margin-bottom: 8px; }

.theme-group-label {
    display: block;
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 10px;
}

.theme-options { display: grid; gap: 8px; }

/* Real radio inputs stay in the DOM for keyboard and AT support; the label body
   is the visible control. */
.theme-option { position: relative; display: block; }

/* Scoped to radios ONLY. Unscoped, this also caught the background range sliders and
   made them position:absolute + opacity:0 — invisible, and sized against .theme-option
   instead of their own container. */
.theme-option input[type="radio"] {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.theme-option-body {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 2px solid var(--line);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.theme-option-body:hover { border-color: var(--accent); }

.theme-option input[type="radio"]:checked + .theme-option-body {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.theme-option input[type="radio"]:focus-visible + .theme-option-body {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.theme-swatches {
    display: inline-flex;
    flex: 0 0 auto;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid rgba(0, 0, 0, 0.12);
}

.theme-swatches span {
    width: 15px;
    height: 30px;
    display: block;
}

.theme-option-text { display: grid; gap: 1px; min-width: 0; }

.theme-option-name { font-weight: 600; color: var(--ink); }

.theme-option-note {
    color: var(--muted);
    font-size: 0.8125rem;
    line-height: 1.4;
}

.theme-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: var(--radius-pill);
    background: var(--spark-soft);
    color: var(--ink-soft);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.theme-switcher-reset {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: var(--size-small);
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

@media (max-width: 520px) {
    .theme-switcher { right: 12px; bottom: 12px; }
    .theme-swatches span { width: 12px; height: 26px; }
}

/* Swatch fills read the --p-* layer, so an option carrying data-palette paints
   itself in that palette's colors with no inline styles and no repeated hexes. */
.theme-swatch--surface { background: var(--p-surface-alt); }
.theme-swatch--dark    { background: var(--p-dark); }
.theme-swatch--accent  { background: var(--p-accent); }
.theme-swatch--spark   { background: var(--p-spark); }

/* Only one lockup variant is visible at a time; palettes with dark chrome swap it
   (see the [data-palette] blocks above). Default chrome is light. */
.brand-logo--light { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════════
   CUSTOM PALETTE — every token derived from a single hue via OKLCH.

   OKLCH is the reason this works: lightness is perceptually uniform, so pinning L
   per role keeps contrast roughly constant no matter which hue the client lands on.
   The same trick in HSL would go muddy at yellow and black at blue.

   --user-hue is set on <html> by the wheel (CSSOM, not a style attribute — our CSP
   blocks the latter, see CLAUDE.md §20.1). Falls back to the brand accent's hue.
   ═══════════════════════════════════════════════════════════════════════════════ */

[data-palette="custom"][data-custom-mode="light"] {
    --p-accent:      oklch(50% 0.19 var(--user-hue, 255));
    --p-accent-2:    oklch(42% 0.19 var(--user-hue, 255));
    --p-accent-soft: oklch(95% 0.035 var(--user-hue, 255));

    --p-dark:        oklch(26% 0.09 var(--user-hue, 255));
    --p-dark-2:      oklch(33% 0.09 var(--user-hue, 255));
    --p-dark-soft:   oklch(93% 0.03 var(--user-hue, 255));

    /* Spark sits 45° around the wheel so it reads as a companion, not a near-miss. */
    --p-spark:       oklch(68% 0.16 calc(var(--user-hue, 255) + 45));
    --p-spark-soft:  oklch(95% 0.05 calc(var(--user-hue, 255) + 45));

    --p-ink:         oklch(23% 0.03 var(--user-hue, 255));
    --p-ink-soft:    oklch(42% 0.03 var(--user-hue, 255));
    --p-muted:       oklch(57% 0.025 var(--user-hue, 255));

    /* --p-surface / --p-surface-alt / --p-line come from the BACKGROUND WHEEL block
       at the end of this file, which is a second hue independent of this one. */

    --p-on-dark:        oklch(88% 0.025 var(--user-hue, 255));
    --p-on-dark-accent: oklch(80% 0.14 var(--user-hue, 255));
}

/* ─── The hue wheels ───────────────────────────────────────────────────────────
   Each is a real ARIA slider: focusable, arrow-key operable, announces its value.
   The accent ring is a conic-gradient of the same OKLCH accent expression the palette
   uses, so what you point at is literally what you get.

   Two rings stack per build — accent, then background. The wrap is the stack; each
   .hue-dial is one ring beside its readout.
   ──────────────────────────────────────────────────────────────────────────── */

.hue-wheel-wrap {
    display: grid;
    gap: 14px;
    margin-top: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--line);
}

.hue-dial {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
}

.hue-wheel {
    position: relative;
    flex: 0 0 auto;
    width: 76px;
    height: 76px;
    border-radius: 50%;
    cursor: crosshair;
    touch-action: none;
    background:
        conic-gradient(
            from 90deg,
            oklch(50% 0.19 0),   oklch(50% 0.19 30),  oklch(50% 0.19 60),
            oklch(50% 0.19 90),  oklch(50% 0.19 120), oklch(50% 0.19 150),
            oklch(50% 0.19 180), oklch(50% 0.19 210), oklch(50% 0.19 240),
            oklch(50% 0.19 270), oklch(50% 0.19 300), oklch(50% 0.19 330),
            oklch(50% 0.19 360)
        );
    /* Punch out the middle so it reads as a ring, and park a preview swatch there. */
    mask: radial-gradient(circle, transparent 0 42%, #000 43% 100%);
    -webkit-mask: radial-gradient(circle, transparent 0 42%, #000 43% 100%);
}

.hue-wheel:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* The thumb's fill is pure CSS, keyed off the same variable the wheel writes, so each
   ring previews its own role and JS never has to know a token's L and C. */
.hue-wheel-thumb {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 14px;
    height: 14px;
    margin: -7px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
    pointer-events: none;
}

.hue-dial--accent .hue-wheel-thumb {
    background: oklch(50% 0.19 var(--user-hue, 255));
}

.hue-wheel-readout {
    display: grid;
    gap: 2px;
    min-width: 0;
}

.hue-wheel-value {
    font-family: var(--font-mono);
    font-size: var(--size-small);
    color: var(--ink);
}

.hue-wheel-hint {
    font-size: 0.8125rem;
    color: var(--muted);
    line-height: 1.4;
}

/* Live preview of the derived palette, so the wheel shows consequences. */
.hue-preview {
    display: inline-flex;
    margin-top: 4px;
    border-radius: 5px;
    overflow: hidden;
    border: 1px solid var(--line);
}

.hue-preview span {
    width: 18px;
    height: 18px;
    display: block;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   PANEL DEMO — wireframe resolving into a finished design, as a hero panel background.
   ═══════════════════════════════════════════════════════════════════════════════ */

.panel-demo {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

/* From 900px the split is side by side and the copy has a fixed side, so the demo
   moves to the OUTER half and feathers its inner edge. Full-bleed put the demo's own
   headline directly under ours — two headlines fighting. */
@media (min-width: 900px) {
    .split-panel--util .panel-demo {
        left: 55%;
        mask-image: linear-gradient(90deg, transparent 0, #000 30%, #000 100%);
        -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 30%, #000 100%);
    }

    .split-panel--create .panel-demo {
        left: 34%;
        mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 100%);
        -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 100%);
    }
}

.panel-demo-final {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
}

/* The vector artwork takes its single accent from the active palette. */
.panel-demo-art { color: var(--accent); }

/* The screenshot's own headline lives on its left, exactly where the panel's copy
   sits. Pushing the crop right keeps two headlines from ghosting over each other. */
.split-panel--util .panel-demo-final { object-position: 50% 64%; }

/* Same problem on the creative panel, solved by anchoring right instead: the mockup's
   own headline sits far left, so a right-anchored crop drops it and leaves the figure.
   This MUST stay paired with preserveAspectRatio="xMaxYMid slice" on the wireframe svg
   in includes/panel-demo.php — if the two layers crop differently the wireframe boxes
   stop landing on real elements and the resolve effect falls apart. */
.split-panel--create img.panel-demo-final { object-position: 100% 50%; }

.panel-demo-wire {
    position: absolute;
    inset: 0;
    background: var(--surface-alt);
    color: var(--accent);
}

.split-panel--create .panel-demo-wire { background: #0B0B0F; }

.panel-demo-wire svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* Wireframe vocabulary */
.panel-demo-wire .pw-line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    opacity: 0.5;
}

.panel-demo-wire .pw-thin   { stroke-width: 1.25; opacity: 0.35; }
.panel-demo-wire .pw-accent { opacity: 0.95; stroke-width: 2.5; }
.panel-demo-wire .pw-diag   { opacity: 0.15; }
.panel-demo-wire .pw-well   { opacity: 0.25; }

.panel-demo-wire .pw-fill {
    fill: currentColor;
    opacity: 0.05;
    stroke: none;
}

/* Scrim — brand guide §08 asks for 40–60% over full-bleed imagery behind headlines.
   The light panel needs a heavier one because its copy is dark. */
.panel-demo-scrim { position: absolute; inset: 0; }

.split-panel--util .panel-demo-scrim {
    background: linear-gradient(
        100deg,
        color-mix(in srgb, var(--surface-alt) 72%, transparent) 0%,
        color-mix(in srgb, var(--surface-alt) 40%, transparent) 40%,
        color-mix(in srgb, var(--surface-alt) 12%, transparent) 100%
    );
}

.split-panel--create .panel-demo-scrim {
    background: linear-gradient(
        100deg,
        color-mix(in srgb, var(--brand-navy) 78%, transparent) 0%,
        color-mix(in srgb, var(--brand-navy) 40%, transparent) 42%,
        color-mix(in srgb, var(--brand-navy) 10%, transparent) 100%
    );
}

/* Panel copy sits above all three layers. */
.split-panel > *:not(.panel-demo) { position: relative; z-index: 1; }

/* Frame-by-frame resolve. steps() is the point — it advances in discrete passes
   instead of dissolving, so it reads as a render rather than a crossfade. */
.js .split-panel.is-visible .panel-demo-wire {
    animation: panel-demo-resolve 1.9s steps(9, end) 0.2s forwards;
}

@keyframes panel-demo-resolve {
    from { clip-path: inset(0 0 0 0); }
    to   { clip-path: inset(0 0 0 100%); }
}

/* Without JS the wireframe would sit there forever — show the finished design. */
.no-js .panel-demo-wire { display: none; }

@media (prefers-reduced-motion: reduce) {
    .js .split-panel.is-visible .panel-demo-wire {
        animation: none;
        clip-path: inset(0 0 0 100%);
    }
}

/* The gridded texture on the utilitarian panel fought the screenshot behind it. */
/* The creative panel's radial blob is redundant now that artwork sits behind it. */
.split-panel--create:has(.panel-demo)::before { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════════
   THEME SWITCHER — header placement (overrides the fixed-corner rules above).

   It was originally a fixed pill at bottom-right. Blake could not find it twice, so
   it now sits in the header beside the nav, where a light/dark toggle is expected.
   Do not move it back to a floating corner.
   ═══════════════════════════════════════════════════════════════════════════════ */

.site-header .theme-switcher {
    position: relative;
    right: auto;
    bottom: auto;
    z-index: 130;
    flex: 0 0 auto;
    order: 3;
}

/* Compact swatch button — reads as a color control at a glance. */
.site-header .theme-switcher-toggle {
    gap: 7px;
    padding: 8px 13px;
    background: transparent;
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: none;
    font-size: var(--size-small);
}

.site-header .theme-switcher-toggle:hover {
    border-color: var(--accent);
    color: var(--accent);
}

[data-palette="midnight"] .site-header .theme-switcher-toggle {
    background: transparent;
    color: var(--ink);
}

.site-header .theme-switcher-dots span {
    width: 11px;
    height: 11px;
    border-radius: 3px;
}

/* Panel drops DOWN from the header instead of up from the corner. */
.site-header .theme-switcher-panel {
    top: calc(100% + 12px);
    bottom: auto;
    right: 0;
    max-height: min(78vh, 620px);
}

/* On narrow screens the label would crowd the Menu button — keep the swatches. */
@media (max-width: 560px) {
    /* DIRECT child only. Unscoped, `span:not(.theme-switcher-dots)` also matched the
       three swatch spans nested inside the dots wrapper, so the pill went empty and
       collapsed to 22px — the control was on screen and invisible. Same failure mode
       as the radio-hiding rule in CLAUDE.md §22. */
    .site-header .theme-switcher-toggle > span:not(.theme-switcher-dots) { display: none; }
    .site-header .theme-switcher-toggle { padding: 8px 10px; }

    /* The row is brand + Menu + swatch button, and at 20px gaps it did not fit: the
       switcher carries order:3 so it was the one pushed past the right edge, i.e. an
       approved control silently disappearing on phones. Measured at 390px:
       brand 213 + Menu 103 + switcher 42 + two 20px gaps + 48px padding = 446 > 375.
       Tighter gaps, and below 430px the hamburger drops its word, brings it to 357. */
    .header-inner { gap: 10px; }
}

@media (max-width: 430px) {
    .nav-toggle span:not(.nav-toggle-bars) { display: none; }
    .nav-toggle { padding: 10px 12px; }

    /* The real two-line lockup is 249px wide at 96px tall, which leaves no room for
       the switcher and pushed it off the right edge again. At 390px the row has
       375 - 48 padding - 44 hamburger - 61 switcher - 20 gaps = 202px for the brand,
       so cap the art at 72px tall (~187px wide) and everything fits. */
    .brand-logo { height: 72px; }
    .site-header.is-scrolled .brand-logo { height: 60px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   CUSTOM PALETTE — DARK BUILD

   The custom palette ships in two builds, each with its own hue wheel:
   [data-custom-mode="light"] (the block above) and this one. Same OKLCH trick, but
   the lightness scale is inverted: surfaces go dark, ink goes light, and the accent
   is pushed up to ~72% L so it reads on a dark ground.

   Fixed L per role is what makes this safe at any hue. Only the hue rotates.
   ═══════════════════════════════════════════════════════════════════════════════ */

[data-palette="custom"][data-custom-mode="dark"] {
    --p-accent:      oklch(72% 0.16 var(--user-hue-dark, 255));
    --p-accent-2:    oklch(80% 0.14 var(--user-hue-dark, 255));
    --p-accent-soft: oklch(28% 0.07 var(--user-hue-dark, 255));

    --p-dark-soft:   oklch(26% 0.045 var(--user-hue-dark, 255));

    --p-spark:       oklch(78% 0.15 calc(var(--user-hue-dark, 255) + 45));
    --p-spark-soft:  oklch(30% 0.08 calc(var(--user-hue-dark, 255) + 45));

    --p-ink:         oklch(95% 0.012 var(--user-hue-dark, 255));
    --p-ink-soft:    oklch(80% 0.022 var(--user-hue-dark, 255));
    --p-muted:       oklch(65% 0.025 var(--user-hue-dark, 255));

    /* --p-surface / --p-surface-alt / --p-line / --p-dark / --p-dark-2 come from the
       BACKGROUND WHEEL block at the end of this file — a second, independent hue. */

    --p-on-dark:        oklch(85% 0.022 var(--user-hue-dark, 255));
    --p-on-dark-accent: oklch(78% 0.14 var(--user-hue-dark, 255));
}

/* Dark chrome: the knockout lockup, and the accent carries the primary CTA because a
   dark fill would sit darker than the page. Dark text ON the accent, since a 72%-L
   accent with white text lands near 2:1. Same lesson as Midnight (CLAUDE.md §22.3). */
[data-palette="custom"][data-custom-mode="dark"] .brand-logo--dark  { display: none; }
[data-palette="custom"][data-custom-mode="dark"] .brand-logo--light { display: block; }

[data-palette="custom"][data-custom-mode="dark"] .btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
}

[data-palette="custom"][data-custom-mode="dark"] .btn-primary:hover {
    background: var(--accent-2);
    border-color: var(--accent-2);
    color: var(--surface);
}

[data-palette="custom"][data-custom-mode="dark"] .panel-demo-wire { background: var(--surface-alt); }

/* ─── Light / dark segmented control ───────────────────────────────────────── */

.hue-modes {
    display: inline-flex;
    margin-top: 12px;
    padding: 3px;
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    background: var(--surface-alt);
}

.hue-mode-btn {
    padding: 5px 14px;
    border: 0;
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--muted);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
}

.hue-mode-btn[aria-pressed="true"] {
    background: var(--accent);
    color: var(--surface);
}

.hue-mode-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Only the active build's pair of wheels is shown, so the panel stays compact. Both
   builds remain in the DOM and keep their values, so flipping back restores them. */
.hue-wheel-wrap[data-wheel-mode] { display: none; }

[data-custom-mode="light"] .hue-wheel-wrap[data-wheel-mode="light"],
[data-custom-mode="dark"]  .hue-wheel-wrap[data-wheel-mode="dark"] {
    display: grid;
}

.hue-wheel-label {
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

/* The dark build's accent ring previews dark-build accents, not light-build ones. */
[data-custom-mode="dark"] .hue-dial--accent .hue-wheel {
    background:
        conic-gradient(
            from 90deg,
            oklch(72% 0.16 0),   oklch(72% 0.16 30),  oklch(72% 0.16 60),
            oklch(72% 0.16 90),  oklch(72% 0.16 120), oklch(72% 0.16 150),
            oklch(72% 0.16 180), oklch(72% 0.16 210), oklch(72% 0.16 240),
            oklch(72% 0.16 270), oklch(72% 0.16 300), oklch(72% 0.16 330),
            oklch(72% 0.16 360)
        );
}

[data-custom-mode="dark"] .hue-dial--accent .hue-wheel-thumb {
    background: oklch(72% 0.16 var(--user-hue-dark, 255));
}

/* ═══════════════════════════════════════════════════════════════════════════════
   CUSTOM PALETTE — THE BACKGROUND WHEEL

   The page ground is a second hue wheel, the same control as the accent, because the
   two questions are the same question. Lightness is deliberately NOT on a wheel: the
   Light / Dark segmented control already decides that, so a ring only ever answers
   "which hue".

     Light build   pale tinted paper: cream near 80°, blush near 20°, mint near 150°
     Dark build    tinted near-black; the tint reads in the bands and the hairlines

   The expressions live on :root so the palette blocks below and the ring's centre
   preview both read the same values — one definition, two consumers. Chroma is low on
   purpose: enough that the tint is unmistakable beside white, never enough to look
   like a colored page.

   These blocks come AFTER the custom light/dark blocks above and own the surface
   family outright, so the accent wheel drives accents and this one drives the ground.
   ═══════════════════════════════════════════════════════════════════════════════ */

:root {
    --bg-light-surface: oklch(98.4% 0.011 var(--bg-hue, 255));
    --bg-light-alt:     oklch(96.2% 0.021 var(--bg-hue, 255));
    --bg-light-line:    oklch(89%   0.018 var(--bg-hue, 255));

    --bg-dark-surface:  oklch(17%   0.024 var(--bg-hue-dark, 255));
    --bg-dark-alt:      oklch(21.5% 0.030 var(--bg-hue-dark, 255));
    --bg-dark-line:     oklch(32%   0.036 var(--bg-hue-dark, 255));
    --bg-dark-band:     oklch(11.5% 0.030 var(--bg-hue-dark, 255));
    --bg-dark-band-2:   oklch(19.5% 0.032 var(--bg-hue-dark, 255));
}

[data-palette="custom"][data-custom-mode="light"] {
    --p-surface:     var(--bg-light-surface);
    --p-surface-alt: var(--bg-light-alt);
    --p-line:        var(--bg-light-line);
}

[data-palette="custom"][data-custom-mode="dark"] {
    --p-surface:     var(--bg-dark-surface);
    --p-surface-alt: var(--bg-dark-alt);
    --p-line:        var(--bg-dark-line);

    /* Dark bands must stay darker than the canvas or they stop reading as bands. */
    --p-dark:   var(--bg-dark-band);
    --p-dark-2: var(--bg-dark-band-2);
}

/* ─── The background ring ──────────────────────────────────────────────────────
   Two differences from the accent ring, both deliberate:

   1. The centre is FILLED with the computed surface instead of punched out, so the
      ring shows its own consequence. That rules out the mask the accent ring uses —
      mask clips descendants too, so anything in the hole would vanish — hence a
      layered background: a radial-gradient disc over the conic spectrum.
   2. The spectrum itself is drawn at a higher chroma than it applies. At the real
      0.011 every hue looks like the same white and the ring is unusable. The filled
      centre and the page itself are the honest preview; the ring is the compass.
   ──────────────────────────────────────────────────────────────────────────── */

.hue-dial--bg .hue-wheel {
    mask: none;
    -webkit-mask: none;
    background:
        radial-gradient(circle,
            var(--bg-light-surface) 0 42%, transparent 43%),
        conic-gradient(
            from 90deg,
            oklch(92% 0.05 0),   oklch(92% 0.05 30),  oklch(92% 0.05 60),
            oklch(92% 0.05 90),  oklch(92% 0.05 120), oklch(92% 0.05 150),
            oklch(92% 0.05 180), oklch(92% 0.05 210), oklch(92% 0.05 240),
            oklch(92% 0.05 270), oklch(92% 0.05 300), oklch(92% 0.05 330),
            oklch(92% 0.05 360)
        );
    box-shadow: inset 0 0 0 1px var(--line);
}

.hue-dial--bg .hue-wheel-thumb {
    background: var(--bg-light-surface);
}

[data-custom-mode="dark"] .hue-dial--bg .hue-wheel {
    background:
        radial-gradient(circle,
            var(--bg-dark-surface) 0 42%, transparent 43%),
        conic-gradient(
            from 90deg,
            oklch(40% 0.06 0),   oklch(40% 0.06 30),  oklch(40% 0.06 60),
            oklch(40% 0.06 90),  oklch(40% 0.06 120), oklch(40% 0.06 150),
            oklch(40% 0.06 180), oklch(40% 0.06 210), oklch(40% 0.06 240),
            oklch(40% 0.06 270), oklch(40% 0.06 300), oklch(40% 0.06 330),
            oklch(40% 0.06 360)
        );
}

[data-custom-mode="dark"] .hue-dial--bg .hue-wheel-thumb {
    background: var(--bg-dark-surface);
}

/* One hint for the pair, not one per ring. */
.hue-wheel-foot {
    font-size: 0.8125rem;
    color: var(--muted);
    line-height: 1.4;
}

/* ─── Panel overflow guards ────────────────────────────────────────────────────
   Flex and grid items default to min-width:auto, so they refuse to shrink below their
   content and anything wide inside an option overhangs the panel. The root cause was
   .theme-option-text being an implicit single-column grid, whose `auto` column sizes
   to max-content; minmax(0, 1fr) caps it. Kept as a standing guard.
   ──────────────────────────────────────────────────────────────────────────── */

.theme-switcher-panel,
.site-header .theme-switcher-panel {
    width: min(384px, calc(100vw - 32px));
    overflow-x: hidden;
}

.theme-option-body { min-width: 0; }
.theme-option-note { overflow-wrap: anywhere; }

.theme-option-text,
.theme-option-text > *,
.hue-wheel-wrap,
.hue-dial,
.hue-wheel-readout { min-width: 0; }

.theme-option-text { grid-template-columns: minmax(0, 1fr); }

/* ─── Name a look and copy it out ──────────────────────────────────────────────
   The switcher's payoff: a client lands on something, names it, and copies a spec
   instead of describing a color over the phone.
   ──────────────────────────────────────────────────────────────────────────── */

.theme-export {
    margin: 4px 0 12px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
}

.theme-export-label {
    display: block;
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 8px;
}

.theme-export-row {
    display: flex;
    gap: 8px;
    align-items: stretch;
}

.theme-export-name {
    flex: 1 1 auto;
    min-width: 0;
    padding: 8px 10px;
    font: inherit;
    font-size: var(--size-small);
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.theme-export-name:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
    border-color: var(--accent);
}

.theme-export-copy {
    flex: 0 0 auto;
    padding: 8px 14px;
    font-size: var(--size-small);
    white-space: nowrap;
}

.theme-export-hint {
    margin: 8px 0 0;
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--muted);
}

.theme-export-fallback-label {
    display: block;
    margin-top: 8px;
    font-size: 0.8125rem;
    color: var(--muted);
}

.theme-export-fallback {
    width: 100%;
    margin-top: 4px;
    padding: 8px 10px;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--ink);
    background: var(--surface-alt);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    resize: vertical;
}

.theme-export-fallback[hidden],
.theme-export-fallback-label[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════════
   PAGE HERO — shared by every non-home page. Compact, no imagery.
   ═══════════════════════════════════════════════════════════════════════════════ */

.page-hero {
    padding: clamp(40px, 6vw, 72px) 0 clamp(28px, 4vw, 44px);
    border-bottom: 1px solid var(--line);
}

.page-hero h1 { margin-bottom: 0.3em; }

.page-hero .lede { max-width: 56ch; margin: 0; }

/* ═══════════════════════════════════════════════════════════════════════════════
   CASE STUDIES — work.php

   Media beside copy, alternating sides. Mobile stacks with the media first, so the
   proof is what a phone visitor sees before the words.
   ═══════════════════════════════════════════════════════════════════════════════ */

.case-list {
    display: grid;
    gap: clamp(40px, 7vw, 88px);
}

.case {
    display: grid;
    gap: clamp(20px, 3vw, 40px);
    align-items: center;
}

@media (min-width: 860px) {
    .case { grid-template-columns: 1.15fr 1fr; }

    /* Flip puts the media on the right. The DOM order never changes, so the reading
       order stays media-then-copy for screen readers and on mobile. */
    .case--flip .case-media { order: 2; }
}

.case-media { min-width: 0; }

.case-shot {
    width: 100%;
    height: auto;
    display: block;
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--f-card-shadow);
}

/* Shown when a project has no client-approved screenshot. It has to read as a
   deliberate gap, not a broken image, and never as a stock stand-in. */
.case-shot-gap {
    display: grid;
    align-content: center;
    gap: 6px;
    aspect-ratio: 16 / 10;
    padding: clamp(20px, 3vw, 32px);
    text-align: center;
    background: var(--surface-alt);
    border: 1px dashed var(--line);
    border-radius: var(--radius-lg);
}

.case-shot-gap-label {
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent);
}

.case-shot-gap-note {
    font-size: var(--size-small);
    color: var(--muted);
    max-width: 30ch;
    margin: 0 auto;
}

.case-body { min-width: 0; }

.case-body h2 { margin: 12px 0 0.35em; }

.case-body > p {
    color: var(--ink-soft);
    font-size: var(--size-lead);
    margin-bottom: 20px;
}

.case-did {
    margin: 0 0 22px;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 9px;
    font-size: var(--size-small);
    color: var(--ink-soft);
}

.case-did li {
    display: grid;
    grid-template-columns: 18px 1fr;
    gap: 8px;
    align-items: start;
}

.case-did li::before {
    content: "✓";
    color: var(--accent);
    font-weight: 700;
}

/* Non-production only: what this entry is still missing. */
.case-pending {
    margin: 18px 0 0;
    padding: 10px 12px;
    font-size: var(--size-small);
    color: var(--ink-soft);
    background: var(--spark-soft);
    border-radius: var(--radius);
}

.case-pending strong { color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════════
   STUDIO — the site's own look. Black, white, one red.

   Blake's direction, 2026-08-10: "basic black and white with some red highlight,
   cool and edgy yet simple and professional." This is also the answer to the open
   §21.2 question about whether the Studio differentiates from the Nextekk guide.
   It does, and this is how.

   The chrome is monochrome ON PURPOSE so the client screenshots are the only colour
   on the page. Never put a filter on .case-shot or .panel-demo-final.
   ═══════════════════════════════════════════════════════════════════════════════ */

[data-palette="studio"] {
    --p-dark:        #0A0A0A;
    --p-dark-2:      #1C1C1C;
    --p-dark-soft:   #EDEDED;

    --p-accent:      #D10026;   /* darkened from a pure red so links pass AA on white */
    --p-accent-2:    #A80020;
    --p-accent-soft: #FCE8EC;

    --p-spark:       #E4002B;   /* the hot red, for fills and marks only, never body text */
    --p-spark-soft:  #FFE4E9;

    --p-ink:         #0A0A0A;
    --p-ink-soft:    #3D3D3D;
    --p-muted:       #6E6E6E;
    --p-line:        #E2E2E2;
    --p-surface:     #FFFFFF;
    --p-surface-alt: #F4F4F4;

    --p-on-dark:        #D6D6D6;
    --p-on-dark-accent: #FF3355;
}

/* The logo art is near-black, so it needs the white variant on any dark band. */
[data-palette="studio"] .site-footer .brand-logo--dark  { display: none; }
[data-palette="studio"] .site-footer .brand-logo--light { display: block; }

/* ═══════════════════════════════════════════════════════════════════════════════
   CXDEMO — the theme demo page.

   Deliberately thin: the page exists to show the EXISTING components under every
   palette and format, so almost everything on it is a shared class. Only the few
   things that have no home elsewhere live here.
   ═══════════════════════════════════════════════════════════════════════════════ */

.cx-noscript {
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-alt);
    color: var(--ink-soft);
}

.cx-small {
    font-size: var(--size-small);
    color: var(--muted);
}

.cx-btn-row { margin-bottom: clamp(28px, 4vw, 44px); }

.cx-cards { margin-top: 0; }

/* Four token swatches, labeled. The label sits ON the swatch so a client can see the
   contrast pairing that token is actually used at, not just the hue. */
.cx-swatches {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    align-content: start;
}

.cx-swatch {
    display: flex;
    align-items: flex-end;
    min-height: 96px;
    padding: 12px;
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.cx-swatch--ink    { background: var(--ink);         color: var(--surface); }
.cx-swatch--accent { background: var(--accent);      color: var(--surface); }
.cx-swatch--dark   { background: var(--brand-navy);  color: #fff; }
.cx-swatch--alt    { background: var(--surface-alt); color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════════
   CITY LINKS — /metroplex/ hub and the nearby row on each city page.

   A link list, not a card grid: these are navigation between markets, and dressing
   them as cards would imply each one is a case study.
   ═══════════════════════════════════════════════════════════════════════════════ */

.city-links {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.city-links--hub { gap: 14px; }

.city-link {
    display: grid;
    gap: 2px;
    height: 100%;
    padding: 16px 18px;
    text-decoration: none;
    color: var(--ink);
    background: var(--surface);
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.city-link:hover,
.city-link:focus-visible {
    text-decoration: none;
    border-color: var(--accent);
    box-shadow: var(--f-card-shadow-hover);
}

.city-link-name {
    font-weight: 700;
    font-size: var(--size-lead);
    line-height: 1.2;
}

.city-link-county {
    font-size: var(--size-small);
    color: var(--muted);
}

/* The catch-all sits at the end of the nearby row and reads as an exit, not a peer. */
.city-link--all { background: var(--surface-alt); }

/* ═══════════════════════════════════════════════════════════════════════════════
   SERVICE DETAIL — /services/* and the shared blocks about.php reuses.
   ═══════════════════════════════════════════════════════════════════════════════ */

.svc-split {
    display: grid;
    gap: clamp(32px, 5vw, 64px);
}

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

.svc-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 12px;
    color: var(--ink-soft);
}

.svc-list li {
    display: grid;
    grid-template-columns: 20px 1fr;
    gap: 10px;
    align-items: start;
}

.svc-list li::before {
    content: "✓";
    color: var(--accent);
    font-weight: 700;
}

/* Numbered stages. A list, not cards: these are sequential, and card borders would
   read as four separate offers rather than one process. */
.svc-steps {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: clamp(20px, 3vw, 32px);
    counter-reset: none;
}

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

@media (min-width: 1080px) {
    .svc-steps { grid-template-columns: repeat(4, 1fr); }
}

.svc-step {
    padding-top: 16px;
    border-top: 2px solid var(--accent);
}

.svc-step-num {
    display: block;
    font-family: var(--font-mono);
    font-size: var(--size-small);
    color: var(--muted);
    margin-bottom: 6px;
}

.svc-step h3 { margin: 0 0 6px; }

.svc-step p {
    margin: 0;
    color: var(--ink-soft);
    font-size: var(--size-small);
}

/* ─── FAQ ──────────────────────────────────────────────────────────────────────
   <details> so it works with no JS at all, which matters because the same answers
   are what FAQPage schema publishes.
   ──────────────────────────────────────────────────────────────────────────── */

.svc-faq {
    display: grid;
    gap: 10px;
    max-width: 72ch;
}

.svc-faq-item {
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    padding: 0;
}

.svc-faq-item summary {
    padding: 16px 18px;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    display: flex;
    justify-content: space-between;
    gap: 16px;
    align-items: center;
}

.svc-faq-item summary::-webkit-details-marker { display: none; }

/* The marker is drawn rather than typed so it can rotate on open. */
.svc-faq-item summary::after {
    content: "";
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    border-right: 2px solid var(--accent);
    border-bottom: 2px solid var(--accent);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform 0.18s ease;
}

.svc-faq-item[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }

.svc-faq-item summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
    border-radius: var(--radius-lg);
}

.svc-faq-item p {
    margin: 0;
    padding: 0 18px 18px;
    color: var(--ink-soft);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   CONTACT — the lead form. Extends the base .field styles above rather than
   restating them.
   ═══════════════════════════════════════════════════════════════════════════════ */

.contact-grid {
    display: grid;
    gap: clamp(32px, 5vw, 64px);
    align-items: start;
}

@media (min-width: 900px) {
    .contact-grid { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
}

.lead-form { max-width: 68ch; }

/* Two fields per row on anything wider than a phone. minmax(0,1fr) so a long value
   cannot push the column past its share. */
.field-row {
    display: grid;
    gap: 0 18px;
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 620px) {
    .field-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.field textarea { resize: vertical; min-height: 120px; }

/* "required" as a word, not an asterisk nobody has a legend for. */
.req {
    font-size: var(--size-eyebrow);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--accent);
    margin-left: 6px;
}

.field-hint {
    font-size: var(--size-small);
    color: var(--muted);
    margin: 4px 0 0;
}

/* Honeypot. Off-screen rather than display:none, because some bots skip anything
   that is not rendered. Never give this a tab stop or a real label position. */
.hp-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.form-note {
    padding: 16px 18px;
    border-radius: var(--radius-lg);
    border: 1px solid var(--line);
    margin-bottom: 24px;
}

.form-note--error {
    border-color: var(--danger);
    color: var(--danger);
    background: var(--surface-alt);
}

.form-note--ok {
    border-color: var(--accent);
    background: var(--surface-alt);
    padding: clamp(24px, 4vw, 40px);
    max-width: 64ch;
}

.form-note--ok h2 { margin-top: 0; }

.contact-aside {
    padding: clamp(20px, 3vw, 28px);
    background: var(--surface-alt);
    border: var(--f-border-width) solid var(--line);
    border-radius: var(--radius-lg);
}

.contact-aside h2 {
    font-size: var(--size-lead);
    margin: 0 0 18px;
}

.contact-aside-heading { margin-top: 28px; }

.contact-aside-note {
    margin: 20px 0 0;
    font-size: var(--size-small);
    color: var(--muted);
}

/* The aside is a narrow column, so its steps stack instead of running four across. */
.svc-steps--stack { grid-template-columns: minmax(0, 1fr); gap: 18px; }

@media (min-width: 720px) {
    .svc-steps--stack { grid-template-columns: minmax(0, 1fr); }
}

@media (min-width: 1080px) {
    .svc-steps--stack { grid-template-columns: minmax(0, 1fr); }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   LEGAL PAGES — privacy.php and terms.php.

   Long-form prose, so the measure is capped and the vertical rhythm is looser than
   a marketing section. Nothing here is themeable beyond the tokens, on purpose:
   a legal page should read the same in every palette.
   ═══════════════════════════════════════════════════════════════════════════════ */

.legal-prose {
    max-width: 72ch;
    color: var(--ink-soft);
}

.legal-prose h2 {
    font-size: var(--size-lead);
    margin: 40px 0 12px;
    color: var(--ink);
}

.legal-prose h2:first-child { margin-top: 0; }

.legal-prose p { margin: 0 0 16px; }

.legal-prose ul {
    margin: 0 0 16px;
    padding-left: 22px;
}

.legal-prose li { margin-bottom: 7px; }

.legal-updated {
    margin-top: 18px;
    font-size: var(--size-small);
    color: var(--muted);
}
