/* ============================================================
   PAUL WILSON — PORTFOLIO
   Base CSS | Typography-first
   Font:       Akzidenz Grotesk (Helvetica Neue fallback)
   Type scale: 1.5 ratio — base 16px
   ============================================================ */

/* ─── 1. Custom Properties ──────────────────────────────────── */

:root {
    /* Type scale — ratio 1.5, base 1rem (16px)
       step–1  = 16 ÷ 1.5       ≈  10.67px
       step 0  = 16              =  16px
       step 1  = 16 × 1.5       =  24px
       step 2  = 16 × 1.5²      =  36px
       step 3  = 16 × 1.5³      =  54px
       step 4  = 16 × 1.5⁴      =  81px
       step 5  = 16 × 1.5⁵      ≈ 121.5px
    ─────────────────────────────────────── */
    --step--1: 0.667rem;
    --step-0: 1rem;
    --step-1: 1.5rem;
    --step-2: 2.25rem;
    --step-3: 3.375rem;
    --step-4: 5.063rem;
    --step-5: 7.594rem;

    /* Colours — default palette: Ink. Repeated in [data-theme="ink"]
       so the ring can switch back to it explicitly. */
    --color-bg: #121417;
    --color-text: #E8E4DA;
    --color-text-bright: #E8E4DA;
    --color-text-muted: #a8a49a;
    --color-accent: #AFCDE9;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #E8E4DA 20%, #121417);

    /* Spacing — 8pt grid */
    --space-1: 0.5rem; /*   8px */
    --space-2: 1rem; /*  16px */
    --space-3: 1.5rem; /*  24px */
    --space-4: 2rem; /*  32px */
    --space-6: 3rem; /*  48px */
    --space-8: 4rem; /*  64px */
    --space-12: 6rem; /*  96px */
    --space-16: 8rem; /* 128px */
    --space-20: 10rem; /* 160px */

    /* Film grain strength — set to 0 to switch the overlay off */
    --grain-opacity: 0.06;

    /* How fast the whole page crossfades when the palette changes */
    --theme-speed: 400ms;

    /* Layout */
    --container-max: 1280px;
    --gutter: clamp(1.5rem, 5vw, 4rem);

    /* Typography — Adobe Fonts */
    --font-sans: "univers-next-pro", sans-serif;
    --font-serif: "le-monde-livre-std", serif;
    --font-mono: "SF Mono", "Fira Code", "Cascadia Code", monospace;
    --lh-tight: 1.08;
    --lh-snug: 1.3;
    --lh-normal: 1.5;
    --lh-loose: 1.75;
    --ls-tight: -0.02em;
    --ls-display: -0.03em;
    --ls-normal: 0;
    --ls-wide: 0.08em;
    --ls-wider: 0.12em;
}

/* ── Palettes ─────────────────────────────────────────────
   Seven themes, each supplying bg / fg / fg-sub / accent.
   Ink is the default and also lives in :root above. */

[data-theme="ink"] {
    --color-bg: #121417;
    --color-text: #E8E4DA;
    --color-text-bright: #E8E4DA;
    --color-text-muted: #a8a49a;
    --color-accent: #AFCDE9;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #E8E4DA 20%, #121417);
}

[data-theme="material"] {
    --color-bg: #16274A;
    --color-text: #7C9AD1;
    --color-text-bright: #7C9AD1;
    --color-text-muted: #8CA6D8;
    --color-accent: #FFFFFF;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #7C9AD1 20%, #16274A);
}

[data-theme="acid"] {
    --color-bg: #E9FA6B;
    --color-text: #121212;
    --color-text-bright: #121212;
    --color-text-muted: #2a2a1e;
    --color-accent: #8B5A9E;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #121212 20%, #E9FA6B);
}

[data-theme="ember"] {
    --color-bg: #141414;
    --color-text: #F26522;
    --color-text-bright: #F26522;
    --color-text-muted: #E8703A;
    --color-accent: #F26522;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #F26522 20%, #141414);
}

[data-theme="zurich"] {
    --color-bg: #B81C22;
    --color-text: #F2EFE6;
    --color-text-bright: #F2EFE6;
    --color-text-muted: #EADCD2;
    --color-accent: #000000;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #F2EFE6 20%, #B81C22);
}

[data-theme="rose"] {
    --color-bg: #F7E2EA;
    --color-text: #123D24;
    --color-text-bright: #123D24;
    --color-text-muted: #1B4D2E;
    --color-accent: #B81C22;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #123D24 20%, #F7E2EA);
}

[data-theme="twilight"] {
    --color-bg: #1C1140;
    --color-text: #F5C98A;
    --color-text-bright: #F5C98A;
    --color-text-muted: #D9A87A;
    --color-accent: #C25E7A;
    /* not supplied per palette — derived so hairlines and image
       placeholders sit just off the background in every theme */
    --color-border: color-mix(in srgb, #F5C98A 20%, #1C1140);
}

/* Every palette defines an accent, so accent hovers are universal */
.nav-links a:hover {
    color: var(--color-accent);
}

.project-card:hover .project-name {
    color: var(--color-accent);
}

/* ─── 2. Reset ──────────────────────────────────────────────── */

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

html {
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    scroll-behavior: smooth;
    hanging-punctuation: first last;
}

body {
    font-family: var(--font-serif);
    font-size: var(--step-0);
    line-height: var(--lh-normal);
    color: var(--color-text);
    background-color: var(--color-bg);
    cursor: crosshair;
    transition:
        background-color var(--theme-speed) ease,
        color var(--theme-speed) ease;
}

/* Theme-change colour transitions on elements that carry their own
   background / border / colour values (not inherited from body).
   Placed after the element rules below so it wins on the `transition`
   shorthand. */
.site-header,
.work-section,
.about-section,
.site-footer,
.project-item,
.hero-scroll,
.project-index,
.project-category,
.project-year,
.footer-copy,
.footer-tagline {
    transition:
        background-color var(--theme-speed) ease,
        color var(--theme-speed) ease,
        border-color var(--theme-speed) ease;
}

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

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

ul,
ol {
    list-style: none;
}

/* Pointer cursor on all clickable elements (body default is crosshair) */
a,
button,
.project-card,
.nav-logo,
.nav-links a,
.cta-link {
    cursor: pointer;
}

/* ─── 3. Layout ─────────────────────────────────────────────── */

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

/* ─── 4. Navigation ─────────────────────────────────────────── */

.site-header {
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: var(--color-bg);
    border-bottom: 1px solid transparent;
    padding-block: var(--space-3);
    transition: border-color 0.3s ease;
}

.nav {
    /* logo left, links right — the centre slot held the old switcher */
    display: flex;
    justify-content: space-between;
    align-items: center;
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.nav-logo {
    font-family: var(--font-sans);
    font-size: var(--step-0);
    font-weight: 500;
    letter-spacing: var(--ls-tight);
    line-height: 1;
    color: var(--color-accent, var(--color-text-muted));
    transition: color 0.2s ease;
}

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

.nav-links {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.nav-links a {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
    transition: color 0.2s ease;
}

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

/* ─── 5. Hero ───────────────────────────────────────────────── */

.hero {
    position: relative; /* anchor for absolute micrographics */
    min-height: calc(100svh - 5rem); /* subtract nav height */
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding-top: var(--space-12);
    padding-bottom: var(--space-12);
}

.hero .container {
    width: 100%;
    position: relative; /* anchor for §01 bottom-left mark */
}


/* Hero content uses a 4-col grid (index | title | meta | arrow) so the
   headline and subheadline align to a consistent left edge */
.hero-grid {
    display: grid;
    grid-template-columns: 3.5rem 1fr auto 2rem;
    column-gap: var(--space-4);
}

.hero-headline,
.hero-subheadline,
.hero-scroll,
.palette-panel {
    grid-column: 2 / -1;
}

/* Two-column hero from 1024px up: text left, palette panel right.
   Below that the panel drops under the text at full width. */
@media (min-width: 1024px) {
    .hero-headline    { grid-column: 2 / 3; grid-row: 1; }
    .hero-subheadline { grid-column: 2 / 3; grid-row: 2; }
    .hero-scroll      { grid-column: 2 / -1; grid-row: 3; }

    .palette-panel {
        grid-column: 3 / -1;
        grid-row: 1 / span 2;
        align-self: end;        /* sit on the subheadline's baseline */
        padding-left: var(--space-8);
    }
}

.hero-headline {
    font-family: var(--font-sans);
    font-size: clamp(var(--step-3), 7.5vw, var(--step-5));
    font-weight: 500;
    line-height: var(--lh-tight);
    letter-spacing: -0.045em;
    color: var(--color-text-bright);
}

.hero-subheadline {
    margin-top: var(--space-3);
    font-family: var(--font-serif);
    font-size: clamp(var(--step-1), 2.8vw, var(--step-2));
    font-weight: 400;
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-normal);
    color: var(--color-text-bright);
    /* no max-width — wraps at container right edge (arrow column end) */
}

.hero-scroll {
    margin-top: var(--space-12);
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/* ─── 6. Work / Project Masonry ─────────────────────────────── */

.work-section {
    padding-top: var(--space-2);
    padding-bottom: var(--space-16);
}

/* Masonry via CSS multicolumn — images keep native aspect ratios and
   flow top-to-bottom down each column. No JS, no crop. */
.project-list {
    column-count: 3;
    column-gap: var(--space-8);
}

.project-item {
    break-inside: avoid;         /* never split a card across columns */
    margin-bottom: var(--space-8);
}

.project-card {
    display: block;
}

/* Image */
.project-card-media {
    display: block;
    overflow: hidden;
    background-color: var(--color-border);
    transition: background-color var(--theme-speed) ease;
}

.project-card-media img {
    width: 100%;
    height: auto;
    display: block;
    transition: opacity var(--theme-speed) ease, transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Image quiets slightly on hover so the revealed caption reads clearly */
.project-card:hover .project-card-media img {
    opacity: 0.82;
    transform: scale(1.015);
}

/* Caption under the image: index always visible, rest revealed on hover.
   The reveal block keeps its layout space at all times (opacity only) so
   the masonry never reflows when text appears. */
.project-card-caption {
    display: grid;
    grid-template-columns: 2rem 1fr;
    column-gap: var(--space-3);
    align-items: baseline;
    padding-top: var(--space-3);
}

.project-index {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    font-weight: 400;
    color: var(--color-text-muted);
    letter-spacing: var(--ls-normal);
}

.project-reveal {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 250ms ease, transform 250ms ease;
}

.project-card:hover .project-reveal,
.project-card:focus-visible .project-reveal {
    opacity: 1;
    transform: none;
}

.project-name {
    font-family: var(--font-sans);
    font-size: var(--step-0);
    font-weight: 500;
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-display);
    color: var(--color-text);
    transition: color var(--theme-speed) ease;
}

.project-meta {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.project-category {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    color: var(--color-text-muted);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
}

.project-year {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    letter-spacing: var(--ls-normal);
    color: var(--color-text-muted);
}

/* Fewer columns as the viewport narrows */
@media (max-width: 1024px) {
    .project-list {
        column-count: 2;
    }
}

/* Touch devices can't hover — always show the caption, and drop the
   image dim/scale hover treatment. */
@media (hover: none) {
    .project-reveal {
        opacity: 1;
        transform: none;
    }
    .project-card:hover .project-card-media img {
        opacity: 1;
        transform: none;
    }
}

/* ─── 7. About Section ──────────────────────────────────────── */

.about-section {
    position: relative; /* anchor for absolute micrographics */
    padding-top: var(--space-20);
    padding-bottom: var(--space-20);
}

/* Large pull quote — full width */
.about-pull {
    margin-bottom: var(--space-12);
}

.about-pull p {
    font-size: clamp(var(--step-2), 3.5vw, var(--step-3));
    font-weight: 500;
    line-height: var(--lh-tight);
    letter-spacing: var(--ls-display);
    max-width: 22ch;
    color: var(--color-text-bright);
}

/* Body text + CTA — two column, offset right */
.about-body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-8);
    margin-left: auto;
    max-width: calc(var(--container-max) * 0.75);
}

.about-text {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.about-text p {
    font-size: var(--step-0);
    line-height: var(--lh-loose);
    color: var(--color-text);
}

.about-cta {
    display: flex;
    align-items: flex-start;
    justify-content: flex-end;
    padding-top: 0.3em;
}

/* Outlined button. Hover inverts to a solid fill, matching the
   tooltip treatment (text colour as ground, page colour as type). */
.cta-link {
    display: inline-block;
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--color-text);
    border: 1px solid var(--color-accent, var(--color-text));
    padding: 0.9em 1.75em;
    transition:
        background-color 0.2s ease,
        color 0.2s ease,
        border-color var(--theme-speed) ease;
}

.cta-link:hover {
    background-color: var(--color-accent, var(--color-text));
    color: var(--color-bg);
}

/* ── Palette panel (hero) ─────────────────────────────────
   Wrapper that puts the ring in the hero's right-hand column. */

.palette-panel {
    margin-top: var(--space-6);
    max-width: 320px;
}

.palette-title {
    display: block;
    font-family: var(--font-mono);
    font-size: 8px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    transition: color var(--theme-speed) ease;
}

.palette-note {
    margin-top: var(--space-1);
    font-family: var(--font-serif);
    font-size: var(--step--1);
    line-height: var(--lh-normal);
    color: var(--color-text-muted);
    transition: color var(--theme-speed) ease;
}


/* ─── 8. Theme ring ─────────────────────────────────────────── */
/*
   Seven palette dots: six on a circle, the active one in the middle
   showing its number. Clicking an outer dot swaps it with the centre —
   the two trade places along the same axis and cross mid-flight, so no
   slot is ever empty. Click commits; hovering never previews the page.
   Positions are set from JS (see script.js) into --x / --y.
*/

.ring {
    position: relative;
    width: 140px;
    height: 140px;
    margin-top: var(--space-3);
}

.ring-dot {
    position: absolute;
    left: var(--x, 50%);
    top: var(--y, 50%);
    width: 19px;
    height: 19px;
    padding: 0;
    border: none;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    background: var(--dot-bg);
    cursor: pointer;
    /* 1px ring in the page's text colour at half strength, so dark dots
       stay visible on dark backgrounds */
    box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 50%, transparent);
    transition:
        left 560ms cubic-bezier(.45, .05, .25, 1),
        top 560ms cubic-bezier(.45, .05, .25, 1),
        width 480ms cubic-bezier(.45, .05, .25, 1),
        height 480ms cubic-bezier(.45, .05, .25, 1),
        box-shadow 200ms ease;
}

.ring-dot:hover,
.ring-dot:focus-visible {
    box-shadow: 0 0 0 2px currentColor;
}

.ring-dot:focus-visible {
    outline: none;
}

/* The active palette, parked in the middle */
.ring-dot.is-center {
    width: 48px;
    height: 48px;
    box-shadow: 0 0 0 1.5px currentColor;
    cursor: default;
    z-index: 1;
}

/* Number reads on any fill by inverting whatever is behind it */
.ring-num {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-family: var(--font-mono);
    font-size: 9px;
    letter-spacing: 0.06em;
    color: #fff;
    mix-blend-mode: difference;
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms ease;
}

.ring-dot.is-center .ring-num {
    opacity: 1;
}

.ring-dot[data-theme-set="ink"],
.nav-dot[data-theme-set="ink"] { --dot-bg: #121417; }
.ring-dot[data-theme-set="material"],
.nav-dot[data-theme-set="material"] { --dot-bg: #16274A; }
.ring-dot[data-theme-set="acid"],
.nav-dot[data-theme-set="acid"] { --dot-bg: #E9FA6B; }
.ring-dot[data-theme-set="ember"],
.nav-dot[data-theme-set="ember"] { --dot-bg: #141414; }
.ring-dot[data-theme-set="zurich"],
.nav-dot[data-theme-set="zurich"] { --dot-bg: #B81C22; }
.ring-dot[data-theme-set="rose"],
.nav-dot[data-theme-set="rose"] { --dot-bg: #F7E2EA; }
.ring-dot[data-theme-set="twilight"],
.nav-dot[data-theme-set="twilight"] { --dot-bg: #1C1140; }

/* ── Nav dots (work pages) ────────────────────────────────
   The ring is ~140px tall, which would roughly double the nav height,
   so work pages get a flat row of the same seven swatches instead. */

.nav-dots {
    display: flex;
    align-items: center;
    gap: 6px;
}

.nav-dot {
    width: 12px;
    height: 12px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--dot-bg);
    cursor: pointer;
    box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 50%, transparent);
    transition:
        width 200ms ease,
        height 200ms ease,
        box-shadow 200ms ease;
}

.nav-dot:hover,
.nav-dot:focus-visible {
    box-shadow: 0 0 0 2px currentColor;
    outline: none;
}

.nav-dot[aria-checked="true"] {
    width: 16px;
    height: 16px;
    box-shadow: 0 0 0 1.5px currentColor;
    cursor: default;
}

/* Reduced motion: dots reposition and resize instantly; only the ring
   affordance still fades. Matches the scroll-reveal handling. */
@media (prefers-reduced-motion: reduce) {
    .ring-dot,
    .nav-dot {
        transition: box-shadow 200ms ease;
    }
    .ring-num {
        transition: none;
    }
}

@media (max-width: 560px) {
    .nav-dots {
        display: none;   /* no room beside the logo and links */
    }
}

/* ─── 8b. Grain overlay ─────────────────────────────────────────
   Fine film grain over every palette. Switch it off by setting
   --grain-opacity: 0 in :root (or deleting the .grain element). */

.grain {
    position: fixed;
    inset: 0;
    z-index: 300;
    pointer-events: none;
    opacity: var(--grain-opacity, 0.06);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* ─── 9. Footer ────────────────────────────────────────────── */

.site-footer {
    padding-block: var(--space-6);
}

.site-footer .container {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.footer-copy,
.footer-tagline {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    color: var(--color-text-muted);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
}

/* ─── 10. Responsive ────────────────────────────────────────── */

@media (max-width: 768px) {
    .hero-grid {
        grid-template-columns: 2.5rem 1fr 1.5rem;
    }

    .project-list {
        column-count: 1;
    }

    .about-body {
        grid-template-columns: 1fr;
        max-width: 100%;
        margin-left: 0;
    }

    .about-cta {
        padding-top: 0;
    }

    .site-footer .container {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-1);
    }
}

@media (max-width: 480px) {
    .nav-links {
        gap: var(--space-4);
    }

    .hero-scroll {
        margin-top: var(--space-8);
    }
}


/* ─── 11. Micrographics ─────────────────────────────────────── */

.micro {
    font-family: var(--font-mono);
    font-size: 8px;
    letter-spacing: 0.04em;
    line-height: 1.35;
    color: var(--color-text-muted);
    text-transform: uppercase;
    pointer-events: none;
}

/* Boot: typing-style reveal. All micros start hidden (opacity 0)
   and type in char-by-char via a clip-path sweep left → right.
   html.no-boot skips the animation for returning visitors. */

.micro {
    opacity: 0;
    animation: micro-type 400ms steps(24, end) both;
}

/* Non-text graphical marks just fade in (clip-path would hide them) */
.micro-reg,
.micro-about-folio {
    animation: micro-fade var(--theme-speed) ease-out both;
}

@keyframes micro-type {
    0%   { opacity: 1; clip-path: inset(0 100% 0 0); }
    100% { opacity: 1; clip-path: inset(0 0 0 0); }
}

@keyframes micro-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Boot groups — 200ms stagger */
.micro-coords,
.micro-section-00          { animation-delay:    0ms; }

.micro-reg,
.micro-dots-spec           { animation-delay:  200ms; }

.micro-crop                { animation-delay:  400ms; }

.micro-section-01,
.micro-cluster             { animation-delay:  600ms; }

.micro-about-coords,
.micro-about-folio,
.micro-about-projects      { animation-delay: 1000ms; }

.micro-about-specs,
.micro-about-route         { animation-delay: 1200ms; }

.micro-about-caps,
.micro-about-hcd,
.micro-about-status        { animation-delay: 1400ms; }

/* Skip boot entirely on subsequent visits (per-session flag) */
html.no-boot .micro {
    opacity: 1;
    animation: none;
    clip-path: none;
}

/* Top-left: coordinates */
.micro-coords {
    position: absolute;
    top: 1.25rem;
    left: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Registration mark: line · circle · line */
.micro-reg {
    position: absolute;
    top: 4.25rem;
    left: 1.75rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
}

.micro-reg .reg-line {
    width: 1px;
    height: 8px;
    background: var(--color-text-muted);
}

.micro-reg .reg-circle {
    width: 7px;
    height: 7px;
    border: 1px solid var(--color-text-muted);
    border-radius: 50%;
    animation: micro-rotate 20s linear infinite;
}

@keyframes micro-rotate {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* Top-right corner: §00 */
.micro-section-00 {
    position: absolute;
    top: 1.25rem;
    right: 1.5rem;
}

/* Top-right: three dots + R1.5 × B16 */
.micro-dots-spec {
    position: absolute;
    top: 4rem;
    right: 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 5px;
}

.micro-dots {
    display: flex;
    gap: 4px;
}

.micro-dots .dot {
    width: 3px;
    height: 3px;
    background: var(--color-text-muted);
    border-radius: 50%;
}

/* Crop mark at headline origin (inside hero-grid col 2) */
.micro-crop {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin-bottom: var(--space-1);
}

.micro-crop .crop-label {
    opacity: 0.7;
}

.micro-crop .crop-mark {
    display: block;
    width: 10px;
    height: 10px;
    border-top: 1px solid var(--color-text-muted);
    border-left: 1px solid var(--color-text-muted);
}

/* §01 — bottom-left at content column */
.micro-section-01 {
    position: absolute;
    bottom: 1.25rem;
    left: calc(var(--gutter) + 3.5rem + var(--space-4));
}

/* Bottom-right cluster: crosshair + spec lines */
.micro-cluster {
    position: absolute;
    bottom: 1.25rem;
    right: 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 3px;
}

.micro-cluster .crosshair {
    display: block;
    position: relative;
    width: 10px;
    height: 10px;
    margin-bottom: 3px;
}

.micro-cluster .crosshair::before,
.micro-cluster .crosshair::after {
    content: "";
    position: absolute;
    background: var(--color-text-muted);
}

.micro-cluster .crosshair::before {
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    transform: translateX(-50%);
}

.micro-cluster .crosshair::after {
    top: 50%;
    left: 0;
    right: 0;
    height: 1px;
    transform: translateY(-50%);
}


/* ─── About section micrographics ───────────────────────────── */

/* Top-left: Paris coordinates */
.micro-about-coords {
    position: absolute;
    top: 1.5rem;
    left: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Top-right: §02 folio */
.micro-about-folio {
    position: absolute;
    top: 1.5rem;
    right: 1.5rem;
}

/* Top-right (below §02): circled 9 + PROJECTS */
.micro-about-projects {
    position: absolute;
    top: 3.75rem;
    right: 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
}

.micro-about-projects .circled-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: 0.5px solid var(--color-text-muted);
    border-radius: 50%;
    font-size: 9px;
    line-height: 1;
}

.micro-about-projects .projects-label {
    font-size: 6px;
    letter-spacing: 0.08em;
}

/* Right side: serial / est / mfg stack */
.micro-about-specs {
    position: absolute;
    top: 9rem;
    right: 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 3px;
}

.micro-about-specs .spec-line {
    display: block;
}

/* Right side: vertical route */
.micro-about-route {
    position: absolute;
    top: 14rem;
    right: 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding-left: 8px;
    border-left: 0.5px solid var(--color-text-muted);
}

.micro-about-route .route-stack {
    display: flex;
    flex-direction: column;
    gap: 2px;
    line-height: 1.2;
}

.micro-about-route .route-label {
    opacity: 0.6;
    font-size: 6px;
    letter-spacing: 0.08em;
}

/* Bottom-left: capability rows */
.micro-about-caps {
    position: absolute;
    bottom: 3.5rem;
    left: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.micro-about-caps .cap-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.micro-about-caps .cap-sq {
    display: inline-block;
    width: 6px;
    height: 6px;
    border: 0.5px solid var(--color-text-muted);
}

.micro-about-caps .cap-sq.is-filled {
    background: var(--color-text-muted);
}

/* Bottom-left (below caps): HCD—AA */
.micro-about-hcd {
    position: absolute;
    bottom: 1.5rem;
    left: 1.5rem;
}

/* Bottom-right: current location status */
.micro-about-status {
    position: absolute;
    bottom: 1.5rem;
    right: 1.5rem;
}

/* Micros with data-note become hoverable (override base pointer-events) */
.micro [data-note],
.micro[data-note] {
    pointer-events: auto;
}


/* ─── Annotation tooltip (for micrographic notes) ───────────── */

.annotation-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    background: var(--color-text);
    color: var(--color-bg);
    font-family: var(--font-serif);
    font-style: italic;
    font-size: 11px;
    line-height: 1.3;
    padding: 6px 10px;
    border-radius: 3px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 150ms ease;
    z-index: 200;
    white-space: nowrap;
}

.annotation-tooltip.is-visible {
    opacity: 1;
}


/* ─── 12. Hover inspection tooltips ─────────────────────────── */

.hero-headline,
.hero-subheadline,
.hero-scroll,
.footer-copy,
.footer-tagline,
.about-pull p,
.about-text p {
    position: relative;
}

/* Vertical measurement line with step label on the left edge */
.hero-headline::before,
.hero-subheadline::before,
.hero-scroll::before,
.footer-copy::before,
.footer-tagline::before,
.about-pull p::before,
.about-text p::before {
    content: attr(data-step);
    position: absolute;
    left: -16px;
    top: 0;
    bottom: 0;
    padding: 4px 0 0 4px;
    border-left: 1px solid currentColor;
    font-family: var(--font-mono);
    font-size: 8px;
    letter-spacing: 0.08em;
    writing-mode: vertical-rl;
    text-orientation: mixed;
    color: var(--color-text);
    opacity: 0;
    transition: opacity 200ms ease;
    pointer-events: none;
    white-space: nowrap;
}

.hero-headline:hover::before,
.hero-subheadline:hover::before,
.hero-scroll:hover::before,
.footer-copy:hover::before,
.footer-tagline:hover::before,
.about-pull p:hover::before,
.about-text p:hover::before {
    opacity: 0.15;
}

/* Floating inspection tooltip (populated via JS) */
.inspect-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    background: var(--color-text);
    color: var(--color-bg);
    font-family: var(--font-mono);
    font-size: 8px;
    letter-spacing: 0.04em;
    line-height: 1.5;
    padding: 8px 10px;
    border-radius: 4px;
    pointer-events: none;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 200ms ease, transform 200ms ease;
    z-index: 200;
    white-space: nowrap;
}

.inspect-tooltip.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.inspect-tooltip .label {
    display: block;
    opacity: 0.5;
    text-transform: uppercase;
    margin-top: 4px;
}

.inspect-tooltip .label:first-child {
    margin-top: 0;
}

.inspect-tooltip .value {
    display: block;
    opacity: 1;
}

/* Connecting stem from tooltip down to target */
.inspect-tooltip::after {
    content: "";
    position: absolute;
    bottom: -10px;
    left: 50%;
    width: 1px;
    height: 10px;
    background: var(--color-text);
    transform: translateX(-50%);
}


/* ─── Hide micrographics + tooltips on mobile ───────────────── */

@media (max-width: 768px) {
    .about-section {
        position: static;
    }

    .micro,
    .inspect-tooltip,
    .annotation-tooltip {
        display: none;
    }

    .hero-headline::before,
    .hero-subheadline::before,
    .hero-scroll::before,
    .footer-copy::before,
    .footer-tagline::before,
    .about-pull p::before,
    .about-text p::before {
        display: none;
    }

    body {
        cursor: auto;
    }
}


/* ─── 13. Project / Case Study Pages ───────────────────────── */

/* ── Case Header ──────────────────────────────────────── */

.case-header {
    position: relative;
    padding-top: var(--space-12);
    padding-bottom: var(--space-8);
}

/* Same 4-col grid as the project list and hero */
.case-header-inner {
    display: grid;
    grid-template-columns: 3.5rem 1fr auto 2rem;
    column-gap: var(--space-4);
    align-items: end;
}

.case-number {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    color: var(--color-text-muted);
    padding-bottom: 0.35em; /* optical align with title baseline */
}

.case-title {
    font-family: var(--font-sans);
    font-size: clamp(var(--step-2), 5vw, var(--step-4));
    font-weight: 500;
    line-height: var(--lh-tight);
    letter-spacing: -0.04em;
    color: var(--color-text-bright);
}

.case-header-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.25rem;
    padding-bottom: 0.35em;
}

.case-header-category {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    color: var(--color-text-muted);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    white-space: nowrap;
}

.case-header-year {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    color: var(--color-text-muted);
}

.case-header-rule {
    border: none;
    border-top: 1px solid var(--color-border);
    margin-top: var(--space-8);
    transition: border-color var(--theme-speed) ease;
}

/* Micrographic: folio marker top-right (§XX) */
.micro-case-folio {
    position: absolute;
    top: 1.5rem;
    right: 1.5rem;
}

/* ── Context bar (Role · Scope · Year · Tools · Type) ─── */

.case-context {
    display: grid;
    grid-template-columns: 3.5rem 1fr;
    column-gap: var(--space-4);
    padding-bottom: var(--space-8);
    margin-bottom: var(--space-16);
    border-bottom: 1px solid var(--color-border);
    transition: border-color var(--theme-speed) ease;
}

.case-context-items {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8) var(--space-12);
}

.case-context-item {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* Small mono label — context-bar field labels and case-study section
   names share this exact treatment. */
.case-context-label,
.cs-block-name {
    font-family: var(--font-mono);
    font-size: 8px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.case-context-value {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--color-text);
}

/* ── Case body ────────────────────────────────────────── */

.case-body {
    padding-bottom: var(--space-20);
}

/* Large opener for a section (reused inside .cs-block-main) */
.case-lead {
    font-family: var(--font-serif);
    font-size: clamp(var(--step-1), 2.5vw, var(--step-2));
    font-weight: 400;
    line-height: var(--lh-snug);
    letter-spacing: -0.01em;
    color: var(--color-text-bright);
    max-width: 38ch;
}

/* Running body text */
.case-text {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    max-width: 60ch;
}

.case-text p {
    font-family: var(--font-serif);
    font-size: var(--step-0);
    line-height: var(--lh-loose);
    color: var(--color-text);
}

/* ── Project navigation (Prev / Back / Next) ──────────── */

.case-nav {
    border-top: 1px solid var(--color-border);
    padding-block: var(--space-8);
    transition: border-color var(--theme-speed) ease;
}

.case-nav-inner {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.case-nav-back,
.case-nav-prev,
.case-nav-next {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
    transition: color 0.2s ease;
}

.case-nav-back:hover,
.case-nav-prev:hover,
.case-nav-next:hover {
    color: var(--color-text);
}

.case-nav-side {
    display: flex;
    gap: var(--space-8);
}

/* ── Prev / Next hover peek ───────────────────────────────
   Shows where the link goes: thumbnail + index + name + meta,
   mirroring the home-page card. Floats above the link so it
   never shifts the footer. Desktop hover only. */

.case-nav-prev,
.case-nav-next {
    position: relative;
}

.case-nav-peek {
    position: absolute;
    bottom: calc(100% + var(--space-3));
    width: 260px;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--space-2);
    background-color: var(--color-bg);
    opacity: 0;
    transform: translateY(6px);
    pointer-events: none;
    transition:
        opacity 250ms ease,
        transform 250ms ease,
        background-color var(--theme-speed) ease;
    z-index: 10;
}

/* Both links sit in the right-hand cluster, so both panels hang from
   their link's right edge. Left-anchoring the prev panel pushed it past
   the viewport and produced a horizontal scrollbar. */
.case-nav-peek--prev,
.case-nav-peek--next {
    right: 0;
    text-align: right;
}

.case-nav-prev:hover .case-nav-peek,
.case-nav-next:hover .case-nav-peek,
.case-nav-prev:focus-visible .case-nav-peek,
.case-nav-next:focus-visible .case-nav-peek {
    opacity: 1;
    transform: none;
}

.case-nav-peek-media {
    display: block;
    margin-bottom: var(--space-1);
    background-color: var(--color-border);
    transition: background-color var(--theme-speed) ease;
}

.case-nav-peek-media img {
    width: 100%;
    height: 100%;
    aspect-ratio: 4 / 3;   /* uniform crop keeps both panels the same size */
    object-fit: cover;
    display: block;
}

.case-nav-peek-index {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    letter-spacing: var(--ls-normal);
    text-transform: none;
    color: var(--color-text-muted);
}

.case-nav-peek-name {
    font-family: var(--font-sans);
    font-size: var(--step-0);
    font-weight: 500;
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-display);
    text-transform: none;
    /* third palette colour (fg-sub), not the second (fg) */
    color: var(--color-text-muted);
}

.case-nav-peek-meta {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.case-nav-peek-category {
    font-family: var(--font-sans);
    font-size: var(--step--1);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.case-nav-peek-year {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    letter-spacing: var(--ls-normal);
    text-transform: none;
    color: var(--color-text-muted);
}

/* Touch devices can't hover, and narrow windows have no room for the
   panel — the plain labels stand on their own in both cases. */
@media (hover: none), (max-width: 768px) {
    .case-nav-peek { display: none; }
}

/* ── Project page responsive ──────────────────────────── */

@media (max-width: 768px) {
    .case-header-inner {
        grid-template-columns: 2.5rem 1fr 1.5rem;
    }

    /* Hide meta on small screens (same as project list) */
    .case-header-meta {
        display: none;
    }

    .case-context {
        grid-template-columns: 1fr;
    }

    .case-nav-side {
        gap: var(--space-4);
    }
}


/* ─── 14. Scroll motion ─────────────────────────────────────── */

/* Hidden start-state scoped to html.js so the page still shows
   everything if JS never runs. Subtle fade + rise on enter. */
html.js [data-reveal] {
    opacity: 0;
    transform: translateY(20px);
    transition:
        opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
        transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
    will-change: opacity, transform;
}

html.js [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    html.js [data-reveal] {
        opacity: 1;
        transform: none;
        transition: none;
    }
}


/* ─── 15. Case study — layout v2 (.cs-*) ────────────────────── */
/*
   Structure: header + context reuse §14 classes. The BODY is a
   vertical sequence of two things:
     • .cs-block — a titled text section; its label pins (sticky)
       while the prose scrolls. Constrained, readable.
     • full-width media bands (.cs-cover / .cs-full) — images
       breathe edge-to-edge for impact.
   Text is for reading, imagery is for looking; they don't fight
   for the same column.
*/

/* Full-bleed cover directly under the header rule */
.cs-cover {
    margin-bottom: var(--space-16);
    background-color: var(--color-border);
    transition: background-color var(--theme-speed) ease;
}

.cs-cover img {
    width: 100%;
    height: clamp(320px, 62vh, 760px);
    object-fit: cover;
    display: block;
}

.cs-body {
    padding-bottom: var(--space-20);
}

/* A titled section: sticky label column + body column */
.cs-block {
    display: grid;
    grid-template-columns: 12rem 1fr;
    column-gap: var(--space-8);
    padding-top: var(--space-16);
}

.cs-block-head {
    position: sticky;
    top: var(--space-8);
    align-self: start;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.cs-block-num {
    font-family: var(--font-serif);
    font-size: var(--step-1);
    line-height: 1;
    color: var(--color-text-muted);
}

/* .cs-block-name is grouped with .case-context-label in §13 */

.cs-block-main {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    min-width: 0; /* let images shrink inside the grid cell */
}

/* Figures inside a block (≈ body width — the readable measure) */
.cs-figure {
    margin: 0;
}

.cs-figure img {
    width: 100%;
    height: auto;
    display: block;
    background-color: var(--color-border);
    transition: background-color var(--theme-speed) ease;
}

/* Wide figure — breaks left across the label column to fill the full
   content width (bigger; used for diagrams, mockups, screens). The
   label still pins beside the text above it. */
.cs-figure--wide {
    margin-left: calc(-1 * (12rem + var(--space-8)));
}

/* Edge-to-edge figure, for dense artefacts (affinity diagrams, journey
   maps) that need every pixel of width to stay legible. Reclaims the
   label column AND the container gutters, so it spans the full container
   width (= the viewport below 1280px).
   Deliberately avoids the `50% - 50vw` bleed trick: this figure's
   containing block is an offset grid cell, not a viewport-centred box,
   so that form mis-computes and produces a horizontal scrollbar. */
.cs-figure--bleed {
    margin-left: calc(-1 * (12rem + var(--space-8) + var(--gutter)));
    margin-right: calc(-1 * var(--gutter));
}

/* Photo that shouldn't dominate — caps height so it stays a detail,
   not a poster. */
.cs-figure--cap img {
    max-height: 520px;
    object-fit: cover;
}

/* Flush stack — consecutive figures that should read as one continuous
   sequence, with no vertical gap between them. The stack itself is a
   single flex child, so .cs-block-main's normal gap still applies above
   and below the group. */
.cs-stack {
    display: flex;
    flex-direction: column;
}

/* Aligned image pair — tops line up, equal columns */
.cs-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
    align-items: start;
}

/* Inline video — centered at phone width. The container is slightly
   narrower than the source and biases left via object-position, which
   crops a watermark off the right edge. */
.cs-video {
    max-width: 380px;
    margin-inline: auto;
    aspect-ratio: 60 / 96;      /* narrower than the 3:4 source → crops the sides */
    overflow: hidden;
    background-color: #000814;
}

.cs-video video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 0% center; /* left-aligned → the rotato watermark on the right is cropped off */
    display: block;
}

/* Full-bleed media band between blocks (sibling of .container) */
.cs-full {
    margin-block: var(--space-12);
    background-color: var(--color-border);
    transition: background-color var(--theme-speed) ease;
}

/* Full-bleed media (case-study .cs-full and visual .vis-band) */
.cs-full img,
.vis-band img {
    width: 100%;
    height: auto;
    display: block;
}

/* Height-capped band, for source art on a tall canvas that is mostly empty
   (e.g. a statement centred in a portrait frame). Without this the empty
   margins of the artwork itself read as a void. Crops equally top/bottom,
   so centred content stays centred.
   NB: must come after `.cs-full img` above — equal specificity, later wins. */
.cs-full--cap {
    max-height: 62vh;
    overflow: hidden;
}

.cs-full--cap img {
    height: 62vh;
    object-fit: cover;
    object-position: center;
}

/* Small mono caption under a figure, full-bleed band, or gallery item.
   Shared by case-study (.cs-caption) and visual (.vis-caption) pages. */
.cs-full + .cs-caption,
.cs-caption,
.vis-caption {
    font-family: var(--font-mono);
    font-size: 8px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-top: var(--space-2);
}


/* ─── 16. Visual / gallery — layout (.vis-*) ────────────────── */
/*
   Image-forward. A short opening statement, then a rhythm of
   full-bleed bands and tidy uniform-crop grids. Minimal text.
*/

.vis-body {
    padding-bottom: var(--space-20);
}

/* Opening statement */
.vis-intro {
    padding-top: var(--space-12);
    padding-bottom: var(--space-16);
}

/* A .vis-intro inside .vis-body is a CLOSING statement (the opening one
   sits above .vis-body). It needs air above it to read as a close rather
   than another caption, and must not double up on the body's own bottom
   padding — that combination was leaving it tight above and floating below. */
.vis-body .vis-intro {
    padding-top: var(--space-16);
    padding-bottom: 0;
}

.vis-statement {
    font-family: var(--font-serif);
    font-size: clamp(var(--step-2), 3.5vw, var(--step-3));
    font-weight: 500;
    line-height: var(--lh-tight);
    letter-spacing: var(--ls-display);
    max-width: 20ch;
    color: var(--color-text-bright);
}

.vis-note {
    font-family: var(--font-serif);
    font-size: var(--step-0);
    line-height: var(--lh-loose);
    color: var(--color-text);
    max-width: 46ch;
    margin-top: var(--space-6);
}

/* Full-bleed band */
.vis-band {
    margin-block: var(--space-8);
    background-color: var(--color-border);
    transition: background-color var(--theme-speed) ease;
}

/* .vis-band img is grouped with .cs-full img in §15 */

/* Uniform-crop gallery grid (applications, social, product shots) */
.vis-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
    margin-block: var(--space-8);
}

.vis-grid--3 {
    grid-template-columns: repeat(3, 1fr);
}

.vis-grid img {
    width: 100%;
    height: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    display: block;
    background-color: var(--color-border);
}

/* Whole grid keeps native proportions — for product renders / boxes /
   bottles where a square crop would cut the packaging off. Items in a
   group share a ratio, so rows still line up. */
.vis-grid--natural {
    align-items: start;
}

.vis-grid--natural img {
    aspect-ratio: auto;
    height: auto;
}

/* .vis-caption is grouped with .cs-caption in §15 */

/* Masonry — for dense sets of mixed-ratio images (e.g. book spreads).
   Preserves native proportions with no uneven-row gaps. */
.vis-masonry {
    column-count: 2;
    column-gap: var(--space-4);
    margin-block: var(--space-8);
}

.vis-masonry img {
    width: 100%;
    height: auto;
    display: block;
    margin-bottom: var(--space-4);
    break-inside: avoid;
    background-color: var(--color-border);
}

/* Group label for collection pages (sub-project heading) */
.vis-group {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    margin-top: var(--space-12);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
    transition: border-color var(--theme-speed) ease;
}

.vis-group-num {
    font-family: var(--font-serif);
    font-size: var(--step--1);
    color: var(--color-text-muted);
}

.vis-group-name {
    font-family: var(--font-sans);
    font-size: var(--step-0);
    font-weight: 500;
    letter-spacing: var(--ls-display);
    color: var(--color-text);
}


/* ─── 17. Project page v2 — responsive ──────────────────────── */

@media (max-width: 768px) {
    .cs-block {
        grid-template-columns: 1fr;
        row-gap: var(--space-6);
        padding-top: var(--space-12);
    }

    /* Label sits inline, not pinned, on mobile */
    .cs-block-head {
        position: static;
        flex-direction: row;
        align-items: baseline;
        gap: var(--space-3);
    }

    .cs-block-num {
        font-size: var(--step-0);
    }

    .cs-pair,
    .vis-grid,
    .vis-grid--3 {
        grid-template-columns: 1fr;
    }

    .vis-masonry {
        column-count: 1;
    }

    /* No label column to reclaim on mobile — it is already collapsed, so
       the desktop negative margin would drag the figure off-screen left. */
    .cs-figure--wide {
        margin-left: 0;
    }

    /* Bleed still reaches the gutters, just not the (absent) label column */
    .cs-figure--bleed {
        margin-left: calc(-1 * var(--gutter));
    }

    .cs-cover img {
        height: clamp(240px, 50vh, 420px);
    }

    .cs-full {
        margin-block: var(--space-8);
    }
}
