/* ==========================================================================
   Sociologix v2.1 — refined
   Charcoal foundation + single turquoise pop. Robotic display type.
   ========================================================================== */

:root {
    /* ── Charcoal foundation ──────────────────────────── */
    --bg-deepest: #14141a;
    --bg-base:    #1a1a22;
    --bg-elev-1:  #22232c;
    --bg-elev-2:  #2b2c36;
    --bg-elev-3:  #353742;

    /* Section accents — Subfield brand palette (peach → coral → red → magenta → purple) */
    --subfield-1: #EB5750;          /* primary red-coral */
    --subfield-2: #B51658;          /* magenta */
    --subfield-3: #F57F59;          /* coral */
    --subfield-4: #F8DCC7;          /* peach */
    --subfield-5: #281436;          /* deep purple */
    --subfield-soft: rgba(235, 87, 80, 0.10);
    --subfield-glow: rgba(235, 87, 80, 0.30);

    --num-accent: #d65a5a;  /* slight red umbrella */
    --num-soft:   rgba(214, 90, 90, 0.10);
    --num-glow:   rgba(214, 90, 90, 0.28);

    /* ── Borders ──────────────────────────────────────── */
    --border:        rgba(255, 255, 255, 0.06);
    --border-strong: rgba(255, 255, 255, 0.14);
    --border-accent: rgba(93, 219, 232, 0.22);

    /* ── Text (warm bone) ─────────────────────────────── */
    --text:        #ebe8e0;
    --text-muted:  #8e8c87;
    --text-faint:  #565551;

    /* ── Accent — turquoise reflection (v1 signature) ─── */
    --accent:        #5ddbe8;
    --accent-hover:  #8feaf3;
    --accent-soft:   rgba(93, 219, 232, 0.10);
    --accent-glow:   rgba(93, 219, 232, 0.35);
    --accent-deep:   #2da6b3;

    /* Secondary signal — warm amber (replaces mint) */
    --warm:        #e8a96a;
    --warm-soft:   rgba(232, 169, 106, 0.12);

    /* ── Type ─────────────────────────────────────────── */
    --font-display: 'Space Grotesk', 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    --font-body:    'IBM Plex Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    --font-serif:   'Fraunces', 'Iowan Old Style', Georgia, serif;
    --font-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

    /* ── Spacing ──────────────────────────────────────── */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 1rem;
    --space-4: 1.5rem;
    --space-5: 2rem;
    --space-6: 3rem;
    --space-7: 5rem;
    --space-8: 8rem;

    /* ── Radii ────────────────────────────────────────── */
    --radius-sm: 4px;
    --radius:    8px;
    --radius-lg: 14px;
    --radius-xl: 20px;

    /* ── Layout ───────────────────────────────────────── */
    /* 1320px cap is a deliberate choice — keeps prose lines under the
       readability ceiling (~80ch for body, the section-sub overrides
       to that explicitly). Ultra-wide viewports (4K, 21:9) accept
       large symmetric gutters; the log-rail decoration fills some of
       the left gutter. Tried 1480 briefly; right gutter felt too tight
       on common 1920p screens for a body-text-heavy site. */
    --content: min(1320px, calc(100vw - 3rem));
    --content-narrow: min(860px, calc(100vw - 3rem));

    /* ── Motion ───────────────────────────────────────── */
    --ease:     cubic-bezier(0.2, 0.8, 0.2, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --t-fast:   160ms;
    --t-base:   280ms;
    --t-slow:   520ms;

    /* ── Effects ──────────────────────────────────────── */
    --shadow-card:  0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 32px rgba(0, 0, 0, 0.25);
    --shadow-lift:  0 4px 8px rgba(0, 0, 0, 0.4), 0 24px 60px rgba(0, 0, 0, 0.5);
    --shadow-glow:  0 0 0 1px var(--border-accent), 0 24px 60px rgba(0, 0, 0, 0.5), 0 0 40px var(--accent-glow);
}

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

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

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

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

body {
    margin: 0;
    font-family: var(--font-body);
    font-weight: 400;
    line-height: 1.55;
    color: var(--text);
    background: var(--bg-base);
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}
/* Body scroll lock when a modal is open — JS toggles .modal-open
   on body when openInfoModal / openSupportModal fires. Stops the
   page behind the modal from scrolling under the user's wheel /
   touch / keyboard. */
body.modal-open {
    overflow: hidden;
}

/* Body::before — faint horizontal scanline pattern across the whole
   viewport for a 90s CRT/terminal feel. Pure texture, sits behind
   foreground content. Pointer-events disabled. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background-image: repeating-linear-gradient(
        0deg,
        transparent 0px,
        transparent 11px,
        rgba(255, 255, 255, 0.006) 11px,
        rgba(255, 255, 255, 0.006) 12px
    );
}

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

a {
    color: var(--accent);
    text-decoration: none;
    transition: color var(--t-fast) var(--ease);
}
a:hover { color: var(--accent-hover); }

button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
    padding: 0;
}

ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0; }
em, i { font-style: italic; }

::selection {
    background: var(--accent);
    color: var(--bg-deepest);
}

.skip-link {
    position: absolute;
    top: -100px;
    left: var(--space-3);
    z-index: 1000;
    background: var(--accent);
    color: var(--bg-deepest);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font-weight: 600;
    transition: top var(--t-fast) var(--ease);
}
.skip-link:focus { top: var(--space-3); }

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

.site-header {
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) var(--space-5);
    background: color-mix(in oklab, var(--bg-deepest) 70%, transparent);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid transparent;
    transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.site-header[data-scrolled="true"] {
    background: color-mix(in oklab, var(--bg-deepest) 92%, transparent);
    border-bottom-color: var(--border);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--text);
    font-weight: 500;
    letter-spacing: -0.005em;
}
.brand-mark {
    width: 30px;
    height: 30px;
    color: var(--accent);
    transition: transform var(--t-base) var(--ease);
}
.brand:hover .brand-mark { transform: rotate(45deg) scale(1.05); }
.brand-name {
    font-family: var(--font-mono);
    font-size: 0.98rem;       /* slight bump-down — mono runs wider per glyph */
    font-weight: 500;
    letter-spacing: 0.01em;   /* mono prefers a touch of positive tracking */
}

.primary-nav ul {
    display: flex;
    gap: var(--space-5);
    align-items: center;
}
.primary-nav a {
    color: var(--text-muted);
    font-family: var(--font-display);
    font-size: 0.86rem;
    font-weight: 500;
    letter-spacing: 0.01em;
    position: relative;
    padding: var(--space-1) 0;
}
.primary-nav a::after {
    content: '';
    position: absolute;
    inset: auto 0 -3px 0;
    height: 1px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--t-base) var(--ease);
}
.primary-nav a:hover { color: var(--text); }
.primary-nav a:hover::after,
.primary-nav a[aria-current="true"]::after { transform: scaleX(1); }
.primary-nav a[aria-current="true"] { color: var(--text); }

.primary-nav .hire-link {
    color: var(--accent);
    border: 1px solid var(--border-accent);
    padding: var(--space-2) var(--space-3);
    border-radius: 999px;
    transition: all var(--t-base) var(--ease);
}
.primary-nav .hire-link::after { display: none; }
.primary-nav .hire-link:hover {
    background: var(--accent);
    color: var(--bg-deepest);
    border-color: var(--accent);
}

.nav-toggle {
    display: none;
    width: 40px;
    height: 40px;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
}

/* Header chat trigger — mobile-only entry point to patrick.exe.
   Lives between the brand and the hamburger; styled to match the
   floating ask-patrick chip but flat (no shadow/blur) for the bar. */
.header-chat-btn {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    margin-left: auto;
    margin-right: var(--space-2);
    background: color-mix(in oklab, var(--accent) 8%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent);
    border-radius: 999px;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.74rem;
    cursor: pointer;
    transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.header-chat-btn:hover,
.header-chat-btn:focus-visible {
    background: color-mix(in oklab, var(--accent) 18%, transparent);
    border-color: var(--accent);
}
.header-chat-icon {
    color: var(--accent);
    font-weight: 600;
    letter-spacing: -0.04em;
}

/* Tiny support mug in the header chrome — fallback discoverability
   layer for mobile (where the section-dots rail is hidden). On
   desktop, the mug lives at the bottom of the section-dots instead;
   this button is hidden. Amber to fit the support identity, opens
   the support modal on click. */
.header-support-btn {
    /* SVG carries its own colors (Ko-fi mark — light grey outline,
       teal heart). Pill is neutral teal-tinted to harmonize with
       the section-dot mug story. */
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-left: auto;
    margin-right: var(--space-2);
    padding: 0;
    background: color-mix(in oklab, var(--accent) 6%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 28%, transparent);
    border-radius: 999px;
    cursor: pointer;
    transition: background var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                transform var(--t-base) var(--ease);
}
.header-support-btn:hover,
.header-support-btn:focus-visible {
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    border-color: var(--accent);
    transform: translateY(-1px);
    outline: none;
}
.header-support-btn svg {
    display: block;
}

/* Mobile-nav drawer "Support" entry — sits under Hire. Teal to match
   the section-dot mug heart and the chrome mug. */
.mobile-nav .support-link {
    color: var(--accent);
}
.nav-toggle span {
    display: block;
    width: 22px;
    height: 1.5px;
    background: var(--text);
    transition: transform var(--t-base) var(--ease), opacity var(--t-fast);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

.mobile-nav {
    position: fixed;
    inset: 64px 0 0 0;
    z-index: 99;
    background: color-mix(in oklab, var(--bg-deepest) 96%, transparent);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    padding: var(--space-5);
    transform: translateY(-12px);
    opacity: 0;
    pointer-events: none;
    transition: transform var(--t-base) var(--ease), opacity var(--t-base) var(--ease);
}
.mobile-nav[data-open="true"] {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
}
.mobile-nav nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.mobile-nav a {
    font-family: var(--font-display);
    font-size: 1.4rem;
    font-weight: 500;
    color: var(--text);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border);
    letter-spacing: -0.01em;
}
.mobile-nav .hire-link { color: var(--accent); }

/* ──────────────────────────────────────────────────────
   Section dots (right rail)
   ────────────────────────────────────────────────────── */

.section-dots {
    position: fixed;
    right: var(--space-3);
    top: 50%;
    transform: translateY(-50%);
    z-index: 90;
    display: flex;
    flex-direction: column;
    align-items: flex-end;       /* anchor every row to the right */
    gap: var(--space-3);
    padding: var(--space-3);
    opacity: 1;
    pointer-events: auto;
    transition: opacity var(--t-base) var(--ease);
}
.section-dots[data-visible="true"] {
    opacity: 1;
    pointer-events: auto;
}
.section-dots a {
    display: flex;
    flex-direction: row-reverse;  /* DOM is span,em → reverse renders em (label) first, span (dot) last on the right */
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-2);
    color: var(--text-muted);
    position: relative;
    padding: 4px 0;
    align-self: flex-end;         /* row's right-edge stays put as label grows leftward */
}
.section-dots span {
    display: block;
    position: relative;
    width: 10px;
    height: 10px;
    border: 1.5px solid #b9c0cb;     /* light grey — echoes mug outline */
    border-radius: 50%;
    background: transparent;
    transition: all var(--t-base) var(--ease);
    flex-shrink: 0;
}
/* Tiny teal core — echoes the mug's teal heart, gives every dot
   a faint inner glow that ties the rail together as one family.
   Hidden when the dot is active (the section colour fill takes over). */
.section-dots a span::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: var(--accent);
    opacity: 0.55;
    transform: translate(-50%, -50%);
    transition: opacity var(--t-base) var(--ease),
                background var(--t-base) var(--ease),
                transform var(--t-base) var(--ease);
}
.section-dots a:hover span::before {
    background: var(--warm);          /* amber on hover */
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.3);
}
.section-dots a[data-active="true"] span::before {
    opacity: 0;                        /* let the active fill carry the colour */
}
.section-dots em {
    font-style: normal;
    font-size: 0.72rem;
    font-family: var(--font-mono);
    letter-spacing: 0.06em;
    color: var(--text-muted);
    opacity: 0;
    transform: translateX(6px);
    transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
    pointer-events: none;
    text-transform: uppercase;
    text-align: right;
    white-space: nowrap;
    background: color-mix(in oklab, var(--bg-deepest) 80%, transparent);
    border: 1px solid var(--border);
    padding: 3px 8px;
    border-radius: 4px;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}
.section-dots:hover em,
.section-dots a[data-active="true"] em {
    opacity: 1;
    transform: translateX(0);
}
/* Per-section dot tinting — each link inherits its section's primary
   colour, so the dot and label tint match the section underneath when
   it's the active one. Sections without a custom palette fall back to
   the global cyan accent. */
.section-dots a { --dot-color: var(--accent); }
.section-dots a[data-dot="subfield"] { --dot-color: var(--subfield-1); }
.section-dots a[data-dot="num"]      { --dot-color: var(--num-accent); }

.section-dots a:hover span {
    border-color: var(--warm);  /* amber border on hover — third colour in the trio */
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--warm) 12%, transparent);
}
.section-dots a[data-active="true"] span {
    background: var(--dot-color);
    border-color: var(--dot-color);
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--dot-color) 22%, transparent);
}
.section-dots a[data-active="true"] em {
    color: var(--dot-color);
    border-color: color-mix(in oklab, var(--dot-color) 32%, transparent);
}

/* Support-me entry — sits at the bottom of the dots rail, end-of-list
   matches the bottom-of-page position of the Hire/Support section.
   Click smooth-scrolls to #hire AND opens the support modal.
   SVG renders directly in the flex flow at 32px (no wrapper — that
   was constraining the SVG's intrinsic size). Mug is wider than the
   dots above; negative margin-right pulls the row right so the mug's
   visual center sits slightly off the dot column line, keeping the
   label closer to where the dot-row labels land. */
.section-dots a.support-dot {
    --dot-color: var(--warm);
    color: var(--warm);
    margin-top: 4px;
    margin-right: -12px;
}
.section-dots a.support-dot .support-dot-mug {
    width: 32px;
    height: 26px;
    flex-shrink: 0;
    transition: transform var(--t-base) var(--ease);
}
.section-dots a.support-dot:hover .support-dot-mug {
    transform: scale(1.12);
}
@media (prefers-reduced-motion: reduce) {
    .section-dots a.support-dot .support-dot-mug { transition: none; }
}

/* ──────────────────────────────────────────────────────
   Snap-scroll mode
   ────────────────────────────────────────────────────── */

html[data-snap="true"] {
    scroll-snap-type: y mandatory;
}
html[data-snap="true"] body {
    scroll-snap-type: y mandatory;
}
html[data-snap="true"] .section,
html[data-snap="true"] .hero {
    scroll-snap-align: start;
    scroll-snap-stop: always;
}
/* Snap mode: every section fills at least the viewport so the next
   section never bleeds in. Content top-anchors (header sits near the
   reserved top zone, bento/grid grows downward into remaining space).
   Sections whose content is naturally short can opt back into vertical
   centering with .section--center if needed. */
html[data-snap="true"] .section {
    min-height: 100vh;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    box-sizing: border-box;
    /* Reserve space top and bottom for the fixed UI rails — header
       at top, sub-view + ask-patrick chips at bottom. Section
       content (bento, copy, etc.) must stay above the bottom
       reserved zone in every section, not just visuals. */
    padding-top:    clamp(80px, 11vh, 130px);
    padding-bottom: clamp(110px, 14vh, 160px);
}
html[data-snap="true"] .section.section--center {
    justify-content: center;
}

/* Snap-mode disable for narrow OR short viewports.
   Short height: 1080p / older laptops where snap forces section to
   100vh and content gets clipped.
   Narrow width: tablet portrait (e.g. iPad Mini 744×1133). At 2-col
   layout the snap-mode 4-row grid squashes the medium tiles to ~150px
   tall — combo and relational-academia tiles barely show content.
   Either condition: drop snap entirely, let sections grow naturally.
   Hero stays 100vh (it's a poster, no overflow). */
@media (max-height: 900px), (max-width: 1080px) {
    html[data-snap="true"],
    html[data-snap="true"] body {
        scroll-snap-type: none;
    }
    html[data-snap="true"] .section,
    html[data-snap="true"] .hero {
        scroll-snap-align: none;
        scroll-snap-stop: normal;
    }
    html[data-snap="true"] .section {
        min-height: auto;
        padding-top:    clamp(56px, 7vh, 88px);
        padding-bottom: clamp(72px, 9vh, 110px);
    }
    /* Snap-mode side-rail dots are a navigation aid for snap users.
       With snap disabled on short viewports, the top nav bar covers
       the same job — hide the side rail to declutter. */
    .section-dots {
        display: none !important;
    }
    .hero-title {
        font-size: clamp(2.6rem, 5.5vw, 4.2rem);
    }
    .hero-tagline {
        font-size: clamp(1.1rem, 1.8vw, 1.4rem);
    }
    .section-title {
        font-size: clamp(2rem, 4.5vw, 3.2rem);
    }
}

/* Big-screen complement to the rule above: when snap mode IS active
   (tall + wide viewport), the side-rail dots cover section navigation.
   The primary-nav links in the header are redundant in that mode —
   hide them. Brand logo and ask-patrick button stay (separate jobs). */
@media (min-width: 901px) and (min-height: 901px) {
    .primary-nav {
        display: none;
    }
}

/* ──────────────────────────────────────────────────────
   Sections
   ────────────────────────────────────────────────────── */

.section {
    width: var(--content);
    margin: 0 auto;
    padding: var(--space-8) 0;
    scroll-margin-top: 80px;
    position: relative;
}

.section-header {
    /* All section headers span the full content width so they line up
       visually with the multi-column grids beneath them. The sub-text
       inside still gets its own narrower reading max-width below. */
    max-width: var(--content);
    margin: 0 auto var(--space-7);
    text-align: left;
}

.section-num {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--accent);
    letter-spacing: 0.18em;
    margin-bottom: var(--space-3);
    padding: 0;
    text-transform: uppercase;
}
.section-num::before {
    content: '';
    display: inline-block;
    width: 28px;
    height: 1px;
    background: currentColor;
}

/* Subfield section uses the Subfield brand palette */
.subfield-feature .section-num { color: var(--subfield-2); }
.subfield-feature .section-title em {
    background: linear-gradient(95deg, var(--subfield-3) 0%, var(--subfield-1) 45%, var(--subfield-2) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
.subfield-feature .subfield-stat strong { color: var(--subfield-1); }
.subfield-feature .subfield-stat strong sup { color: var(--subfield-2); }
/* Subfield CTA — bracketed mono ghost button that fits the
   terminal-tab aesthetic of the redesigned section. Less visually
   loud than the old filled-purple pill; reads as a cmd line that
   happens to be clickable. */
.subfield-feature .subfield-cta {
    padding: 7px 14px;
    background: transparent;
    border: 1px solid color-mix(in oklab, var(--subfield-1) 50%, transparent);
    color: var(--subfield-1);
    font-family: var(--font-mono);
    font-size: 0.82rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    border-radius: 6px;
    box-shadow: none;
    gap: 6px;
}
.subfield-feature .subfield-cta:hover {
    background: color-mix(in oklab, var(--subfield-1) 14%, transparent);
    border-color: var(--subfield-1);
    color: #fff;
    box-shadow: 0 4px 16px var(--subfield-glow);
    transform: none;
}
.subfield-cta-bracket {
    color: color-mix(in oklab, var(--subfield-1) 70%, transparent);
    font-weight: 400;
}
.subfield-feature .subfield-cta:hover .subfield-cta-bracket {
    color: var(--subfield-1);
}
.subfield-cta-cmd {
    color: var(--text-faint);
    font-weight: 400;
}
.subfield-feature .subfield-cta:hover .subfield-cta-cmd {
    color: var(--text-muted);
}
.subfield-cta-target {
    font-weight: 600;
    letter-spacing: 0.02em;
}
.subfield-cta-arrow {
    font-size: 0.92em;
    line-height: 1;
    transition: transform var(--t-base) var(--ease);
    color: var(--warm);
}
.subfield-feature .subfield-cta:hover .subfield-cta-arrow {
    transform: translate(2px, -2px);
}
.subfield-feature .subfield-visual {
    /* Flares removed — solid panel, the logo carries the visual weight. */
    background: var(--bg-elev-1);
    border-color: var(--border);
}
/* Subfield + Networks no longer need to override .section-header
   max-width — the base now uses --content for all sections. Their
   wider .section-sub reading width is preserved below. */
.subfield-feature .section-sub,
.literature-networks .section-sub {
    max-width: 80ch;
}
/* Networks section is content-dense across two groups + cmd lines —
   keep the header compact so the whole thing fits one viewport in
   snap-scroll mode. */
.literature-networks .section-header {
    margin: 0 auto var(--space-4);
}
.literature-networks .section-title {
    font-size: clamp(2rem, 4.2vw, 3.4rem);
    line-height: 1.05;
}
.literature-networks .section-sub {
    font-size: 0.96rem;
    line-height: 1.5;
}

/* ─── Networks section: cmd-line flavour + amber accents ─── */
.literature-networks .section-num { color: var(--warm); }
.literature-networks .section-title em { color: var(--accent); }

/* Top cmd prompt — visible enough to read as a header decoration, not
   a footnote. */
.literature-networks .networks-cmd {
    margin: 0 0 var(--space-2);
    font-size: 0.84rem;
    color: var(--text-muted);
}
.literature-networks .networks-cmd .term-prompt { color: var(--accent); font-weight: 500; }
.literature-networks .networks-cmd .term-sep    { color: var(--text-faint); }
.literature-networks .networks-cmd .term-cwd    { color: var(--warm); font-weight: 500; }
.literature-networks .networks-cmd .term-typed  { color: var(--text); }

/* Card stat numbers — amber tabular-nums so 47,789 / 62,190 / 175,918
   read as data points, not bolded prose. */
.literature-networks .card--network .card-blurb strong {
    color: var(--warm);
    font-family: var(--font-mono);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
}

/* Card link CTAs in this section pick up amber so they pop against the
   cyan-heavy default styling everywhere else. */
.literature-networks .card-link {
    color: var(--warm);
}
.literature-networks .card-link:hover {
    color: var(--text);
}

/* Bottom cmd outlink — full-width band, stronger contrast. */
.literature-networks .networks-outcmd {
    max-width: var(--content);
    margin: var(--space-3) auto 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid color-mix(in oklab, var(--warm) 32%, var(--border));
    border-radius: var(--radius);
    background: color-mix(in oklab, var(--warm) 6%, transparent);
    font-size: 0.82rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px;
    color: var(--text);
}
.literature-networks .networks-outcmd .term-prompt { color: var(--accent); font-weight: 500; }
.literature-networks .networks-outcmd .term-sep    { color: var(--text-faint); }
.literature-networks .networks-outcmd .term-cwd    { color: var(--warm); font-weight: 500; }
.literature-networks .networks-outcmd .term-typed  { color: var(--text); }
.networks-outcmd-arg {
    color: var(--warm);
    font-family: var(--font-mono);
    border-bottom: 1px dashed color-mix(in oklab, var(--warm) 50%, transparent);
    padding-bottom: 1px;
}
.networks-outcmd-hint {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 0.82rem;
}
.networks-outcmd-hint em {
    font-style: normal;
    color: var(--accent);
}

/* Mobile: drop the right-edge anchor so the hint wraps naturally next to
   the arg instead of leaving a gap on the wrap row. Tighten typography. */
@media (max-width: 640px) {
    .literature-networks .networks-outcmd {
        font-size: 0.74rem;
        padding: 8px 10px;
        gap: 6px;
        line-height: 1.5;
    }
    .networks-outcmd-hint {
        margin-left: 0;
        font-size: 0.74rem;
    }
}

/* ─── Networks row — open-corpus group (3 cards) and prototype group (1 card)
   sit side-by-side on one row, separated by a vertical divider. Columns
   stretch so all 4 cards land at the same height regardless of which
   group has the longest blurb. ─── */
.networks-row {
    display: grid;
    grid-template-columns: 3fr 1fr;
    gap: var(--space-4);
    align-items: stretch;
}
/* Each group is a flex column so the card grid/row underneath the
   header can flex-grow to fill its column. */
.networks-group {
    display: flex;
    flex-direction: column;
}
.networks-group .card-grid,
.networks-group .networks-prototype-row {
    flex: 1 1 auto;
    grid-auto-rows: 1fr;
}
.networks-group--corpus .card-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}
.networks-group--prototype {
    border-left: 1px solid color-mix(in oklab, var(--accent) 25%, var(--border));
    padding-left: var(--space-4);
}
.networks-group--prototype .networks-prototype-row {
    grid-template-columns: minmax(0, 1fr);
}
@media (max-width: 980px) {
    .networks-row {
        grid-template-columns: 1fr;
    }
    .networks-group--prototype {
        border-left: 0;
        border-top: 1px solid color-mix(in oklab, var(--accent) 25%, var(--border));
        padding-left: 0;
        padding-top: var(--space-3);
    }
}
/* Narrow viewports — three corpus cards crammed side-by-side clip the
   titles and overlap the status pill onto the meta bar. Stack them. */
@media (max-width: 720px) {
    .networks-group--corpus .card-grid {
        grid-template-columns: minmax(0, 1fr);
    }
    .networks-prototype-row {
        grid-template-columns: minmax(0, 1fr);
    }
}
.networks-group {
    margin-top: 0;
}
.networks-group-head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0 0 var(--space-3);
    font-family: var(--font-mono);
    font-size: 0.82rem;
    flex-wrap: wrap;
}
.networks-group-tag {
    color: var(--warm);
    letter-spacing: 0.06em;
    font-weight: 500;
    flex-shrink: 0;
}
.networks-group-title {
    color: var(--text);
    font-size: 0.92rem;
    letter-spacing: 0.02em;
    flex-shrink: 0;
}
.networks-group-rule {
    flex: 1;
    height: 1px;
    min-width: 40px;
    background: linear-gradient(
        90deg,
        color-mix(in oklab, var(--warm) 45%, transparent) 0%,
        var(--border) 70%,
        transparent 100%
    );
}
.networks-group-meta {
    color: var(--text-muted);
    font-style: italic;
    font-size: 0.78rem;
    flex-shrink: 0;
}
.networks-group-meta em {
    font-style: italic;
    color: var(--text);
}
.networks-group--prototype .networks-group-tag { color: var(--accent); }
.networks-group--prototype .networks-group-rule {
    background: linear-gradient(
        90deg,
        color-mix(in oklab, var(--accent) 40%, transparent) 0%,
        var(--border) 70%,
        transparent 100%
    );
}

/* Pilot row: a single card constrained so the section doesn't overflow. */
.networks-prototype-row {
    display: grid;
    grid-template-columns: minmax(280px, 360px);
    justify-content: start;
}

/* In-build pill — amber so it reads distinct from the cyan Live pill. */
.card-status--build {
    color: var(--warm);
    border-color: color-mix(in oklab, var(--warm) 45%, transparent);
    background: color-mix(in oklab, var(--warm) 12%, var(--bg-deepest));
}
.card-status--build::before {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    background: var(--warm);
    border-radius: 50%;
    margin-right: 6px;
    transform: translateY(-1px);
    box-shadow: 0 0 8px var(--warm);
    animation: livePulse 2s ease-in-out infinite;
}
.subfield-feature .term-prompt { color: var(--subfield-2); }

/* The log rail itself carries the parallax — JS translates it on
   scroll. Mark it as a transform target. */
.log-rail { will-change: transform; }
@media (prefers-reduced-motion: reduce) {
    .log-rail { transform: none !important; }
}

/* ─── Background log rail — fixed left column, faint mono lines that
   type out per section as you scroll. Pure decoration; does not block
   interaction. Hidden on narrow viewports where it'd overlap content. */
.log-rail {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    pointer-events: none;
    z-index: 1;
    padding: 88px 32px 80px 22px;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    line-height: 1.5;
    overflow: hidden;
    /* Soft fade at top + bottom so older lines age out, newer lines
       precipitate in. Plus the whole rail sits at low opacity so it
       reads as background atmosphere. */
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 16%, black 82%, transparent 100%);
            mask-image: linear-gradient(180deg, transparent 0%, black 16%, black 82%, transparent 100%);
    color: var(--text-muted);
    /* Default kept dim now that the xray hold reveals everything on
       demand — the rail should read as background atmosphere, not
       compete with the foreground copy. */
    opacity: 0.16;
    transition: opacity 240ms var(--ease);
}
.log-rail:hover { opacity: 0.4; }
@media (max-width: 1180px) {
    .log-rail { display: none; }
}
.log-rail-line {
    margin: 0 0 5px;
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--text-muted);
    text-shadow: 0 0 0 transparent; /* GPU-promote */
    will-change: contents;
}
.log-rail-line .lr-prompt { color: var(--accent); }
.log-rail-line .lr-tag    { color: var(--warm); }
.log-rail-line .lr-em     { color: var(--text); font-style: italic; }
.log-rail-line.lr-section {
    margin-top: var(--space-2);
    color: var(--accent);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    opacity: 0.85;
}

/* Bash noise — interleaved cmd lines + their faint output. */
.log-rail-line.lr-cmd .lr-dollar { color: var(--warm); }
.log-rail-line.lr-cmd  { color: var(--text-muted); }
.log-rail-line.lr-out  { color: var(--text-faint); padding-left: 2px; }
.log-rail-caret {
    display: inline-block;
    color: var(--accent);
    animation: termBlink 1s steps(2, jump-none) infinite;
}

/* ─── Xray toggle — hold-to-reveal the background log rail.
   Mousedown/touchstart sets [data-xray] on body; foreground dims,
   rail brightens to full opacity above content. Release returns. */
.xray-toggle {
    position: fixed;
    /* Bottom-left, baselined with the ask-patrick.exe pill on the
       bottom-right. The system-log indexer ticker has been bumped
       up so the two no longer overlap. */
    bottom: var(--space-3);
    left: var(--space-3);
    z-index: 95;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px 8px 12px;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    /* Neutral by default — earns the accent only when the user reaches
       the Hire section. Quiet pill until that point. */
    color: var(--text-faint);
    background: color-mix(in oklab, var(--bg-deepest) 80%, transparent);
    border: 1px solid var(--border);
    border-radius: 999px;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    letter-spacing: 0.04em;
    box-shadow: 0 6px 22px rgba(0,0,0,0.32), 0 0 0 1px rgba(255,255,255,0.02);
    transition: color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                background var(--t-base) var(--ease),
                transform var(--t-base) var(--ease);
}
.xray-toggle:hover {
    color: var(--text-muted);
    border-color: color-mix(in oklab, var(--text-muted) 35%, transparent);
}
/* Once the reader hits Hire, the button lifts into the accent palette
   so 'click to lock' is a visible affordance. */
.xray-toggle.is-at-hire {
    color: var(--accent);
    border-color: color-mix(in oklab, var(--accent) 35%, transparent);
}
.xray-toggle.is-at-hire:hover {
    color: var(--accent);
    border-color: color-mix(in oklab, var(--accent) 55%, transparent);
}
.xray-toggle:active,
body[data-xray="true"] .xray-toggle {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in oklab, var(--accent) 12%, var(--bg-deepest));
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 18%, transparent);
}
.xray-toggle.is-sticky {
    color: var(--accent-hover);
    border-color: var(--accent);
    background: color-mix(in oklab, var(--accent) 18%, var(--bg-deepest));
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 22%, transparent);
}
.xray-toggle-icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}
.xray-toggle-label {
    color: inherit;
    font-weight: 500;
}
@media (max-width: 1180px) {
    .xray-toggle { display: none; }
}

/* Foreground dim + rail-on-top while holding the xray toggle. */
body[data-xray="true"] #main,
body[data-xray="true"] .site-header,
body[data-xray="true"] .site-footer,
body[data-xray="true"] .section-dots,
body[data-xray="true"] [data-float-term],
body[data-xray="true"] .mobile-nav,
body[data-xray="true"] .hero-bg {
    opacity: 0.08;
    transition: opacity 200ms var(--ease);
}
body[data-xray="true"] .log-rail {
    opacity: 1;
    z-index: 90;
    color: var(--text);
    transition: opacity 200ms var(--ease);
}
body[data-xray="true"] .log-rail .lr-prompt { color: var(--accent); }
body[data-xray="true"] .log-rail .lr-tag    { color: var(--warm); }

/* Sticky lock — when the user clicks the sub-view at Hire to keep
   the rail open, the rail itself captures scroll. The page is
   prevented from scrolling so the wheel/touch gesture stays inside
   the rail. Parallax-translate is neutralised while sticky so the
   rail isn't fighting its own scrollTop. */
html:has(body[data-xray-sticky="true"]),
body[data-xray-sticky="true"] {
    overflow: hidden;
}
body[data-xray-sticky="true"] .log-rail {
    pointer-events: auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    transform: none !important;
}

/* Easter-egg button at the end of the hire signoff. Quiet, mono. */
.log-egg {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: var(--space-2);
    padding: 3px 10px;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-faint);
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 4px;
    cursor: pointer;
    letter-spacing: 0.02em;
    transition: color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                background var(--t-base) var(--ease);
}
.log-egg:hover {
    color: var(--accent);
    border-color: color-mix(in oklab, var(--accent) 50%, transparent);
    background: color-mix(in oklab, var(--accent) 8%, transparent);
}

/* "info" link in a terminal chrome bar — opens the about-modal.
   Sits next to the date/time, before the export icon and window
   controls. Quiet by default, accent on hover. */
.term-chrome-info {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    margin-right: 6px;
    background: transparent;
    border: 1px solid color-mix(in oklab, var(--text-faint) 35%, transparent);
    border-radius: 4px;
    color: var(--text-faint);
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: background var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                color var(--t-base) var(--ease);
}
.term-chrome-info:hover {
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    border-color: color-mix(in oklab, var(--accent) 50%, transparent);
    color: var(--accent);
}

/* "about patrick.exe" disclaimer modal. Shares structural patterns
   with .log-modal but its body is prose, not a scrolling log. */
.info-modal {
    border: 0;
    padding: 0;
    background: transparent;
    color: inherit;
    max-width: min(680px, calc(100vw - 2rem));
    width: 100%;
    border-radius: var(--radius-lg);
}
.info-modal::backdrop {
    background: rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.info-modal-inner {
    overflow: hidden;
    max-height: 86vh;
    display: flex;
    flex-direction: column;
}
.info-modal .doc-window-frame {
    width: 100%;
    height: auto;
    max-height: 86vh;
    animation: none;
}
.info-modal-body {
    flex: 1;
    overflow: auto;
    padding: var(--space-5) var(--space-5) var(--space-4);
    background: var(--bg-elev-1);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.93rem;
    line-height: 1.62;
}
.info-modal-body .info-title {
    margin: 0 0 var(--space-4);
    font-family: var(--font-mono);
    font-size: 0.86rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--accent);
    text-transform: lowercase;
}
.info-modal-body p {
    margin: 0 0 var(--space-3);
    color: var(--text-muted);
}
.info-modal-body p:last-child {
    margin-bottom: 0;
}

/* ──────────────────────────────────────────────────────────────────
   Support-modal entrance animations.
   Staggered fade-up on body content + a more dramatic slide-and-bounce
   on the Ko-fi CTA. Spring easing throughout (the 1.34 in the second
   control point overshoots slightly before settling — Apple's iPad-Air-
   style "bounce open" feel). Triggered by JS adding .is-animating-in
   to the modal when openSupportModal() fires.

   Respects prefers-reduced-motion: animations disabled if requested.
   ────────────────────────────────────────────────────────────────── */

@keyframes support-fade-up {
    0%   { opacity: 0; transform: translateY(14px); }
    100% { opacity: 1; transform: translateY(0); }
}
@keyframes support-cta-bounce {
    0%   { opacity: 0; transform: translateY(32px) scale(0.88); }
    55%  { opacity: 1; transform: translateY(-5px) scale(1.04); }
    78%  { transform: translateY(2px) scale(0.992); }
    100% { transform: translateY(0) scale(1); }
}

.support-modal.is-animating-in .info-title,
.support-modal.is-animating-in .support-modal-body > p,
.support-modal.is-animating-in .support-tiers li,
.support-modal.is-animating-in .support-fineprint,
.support-modal.is-animating-in .support-chat,
.support-modal.is-animating-in .support-eyebrow,
.support-modal.is-animating-in .support-cta-secondary {
    animation: support-fade-up 600ms cubic-bezier(0.34, 1.34, 0.64, 1) both;
}
/* Stagger delays — each successive element fires ~60ms after. */
.support-modal.is-animating-in .info-title              { animation-delay: 60ms; }
.support-modal.is-animating-in .support-modal-body > p:nth-of-type(1) { animation-delay: 140ms; }
.support-modal.is-animating-in .support-modal-body > p:nth-of-type(2) { animation-delay: 200ms; }
.support-modal.is-animating-in .support-tiers li:nth-child(1) { animation-delay: 280ms; }
.support-modal.is-animating-in .support-tiers li:nth-child(2) { animation-delay: 330ms; }
.support-modal.is-animating-in .support-tiers li:nth-child(3) { animation-delay: 380ms; }
.support-modal.is-animating-in .support-tiers li:nth-child(4) { animation-delay: 430ms; }
/* CTA gets the dramatic bounce-in instead of the gentle fade-up. */
.support-modal.is-animating-in .support-cta-row {
    animation: support-cta-bounce 800ms cubic-bezier(0.34, 1.34, 0.64, 1) 540ms both;
}
.support-modal.is-animating-in .support-fineprint:not(.support-fineprint--last) { animation-delay: 700ms; }
.support-modal.is-animating-in .support-eyebrow        { animation-delay: 820ms; }
.support-modal.is-animating-in .support-eyebrow + p    { animation-delay: 880ms; }
.support-modal.is-animating-in .support-cta-secondary  { animation-delay: 940ms; }
.support-modal.is-animating-in .support-chat           { animation-delay: 1040ms; }
.support-modal.is-animating-in .support-fineprint--last{ animation-delay: 1240ms; }

@media (prefers-reduced-motion: reduce) {
    .support-modal.is-animating-in * {
        animation: none !important;
    }
}

/* Support-modal specifics — inherits everything from .info-modal
   (the disclaimer modal) plus an Apple-class craft pass:
     - Modal frame: amber halo + wide ambient glow on the dialog
     - Body: dual radial gradients in amber for soft depth
     - Title: bigger, more presence
     - Tier rows: subtle hover tint with smooth transition
     - Ko-fi CTA: gradient sheen at rest, dramatic spring-eased lift
       + larger glow on hover (the visual headline of the modal)
     - Eyebrow: subtle separator line above the commission section

   Color note: support uses --warm (amber) for primary accents
   throughout — title, tier amounts, CTA, eyebrow, secondary link.
   Mirrors the amber-tinted support pane in the Hire section so
   "support" reads as a consistent visual identity across the site,
   distinct from the teal-dominant rest. */
.support-modal {
    max-width: min(720px, calc(100vw - 2rem));
}
/* Modal frame: deeper drop shadow + thin amber halo ring. Dropped
   the wide ambient amber glow that read as "Web 2.0 button glow" —
   motion does the eye-drawing work now, not static lighting. */
.support-modal .info-modal-inner {
    box-shadow:
        0 28px 72px rgba(0, 0, 0, 0.55),
        0 0 0 1px rgba(232, 169, 106, 0.10);
}
/* Layered radial gradients give the body surface dimension —
   amber wash from the upper-left and a hint from bottom-right. */
.support-modal-body {
    padding: var(--space-6) var(--space-5) var(--space-5);
    background:
        radial-gradient(120% 40% at 0% 0%, rgba(232, 169, 106, 0.06), transparent 60%),
        radial-gradient(80% 50% at 100% 100%, rgba(232, 169, 106, 0.03), transparent 60%),
        var(--bg-elev-1);
}
.support-modal .info-title {
    color: var(--warm);
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    margin: 0 0 var(--space-4);
}
.support-tiers {
    list-style: none;
    margin: var(--space-5) calc(-1 * var(--space-3));
    padding: 0;
}
.support-tiers li {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: 12px var(--space-3);
    border-radius: 6px;
    border-bottom: 1px solid color-mix(in oklab, var(--warm) 8%, var(--border));
    transition: background 240ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.support-tiers li:last-child {
    border-bottom: 0;
}
.support-tiers li:hover {
    background: rgba(232, 169, 106, 0.04);
}
.support-tier-amt {
    flex: 0 0 96px;
    font-family: var(--font-mono);
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--warm);
    letter-spacing: 0.02em;
}
.support-tier-impact {
    flex: 1;
    color: var(--text-muted);
    font-size: 0.94rem;
    line-height: 1.55;
}
.support-cta-row {
    margin: var(--space-5) 0;
    text-align: center;
}
.support-cta {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 14px 28px;
    background:
        linear-gradient(180deg, rgba(93, 219, 232, 0.14) 0%, rgba(93, 219, 232, 0.04) 100%),
        var(--bg-elev-1);
    border: 1px solid var(--accent);
    border-radius: 999px;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.94rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-decoration: none;
    cursor: pointer;
    /* Quiet at rest — just a hint of shadow. The entrance animation
       does the eye-drawing, then the button settles into a calm state.
       Hover wakes it back up dramatically. */
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.20);
    transition: background 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
                color 240ms ease,
                transform 280ms cubic-bezier(0.34, 1.34, 0.64, 1),
                box-shadow 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.support-cta:hover {
    background:
        linear-gradient(180deg, rgba(93, 219, 232, 0.22) 0%, rgba(93, 219, 232, 0.06) 100%),
        var(--bg-elev-1);
    color: var(--text);
    transform: translateY(-2px) scale(1.012);
    /* Lift + scale + gradient shift carry the hover signal — quiet
       teal halo replaces the previous amber glow. */
    box-shadow: 0 6px 16px rgba(93, 219, 232, 0.16);
}
.support-cta:active {
    transform: translateY(-1px) scale(0.99);
    transition-duration: 100ms;
}
.support-cta .ext-icon {
    width: 0.82em;
    height: 0.82em;
}
/* Ko-fi mug mark — inline SVG (paths originally from Ko-fi's
   official kofi_symbol.svg, recolored for the dark-bg site:
   light-grey #e6e7ea cup outline, dark interior, teal #5ddbe8
   heart instead of brand-orange). Subtle lub-dub heartbeat at
   ~60bpm — two close pulses then a long rest, like a calm
   resting cardiac rhythm. Pauses on hover so the button's lift
   carries the interaction. */
.support-cta .kofi-mark {
    width: 1.7em;
    height: auto;
    flex-shrink: 0;
    transform-origin: center;
    animation: kofi-heartbeat 2s ease-in-out infinite;
    animation-delay: 600ms;
}
@keyframes kofi-heartbeat {
    0%, 55%, 100% { transform: scale(1); }
    60%           { transform: scale(1.10); }  /* lub  */
    66%           { transform: scale(1.00); }
    72%           { transform: scale(1.07); }  /* dub — smaller */
    78%           { transform: scale(1.00); }
}
.support-cta:hover .kofi-mark {
    animation-play-state: paused;
    transform: scale(1.06);
    transition: transform 260ms cubic-bezier(0.34, 1.34, 0.64, 1);
}
@media (prefers-reduced-motion: reduce) {
    .support-cta .kofi-mark { animation: none; transform: none; }
}
/* Standalone email link under the "or commission something specific"
   paragraph. Left-aligned (paragraph context, not button-row),
   slightly more readable than the previous under-button placement
   since it's now the only call-to-action for the commission path. */
.support-cta-secondary {
    margin: var(--space-2) 0 0;
    font-family: var(--font-mono);
    font-size: 0.84rem;
    letter-spacing: 0.02em;
}
.support-cta-secondary a {
    color: var(--warm);
    text-decoration: underline;
    text-decoration-color: color-mix(in oklab, var(--warm) 50%, transparent);
    text-underline-offset: 3px;
    transition: color var(--t-base) var(--ease),
                text-decoration-color var(--t-base) var(--ease);
}
.support-cta-secondary a:hover {
    color: var(--text);
    text-decoration-color: var(--warm);
}
.support-eyebrow {
    /* Subtle hairline separator above the commission sub-section so
       it reads as 'second half of the offer' rather than continuous. */
    margin: var(--space-5) 0 var(--space-2);
    padding-top: var(--space-4);
    border-top: 1px solid color-mix(in oklab, var(--warm) 14%, var(--border));
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--warm);
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: lowercase;
}

/* ── Inline support-scoped chat (inside the support modal) ──────────
   Modeled after .paper-chat (paper viewers) but in the amber palette.
   Visitors can ask context questions about how support works without
   leaving the modal. Disclaimer surfaces upfront: informational only,
   not commitments. Email pivot at the bottom for personal/binding
   questions that the AI can't (and shouldn't) handle. */
.support-chat {
    margin: var(--space-4) 0 0;
    padding: var(--space-3) var(--space-4);
    background: color-mix(in oklab, var(--bg-elev-2) 35%, transparent);
    border: 1px solid color-mix(in oklab, var(--warm) 18%, var(--border));
    border-radius: var(--radius);
    font-family: var(--font-mono);
}
.support-chat-head {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: var(--space-2);
}
.support-chat-title {
    font-size: 0.84rem;
    color: var(--text-muted);
    letter-spacing: 0.02em;
    line-height: 1.4;
}
.support-chat-title em {
    font-style: normal;
    font-weight: 500;
    color: var(--warm);
}
.support-chat-hint {
    font-size: 0.74rem;
    color: var(--text-faint);
    line-height: 1.45;
    font-family: var(--font-body);
    font-style: italic;
}
.support-chat-form {
    /* Form sits at the bottom of the chat block (compose area).
       Border-top divider in teal so the prompt area reads as
       part of the standard terminal-shell language (matches the
       rest of the site). Amber is the "support" identity marker
       on the modal's headline elements; teal is the terminal/
       interaction language. Mix keeps the modal feeling like
       part of Sociologix, not a separate amber popup. */
    display: flex;
    align-items: baseline;
    gap: 0;
    border: 0;
    border-top: 1px dashed color-mix(in oklab, var(--accent) 22%, var(--border));
    padding: 12px 0 8px;
    margin-top: var(--space-2);
    cursor: text;
    font-size: 0.92rem;
    color: var(--text-muted);
}
.support-chat-input-wrap {
    position: relative;
    flex: 1 1 auto;
    display: inline-block;
    min-width: 0;
    margin-left: 4px;
}
.support-chat-input {
    width: 100%;
    background: transparent;
    border: 0;
    outline: 0;
    color: var(--text);
    font-family: inherit;
    font-size: inherit;
    padding: 0 0 0 14px;
    margin: 0;
    caret-color: var(--accent);
}
.support-chat-input-wrap > .term-caret {
    position: absolute;
    left: 0;
    top: 0;
    pointer-events: none;
    color: var(--accent);
}
.support-chat-input:focus ~ .term-caret,
.support-chat-input:not(:placeholder-shown) ~ .term-caret {
    display: none;
}
.support-chat-log {
    /* Fixed-viewport scroll container — like Slack/ChatGPT/etc. New
       messages push old ones up; user can scroll back through the
       history within this 36vh window. min-height keeps the
       container visible even with one message so the input doesn't
       jump as content arrives. */
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-height: 60px;
    max-height: 36vh;
    overflow-y: auto;
    padding: var(--space-2) 0;
    font-size: 0.86rem;
    line-height: 1.55;
    scrollbar-width: thin;
    /* Scrollbar in teal — chat-interaction surface, not a support
       identity element. */
    scrollbar-color: color-mix(in oklab, var(--accent) 28%, transparent) transparent;
}
.support-chat-log:empty {
    /* Empty state — no min-height so the head + form can sit close
       together when there's no conversation yet. */
    min-height: 0;
    padding: 0;
}
.support-chat-log .schat-q,
.support-chat-log .schat-a {
    margin: 0;
    padding: 6px 10px;
    border-radius: 4px;
    word-break: break-word;
    white-space: pre-wrap;
}
.support-chat-log .schat-q {
    color: var(--text-muted);
    background: color-mix(in oklab, var(--bg-deepest) 35%, transparent);
}
.support-chat-log .schat-q::before {
    /* Teal terminal prompt — matches the rest of the site's shell
       prompts. Amber is reserved for the support-identity headlines. */
    content: '~/sociologix ';
    color: var(--accent);
}
.support-chat-log .schat-a {
    color: var(--text);
    /* Answer left-border in teal — same convention as the
       paper-scoped chats throughout the site. */
    border-left: 2px solid color-mix(in oklab, var(--accent) 45%, transparent);
    padding-left: var(--space-3);
    font-family: var(--font-body);
    font-size: 0.94rem;
    line-height: 1.6;
}
.support-chat-log .schat-a.schat-thinking {
    color: var(--accent);
    opacity: 0.65;
    font-style: italic;
    animation: logPulse 1.4s ease-in-out infinite;
}
.support-chat-log .schat-a.schat-error {
    color: #ff8a85;
    border-left-color: rgba(255, 138, 133, 0.4);
}
.support-chat-pivot {
    margin: var(--space-3) 0 0;
    font-size: 0.78rem;
    color: var(--text-faint);
    font-family: var(--font-body);
    line-height: 1.5;
}
.support-chat-pivot a {
    /* Teal — this is a "send the visitor to email Patrick" link,
       a routing affordance rather than a support-identity marker.
       Teal keeps it consistent with other email links across the
       site (footer pjb@..., header brand-link, etc.). */
    color: var(--accent);
    text-decoration: underline;
    text-decoration-color: color-mix(in oklab, var(--accent) 50%, transparent);
    text-underline-offset: 3px;
}
.support-chat-pivot a:hover {
    color: var(--text);
    text-decoration-color: var(--accent);
}
.support-fineprint {
    margin: var(--space-3) 0 0;
    font-size: 0.86rem;
    color: var(--text-faint);
    line-height: 1.55;
}
.support-fineprint--last {
    margin-top: var(--space-3);
    font-style: italic;
    color: var(--text-muted);
}

/* Hire-section "support" door — same shell as the project enquiry
   and LinkedIn doors, but hover tints amber (var(--warm)) instead
   of the accent teal so it reads as a kindred-but-distinct option
   (not the headline ask). The [support] tag text also picks up
   the warm tone to match. */
.hire-door--support {
    /* Button-reset overrides since this one's a <button>, not <a>. */
    font: inherit;
    color: inherit;
    text-align: left;
    width: 100%;
}
.hire-door--support:hover {
    background: color-mix(in oklab, var(--warm) 9%, transparent);
    border-color: color-mix(in oklab, var(--warm) 40%, var(--border));
}
.hire-door--support .hire-door-tag {
    color: var(--warm);
}
.hire-door--support .hire-door-glyph {
    color: var(--warm);
}

/* Quiet "support the work" footer link — sits next to email and
   © year, reads as a third meta line, low-volume but always
   findable. */
.footer-support-link {
    font: inherit;
    background: transparent;
    border: 0;
    padding: 0;
    color: inherit;
    cursor: pointer;
    text-decoration: underline;
    text-decoration-color: color-mix(in oklab, currentColor 35%, transparent);
    text-underline-offset: 3px;
    transition: color var(--t-base) var(--ease);
}
.footer-support-link:hover {
    color: var(--accent);
}

/* Modal that prints the full collected log. Reuses .doc-window-frame. */
.log-modal {
    border: 0;
    padding: 0;
    background: transparent;
    color: inherit;
    max-width: min(720px, calc(100vw - 2rem));
    width: 100%;
    border-radius: var(--radius-lg);
}
.log-modal::backdrop {
    background: rgba(0, 0, 0, 0.20);
}
.log-modal-inner {
    overflow: hidden;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
}
/* Same constraint we apply to .modal .doc-window-frame — keep the
   nested frame from inheriting its own viewport-relative width and
   overflowing the dialog. */
.log-modal .doc-window-frame {
    width: 100%;
    height: auto;
    max-height: 80vh;
    animation: none;
}
.log-modal-body {
    flex: 1;
    overflow: auto;
    padding: var(--space-4) var(--space-5);
    background: var(--bg-elev-1);
    font-family: var(--font-mono);
    font-size: 0.84rem;
    line-height: 1.6;
    color: var(--text-muted);
    white-space: pre-wrap;
}
.log-modal-body .lr-prompt { color: var(--accent); }
.log-modal-body .lr-tag    { color: var(--warm); }
.log-modal-body .lr-em     { color: var(--text); font-style: italic; }
.log-modal-body[data-format="json"] {
    color: var(--text);
}

/* External-link icon — sized via em so it tracks the surrounding text. */
.ext-icon {
    width: 0.85em;
    height: 0.85em;
    display: inline-block;
    vertical-align: -0.06em;
    fill: currentColor;
    flex-shrink: 0;
}
.subfield-feature .card-status {
    color: var(--subfield-1);
    border-color: color-mix(in oklab, var(--subfield-1) 35%, transparent);
}

/* NUM section uses a slight red umbrella accent */
.num-section .section-num,
.num-section .section-title em { color: var(--num-accent); }

/* Co-author credit — Dr. Raven Bowen, NUM's Head of Research,
   Innovation, and Expansion, has been Patrick's primary collaborator
   on this work since 2021. Frame quietly under the main lede so the
   credit is unmistakable but doesn't compete. */
.num-coauth {
    margin-top: var(--space-2);
    font-size: 0.86rem;
    color: var(--text-muted);
    font-style: italic;
}
.num-coauth-link {
    color: var(--num-accent);
    text-decoration: none;
    border-bottom: 1px dashed color-mix(in oklab, var(--num-accent) 50%, transparent);
    transition: color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease);
    font-style: normal;
    white-space: nowrap;
}
.num-coauth-link:hover {
    color: var(--text);
    border-bottom-color: var(--text);
}
.num-coauth-link i {
    margin-right: 4px;
    font-size: 0.92em;
}
.num-section .card-shot::after {
    background:
        radial-gradient(120% 80% at 50% 100%, rgba(214, 90, 90, 0.08), transparent 55%),
        linear-gradient(180deg, transparent 60%, rgba(20, 20, 26, 0.4));
}
.num-section .card:hover {
    border-color: rgba(214, 90, 90, 0.22);
}
.num-section .card::before {
    background: linear-gradient(135deg, var(--num-accent), transparent 50%);
}
.num-section .card-meta {
    color: var(--num-accent);
    border-color: rgba(214, 90, 90, 0.24);
}
.num-section .card-shot-icon { color: rgba(214, 90, 90, 0.5); }

/* NUM cmd-line decorations — faint mono prompt above the header and a
   command-style outlink to the canonical NUM memorial map below the
   card grid. Tinted into the section's red accent so they read as the
   section's own voice. */
.num-section .num-cmd {
    margin: 0 0 var(--space-2);
    opacity: 0.55;
    font-size: 0.78rem;
}
.num-section .num-cmd .term-prompt,
.num-section .num-cmd .term-sep    { color: var(--num-accent); }
.num-section .num-cmd .term-cwd    { color: color-mix(in oklab, var(--num-accent) 75%, var(--text-muted)); }
.num-section .num-cmd .term-typed  { color: var(--text-muted); }

.num-section .num-outlink {
    max-width: var(--content-narrow);
    margin: var(--space-5) auto 0;
    padding: var(--space-3) var(--space-4);
    border-top: 1px dashed color-mix(in oklab, var(--num-accent) 30%, var(--border));
    font-size: 0.82rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px;
    color: var(--text-muted);
}
.num-section .num-outlink .term-prompt,
.num-section .num-outlink .term-sep   { color: var(--num-accent); }
.num-section .num-outlink .term-cwd   { color: color-mix(in oklab, var(--num-accent) 75%, var(--text-muted)); }
.num-section .num-outlink .term-typed { color: var(--text); }
.num-outlink-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--num-accent);
    text-decoration: none;
    border-bottom: 1px dashed color-mix(in oklab, var(--num-accent) 35%, transparent);
    padding-bottom: 1px;
    transition: color var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.num-outlink-link:hover {
    color: var(--text);
    border-bottom-color: var(--num-accent);
}

.section-title {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: clamp(2.4rem, 5.5vw, 4.2rem);
    line-height: 1.0;
    letter-spacing: -0.035em;
    margin: 0 0 var(--space-3);
    color: var(--text);
}
.section-title em {
    font-style: normal;
    color: var(--accent);
    font-family: inherit;
    font-weight: 600;
}

.section-sub {
    font-size: 1.05rem;
    color: var(--text-muted);
    line-height: 1.6;
    /* 80ch is a comfortable read length that doesn't hug the much-wider
       section title above. Some sections widen this further (Hire goes
       to 88ch via #hire .section-sub) but 80ch is the consistent floor. */
    max-width: 80ch;
    font-family: var(--font-body);
}

/* ──────────────────────────────────────────────────────
   Hero
   ────────────────────────────────────────────────────── */

.hero {
    position: relative;
    min-height: 100vh;
    min-height: 100svh;
    display: flex;
    align-items: center;
    padding: max(var(--space-8), 12vh) var(--space-5) var(--space-7);
    overflow: hidden;
}

.hero-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}
.hero-bg canvas {
    width: 100%;
    height: 100%;
    opacity: 0.85;
}

.hero-inner {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: var(--content);
    margin: 0 auto;
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
    gap: var(--space-7);
    align-items: center;
}
@media (max-width: 980px) {
    .hero-inner {
        grid-template-columns: 1fr;
        gap: var(--space-5);
    }
}

.hero-content {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    max-width: 640px;
}

.kicker {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-muted);
    letter-spacing: 0.1em;
    margin: 0;
    text-transform: uppercase;
}
.kicker span {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1) var(--space-2);
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: 4px;
}
.kicker .dot {
    width: 6px;
    height: 6px;
    background: var(--accent);
    border-radius: 50%;
    padding: 0;
    border: 0;
    box-shadow: 0 0 8px var(--accent-glow);
}

.hero-title {
    font-family: var(--font-display);
    font-weight: 500;
    /* Was clamp(3rem, 9vw, 7rem). 9vw + 7rem cap meant any viewport
       wider than ~1240px hit the 7rem (112px) ceiling — overwhelming
       on a typical 1080p monitor where browser chrome leaves ~900px
       of vertical room. Now scales 6vw with a 5.5rem (88px) ceiling
       so the two-line "Patrick Burnett" reads bold but not
       view-eating. Larger monitors still get the visual weight. */
    font-size: clamp(3rem, 6vw, 5.5rem);
    line-height: 0.92;
    letter-spacing: -0.045em;
    margin: 0;
    color: var(--text);
}
.hero-title-line { display: block; }
.hero-title-line.italic {
    font-family: inherit;
    font-style: normal;
    font-weight: 500;
    color: var(--accent);
    letter-spacing: -0.03em;
}

.hero-tagline {
    font-family: var(--font-display);
    font-size: clamp(1.3rem, 2.2vw, 1.8rem);
    font-weight: 400;
    color: var(--text);
    margin: var(--space-3) 0 var(--space-2);
    line-height: 1.25;
    letter-spacing: -0.02em;
}
.hero-tagline em {
    font-family: inherit;
    color: var(--accent);
    font-style: normal;
    font-weight: 600;
}

.hero-lede {
    font-size: 1.05rem;
    line-height: 1.7;
    color: var(--text-muted);
    max-width: 56ch;
    margin: 0;
}
.hero-lede em { color: var(--text); font-style: italic; }
.hero-lede strong {
    color: var(--accent);
    font-weight: 500;
}

.hero-socials {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}
.hero-socials a {
    display: inline-flex;
    align-items: center;
    padding: var(--space-2) var(--space-3);
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    font-weight: 500;
    border: 1px solid var(--border);
    border-radius: 4px;
    transition: all var(--t-base) var(--ease);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.hero-socials a:hover {
    color: var(--accent);
    border-color: var(--border-accent);
    background: var(--accent-soft);
    transform: translateY(-1px);
}

/* Hero glyph (right side) */
.hero-glyph {
    position: relative;
    aspect-ratio: 1;
    width: 100%;
    max-width: 420px;
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: center;
}
@media (max-width: 980px) {
    .hero-glyph { display: none; }
}

.hero-stats {
    position: absolute;
    inset: 0;
    pointer-events: none;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-muted);
    letter-spacing: 0.04em;
}
.hero-stat {
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-2) var(--space-3);
    background: color-mix(in oklab, var(--bg-base) 80%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}
.hero-stat strong {
    color: var(--accent);
    font-size: 1rem;
    font-weight: 500;
}
.hero-stat em {
    font-style: normal;
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-faint);
}

.hero-scroll {
    position: absolute;
    bottom: var(--space-5);
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    color: var(--text-faint);
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    z-index: 2;
}
.hero-scroll svg { animation: bobbing 2.4s var(--ease) infinite; }
@keyframes bobbing {
    0%, 100% { transform: translateY(0); opacity: 0.5; }
    50%      { transform: translateY(4px); opacity: 1; }
}

/* ──────────────────────────────────────────────────────
   Cards (shared)
   ────────────────────────────────────────────────────── */

.card-grid {
    display: grid;
    gap: var(--space-4);
}
.card-grid--three { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.card-grid--four  { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

.card {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: all var(--t-base) var(--ease);
}
.card::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(135deg, var(--accent), transparent 50%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    opacity: 0;
    transition: opacity var(--t-base) var(--ease);
    pointer-events: none;
}
.card:hover {
    background: var(--bg-elev-2);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lift);
    border-color: var(--border-strong);
}
.card:hover::before { opacity: 0.5; }

/* Screenshot-placeholder visual frame */
.card-shot {
    position: relative;
    aspect-ratio: 16 / 10;
    background: var(--bg-elev-2);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
    background-image:
        linear-gradient(135deg, rgba(255,255,255,0.02) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.02) 50%, rgba(255,255,255,0.02) 75%, transparent 75%);
    background-size: 8px 8px;
}
.card-shot::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(120% 80% at 50% 100%, rgba(93, 219, 232, 0.08), transparent 55%),
        linear-gradient(180deg, transparent 60%, rgba(8, 8, 10, 0.4));
    pointer-events: none;
}
/* Bottom bar pinned to the card-shot frame — spans full width with a
   translucent charcoal wash so labels stay legible over the image. */
.card-shot-meta {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 8px 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    z-index: 2;
    background: color-mix(in oklab, var(--bg-deepest) 55%, transparent);
    border-top: 1px solid var(--border);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
.card-shot-tag {
    font-family: var(--font-mono);
    font-size: 0.64rem;
    color: var(--text-faint);
    letter-spacing: 0.1em;
    text-transform: uppercase;
}
.card-shot-desc {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    color: var(--text);
    letter-spacing: 0.02em;
    line-height: 1.4;
    text-align: right;
    flex: 1 1 auto;
    min-width: 0;
}
.card-shot-icon {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    color: var(--text-faint);
    z-index: 1;
    opacity: 0.45;
    transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.card:hover .card-shot-icon { opacity: 0.7; transform: scale(1.05); }
.card-shot-icon svg { width: 38%; height: auto; }

/* Real screenshot image — fills the card-shot frame, gets a slight
   zoom on card hover. Sits below the meta overlay. Filter dims and
   ages the image so it reads as an archived/captured artefact rather
   than a live screenshot — a faint sepia + reduced saturation pulls
   the screenshots into the section's warm-coral palette. */
.card-shot-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 1;
    opacity: 0.62;
    filter: saturate(0.55) brightness(0.78) contrast(0.92) sepia(0.18);
    transition:
        transform 420ms var(--ease),
        opacity var(--t-base) var(--ease),
        filter var(--t-base) var(--ease);
}
.card:hover .card-shot-img {
    transform: scale(1.05);
    opacity: 0.82;
    filter: saturate(0.7) brightness(0.88) contrast(0.95) sepia(0.12);
}

.card-status {
    position: absolute;
    top: var(--space-3);
    left: var(--space-3);
    z-index: 3;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 3px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: color-mix(in oklab, var(--bg-deepest) 70%, transparent);
    backdrop-filter: blur(6px);
}
.card-status--live {
    color: var(--accent);
    border-color: var(--border-accent);
}
.card-status--live::before {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    background: var(--accent);
    border-radius: 50%;
    margin-right: 6px;
    transform: translateY(-1px);
    box-shadow: 0 0 8px var(--accent);
    animation: livePulse 2s ease-in-out infinite;
}
@keyframes livePulse {
    0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
    50%      { box-shadow: 0 0 0 6px transparent; }
}

.card-body {
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1;
}

.card-title {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 1.25rem;
    line-height: 1.2;
    letter-spacing: -0.02em;
    margin: 0;
    color: var(--text);
}

.card-blurb {
    color: var(--text-muted);
    font-size: 0.93rem;
    line-height: 1.6;
    margin: 0;
    flex: 1;
}
.card-blurb em { color: var(--text); font-style: italic; }
.card-blurb strong { color: var(--text); font-weight: 600; }

.card-link {
    align-self: flex-start;
    margin-top: var(--space-3);
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--accent);
    padding: var(--space-1) 0;
    border-bottom: 1px solid transparent;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    transition: all var(--t-base) var(--ease);
}
.card-link:hover {
    color: var(--accent-hover);
    border-bottom-color: var(--accent-hover);
}
.card-link:hover span { transform: translateX(3px); }
.card-link span { transition: transform var(--t-base) var(--ease); }
.card-link[data-pending] {
    color: var(--text-muted);
    pointer-events: none;
}

.card-meta {
    display: inline-flex;
    align-items: center;
    margin-top: var(--space-3);
    padding: 3px 8px;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    color: var(--text-muted);
    letter-spacing: 0.06em;
    border: 1px solid var(--border);
    border-radius: 4px;
    align-self: flex-start;
}

/* ──────────────────────────────────────────────────────
   Subfield feature
   ────────────────────────────────────────────────────── */

.subfield-grid {
    display: grid;
    grid-template-columns: 4fr 7fr;
    gap: var(--space-5);
    align-items: start;
    max-width: var(--content);
    margin: 0 auto;
}

.subfield-visual {
    position: relative;
    aspect-ratio: 488 / 418;
    max-width: 420px;
    width: 100%;
    justify-self: center;
    /* Flares removed — clean solid panel under the logo. */
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 0;
    overflow: hidden;
}

/* Subfield logo built from controllable squares */
.subfield-logo {
    position: absolute;
    inset: 8% 6%;
    pointer-events: none;
}
.subfield-logo-face {
    position: absolute;
    inset: 0;
}
.subfield-sq {
    position: absolute;
    display: block;
    opacity: 0;
    /* Pre-drop pose: well above the visual frame. The frame has
       overflow:hidden, so cells are clipped during the long fall and
       only become visible as they cross the top edge — which gives
       the genuine "piece falls in from above" Tetris read. */
    transform: translateY(-720%);
    will-change: transform, opacity;
}
/* Variant A — Tetris drop. Pieces fall from above and land like rocks.
   Kept available for re-use; not the live effect right now. */
.subfield-sq.is-in {
    animation: tetrisDrop 380ms cubic-bezier(.55, 0, .9, .42) forwards;
}
@keyframes tetrisDrop {
    0% {
        opacity: 0;
        transform: translateY(-720%);
    }
    6% { opacity: 1; }
    100% {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Variant B — Materialize from nothing. Pieces fade in straight to
   final size; no overshoot, no wiggle. Ordered randomly by JS. */
.subfield-sq.is-materialize {
    transform: none;
    opacity: 0;
    animation: subfieldMaterialize 140ms ease-out forwards;
}
@keyframes subfieldMaterialize {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .subfield-sq {
        transform: none;
    }
    .subfield-sq.is-in,
    .subfield-sq.is-materialize {
        animation: none;
        opacity: 1;
        transform: none;
    }
}


.subfield-copy {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.subfield-stat {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-3) var(--space-4);
    margin: 0 0 var(--space-2);
}
.subfield-stat strong {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: clamp(2.8rem, 7vw, 5rem);
    line-height: 0.92;
    color: var(--accent);
    letter-spacing: -0.04em;
}
.subfield-stat strong sup {
    font-size: 0.4em;
    vertical-align: super;
    margin-left: 0.06em;
    color: var(--warm);
    font-family: var(--font-mono);
    font-weight: 400;
}
.subfield-stat span {
    flex: 1 1 220px;
    font-family: var(--font-serif);
    font-style: italic;
    font-size: 1.08rem;
    line-height: 1.4;
    color: var(--text-muted);
}

.subfield-blurb {
    color: var(--text-muted);
    line-height: 1.65;
    font-size: 1.0rem;
    margin: 0;
}

/* ── Terminal-styled blocks (one style for the whole right column) ── */
.subfield-block {
    margin: 0;
    padding: 0;
    font-family: var(--font-mono);
    font-size: 0.88rem;
}
.subfield-block + .subfield-block { margin-top: var(--space-2); }
.subfield-block .term-cmd {
    margin: 0 0 4px;
    color: var(--text-muted);
}
.subfield-log li {
    color: var(--text-muted);
    line-height: 1.5;
    padding-left: 6px;
}
.subfield-log .term-ts { color: var(--subfield-1); }
.subfield-log .term-num {
    color: var(--text);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}
.subfield-log .term-key { color: var(--warm); }
.subfield-log code {
    font-family: var(--font-mono);
    font-size: 0.92em;
    color: var(--subfield-3);
    background: color-mix(in oklab, var(--subfield-1) 12%, transparent);
    padding: 0 4px;
    border-radius: 3px;
}

/* @-mentions inline — read as tag, not link. */
.subfield-mention {
    font-family: var(--font-mono);
    font-size: 0.94em;
    color: var(--subfield-1);
    background: color-mix(in oklab, var(--subfield-1) 10%, transparent);
    padding: 1px 6px;
    border-radius: 4px;
    border-bottom: 1px solid transparent;
    transition: color var(--t-base) var(--ease), background var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
    white-space: nowrap;
}
.subfield-mention:hover {
    color: var(--text);
    background: color-mix(in oklab, var(--subfield-1) 22%, transparent);
    border-bottom-color: var(--subfield-1);
}

.subfield-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--space-3);
}

/* ── Subfield tabs — story-arc navigation across origin → pipeline →
   surfaces. Click a tab to swap the active panel below. Only one
   panel renders at a time so the section stays bounded inside snap
   mode. Different visual register from the bento/card sections —
   reads as a terminal tab strip. */
.subfield-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 0 0 var(--space-3);
    padding: 0;
    list-style: none;
}
.subfield-tab {
    background: transparent;
    border: 0;
    padding: 6px 10px;
    margin: 0;
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    border-radius: 6px;
    transition: color var(--t-base) var(--ease),
                background var(--t-base) var(--ease);
}
.subfield-tab:hover {
    color: var(--text-muted);
    background: color-mix(in oklab, var(--subfield-1) 6%, transparent);
}
.subfield-tab.is-active {
    color: var(--text);
    background: color-mix(in oklab, var(--subfield-1) 15%, transparent);
}
.subfield-tab-bracket { color: var(--subfield-1); opacity: 0.7; }
.subfield-tab-label { font-weight: 500; }
.subfield-tab.is-active .subfield-tab-label { color: var(--subfield-1); }
.subfield-tab-meta {
    font-size: 0.66rem;
    color: var(--text-faint);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.7;
    margin-left: 2px;
}
.subfield-tab.is-active .subfield-tab-meta {
    color: var(--warm);
    opacity: 0.9;
}

/* Panel container — locks a min-height so swapping tabs doesn't
   bounce the section's vertical anchor. The "tallest panel" rule
   means the container reserves enough room for the longest content. */
.subfield-panels {
    position: relative;
    min-height: 290px;
}
.subfield-panel {
    margin: 0;
    animation: subfieldPanelIn 280ms cubic-bezier(.18, 1.0, .32, 1);
}
.subfield-panel[hidden] { display: none; }
@keyframes subfieldPanelIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

.subfield-panel-cmd {
    margin: 0 0 6px;
    color: var(--text-faint);
    font-size: 0.78rem;
}
.subfield-lede {
    margin: 0 0 var(--space-3);
    font-family: var(--font-body);
    font-size: 1rem;
    line-height: 1.55;
    color: var(--text);
}
.subfield-lede strong {
    color: var(--subfield-1);
    font-weight: 600;
}
.subfield-lede em {
    color: var(--warm);
    font-style: italic;
}

/* Voice quote — the warm "human beat" line at the bottom of each
   panel. Italic, left-bordered with the subfield accent so it reads
   as Patrick's voice cutting through the cmd-style data above. */
.subfield-quote {
    margin: var(--space-3) 0 0;
    padding: 8px var(--space-3);
    border-left: 2px solid color-mix(in oklab, var(--subfield-1) 60%, transparent);
    background: color-mix(in oklab, var(--subfield-1) 5%, transparent);
    font-family: var(--font-body);
    font-size: 0.92rem;
    line-height: 1.5;
    color: var(--text-muted);
    font-style: italic;
    border-radius: 0 var(--radius) var(--radius) 0;
}
.subfield-quote em {
    color: var(--text);
    font-style: italic;
}
.subfield-quote code {
    font-family: var(--font-mono);
    font-size: 0.88em;
    color: var(--subfield-3);
    background: color-mix(in oklab, var(--subfield-1) 12%, transparent);
    padding: 0 4px;
    border-radius: 3px;
    font-style: normal;
}

@media (max-width: 720px) {
    /* Tabs row: keep all four on one line by hiding the meta label
       (the year/timing — fine context on desktop, blocks horizontal
       fit at phone width) and giving each tab an equal share of
       the row via flex: 1. */
    .subfield-tabs {
        gap: 4px;
        flex-wrap: nowrap;
    }
    .subfield-tab {
        flex: 1 1 0;
        min-width: 0;
        padding: 6px 8px;
        font-size: 0.7rem;
        justify-content: flex-start;
        gap: 3px;
    }
    .subfield-tab-meta { display: none; }
    .subfield-panels { min-height: 0; }
    .subfield-lede { font-size: 0.92rem; }
    .subfield-quote { font-size: 0.84rem; padding: 6px 10px; }
}

.subfield-cta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    background: var(--accent);
    color: var(--bg-deepest);
    border-radius: 6px;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 0.95rem;
    letter-spacing: -0.005em;
    transition: all var(--t-base) var(--ease);
    box-shadow: 0 4px 16px var(--accent-glow);
}
.subfield-cta:hover {
    background: var(--accent-hover);
    color: var(--bg-deepest);
    transform: translateY(-1px);
    box-shadow: 0 6px 24px var(--accent-glow);
}
.subfield-cta span {
    transition: transform var(--t-base) var(--ease);
    display: inline-flex;
    align-items: center;
}
/* Old broad-translate-on-hover rule kept off for the redesigned CTA —
   we want only the arrow to move, not every span (brackets/cmd/target). */

@media (max-width: 900px) {
    .subfield-grid {
        grid-template-columns: 1fr;
        gap: var(--space-5);
    }
}

/* ──────────────────────────────────────────────────────
   Publications timeline (3D scrubbable)
   ────────────────────────────────────────────────────── */

/* ── Publications: file-manager window ─────────────────────────── */
.pub-window {
    position: relative;
    width: 100%;
    max-width: var(--content);
    margin: var(--space-4) auto var(--space-2);
    background: color-mix(in oklab, var(--bg-deepest) 58%, transparent);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lift);
    overflow: hidden;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
.pub-window-chrome {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: 10px 16px;
    background: color-mix(in oklab, var(--bg-elev-2) 50%, transparent);
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-faint);
    letter-spacing: 0.04em;
}
.pub-window-path { color: var(--text-muted); }
.pub-window-meta { color: var(--text-faint); }

.pub-files {
    list-style: none;
    margin: 0;
    padding: var(--space-4);
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: var(--space-3) var(--space-3);
}
.pub-file {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 12px 8px 10px;
    border-radius: var(--radius);
    border: 1px solid transparent;
    cursor: pointer;
    transition: background 180ms var(--ease), border-color 180ms var(--ease), transform 180ms var(--ease);
    text-align: center;
}
.pub-file:hover, .pub-file:focus-visible {
    background: color-mix(in oklab, var(--accent) 6%, transparent);
    border-color: color-mix(in oklab, var(--accent) 28%, transparent);
    transform: translateY(-2px);
    outline: none;
}
.pub-file-icon {
    position: relative;
    width: 56px;
    height: 70px;
    background: linear-gradient(180deg,
        color-mix(in oklab, var(--accent-deep) 16%, var(--bg-elev-2)) 0%,
        color-mix(in oklab, var(--accent-deep) 6%, var(--bg-elev-1)) 100%);
    border: 1px solid color-mix(in oklab, var(--accent) 25%, var(--border));
    border-radius: 4px 10px 4px 4px;
    flex-shrink: 0;
    /* Folded-corner effect (top-right triangle) */
    clip-path: polygon(0 0, 70% 0, 100% 22%, 100% 100%, 0 100%);
}
.pub-file-icon::before {
    /* The fold itself — a small darker triangle */
    content: '';
    position: absolute;
    top: 0; right: 0;
    width: 30%;
    height: 25%;
    background: color-mix(in oklab, var(--accent-deep) 30%, var(--bg-elev-2));
    clip-path: polygon(0 0, 100% 100%, 100% 0);
    border-bottom: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
    border-left:   1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.pub-file-icon::after {
    /* Three faux text-lines on the page body, monospace feel */
    content: '';
    position: absolute;
    left: 12%; right: 22%; top: 42%;
    height: 1.5px;
    background: color-mix(in oklab, var(--accent) 60%, transparent);
    box-shadow:
        0  8px 0 0 color-mix(in oklab, var(--accent) 45%, transparent),
        0 16px 0 0 color-mix(in oklab, var(--accent) 30%, transparent);
}
.pub-file-name {
    font-family: var(--font-mono);
    font-size: 0.74rem;
    line-height: 1.3;
    color: var(--text-muted);
    word-break: break-word;
    max-width: 100%;
}
.pub-file:hover .pub-file-name,
.pub-file:focus-visible .pub-file-name {
    color: var(--text);
}
.pub-file-tag {
    position: absolute;
    top: 6px;
    right: 6px;
    font-family: var(--font-mono);
    font-size: 0.58rem;
    letter-spacing: 0.1em;
    color: var(--text-faint);
    padding: 2px 6px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: color-mix(in oklab, var(--bg-deepest) 40%, transparent);
    text-transform: uppercase;
    pointer-events: none;
}
/* Per-kind colour cues on the icon and tag */
.pub-file[data-pub-kind="thesis"] .pub-file-icon {
    border-color: color-mix(in oklab, var(--warm) 50%, var(--border));
}
.pub-file[data-pub-kind="thesis"] .pub-file-icon::after {
    background: color-mix(in oklab, var(--warm) 65%, transparent);
    box-shadow:
        0  8px 0 0 color-mix(in oklab, var(--warm) 50%, transparent),
        0 16px 0 0 color-mix(in oklab, var(--warm) 35%, transparent);
}
.pub-file[data-pub-kind="thesis"] .pub-file-tag {
    color: var(--warm);
    border-color: color-mix(in oklab, var(--warm) 35%, var(--border));
}
.pub-file--feature .pub-file-icon {
    border-color: var(--accent);
    box-shadow: 0 0 14px color-mix(in oklab, var(--accent) 35%, transparent);
}
.pub-file--feature .pub-file-tag {
    color: var(--accent);
    border-color: color-mix(in oklab, var(--accent) 50%, var(--border));
}
.pub-file--draft .pub-file-icon {
    /* Dashed border to read as "in-progress / unpublished" */
    border-style: dashed;
    border-color: color-mix(in oklab, var(--text-faint) 70%, var(--border));
    background: linear-gradient(180deg,
        color-mix(in oklab, var(--text-faint) 12%, var(--bg-elev-2)) 0%,
        color-mix(in oklab, var(--bg-elev-1) 90%, transparent) 100%);
}
.pub-file--draft .pub-file-tag {
    color: var(--text-faint);
    border-style: dashed;
}

/* ── Mobile: collapse the publications grid into a Linux-ls list view —
   one row per file as an explicit 4-column grid:
   tag | icon | filename (ellipsis) | audio-badge (or empty) ── */
@media (max-width: 720px) {
    .pub-files {
        display: block;
        gap: 0;
        padding: var(--space-2) 0;
    }
    .pub-file {
        display: grid !important;
        grid-template-columns: 52px 16px minmax(0, 1fr) 20px;
        grid-template-rows: auto;
        grid-template-areas: "tag icon name badge";
        align-items: center;
        column-gap: var(--space-2);
        padding: 8px var(--space-3);
        border-radius: 0;
        border: 0;
        border-top: 1px solid color-mix(in oklab, var(--border) 50%, transparent);
        text-align: left;
    }
    .pub-file:first-child { border-top: 0; }
    .pub-file:hover, .pub-file:focus-visible {
        transform: none;
        background: color-mix(in oklab, var(--accent) 8%, transparent);
        border-color: color-mix(in oklab, var(--accent) 28%, transparent);
    }
    .pub-file-tag {
        position: static;
        grid-area: tag;
        text-align: center;
        font-size: 0.6rem;
        padding: 2px 6px;
    }
    .pub-file-icon {
        grid-area: icon;
        width: 14px;
        height: 18px;
        border-radius: 2px 4px 2px 2px;
    }
    .pub-file-icon::after {
        left: 18%; right: 24%; top: 35%;
        height: 1px;
        box-shadow:
            0  4px 0 0 color-mix(in oklab, var(--accent) 45%, transparent),
            0  8px 0 0 color-mix(in oklab, var(--accent) 30%, transparent);
    }
    .pub-file-name {
        grid-area: name;
        min-width: 0;
        font-size: 0.72rem;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        text-align: left;
    }
    /* Widen the publications window on mobile — closer to screen edges. */
    .publications .section,
    .publications.section,
    #publications.section { width: calc(100vw - 1.5rem); }
    .pub-window { max-width: 100%; }
}

/* Hover popover — terminal-styled tooltip */
.pub-popover {
    position: absolute;
    z-index: 60;
    width: min(360px, 80vw);
    padding: 12px 14px 14px;
    background: color-mix(in oklab, var(--bg-deepest) 92%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--border-strong));
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift), 0 0 0 4px color-mix(in oklab, var(--accent) 5%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    pointer-events: none;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 140ms var(--ease), transform 140ms var(--ease);
}
.pub-popover[data-visible="true"] {
    opacity: 1;
    transform: translateY(0);
}
.pub-popover[hidden] { display: none; }
.pub-popover-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: 8px;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-faint);
}
.pub-popover-tag { color: var(--accent); }
.pub-popover-title {
    font-family: var(--font-display);
    font-size: 0.96rem;
    line-height: 1.3;
    color: var(--text);
    margin: 0 0 6px;
    font-weight: 500;
    letter-spacing: -0.012em;
}
.pub-popover-venue {
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-muted);
    margin: 0 0 10px;
}
.pub-popover-blurb {
    font-family: var(--font-body);
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-muted);
    margin: 0 0 10px;
}
.pub-popover-actions {
    list-style: none;
    margin: var(--space-2) 0 0;
    padding: var(--space-2) 0 0;
    border-top: 1px dashed var(--border);
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-muted);
    letter-spacing: 0.02em;
}
.pub-popover-actions li {
    display: flex;
    align-items: baseline;
    gap: 8px;
}
.pub-popover-actions-glyph {
    color: var(--accent);
    width: 0.9em;
    flex-shrink: 0;
    font-weight: 600;
}

/* Draft-content styling inside the doc-window viewer */
.draft-content .doc-meta-block {
    border-bottom: 1px solid var(--border);
    padding-bottom: var(--space-3);
    margin-bottom: var(--space-4);
}
.draft-content .doc-byline {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-faint);
    margin: 0;
}
.draft-section {
    margin: 0 0 var(--space-4);
}
.draft-section h2 {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 var(--space-2);
    font-weight: 500;
}
.draft-section p {
    font-family: var(--font-body);
    font-size: 0.95rem;
    line-height: 1.65;
    color: var(--text-muted);
    margin: 0 0 var(--space-2);
    max-width: 72ch;
}
.draft-section strong { color: var(--text); font-weight: 600; }
.draft-section em     { color: var(--accent); font-style: normal; font-weight: 500; }
.draft-toc, .draft-points {
    margin: 0 0 var(--space-2);
    padding-left: var(--space-4);
    color: var(--text-muted);
    font-size: 0.92rem;
    line-height: 1.6;
    max-width: 72ch;
}
.draft-toc li, .draft-points li {
    margin-bottom: 8px;
}
.draft-quote {
    margin: var(--space-2) 0;
    padding: var(--space-3) var(--space-4);
    border-left: 3px solid var(--accent);
    background: color-mix(in oklab, var(--accent) 5%, transparent);
    border-radius: 0 var(--radius) var(--radius) 0;
}
.draft-quote p {
    font-family: var(--font-body);
    font-size: 1.02rem;
    color: var(--text);
    margin: 0 0 6px;
    font-style: italic;
}
.draft-quote cite {
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-faint);
    font-style: normal;
}
.draft-cta-row {
    margin: var(--space-4) 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.draft-cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    font-family: var(--font-mono);
    font-size: 0.84rem;
    color: var(--accent);
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
    border-radius: var(--radius);
    text-decoration: none;
    transition: background 180ms var(--ease), color 180ms var(--ease);
}
.draft-cta:hover {
    background: color-mix(in oklab, var(--accent) 22%, transparent);
    color: var(--text);
}

/* ── Tools rail — audio summary + per-paper chat live above the doc ── */
.paper-tools {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4) var(--space-4);
    border-bottom: 1px solid var(--border-strong);
    background: color-mix(in oklab, var(--bg-elev-2) 28%, transparent);
}
.paper-tools:empty,
.paper-tools:has(.paper-audio[hidden]):has(.paper-chat[hidden]) {
    display: none;
}

/* ── Per-paper chat (inside paper viewers) ─────────────────────── */
.paper-chat {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    background: color-mix(in oklab, var(--bg-elev-2) 35%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-family: var(--font-mono);
}
.paper-chat[hidden] { display: none; }
.paper-chat-head {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: var(--space-2);
    min-width: 0;
}
.paper-chat-title {
    font-size: 0.84rem;
    color: var(--text-muted);
    letter-spacing: 0.02em;
    /* Allow long publication titles to wrap onto a second line rather
       than truncate with an ellipsis. */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: break-word;
    line-height: 1.4;
}
.paper-chat-title em {
    font-style: normal;
    font-weight: 500;
    color: var(--accent);
}
.paper-chat-hint {
    font-size: 0.72rem;
    color: var(--text-faint);
    line-height: 1.4;
    font-family: var(--font-body);
    font-style: italic;
}
.paper-chat-log {
    /* Chat-app layout: fixed-viewport scroll container, new replies
       file in above and bottom-anchor scrolls to latest. Input form
       is pinned below this block, so the log holds the conversation
       history and the form is always reachable at the bottom. */
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-height: 60px;
    max-height: 40vh;
    overflow-y: auto;
    padding: var(--space-2) 0;
    font-size: 0.86rem;
    line-height: 1.55;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, var(--accent) 28%, transparent) transparent;
}
.paper-chat-log:empty {
    /* No min-height when empty — head + form sit close together
       until the first exchange lands. */
    min-height: 0;
    padding: 0;
}
.paper-chat-log .pchat-q,
.paper-chat-log .pchat-a {
    margin: 0;
    padding: 6px 10px;
    border-radius: 4px;
    word-break: break-word;
    white-space: pre-wrap;
}
.paper-chat-log .pchat-q {
    color: var(--text-muted);
    background: color-mix(in oklab, var(--bg-deepest) 35%, transparent);
}
.paper-chat-log .pchat-q::before {
    content: '~/sociologix ';
    color: var(--accent);
}
.paper-chat-log .pchat-a {
    color: var(--text);
    border-left: 2px solid color-mix(in oklab, var(--accent) 45%, transparent);
    padding-left: var(--space-3);
}
.paper-chat-log .pchat-a.pchat-thinking {
    color: var(--accent);
    opacity: 0.6;
    font-style: italic;
    animation: logPulse 1.4s ease-in-out infinite;
}
.paper-chat-log .pchat-a.pchat-error {
    color: #ff8a85;
    border-left-color: rgba(255, 138, 133, 0.4);
}
.paper-chat-form {
    /* Form pinned at the bottom of the chat block (compose area).
       Border-top divider in teal — same chat-app pattern as
       support-chat-form. New replies file in above this line; the
       prompt is always reachable at the bottom regardless of how
       deep the conversation gets. */
    display: flex;
    align-items: baseline;
    gap: 0;
    border: 0;
    border-top: 1px dashed color-mix(in oklab, var(--accent) 22%, var(--border));
    padding: 12px 0 8px;
    margin-top: var(--space-2);
    cursor: text;
    font-size: 0.92rem;
    color: var(--text-muted);
}
.paper-chat-input-wrap {
    position: relative;
    flex: 1 1 auto;
    display: inline-block;
    min-width: 0;
    margin-left: 4px;
}
.paper-chat-input {
    width: 100%;
    background: transparent;
    border: 0;
    outline: 0;
    color: var(--text);
    font-family: inherit;
    font-size: inherit;
    padding: 0 0 0 14px;
    margin: 0;
    caret-color: var(--accent);
}
/* Blinking caret pinned to the LEFT edge of the input area, before
   the placeholder text. Visible only in the idle state — disappears
   on focus or once anything is typed. Mirrors the float-term pattern. */
.paper-chat-input-wrap > .term-caret {
    position: absolute;
    left: 0;
    top: 0;
    pointer-events: none;
    color: var(--accent);
}
.paper-chat-input:focus ~ .term-caret,
.paper-chat-input:not(:placeholder-shown) ~ .term-caret { display: none; }
.paper-chat-input::placeholder {
    color: var(--text-faint);
    opacity: 0.55;
    font-style: italic;
}
.paper-chat-input[disabled] {
    color: var(--text-faint);
    cursor: not-allowed;
}
.paper-chat-pivot {
    margin: var(--space-2) 0 0;
    padding: var(--space-2);
    border: 1px dashed color-mix(in oklab, var(--accent) 30%, var(--border));
    border-radius: var(--radius);
    background: color-mix(in oklab, var(--accent) 5%, transparent);
    font-family: var(--font-body);
    font-size: 0.84rem;
    color: var(--text-muted);
    text-align: center;
}
.paper-chat-pivot[hidden] { display: none; }
.paper-chat-pivot a {
    color: var(--accent);
    border-bottom: 1px solid currentColor;
}
.paper-chat-pivot a:hover { color: var(--accent-hover); }

/* Paper-chat save button — appears once the user has had at least one
   exchange in this paper viewer. Reuses the .term-export-btn pill
   styling but right-aligned in its own row. Class-based visibility
   matching .term-export-row so JS toggling is consistent. */
.paper-chat-actions {
    display: none;
    justify-content: flex-end;
    margin-top: var(--space-2);
}
.paper-chat-actions.is-visible { display: flex; }

/* ── Audio summary player ──────────────────────────────────────── */
.paper-audio {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    border: 1px solid color-mix(in oklab, var(--accent) 30%, var(--border));
    border-radius: var(--radius);
    background: color-mix(in oklab, var(--accent) 5%, transparent);
    font-family: var(--font-mono);
}
.paper-audio[hidden] { display: none; }
.paper-audio-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 8px;
    font-size: 0.78rem;
    color: var(--text-muted);
}
.paper-audio-eyebrow em {
    font-style: normal;
    font-weight: 500;
    color: var(--accent);
}
.paper-audio-meta {
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
    font-size: 0.74rem;
    letter-spacing: 0.04em;
}
.paper-audio-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.paper-audio-play {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 1px solid color-mix(in oklab, var(--accent) 60%, transparent);
    background: color-mix(in oklab, var(--accent) 12%, transparent);
    color: var(--accent);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: inherit;
    font-size: 0.88rem;
    transition: background 180ms var(--ease), color 180ms var(--ease), transform 180ms var(--ease);
}
.paper-audio-play:hover {
    background: color-mix(in oklab, var(--accent) 28%, transparent);
    color: var(--text);
}
.paper-audio-play:active {
    transform: scale(0.95);
}
.paper-audio-play[data-state="playing"] {
    background: color-mix(in oklab, var(--accent) 22%, transparent);
}
.paper-audio-viz {
    flex: 1 1 auto;
    height: 48px;
    width: 100%;
    display: block;
    border-radius: 4px;
    background: color-mix(in oklab, var(--bg-deepest) 30%, transparent);
}
.paper-audio-seek {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 4px;
    margin-top: 8px;
    background: color-mix(in oklab, var(--accent) 18%, var(--border));
    border-radius: 2px;
    cursor: pointer;
    outline: 0;
}
.paper-audio-seek::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--accent);
    border: 0;
    cursor: pointer;
    box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 50%, transparent);
}
.paper-audio-seek::-moz-range-thumb {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--accent);
    border: 0;
    cursor: pointer;
}

/* ── Audio badge on pub-file (only when data-pub-audio is set) ── */
.pub-file-audio-badge {
    position: absolute;
    /* Anchored to the file ICON's top-left corner — the icon is
       flex-centred in the tile, so we offset from the tile's
       horizontal centre (icon is 56px wide, half = 28px). The badge
       sits half on the icon, half off, overlapping the corner. */
    top: 4px;
    left: calc(50% - 36px);
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--warm);
    color: var(--bg-deepest);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    z-index: 2;         /* sits above the file icon */
    /* Subtle pulse so the eye finds it */
    animation: audioBadgePulse 2.4s ease-in-out infinite;
}
.pub-file-audio-badge svg {
    width: 13px;
    height: 13px;
    display: block;
}
@keyframes audioBadgePulse {
    0%, 100% { box-shadow: 0 0 0 2px var(--bg-elev-1), 0 0  6px color-mix(in oklab, var(--warm) 30%, transparent); }
    50%      { box-shadow: 0 0 0 2px var(--bg-elev-1), 0 0 16px color-mix(in oklab, var(--warm) 70%, transparent); }
}

/* Mobile ls-list: audio badge lands in the explicit grid column 4 of
   .pub-file (see the @media block in the publications section). The
   !important overrides defeat the desktop absolute positioning. */
@media (max-width: 720px) {
    .pub-file-audio-badge {
        position: static !important;
        top: auto !important;
        left: auto !important;
        grid-area: badge;
        justify-self: end;
        width: 16px !important;
        height: 16px !important;
        animation: none !important;
        box-shadow: none !important;
    }
    .pub-file-audio-badge svg {
        width: 9px;
        height: 9px;
    }
}

/* ── Legacy timeline styles (kept for any residual references) ── */
.pub-timeline {
    position: relative;
    margin: 0 calc(50% - 50vw);
    padding: var(--space-5) 0 var(--space-6);
    overflow: hidden;
    display: none;  /* old timeline is replaced by .pub-window */
}

.pub-track {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-5) max(var(--space-5), calc(50vw - var(--content) / 2 + var(--space-3)));
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: thin;
    scrollbar-color: var(--accent-deep) transparent;
    perspective: 1400px;
    perspective-origin: 50% 50%;
}
.pub-track::-webkit-scrollbar { height: 8px; }
.pub-track::-webkit-scrollbar-track { background: var(--bg-elev-1); border-radius: 4px; }
.pub-track::-webkit-scrollbar-thumb { background: var(--accent-deep); border-radius: 4px; }

.pub-card {
    flex: 0 0 320px;
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    transition: transform var(--t-base) var(--ease), background var(--t-base) var(--ease), border-color var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease);
    scroll-snap-align: center;
    transform-style: preserve-3d;
    transform-origin: center center;
    will-change: transform;
    min-height: 280px;
}
.pub-card[data-active="true"] {
    background: var(--bg-elev-2);
    border-color: var(--border-accent);
    box-shadow: var(--shadow-glow);
    z-index: 5;
}
.pub-card[data-active="true"] .pub-year {
    color: var(--accent);
}
.pub-card:hover {
    background: var(--bg-elev-2);
    border-color: var(--border-strong);
}

.pub-year {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono);
    font-size: 0.82rem;
    color: var(--text-muted);
    letter-spacing: 0.12em;
    font-weight: 500;
}
.pub-year::before {
    content: '';
    width: 6px;
    height: 6px;
    background: currentColor;
    border-radius: 50%;
}

.pub-title {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 1.05rem;
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--text);
    margin: 0;
}

.pub-journal {
    font-family: var(--font-serif);
    font-size: 0.92rem;
    color: var(--text-muted);
    font-style: italic;
}

.pub-link {
    margin-top: auto;
    align-self: flex-start;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--accent);
    padding-top: var(--space-2);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    border-bottom: 1px solid transparent;
    transition: border-color var(--t-base) var(--ease);
}
.pub-link:hover { border-bottom-color: var(--accent); }

.pub-card--draft .pub-year::after {
    content: ' · DRAFT';
    color: var(--warm);
    margin-left: 0.5em;
    letter-spacing: 0.12em;
}

.pub-card--feature {
    flex-basis: 380px;
    border-color: var(--border-accent);
    background: linear-gradient(135deg, var(--accent-soft) 0%, transparent 50%), var(--bg-elev-1);
}
.pub-card--feature .pub-year::after {
    content: ' · DISSERTATION';
    color: var(--accent);
    margin-left: 0.5em;
    letter-spacing: 0.12em;
}
.pub-card--feature .pub-title {
    font-size: 1.18rem;
}

.pub-rail {
    position: relative;
    height: 2px;
    margin: 0 auto;
    width: var(--content);
    background: var(--border);
    border-radius: 2px;
    overflow: visible;
}
.pub-rail-fill {
    position: absolute;
    inset: 0 auto 0 0;
    background: linear-gradient(90deg, var(--accent-deep), var(--accent));
    border-radius: 2px;
    width: 0%;
    transition: width var(--t-fast) var(--ease);
}
.pub-rail-handle {
    position: absolute;
    top: 50%;
    left: 0%;
    transform: translate(-50%, -50%);
    width: 14px;
    height: 14px;
    background: var(--accent);
    border-radius: 50%;
    box-shadow: 0 0 0 4px var(--accent-soft), 0 0 12px var(--accent-glow);
    transition: left var(--t-fast) var(--ease);
}

.pub-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    margin-top: var(--space-3);
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-muted);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.pub-btn {
    width: 36px;
    height: 36px;
    display: grid;
    place-items: center;
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--text-muted);
    transition: all var(--t-base) var(--ease);
}
.pub-btn:hover {
    background: var(--bg-elev-2);
    border-color: var(--border-accent);
    color: var(--accent);
}

/* ──────────────────────────────────────────────────────
   Visualizations bento
   ────────────────────────────────────────────────────── */

.viz-bento {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: minmax(clamp(150px, 20vh, 220px), auto);
    gap: clamp(var(--space-2), 1.2vw, var(--space-3));
    max-width: var(--content);
    margin: 0 auto;
}

.bento-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    text-align: left;
    width: 100%;
    padding: 0;
    color: var(--text);
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    cursor: pointer;
    transition: all var(--t-base) var(--ease);
}
.bento-tile:hover {
    background: var(--bg-elev-2);
    border-color: var(--border-strong);
    transform: translateY(-3px) scale(1.018);
    box-shadow: var(--shadow-lift);
    z-index: 2;
}
.bento-tile:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.bento-shot {
    background:
        radial-gradient(120% 80% at 50% 100%, rgba(93, 219, 232, 0.08), transparent 55%),
        var(--bg-elev-2);
    background-image:
        linear-gradient(135deg, rgba(255,255,255,0.02) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.02) 50%, rgba(255,255,255,0.02) 75%, transparent 75%);
    background-size: 8px 8px;
    overflow: hidden;
    position: relative;
}
.bento-shot-icon {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    color: var(--text-faint);
    opacity: 0.4;
    transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.bento-tile:hover .bento-shot-icon { opacity: 0.7; transform: scale(1.04); }

/* Real screenshot inside a bento tile — same dimmed/aged filter +
   slight hover-zoom as .card-shot-img so the visualization tiles
   read consistent with the NUM cards. */
.bento-shot-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 1;
    opacity: 0.62;
    filter: saturate(0.55) brightness(0.78) contrast(0.92) sepia(0.18);
    transition:
        transform 420ms var(--ease),
        opacity var(--t-base) var(--ease),
        filter var(--t-base) var(--ease);
}
.bento-tile:hover .bento-shot-img {
    transform: scale(1.05);
    opacity: 0.85;
    filter: saturate(0.7) brightness(0.88) contrast(0.95) sepia(0.12);
}
/* The combo tile contains TWO independently clickable halves —
   suppress the parent-tile hover effect so each half reacts only when
   its own button is hovered, not when the cursor is anywhere on the
   parent frame. */
.bento-tile--combo:hover .bento-shot-img {
    transform: none;
    opacity: 0.62;
    filter: saturate(0.55) brightness(0.78) contrast(0.92) sepia(0.18);
}
.bento-combo-tile:hover .bento-shot-img {
    transform: scale(1.05);
    opacity: 0.85;
    filter: saturate(0.7) brightness(0.88) contrast(0.95) sepia(0.12);
}
.bento-shot-meta {
    position: absolute;
    inset: auto var(--space-3) var(--space-3) var(--space-3);
    z-index: 2;
}
.bento-shot-tag {
    font-family: var(--font-mono);
    font-size: 0.64rem;
    color: var(--text-muted);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    padding: 3px 6px;
    background: color-mix(in oklab, var(--bg-deepest) 70%, transparent);
    border: 1px solid var(--border);
    border-radius: 3px;
    backdrop-filter: blur(6px);
}

.bento-eyebrow {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--accent);
    letter-spacing: 0.14em;
    text-transform: uppercase;
}
/* Inline @handle link inside an eyebrow (e.g. RA's @FrancoisLachapelle).
   Stays inside the eyebrow's mono/uppercase voice, but warm-toned so
   the co-author credit reads as a person, not just label text. */
.bento-eyebrow-link {
    color: var(--warm);
    text-decoration: none;
    border-bottom: 1px dashed color-mix(in oklab, var(--warm) 50%, transparent);
    transition: color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease);
}
.bento-eyebrow-link:hover {
    color: var(--text);
    border-bottom-color: var(--text);
}

.bento-title {
    font-family: var(--font-display);
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--text);
    margin: 0;
}
.bento-title em {
    color: var(--accent);
    font-family: inherit;
    font-style: normal;
    font-weight: 600;
}

.bento-desc {
    color: var(--text-muted);
    font-size: 0.93rem;
    line-height: 1.55;
    margin: 0;
}

.bento-cta,
.bento-cta-small {
    color: var(--accent);
    font-family: var(--font-mono);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    transition: transform var(--t-base) var(--ease);
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}
.bento-cta { font-size: 0.78rem; }
.bento-cta-small { font-size: 0.74rem; }
.bento-cta span,
.bento-cta-small span { transition: transform var(--t-base) var(--ease); }
.bento-tile:hover .bento-cta span,
.bento-tile:hover .bento-cta-small span { transform: translateX(3px); }

.bento-badge {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 2;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--bg-deepest);
    background: var(--accent);
    padding: 3px 10px;
    border-radius: 999px;
    font-weight: 600;
}

.bento-tile--hero {
    grid-column: span 2;
    grid-row: span 2;
    background:
        radial-gradient(80% 80% at 100% 0%, rgba(93, 219, 232, 0.10), transparent 60%),
        radial-gradient(80% 80% at 0% 100%, rgba(232, 169, 106, 0.06), transparent 60%),
        var(--bg-elev-1);
    border-color: var(--border-strong);
    min-height: clamp(340px, 44vh, 480px);
}
.bento-tile--hero .bento-shot {
    flex: 1;
    border-bottom: 1px solid var(--border);
    min-height: clamp(140px, 20vh, 240px);
}
.bento-tile--hero .bento-meta {
    padding: var(--space-3) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.bento-tile--hero .bento-title {
    /* Match the combo tile's title scale so the hero reads as a peer
       rather than a louder twin. */
    font-size: clamp(0.95rem, 1.2vw, 1.15rem);
    letter-spacing: -0.01em;
    line-height: 1.25;
    margin: 0;
}
.bento-tile--hero .bento-desc {
    /* Mono note styling — same family as .bento-combo-note so the hero
       and the combo tile share a typographic register. */
    margin: 0;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    line-height: 1.4;
    color: var(--text-muted);
    letter-spacing: 0.01em;
}
.bento-tile--hero .bento-cta {
    margin-top: 2px;
}

.bento-tile--med {
    grid-column: span 2;
    grid-row: span 1;
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    min-height: clamp(150px, 20vh, 220px);
}
.bento-tile--med .bento-desc {
    /* Match the mono note register used by the hero tile + combo
       header so bento descriptions read as a single typographic
       family across all sizes. */
    font-family: var(--font-mono);
    font-size: 0.7rem;
    line-height: 1.45;
    color: var(--text-muted);
    letter-spacing: 0.01em;
}
.bento-tile--med .bento-meta {
    padding: clamp(var(--space-3), 1.5vw, var(--space-4));
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--space-2);
}
.bento-tile--med .bento-title { font-size: clamp(0.95rem, 1.2vw, 1.18rem); }
.bento-tile--med .bento-shot {
    border-left: 1px solid var(--border);
}

/* Combo tile — two independently-clickable visualisations grouped
   as one bento. Header sits above a 2-col row; each half is a button
   with its own image, label, and modal-target. Defined after
   .bento-tile--med so its display rules override --med's grid. */
.bento-tile--combo {
    cursor: default;
    padding: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    grid-template-columns: none;
}
.bento-tile--combo .bento-combo-head {
    padding: var(--space-3) var(--space-4) var(--space-2);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.bento-tile--combo .bento-combo-head .bento-eyebrow {
    display: block;
    margin-bottom: 4px;
}
.bento-tile--combo .bento-combo-head .bento-title {
    font-size: clamp(0.95rem, 1.2vw, 1.15rem);
    margin: 0;
    line-height: 1.25;
}
.bento-combo-note {
    margin: 6px 0 0;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    line-height: 1.4;
    color: var(--text-muted);
    letter-spacing: 0.01em;
}
.bento-combo-note em {
    font-style: italic;
    color: var(--warm);
}
.bento-combo-row {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    min-height: 0;
}
.bento-combo-tile {
    position: relative;
    background: transparent;
    border: 0;
    cursor: pointer;
    padding: 0;
    overflow: hidden;
    color: inherit;
    text-align: left;
    display: block;
    min-height: 130px;
    transition: background var(--t-base) var(--ease);
}
.bento-combo-tile + .bento-combo-tile {
    border-left: 1px solid var(--border);
}
.bento-combo-tile:hover {
    background: color-mix(in oklab, var(--accent) 6%, transparent);
}
/* Combo year tag — minimal amber label in the top-left of each tile,
   no background pill, just the date and dimension. */
.bento-combo-year {
    position: absolute;
    top: var(--space-2);
    left: var(--space-3);
    z-index: 2;
    color: var(--warm);
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
    pointer-events: none;
}

.bento-tile--small {
    grid-column: span 1;
    grid-row: span 1;
    min-height: clamp(150px, 20vh, 220px);
}
.bento-tile--small .bento-shot {
    flex: 1;
    border-bottom: 1px solid var(--border);
    min-height: clamp(80px, 12vh, 130px);
}
.bento-tile--small .bento-tile-foot {
    padding: clamp(var(--space-2), 1vw, var(--space-3)) clamp(var(--space-3), 1.5vw, var(--space-4));
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.bento-tile--small .bento-title {
    font-size: clamp(0.86rem, 1vw, 1rem);
}
.bento-tile--small .bento-eyebrow {
    font-size: 0.66rem;
}
.bento-tile--small .bento-desc {
    font-size: 0.78rem;
    line-height: 1.45;
    margin: 0;
    color: var(--text-muted);
}

/* Tall variant — for tiles that look better as portrait rectangles
   (phone-screenshot lead, e.g. Send It! and Celiac Saver). Spans
   two rows of the grid; the .bento-shot fills the extra height so
   the screenshot reads at proper proportion. */
.bento-tile--tall {
    grid-row: span 2;
    min-height: clamp(320px, 42vh, 460px);
}
.bento-tile--tall .bento-shot {
    flex: 1;
    min-height: clamp(180px, 26vh, 320px);
}
.bento-tile--tall .bento-shot-img {
    /* Phone screenshots are portrait — let them show top-aligned
       so the title bar / first-screen content is what's visible
       in the cropped area. */
    object-position: center top;
}

/* Wide variant — for med-sized tiles that want to span the full
   row instead of half. Currently unused by default but available. */
.bento-tile--wide {
    grid-column: span 2;
}

/* Block variant — 2 cols × 2 rows. Same internal layout as hero
   (image on top, meta below) but without the featured aura. Used
   to balance the bento into a clean rectangle by absorbing
   leftover cells. Identical small-tile foot styling. */
.bento-tile--block {
    grid-column: span 2;
    grid-row: span 2;
    min-height: clamp(320px, 42vh, 460px);
}
.bento-tile--block .bento-shot {
    flex: 1;
    border-bottom: 1px solid var(--border);
    min-height: clamp(160px, 24vh, 280px);
}
.bento-tile--block .bento-tile-foot {
    padding: var(--space-3) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.bento-tile--block .bento-title {
    font-size: clamp(0.95rem, 1.2vw, 1.15rem);
    line-height: 1.25;
    letter-spacing: -0.01em;
}

/* Intermediate viewport (~1100-1200px wide). Narrow band where the
   default 4-col is genuinely cramped but 2-col is too sparse. Pulled
   tighter than the original draft because 1280-wide screens looked
   weirdly stretched at 3-col span — they keep the 4-col layout now. */
@media (max-width: 1200px) and (min-width: 1081px) {
    .viz-bento { grid-template-columns: repeat(3, 1fr); }
    .bento-tile--hero  { grid-column: span 3; grid-row: span 2; }
    .bento-tile--med   { grid-column: span 3; }
    .bento-tile--small { grid-column: span 1; grid-row: span 1; }
    .bento-tile--tall  { grid-row: span 2; }
    .bento-tile--block { grid-column: span 3; grid-row: span 2; }
    .bento-tile--wide  { grid-column: span 3; }
}
@media (max-width: 1080px) {
    .viz-bento { grid-template-columns: repeat(2, 1fr); }
    .bento-tile--hero { grid-column: span 2; grid-row: span 2; min-height: 460px; }
    .bento-tile--med  { grid-column: span 2; }
    .bento-tile--small { grid-column: span 1; grid-row: span 1; }
    .bento-tile--tall { grid-row: span 2; }
    .bento-tile--block { grid-column: span 2; grid-row: span 2; }
}
@media (max-width: 640px) {
    .viz-bento { grid-template-columns: 1fr; gap: var(--space-3); }
    .bento-tile--hero, .bento-tile--med, .bento-tile--small,
    .bento-tile--tall, .bento-tile--block {
        grid-column: span 1; grid-row: span 1;
    }
    .bento-tile--hero { min-height: 440px; }
    .bento-tile--med {
        display: flex;
        flex-direction: column-reverse;
        min-height: 360px;
    }
    .bento-tile--med .bento-shot {
        border-left: 0;
        border-bottom: 1px solid var(--border);
        min-height: 160px;
    }
    .bento-tile--small { min-height: 240px; }
    .bento-tile--tall  { min-height: 380px; }
    .bento-tile--block { min-height: 360px; }

    /* Combo tile (--med + --combo) — column-reverse from --med flips the
       header below the image row. Restore the natural top-down stack so
       the title/note sits above the two clickable halves on mobile. */
    .bento-tile--combo {
        flex-direction: column;
    }
    .bento-tile--combo .bento-combo-row {
        grid-template-columns: 1fr;
    }
    .bento-tile--combo .bento-combo-tile {
        min-height: 160px;
    }
    .bento-tile--combo .bento-combo-tile + .bento-combo-tile {
        border-left: 0;
        border-top: 1px solid var(--border);
    }
}

/* ──────────────────────────────────────────────────────
   Hire
   ────────────────────────────────────────────────────── */

.section.visualizations {
    padding: clamp(var(--space-6), 8vh, var(--space-8)) 0;
}
.section.visualizations .section-header {
    margin-bottom: clamp(var(--space-4), 4vh, var(--space-7));
}

/* Snap-mode bento layout — DESKTOP / TABLET ONLY.
   On mobile (<720px), the snap-fit-to-viewport rules collapse every
   tile into a sliver since the small viewport can't accommodate 6 tiles
   in one screen. Mobile falls back to the natural-flow @media block at
   max-width:640px above which gives each tile its proper min-height
   and stacks them vertically with a regular page scroll. */
@media (min-width: 720px) {
    /* Snap-mode: lock the visuals section to one viewport. The section
       header reserves its natural height; the bento fills the rest as
       a 4-equal-row grid so all 6 tiles tile into a clean rectangle
       within the section's bottom edge instead of overflowing past it. */
    html[data-snap="true"] #visuals {
        min-height: 100vh;
        min-height: 100svh;
        max-height: 100vh;
        max-height: 100svh;
        display: flex;
        flex-direction: column;
        /* Same reserved top/bottom zones as every other snap-mode section
           so the bento's lower edge always lines up with the global
           bottom limit (clear of the sub-view + ask-patrick chips). */
        padding-top:    clamp(80px, 11vh, 130px);
        padding-bottom: clamp(110px, 14vh, 160px);
        overflow: hidden;
    }
    /* Short OR narrow viewport pass for #visuals specifically.
       Mirrors the global snap-mode disable at the same conditions:
       short heights (older laptops) AND narrow widths (iPad Mini portrait).
       Drops the 100vh cap + overflow:hidden so the bento grid grows
       with content instead of squashing combo + med tiles to ~150px. */
    @media (max-height: 900px), (max-width: 1080px) {
        html[data-snap="true"] #visuals {
            min-height: auto;
            max-height: none;
            overflow: visible;
            padding-top:    clamp(56px, 7vh, 88px);
            padding-bottom: clamp(72px, 9vh, 110px);
        }
    }
    html[data-snap="true"] #visuals .section-header {
        flex-shrink: 0;
    }
    html[data-snap="true"] #visuals .viz-bento {
        flex: 1 1 auto;
        min-height: 0;
        /* Four equal rows; minmax(0, 1fr) prevents content from pushing
           a row taller than its share. */
        grid-template-rows: repeat(4, minmax(0, 1fr));
        grid-auto-rows: minmax(0, 1fr);
    }
    /* Override the larger min-heights from default tile variants — in
       snap mode, height comes from the grid row sizing, not the tile. */
    html[data-snap="true"] #visuals .bento-tile--hero,
    html[data-snap="true"] #visuals .bento-tile--med,
    html[data-snap="true"] #visuals .bento-tile--small,
    html[data-snap="true"] #visuals .bento-tile--tall,
    html[data-snap="true"] #visuals .bento-tile--block {
        min-height: 0;
    }
    /* Tiles can't grow taller than their grid cell; their .bento-shot
       should also not push the tile past its allotted height. */
    html[data-snap="true"] #visuals .bento-tile {
        overflow: hidden;
    }
    html[data-snap="true"] #visuals .bento-tile--hero .bento-shot,
    html[data-snap="true"] #visuals .bento-tile--block .bento-shot,
    html[data-snap="true"] #visuals .bento-tile--tall .bento-shot {
        min-height: 0;
    }

    /* Snap-mode bento content polish — keep CTAs visible and combo labels
       readable without covering the underlying image. */

    /* Hero: push CTA to the bottom of the meta; clamp the long description
       so the CTA is never hidden by overflow. */
    html[data-snap="true"] #visuals .bento-tile--hero .bento-meta {
        justify-content: space-between;
    }
    html[data-snap="true"] #visuals .bento-tile--hero .bento-desc {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
    html[data-snap="true"] #visuals .bento-tile--hero .bento-cta {
        margin-top: auto;
        flex-shrink: 0;
    }

    /* Med tile: clamp description (meta already uses space-between, CTA
       pins naturally). */
    html[data-snap="true"] #visuals .bento-tile--med:not(.bento-tile--combo) .bento-desc {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }

    /* Combo tile: drop the header note in snap mode to give the images
       their full row of breathing space. The amber year tag in each tile
       carries the labelling on its own. */
    html[data-snap="true"] #visuals .bento-combo-note {
        display: none;
    }
}

.hire-body {
    max-width: var(--content-narrow);
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* Hire section lede is the pitch — let it breathe wider than the
   default 60ch so it tracks the section title rather than ending
   mid-thought with empty space to the right. */
#hire .section-sub {
    max-width: 88ch;
}

.hire-lede {
    font-size: 1.1rem;
    line-height: 1.7;
    color: var(--text-muted);
}
.hire-lede em { color: var(--text); font-style: italic; }

/* ─── Hire closer — minimalist closing block, not a hero echo.
   One ask-input that routes to the float-term, two doors out. ─── */
.hire-closer-block {
    max-width: var(--content-narrow);
    margin: 0 auto;
    padding: var(--space-4) var(--space-5);
    background: color-mix(in oklab, var(--bg-elev-1) 70%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    font-family: var(--font-mono);
    font-size: 0.92rem;
}
.hire-cmd {
    margin: 0 0 var(--space-2);
    font-size: 0.86rem;
    color: var(--text-muted);
}
.hire-cmd--doors { margin-top: var(--space-4); }
.hire-cmd .term-prompt { color: var(--accent); font-weight: 500; }
.hire-cmd .term-cwd    { color: var(--warm); font-weight: 500; }
.hire-cmd .term-typed  { color: var(--text); }

/* Ask-input — same caret pattern as the floating terminal. */
.hire-ask {
    display: flex;
    align-items: baseline;
    gap: 0;
    margin: 0 0 var(--space-3);
    padding: 8px 0;
    border-bottom: 1px dashed var(--border);
    cursor: text;
    font-family: var(--font-mono);
    font-size: 0.94rem;
    color: var(--text-muted);
}
.hire-ask .term-prompt { color: var(--accent); font-weight: 500; }
.hire-ask .term-cwd    { color: var(--warm); }
.hire-ask-wrap {
    position: relative;
    flex: 1 1 auto;
    display: inline-block;
    min-width: 0;
    margin-left: 4px;
}
.hire-ask-input {
    width: 100%;
    background: transparent;
    border: 0;
    outline: 0;
    color: var(--text);
    font-family: inherit;
    font-size: inherit;
    padding: 0 0 0 14px;
    margin: 0;
    caret-color: var(--accent);
}
.hire-ask-input::placeholder {
    color: var(--text-faint);
    opacity: 0.55;
    font-style: italic;
}
.hire-ask-wrap > .term-caret {
    position: absolute;
    left: 0;
    top: 0;
    pointer-events: none;
    color: var(--accent);
}
.hire-ask-input:focus ~ .term-caret,
.hire-ask-input:not(:placeholder-shown) ~ .term-caret { display: none; }

.hire-signoff {
    margin: var(--space-4) 0 0;
    padding-top: var(--space-3);
    border-top: 1px dashed var(--border);
    font-size: 0.84rem;
    color: var(--text-muted);
}
.hire-signoff em {
    font-style: normal;
    color: var(--accent);
}

/* ─── Hire/Support pane grid — two-column card layout below the
   ask-one-last-thing prompt. The prompt + ask form span the full
   width above; these two panes split the row beneath. Mobile
   collapses to a single column.  ─── */
.hire-panes {
    margin: var(--space-4) 0 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
}
.hire-pane {
    display: flex;
    flex-direction: column;
    /* Stronger visual presence than the original "60% bg-elev-1" tint —
       solid elevated bg + subtle radial gradient highlight in the
       upper-left corner gives the pane real weight without being loud. */
    background:
        radial-gradient(120% 80% at 0% 0%, rgba(93, 219, 232, 0.05), transparent 55%),
        var(--bg-elev-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    transition: border-color var(--t-base) var(--ease),
                background var(--t-base) var(--ease),
                transform var(--t-base) var(--ease),
                box-shadow var(--t-base) var(--ease);
    /* Initial state for scroll-into-view reveal — invisible + translated
       down. JS IntersectionObserver adds .is-revealed when the pane
       enters the viewport (~20% threshold), triggering the keyframes
       below. Apple iPad-Air-style scroll-driven choreography. */
    opacity: 0;
    transform: translateY(28px);
}
.hire-pane.is-revealed {
    /* Hire pane: calm, elegant fade-up. No overshoot — sits in its
       lane while the support pane (next selector) gets the bounce. */
    animation: hire-pane-reveal 700ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
.hire-pane--support.is-revealed {
    /* Support pane: same trigger but ~150ms later for stagger, more
       drama on the curve (1.34 second control point = overshoot), and
       includes a slight scale-up. Subtle differential from the hire
       pane's calm reveal — eye lands here second, then settles. */
    animation: hire-pane-reveal-bounce 850ms cubic-bezier(0.34, 1.34, 0.64, 1) 180ms both;
}
@keyframes hire-pane-reveal {
    0%   { opacity: 0; transform: translateY(28px); }
    100% { opacity: 1; transform: translateY(0); }
}
@keyframes hire-pane-reveal-bounce {
    0%   { opacity: 0; transform: translateY(40px) scale(0.95); }
    55%  { opacity: 1; transform: translateY(-6px) scale(1.018); }
    78%  { transform: translateY(2px) scale(0.996); }
    100% { opacity: 1; transform: translateY(0) scale(1); }
}

/* Typewriter reveal on the support pane's "support-the-work" command —
   fires after the pane's bounce has settled (~1000ms delay). 16 chars
   total; steps(16) = one character at a time. Cursor (a 2px right
   border) stays solid during typing, blinks 4 times after, then fades
   out cleanly. Hire pane keeps its static prompt — only the support
   pane gets this layered emphasis. */
.hire-pane--support .hire-pane-header .term-typed {
    display: inline-block;
    overflow: hidden;
    white-space: nowrap;
    vertical-align: bottom;
    border-right: 2px solid transparent;
    width: 16ch; /* "support-the-work" = 16 chars in monospace */
}
.hire-pane--support.is-revealed .hire-pane-header .term-typed {
    width: 0;
    border-right-color: var(--warm);
    animation:
        typewriter-support 900ms steps(16, end) 1000ms forwards,
        cursor-blink-warm 600ms step-end 1000ms 4,
        cursor-fade 300ms ease 3400ms forwards;
}
@keyframes typewriter-support {
    to { width: 16ch; }
}
@keyframes cursor-blink-warm {
    0%, 100% { border-right-color: var(--warm); }
    50%      { border-right-color: transparent; }
}
@keyframes cursor-fade {
    to { border-right-color: transparent; }
}

@media (prefers-reduced-motion: reduce) {
    .hire-pane {
        opacity: 1;
        transform: none;
    }
    .hire-pane.is-revealed,
    .hire-pane--support.is-revealed {
        animation: none;
    }
    .hire-pane--support.is-revealed .hire-pane-header .term-typed {
        animation: none;
        width: auto;
        border-right: 0;
    }
}
.hire-pane:hover {
    border-color: color-mix(in oklab, var(--accent) 50%, var(--border));
    transform: translateY(-2px);
    /* Toned down: 30%→20% drop, dropped the thin ring entirely.
       Lift + border-color shift now carries the hover signal. */
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
}
.hire-pane--support {
    /* Amber-tinted radial highlight + slightly stronger gradient than
       the hire pane (this is the "look here" door). Apple-class
       ambient shadow at rest: layered drops + thin amber halo ring +
       a wide soft glow that signals importance without movement. */
    background:
        radial-gradient(120% 80% at 0% 0%, rgba(232, 169, 106, 0.08), transparent 55%),
        radial-gradient(80% 60% at 100% 100%, rgba(232, 169, 106, 0.04), transparent 60%),
        var(--bg-elev-1);
    box-shadow:
        0 4px 18px rgba(0, 0, 0, 0.22),
        0 0 0 1px rgba(232, 169, 106, 0.08),
        0 0 32px rgba(232, 169, 106, 0.04);
    /* Spring-y easing on the hover transition so the lift feels
       intentional, not abrupt. */
    transition: border-color 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
                background var(--t-base) var(--ease),
                transform 320ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
                box-shadow 320ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.hire-pane--support:hover {
    border-color: var(--warm);
    transform: translateY(-3px) scale(1.008);
    /* Toned down dramatically: kept the drop shadow + thin halo,
       removed the wide 48px ambient amber glow that read as Web 2.0
       big-glow. Lift carries the eye now. */
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.32),
        0 0 0 1px rgba(232, 169, 106, 0.18);
}
.hire-pane-header {
    margin: 0 0 var(--space-3);
    font-size: 0.86rem;
    line-height: 1.5;
}
.hire-pane-body {
    margin: 0 0 var(--space-4);
    color: var(--text-muted);
    font-family: var(--font-body);
    font-size: 0.98rem;
    line-height: 1.65;
    flex: 1;
}
.hire-pane-cta {
    /* Flex (was grid). Grid's column constraints made the label wrap
       to two lines on the support pane because [support-how-to] is
       much wider than [mail], eating into the label's column. Flex
       lets each child take its natural width and the glyph anchor
       to the right via margin-left:auto. */
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 14px var(--space-4);
    background: color-mix(in oklab, var(--accent) 6%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 50%, var(--border));
    border-radius: var(--radius);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.92rem;
    text-decoration: none;
    text-align: left;
    cursor: pointer;
    width: 100%;
    transition: background var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                transform var(--t-base) var(--ease),
                box-shadow var(--t-base) var(--ease);
}
.hire-pane-cta-tag {
    flex-shrink: 0;
    white-space: nowrap;
}
.hire-pane-cta-label {
    flex-shrink: 1;
    white-space: nowrap;
}
.hire-pane-cta-glyph {
    margin-left: auto; /* push glyph to the right edge */
    flex-shrink: 0;
}
.hire-pane-cta:hover {
    background: color-mix(in oklab, var(--accent) 18%, transparent);
    border-color: var(--accent);
    transform: translateX(3px);
    /* Toned down — 14px→8px blur, 18%→10% alpha. */
    box-shadow: 0 3px 8px rgba(93, 219, 232, 0.10);
}
.hire-pane-cta--support {
    /* Subtle inner-gradient sheen at rest — like a soft directional
       light on the button surface. Hover motion now matches the
       [mail] button's clean slide-right (no lift, no spring) so both
       Hire panes feel like the same family. Amber colors stay as
       the support identity layer. */
    background:
        linear-gradient(180deg, rgba(232, 169, 106, 0.12) 0%, rgba(232, 169, 106, 0.04) 100%),
        var(--bg-elev-1);
    border-color: color-mix(in oklab, var(--warm) 60%, var(--border));
    box-shadow: 0 2px 10px rgba(232, 169, 106, 0.10);
    transition: background var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                transform var(--t-base) var(--ease),
                box-shadow var(--t-base) var(--ease);
}
.hire-pane-cta--support:hover {
    background:
        linear-gradient(180deg, rgba(232, 169, 106, 0.22) 0%, rgba(232, 169, 106, 0.06) 100%),
        var(--bg-elev-1);
    border-color: var(--warm);
    transform: translateX(3px);  /* matches [mail] — quiet slide-right, no lift */
    box-shadow: 0 3px 8px rgba(232, 169, 106, 0.10);  /* matches [mail]'s subtler shadow */
}
.hire-pane-cta-tag {
    color: var(--accent);
    letter-spacing: 0.04em;
    font-weight: 500;
    line-height: 1;
}
.hire-pane-cta--support .hire-pane-cta-tag {
    color: var(--warm);
}
.hire-pane-cta-label {
    color: var(--text);
    font-weight: 500;
    line-height: 1;
}
.hire-pane-cta-glyph {
    /* inline-flex + line-height:1 normalizes alignment between the
       text cells (mono font, baseline-aligned) and the icon cell
       (Font Awesome glyph, has its own metrics). Without this the
       icon sits ~2px off from the label baseline. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    color: var(--accent);
    opacity: 0.75;
    font-size: 0.92em;
    transition: transform var(--t-base) var(--ease),
                opacity var(--t-base) var(--ease);
}
.hire-pane-cta--support .hire-pane-cta-glyph {
    color: var(--warm);
}
.hire-pane-cta--support .hire-pane-cta-glyph i {
    /* Font Awesome glyph inherits font-size from the wrapper now,
       no override — keeps it consistent with the ⏎ text glyph the
       hire pane uses. */
    font-size: 1em;
    line-height: 1;
}
.hire-pane-cta:hover .hire-pane-cta-glyph {
    transform: translateX(3px);
    opacity: 1;
}
.hire-pane-secondary {
    margin: var(--space-2) 0 0;
    font-size: 0.84rem;
    color: var(--text-faint);
}
.hire-pane-secondary a {
    color: var(--text-muted);
    text-decoration: underline;
    text-decoration-color: color-mix(in oklab, var(--text-faint) 50%, transparent);
    text-underline-offset: 3px;
}
.hire-pane-secondary a:hover {
    color: var(--accent);
    text-decoration-color: var(--accent);
}
.hire-pane-secondary .ext-icon {
    width: 0.78em;
    height: 0.78em;
    vertical-align: -0.05em;
}

/* Mobile: stack panes into a single column. */
@media (max-width: 720px) {
    .hire-panes {
        grid-template-columns: 1fr;
        gap: var(--space-3);
    }
}

/* ─── Doorways list — terminal-log-row aesthetic ─── */
.hire-doors {
    list-style: none;
    margin: var(--space-2) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.hire-door {
    display: grid;
    grid-template-columns: minmax(86px, auto) 1fr auto auto;
    align-items: baseline;
    gap: var(--space-3);
    padding: 10px var(--space-3);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.86rem;
    text-decoration: none;
    transition:
        background var(--t-base) var(--ease),
        border-color var(--t-base) var(--ease),
        transform var(--t-base) var(--ease);
}
.hire-door:hover {
    background: color-mix(in oklab, var(--accent) 8%, transparent);
    border-color: color-mix(in oklab, var(--accent) 35%, var(--border));
    transform: translateX(2px);
}
.hire-door--primary {
    border-color: color-mix(in oklab, var(--accent) 40%, transparent);
    background: color-mix(in oklab, var(--accent) 6%, transparent);
}
.hire-door--primary:hover {
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    border-color: var(--accent);
}
.hire-door-tag {
    color: var(--accent);
    letter-spacing: 0.04em;
    font-weight: 500;
}
.hire-door-label {
    font-family: var(--font-display);
    font-size: 1rem;
    font-weight: 500;
    color: var(--text);
    letter-spacing: -0.01em;
}
.hire-door-detail {
    color: var(--text-muted);
    font-size: 0.78rem;
    letter-spacing: 0.02em;
}
.hire-door-glyph {
    color: var(--text-faint);
    transition: color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
    display: inline-flex;
    align-items: center;
}
.hire-door:hover .hire-door-glyph {
    color: var(--accent);
    transform: translate(2px, -2px);
}
.hire-door--primary .hire-door-glyph {
    color: var(--accent);
}

.hire-closer {
    margin: var(--space-3) 0 0;
    font-family: var(--font-mono);
    font-size: 0.86rem;
    color: var(--text-muted);
    line-height: 1.55;
}
.hire-closer em {
    font-style: normal;
    color: var(--accent);
    font-weight: 500;
}

@media (max-width: 700px) {
    .hire-door {
        grid-template-columns: auto 1fr auto;
        grid-template-rows: auto auto;
        column-gap: var(--space-2);
        row-gap: 0;
        padding: 8px 10px;
        font-size: 0.8rem;
        align-items: baseline;
    }
    .hire-door-tag    { grid-column: 1; grid-row: 1; font-size: 0.74rem; }
    .hire-door-label  { grid-column: 2; grid-row: 1; font-size: 0.92rem; }
    .hire-door-glyph  { grid-column: 3; grid-row: 1 / span 2; align-self: center; }
    .hire-door-detail {
        grid-column: 1 / span 2;
        grid-row: 2;
        margin-top: 2px;
        font-size: 0.72rem;
        word-break: break-word;
    }
    /* Tighter closer-block; the input row was clipping the placeholder
       because the ~/sociologix prefix ate too much width — shrink the
       prefix and let the input use the rest. */
    .hire-closer-block {
        padding: var(--space-3) var(--space-3);
        font-size: 0.86rem;
    }
    .hire-cmd { font-size: 0.78rem; }
    .hire-cmd--doors { margin-top: var(--space-3); }
    .hire-ask {
        font-size: 0.84rem;
        padding: 6px 0;
        margin-bottom: var(--space-2);
    }
    .hire-ask-input::placeholder { font-size: 0.78rem; }
    .hire-doors { gap: 6px; }
    .hire-signoff {
        margin-top: var(--space-3);
        padding-top: var(--space-2);
        font-size: 0.78rem;
    }
}

.hire-cta-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--space-3);
    margin-top: var(--space-5);
}

.hire-cta {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto;
    gap: var(--space-1) var(--space-3);
    padding: var(--space-4);
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    color: var(--text);
    transition: all var(--t-base) var(--ease);
}
.hire-cta:hover {
    background: var(--bg-elev-2);
    border-color: var(--border-strong);
    transform: translateY(-2px);
    box-shadow: var(--shadow-card);
}
.hire-cta-label {
    grid-column: 1;
    grid-row: 1;
    font-family: var(--font-display);
    font-size: 1.1rem;
    font-weight: 500;
    letter-spacing: -0.01em;
    color: var(--text);
}
.hire-cta-detail {
    grid-column: 1;
    grid-row: 2;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-muted);
    letter-spacing: 0.04em;
}
.hire-cta-arrow {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
    font-size: 1.4rem;
    color: var(--accent);
    transition: transform var(--t-base) var(--ease);
}
.hire-cta:hover .hire-cta-arrow { transform: translate(3px, -3px); }

.hire-cta--primary {
    background: linear-gradient(135deg, var(--accent-soft), transparent 60%), var(--bg-elev-1);
    border-color: var(--border-accent);
}
.hire-cta--primary:hover {
    background: linear-gradient(135deg, var(--accent-glow), transparent 60%), var(--bg-elev-2);
}

/* ──────────────────────────────────────────────────────
   Footer
   ────────────────────────────────────────────────────── */

.site-footer {
    border-top: 1px solid var(--border);
    margin-top: var(--space-7);
    padding: var(--space-5) 0;
}
.footer-inner {
    width: var(--content);
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--space-3);
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
}
.footer-brand {
    font-family: var(--font-display);
    font-style: normal;
    color: var(--text);
    font-weight: 500;
    letter-spacing: -0.01em;
}
.footer-meta a { color: var(--text-muted); }
.footer-meta a:hover { color: var(--accent); }

/* ──────────────────────────────────────────────────────
   Modal
   ────────────────────────────────────────────────────── */

/* Modal — wears the same doc-window OS chrome as the CV/Resume viewer.
   Frame, chrome bar, and body all reuse .doc-window-frame /
   .doc-window-chrome / .doc-window-body styles via shared classes. */
.modal {
    border: 0;
    padding: 0;
    background: transparent;
    color: inherit;
    max-width: min(880px, calc(100vw - 2rem));
    width: 100%;
    border-radius: var(--radius-lg);
    overflow: visible;
}
.modal::backdrop {
    /* Just a tiny dim — desktop-popup feel, not a focus-stealing scrim. */
    background: rgba(0, 0, 0, 0.20);
}
.modal[open] .modal-inner { animation: modalIn var(--t-base) var(--ease-out); }
@keyframes modalIn {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Reset .modal-inner — actual frame styles come from .doc-window-frame. */
.modal-inner {
    position: relative;
    padding: 0;
    overflow: hidden;
    /* Local height — modal is shorter than CV viewer (no iframe content). */
    height: auto;
    max-height: 84vh;
}
/* Override doc-window-frame's fixed-viewer dimensions when nested inside a
   centred modal dialog. */
.modal .doc-window-frame {
    width: 100%;
    height: auto;
    max-height: 84vh;
    animation: none;
}
.modal .doc-window-body {
    /* Plain text content, not an iframe — give it the body's elevated panel. */
    background: var(--bg-elev-1);
    padding: 0;
}
.modal-body-inner {
    padding: var(--space-5) var(--space-6) var(--space-6);
}

.modal-title {
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: clamp(1.2rem, 2.2vw, 1.55rem);
    line-height: 1.25;
    letter-spacing: -0.01em;
    margin: 0 0 var(--space-3);
    color: var(--text);
}

.modal-eyebrow {
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--warm);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    margin: 0 0 var(--space-3);
}

.modal-body p {
    font-family: var(--font-mono);
    color: var(--text-muted);
    line-height: 1.6;
    margin: 0 0 var(--space-3);
    font-size: 0.84rem;
}
.modal-body p strong {
    color: var(--warm);
    font-weight: 600;
    font-family: var(--font-mono);
    font-style: normal;
    font-size: 1em;
}

/* Aside paragraph inside modals — the 'in development' / 'GitHub
   when ready' framing block. Read as a small note, not body copy. */
.modal-body .modal-aside {
    margin: var(--space-3) 0 0;
    padding: var(--space-2) var(--space-3);
    border-left: 2px solid color-mix(in oklab, var(--warm) 50%, transparent);
    background: color-mix(in oklab, var(--warm) 6%, transparent);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--text-muted);
    border-radius: 0 var(--radius) var(--radius) 0;
}
.modal-body .modal-aside strong {
    color: var(--warm);
    font-family: inherit;
    font-style: normal;
    font-size: 1em;
}

.modal-cta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding: var(--space-2) var(--space-4);
    background: var(--accent);
    color: var(--bg-deepest);
    font-family: var(--font-display);
    font-weight: 600;
    border-radius: 6px;
    transition: all var(--t-base) var(--ease);
}
.modal-cta:hover {
    background: var(--accent-hover);
    color: var(--bg-deepest);
    transform: translate(2px, -2px);
}

/* ─── Modal image row — two side-by-side supporting screenshots
   inside a viz modal (used by the NeuroDevNet feature). Stacks on
   narrow viewports. ─── */
.modal-image-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
    margin: var(--space-3) 0;
}
.modal-image-row figure {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    background: var(--bg-elev-1);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.modal-image-row img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    background: var(--bg-elev-2);
}
.modal-image-row figcaption {
    padding: 6px 10px 8px;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}
@media (max-width: 720px) {
    .modal-image-row { grid-template-columns: 1fr; gap: var(--space-2); }
}

/* Quad variant — four phone screenshots in a single row across (used
   by Send It! and Celiac Saver). Phone screenshots are tall portrait,
   so we let them keep their natural aspect ratio rather than the
   16:10 crop the default uses for landscape network/Sankey shots.
   Collapses to 2 across at tablet, 1 across at mobile. */
.modal-image-row--quad {
    grid-template-columns: repeat(4, 1fr);
}
.modal-image-row--quad img {
    aspect-ratio: auto;
    object-fit: contain;
    max-height: 380px;
    background: var(--bg-deepest);
}
@media (max-width: 1024px) {
    .modal-image-row--quad { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
    .modal-image-row--quad {
        grid-template-columns: 1fr;
        gap: var(--space-2);
    }
    .modal-image-row--quad img { max-height: 320px; }
}

/* ── Screenshot lightbox — native <dialog> opened via showModal() so it
   sits on the browser's top layer (above the parent paper modal).
   Closes on backdrop click, Escape (native), or the × button. */
.img-lightbox {
    /* Reset <dialog> UA defaults — we want our own full-screen layout. */
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    max-width: none;
    max-height: none;
    margin: 0;
    padding: clamp(20px, 5vh, 60px) clamp(20px, 5vw, 80px);
    border: 0;
    background: rgba(0, 0, 0, 0.85);
    color: inherit;
    overflow: hidden;
}
.img-lightbox[open] {
    display: flex;
    align-items: center;
    justify-content: center;
    animation: vizOverlayIn var(--t-base) var(--ease-out);
}
.img-lightbox::backdrop {
    background: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.img-lightbox-window {
    position: relative;
    max-width: 100%;
    max-height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: vizWindowIn 260ms cubic-bezier(.18, 1.0, .32, 1);
}
.img-lightbox-img {
    max-width: 100%;
    max-height: calc(100vh - 80px);
    object-fit: contain;
    border-radius: 6px;
    border: 1px solid var(--border-strong);
    box-shadow: 0 36px 80px rgba(0,0,0,0.6);
    display: block;
}
.img-lightbox-close {
    position: absolute;
    top: -12px;
    right: -12px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-deepest);
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    color: var(--text-muted);
    font-size: 0.75rem;
    cursor: pointer;
    transition: background var(--t-base), color var(--t-base);
    z-index: 2;
}
.img-lightbox-close:hover { background: var(--bg-elev-2); color: var(--text); }

/* Mark modal images in --quad rows as clickable */
.modal-image-row--quad figure {
    cursor: zoom-in;
}

/* The pitch line — italic + warm accent so it reads as the "elevator
   pitch" wrap-up before the CTA, distinct from the analytical body. */
.modal-pitch {
    margin: var(--space-3) 0 var(--space-2);
    padding-left: var(--space-3);
    border-left: 2px solid color-mix(in oklab, var(--accent) 50%, transparent);
    color: var(--text);
    font-size: 0.95rem;
    line-height: 1.55;
}
.modal-pitch em { color: var(--accent); font-style: italic; }
@media (max-width: 720px) {
    .modal-pitch { font-size: 0.85rem; }
}

/* ─── Mobile heads-up modal — one-time nudge for narrow viewports.
   Styled like the doc-window: chrome bar with × close on the right,
   monospace path on the left, body text below. ─── */
.mobile-warn {
    border: 0;
    padding: 0;
    background: transparent;
    color: inherit;
    max-width: min(440px, calc(100vw - 2rem));
    width: 100%;
    border-radius: 10px;
    overflow: visible;
}
.mobile-warn::backdrop {
    background: rgba(0, 0, 0, 0.35);
}
.mobile-warn[open] .mobile-warn-inner { animation: modalIn var(--t-base) var(--ease-out); }
.mobile-warn-inner {
    position: relative;
    background: var(--bg-elev-1);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(0,0,0,0.55);
}
.mobile-warn-chrome {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 8px 12px;
    background: color-mix(in oklab, var(--bg-elev-2) 50%, transparent);
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-muted);
    letter-spacing: 0.02em;
}
.mobile-warn-path {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mobile-warn-path em {
    color: var(--accent);
    font-style: normal;
}
.mobile-warn-close {
    flex-shrink: 0;
    background: transparent;
    border: 0;
    color: var(--text-faint);
    font-family: inherit;
    font-size: 0.78rem;
    line-height: 1;
    padding: 4px 6px;
    cursor: pointer;
    transition: color var(--t-base) var(--ease);
}
.mobile-warn-close:hover { color: #ff5f57; }
.mobile-warn-body-wrap {
    padding: var(--space-4) var(--space-5);
}
.mobile-warn-body {
    font-family: var(--font-mono);
    font-size: 0.82rem;
    line-height: 1.6;
    color: var(--text-muted);
    margin: 0 0 var(--space-2);
}
.mobile-warn-body:last-child { margin-bottom: 0; }
.mobile-warn-body strong {
    color: var(--accent);
    font-weight: 500;
}
.mobile-warn-body--quiet {
    color: var(--text-faint);
    font-size: 0.74rem;
    margin-top: var(--space-2);
}

/* ─── Viz overlay — windowed iframe shell, floats above the parent
   modal rather than taking the whole viewport. Sized like a modal. */
.viz-overlay {
    position: fixed;
    inset: 0;
    z-index: 260;
    /* Faint scrim only — no blur. Reads as a desktop popup sitting
       on top of the page rather than a focus-stealing modal. */
    background: rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(12px, 2.5vh, 24px);
    animation: vizOverlayIn var(--t-base) var(--ease-out);
}
.viz-overlay[hidden] { display: none; }
@keyframes vizOverlayIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.viz-overlay-window {
    position: relative;
    width: 100%;
    max-width: min(1480px, 96vw);
    height: clamp(620px, 94vh, 1080px);
    background: var(--bg-deepest);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow:
        0 36px 80px rgba(0,0,0,0.6),
        0 0 0 1px rgba(255,255,255,0.02);
    animation: vizWindowIn 280ms cubic-bezier(.18, 1.0, .32, 1);
    display: flex;
    flex-direction: column;
}

/* Chrome bar — same Linux-window pattern as the doc/term windows.
   Min/Max buttons are decorative; only Close is wired. */
.viz-overlay-chrome {
    flex-shrink: 0;
}

/* Side aside — sits to the LEFT of the main window. Mini linux-style
   window with chrome bar, populated by postMessage from the iframed
   visualization. Stays vertically centred to the main window's height. */
.viz-overlay-aside {
    flex-shrink: 0;
    width: 280px;
    max-height: clamp(560px, 86vh, 820px);
    margin-right: 16px;
    display: flex;
    flex-direction: column;
    background: color-mix(in oklab, var(--bg-elev-1) 96%, transparent);
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 18px 40px rgba(0,0,0,0.45);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    animation: vizAsideIn 280ms cubic-bezier(.18, 1.0, .32, 1);
}
.viz-overlay-aside[hidden] { display: none; }
@keyframes vizAsideIn {
    from { opacity: 0; transform: translateX(-12px); }
    to   { opacity: 1; transform: translateX(0); }
}
.viz-aside-chrome {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    background: color-mix(in oklab, var(--bg-elev-2) 50%, transparent);
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--text-muted);
    flex-shrink: 0;
}
.viz-aside-tag { color: var(--warm); letter-spacing: 0.04em; }
.viz-aside-meta { color: var(--text-faint); margin-left: auto; }
.viz-aside-close {
    background: transparent; border: 0; color: var(--text-faint);
    font-family: inherit; font-size: 11px; line-height: 1;
    padding: 2px 4px; cursor: pointer;
    transition: color 180ms;
}
.viz-aside-close:hover { color: #ff5f57; }
.viz-aside-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 12px 14px 14px;
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.55;
    color: var(--text);
}
.viz-aside-title {
    font-family: var(--font-display);
    font-size: 15px;
    font-weight: 500;
    color: var(--teal);
    letter-spacing: -0.01em;
    margin-bottom: 4px;
}
.viz-aside-blurb {
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 10px;
    line-height: 1.5;
}
.viz-aside-list {
    list-style: none;
    padding: 0; margin: 0;
    border-top: 1px dashed var(--border);
    padding-top: 10px;
}
.viz-aside-list li {
    padding: 4px 0;
    color: var(--text);
    font-size: 11.5px;
}
.viz-aside-list li + li {
    border-top: 1px dashed var(--border);
}
.viz-aside-prov {
    color: var(--text-faint);
    font-size: 10px;
    letter-spacing: 0.04em;
    margin-left: 8px;
    text-transform: uppercase;
}
.viz-aside-note {
    /* Span the full body width and align with the items above. The
       teal top-border doubles as the divider when both sections are
       visible; when only the note is active, it just reads as the
       section's top edge. */
    margin: 12px -14px 0;
    padding: 12px 14px 0;
    border-top: 1px solid var(--teal);
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.55;
    color: var(--text-muted);
}
.viz-aside-note[hidden] { display: none; }
.viz-aside-note-tag {
    display: inline-block;
    color: var(--teal);
    letter-spacing: 0.06em;
    font-size: 10px;
    text-transform: uppercase;
    margin-bottom: 4px;
}

/* Note-only mode (no class roster, just dataset definition) — drop
   the divider line and tighten the spacing so the note carries the
   panel on its own. */
.viz-overlay-aside[data-note-only="true"] .viz-aside-title,
.viz-overlay-aside[data-note-only="true"] .viz-aside-blurb,
.viz-overlay-aside[data-note-only="true"] .viz-aside-list {
    display: none;
}
.viz-overlay-aside[data-note-only="true"] .viz-aside-note {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}
.viz-aside-note p {
    margin: 0;
    color: var(--text-muted);
}
.viz-aside-note p strong {
    color: var(--text);
    font-weight: 500;
}

@media (max-width: 1100px) {
    /* Below desktop, the aside would crowd the window; hide it
       and let the iframed page show its own inline class roster
       above the chart instead. */
    .viz-overlay-aside { display: none !important; }
}
@keyframes vizWindowIn {
    from { opacity: 0; transform: translateY(12px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0)    scale(1); }
}
.viz-overlay-frame {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    border: 0;
    background: transparent;
    display: block;
}
body[data-viz-open="true"] {
    overflow: hidden;
}

/* Maximized state — fills the viewport, no margin/border-radius.
   Triggered by the □ chrome button; the ─ button restores default. */
.viz-overlay[data-viz-max="true"] {
    padding: 0;
}
.viz-overlay[data-viz-max="true"] .viz-overlay-window {
    width: 100%;
    max-width: 100%;
    height: 100%;
    border-radius: 0;
    border: 0;
}

@media (max-width: 720px) {
    /* Full-screen on mobile — no padding, no border-radius, no border. */
    .viz-overlay { padding: 0; }
    .viz-overlay-window {
        width: 100%;
        max-width: 100%;
        height: 100%;
        border-radius: 0;
        border: 0;
    }
    .viz-overlay-chrome { font-size: 0.66rem; padding: 8px 10px; }
}

/* ──────────────────────────────────────────────────────
   Reveal-on-scroll
   ────────────────────────────────────────────────────── */

[data-reveal] {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}
[data-reveal][data-revealed="true"] {
    opacity: 1;
    transform: none;
}

/* ──────────────────────────────────────────────────────
   Responsive
   ────────────────────────────────────────────────────── */

@media (max-width: 900px) {
    .primary-nav { display: none; }
    .nav-toggle  { display: inline-flex; }
    .header-chat-btn { display: inline-flex; }
    .header-support-btn { display: inline-flex; margin-left: 8px; }
    .section-dots { display: none; }
    .site-header { padding: var(--space-3) var(--space-4); }

    .section { padding: var(--space-7) 0; }
    .section-header { margin-bottom: var(--space-5); }

    .hero { padding: max(var(--space-7), 14vh) var(--space-4) var(--space-7); }

    .hire-cta-grid { grid-template-columns: 1fr; }
    .footer-inner { flex-direction: column; }

    /* Disable snap on mobile — too aggressive */
    html[data-snap="true"] { scroll-snap-type: none; }
}

@media (max-width: 540px) {
    :root {
        --space-7: 4rem;
        --space-8: 5rem;
    }
    .card-grid { gap: var(--space-3); }
    .hero-socials a { font-size: 0.74rem; padding: var(--space-1) var(--space-2); }
    /* Modal frame — reset the inherited padding so the body's own
       padding controls text density. The previous 32×24px frame
       padding stacked with the body's 32×48px to eat ~72px of
       horizontal space on phones. */
    .modal-inner { padding: 0; }
    .hero-scroll { display: none; }
    .pub-card { flex-basis: 280px; }
    .pub-card--feature { flex-basis: 300px; }
}

/* ─── Mobile fullscreen modals ────────────────────────────────────────
   Edge-to-edge on phones so the chrome bar + content claim every pixel
   instead of floating in a small centred window. The chrome bar still
   carries the close ✕ at the top right. */
@media (max-width: 720px) {
    /* Native <dialog> sizing */
    .modal {
        max-width: 100vw;
        width: 100vw;
        height: 100vh;
        height: 100svh;
        max-height: 100vh;
        max-height: 100svh;
        margin: 0;
        border-radius: 0;
    }
    .modal-inner {
        max-height: 100vh;
        max-height: 100svh;
        height: 100%;
    }
    .modal .doc-window-frame {
        width: 100%;
        max-width: 100%;
        height: 100%;
        max-height: 100%;
        border: 0;
        border-radius: 0;
        box-shadow: none;
    }
    /* No backdrop scrim showing through edges since the dialog fills
       the viewport — but keep the rule explicit for clarity. */
    .modal::backdrop { background: rgba(0, 0, 0, 0.30); }
}

/* ─── Mobile typography pass — high-DPI screens carry smaller text
   well, and tighter copy lets dense terminal blocks breathe without
   wrapping mid-thought. Scoped to ≤720 so desktop is unaffected. ─── */
@media (max-width: 720px) {
    .section-sub { font-size: 0.9rem; line-height: 1.55; }
    .literature-networks .section-sub { font-size: 0.86rem; }

    .card-title { font-size: 1.05rem; }
    .card-blurb { font-size: 0.82rem; line-height: 1.55; }

    .subfield-block { font-size: 0.7rem; }
    .subfield-block .term-cmd { font-size: 0.7rem; }
    .subfield-log li { font-size: 0.7rem; line-height: 1.45; }
    .subfield-log code { font-size: 0.88em; }

    .term-log { font-size: 0.76rem; }

    .num-blurb,
    .nu-stat-label,
    .bento-tile p { font-size: 0.82rem; line-height: 1.5; }

    .modal-body { font-size: 0.86rem; }

    /* ── Modal body density pass — phones / high-DPI screens.
       Targets the project/build/viz modals (Celiac Saver, Send It!,
       Mystère Beaumont, MCA models, etc). The text was wrapping at
       ~5 words per line; tighten padding, shrink type, snug up
       line-height so a phone carries paragraphs in 2 lines, not 6.
       Sides go even tighter than top/bottom — vertical breathing
       still matters for readability, horizontal is just wasted space. */
    .modal-body-inner {
        padding: var(--space-3) 12px var(--space-4);
    }
    .modal-title {
        font-size: clamp(1rem, 4.2vw, 1.2rem);
        line-height: 1.2;
        margin-bottom: var(--space-2);
    }
    .modal-eyebrow {
        font-size: 0.62rem;
        letter-spacing: 0.1em;
        line-height: 1.5;
        margin-bottom: var(--space-2);
    }
    .modal-body p {
        font-size: 0.78rem;
        line-height: 1.5;
        margin-bottom: var(--space-2);
    }
    .modal-body .modal-aside {
        font-size: 0.72rem;
        line-height: 1.5;
        padding: 8px 10px;
    }
    .modal-pitch {
        font-size: 0.78rem;
        line-height: 1.5;
        padding-left: 10px;
        margin: var(--space-2) 0;
    }
    .modal-cta {
        font-size: 0.78rem;
        padding: 8px 14px;
        margin-top: var(--space-2);
    }
    /* Image rows — tighter gap + remove the figure caption padding bloat. */
    .modal-image-row,
    .modal-image-row--quad {
        gap: 8px;
        margin: var(--space-2) 0;
    }

    /* Doc-window / paper viewer — tighten everything inside the modal
       so a phone screen carries one section per scroll instead of
       three lines per section. Side padding compresses to 12px so the
       paper-chat box and doc text both reach close to the screen edge. */
    .doc-content {
        padding: var(--space-3) 12px;
        font-size: 0.74rem;
    }
    /* Paper-chat box — was nested inside doc-content with its own
       generous side padding. Compress horizontally so the box sits
       closer to the doc-content edge and the chat history has more
       room. */
    .paper-chat {
        padding: var(--space-3) 10px;
    }
    .paper-chat-log .pchat-q,
    .paper-chat-log .pchat-a {
        padding: 6px 8px;
    }
    .paper-chat-log .pchat-a {
        padding-left: 10px;
    }
    .doc-name { font-size: clamp(0.92rem, 3.6vw, 1.05rem); line-height: 1.2; }
    .doc-eyebrow { font-size: 0.7rem; line-height: 1.45; }
    .doc-contact { font-size: 0.7rem; }
    .doc-section-title { font-size: 0.66rem; }
    .doc-prose { font-size: 0.76rem; }

    .draft-content .doc-byline { font-size: 0.7rem; }
    .draft-section { margin-bottom: var(--space-3); }
    .draft-section h2 { font-size: 0.66rem; letter-spacing: 0.12em; }
    .draft-section p { font-size: 0.82rem; line-height: 1.55; }
    .draft-toc, .draft-points {
        font-size: 0.8rem;
        line-height: 1.5;
        padding-left: var(--space-3);
    }
    .draft-toc li, .draft-points li { margin-bottom: 6px; }
    .draft-quote { padding: var(--space-2) var(--space-3); }
    .draft-quote p { font-size: 0.88rem; line-height: 1.5; }
    .draft-quote cite { font-size: 0.68rem; }
    .draft-cta { font-size: 0.74rem; padding: 8px 12px; }

    /* Paper-chat block (above the doc body) — shrink the scope line and
       hint so the "ask about this paper" header sits as a quiet rail
       rather than a second title block. */
    .paper-chat-title { font-size: 0.72rem; line-height: 1.4; }
    .paper-chat-hint  { font-size: 0.66rem; }
    .paper-chat-log   { font-size: 0.74rem; }
    .paper-chat-input { font-size: 0.78rem; }
    .paper-chat-input::placeholder { font-size: 0.74rem; }
    /* Audio summary header text in the same rail — match the new scale. */
    .paper-audio-eyebrow { font-size: 0.72rem; }
    .paper-audio-meta    { font-size: 0.7rem; }
}

/* ──────────────────────────────────────────────────────
   Print
   ────────────────────────────────────────────────────── */

@media print {
    .site-header, .section-dots, .mobile-nav, .hero-bg, .hero-scroll { display: none !important; }
    body { background: white; color: black; }
    .card, .pub-card, .bento-tile { break-inside: avoid; border: 1px solid #ccc; }
}

/* ──────────────────────────────────────────────────────
   Terminal hero (default — robotic, data-dense)
   ────────────────────────────────────────────────────── */

.hero-terminal {
    width: 100%;
    max-width: 1080px;
    margin: 0 auto;
    background: color-mix(in oklab, var(--bg-deepest) 58%, transparent);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lift);
    overflow: hidden;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    /* Linux-shell behaviour: the OUTER terminal frame is height-locked.
       Chrome (top bar) is fixed-height; body fills the remaining space;
       inside body, the scroll region pins content to the bottom and the
       prompt sits below it. The frame itself never grows. */
    height: clamp(580px, 84vh, 960px);
    display: flex;
    flex-direction: column;
}
.term-chrome { flex-shrink: 0; }

.term-chrome {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 10px 14px;
    background: color-mix(in oklab, var(--bg-elev-2) 50%, transparent);
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--text-faint);
    letter-spacing: 0.04em;
}
.term-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    flex-shrink: 0;
}
.term-dot--r { background: #ff5f57; }
.term-dot--y { background: #febc2e; }
.term-dot--g { background: #28c840; margin-right: var(--space-3); }
.term-path {
    flex: 1 1 auto;
    min-width: 0;
    text-align: left;
    color: var(--text-muted);
    /* Filename in the chrome stays on one line. If the viewport is
       narrow enough that even the short filename would clip the
       window controls, ellipsis it cleanly. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.term-path em {
    font-style: normal;
    color: var(--accent);
}
.term-chrome-right {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-shrink: 0;
}
.term-available {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    color: var(--accent);
    border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent);
    border-radius: 20px;
    padding: 2px 8px;
    letter-spacing: 0.04em;
}
.term-available-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    flex-shrink: 0;
    animation: termBlink 1.4s ease-in-out infinite;
}
.kv--wide {
    grid-column: 1 / -1;
}
.term-win-controls {
    display: flex;
    align-items: center;
    gap: 6px;
}
.term-win-btn {
    font-size: 0.72rem;
    color: var(--text-faint);
    line-height: 1;
    cursor: pointer;
    user-select: none;
    opacity: 0.55;
    background: transparent;
    border: 0;
    padding: 2px 6px;
    font-family: inherit;
    transition: opacity 180ms, color 180ms;
}
.term-win-btn:hover { opacity: 1; color: var(--text); }
.term-win-close:hover { color: #ff5f57; opacity: 1; }
.term-win-close { opacity: 0.7; }
/* Viz overlay share button — accent on hover so it reads as the
   "share" affordance, not a generic chrome control. */
[data-viz-overlay-share]:hover { color: var(--accent); }

/* Export action — inline row that sits between the conversation output
   and the input form. Hidden until the conversation has content
   (toggled by JS via the `hidden` attribute). Smaller, streamlined
   pill on desktop; collapses to an icon-only circle on mobile. */
/* Default: hidden. Becomes visible only when JS adds .is-visible (which
   happens once the conversation has at least one non-thinking entry).
   Class-based visibility is bulletproof against the `[hidden]`
   attribute being stripped, lost, or specificity-fought. */
.term-export-row {
    display: none;
    flex-shrink: 0;
    justify-content: flex-end;
    padding: 14px 14px 6px;
}
.term-export-row.is-visible { display: flex; }
.term-export-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    background: transparent;
    border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
    border-radius: 999px;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.03em;
    cursor: pointer;
    transition: background var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                color var(--t-base) var(--ease);
}
.term-export-btn:hover {
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    border-color: var(--accent);
    color: var(--text);
}
.term-export-btn i { font-size: 0.72rem; }

/* Mobile: shrink to a circular icon-only button to claim back screen
   real-estate. Hide the text label, square the box. */
@media (max-width: 720px) {
    .term-export-btn {
        gap: 0;
        width: 30px;
        height: 30px;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
    }
    .term-export-btn span { display: none; }
    .term-export-btn i { font-size: 0.82rem; }
}

/* Chrome-bar export button — used by the float terminal to save vertical
   space inside the small popup. Same accent styling as the inline
   button but compact, fits between the path and the close button. */
/* Default: hidden. .is-visible (added by JS once a conversation has
   any non-thinking entries) flips to inline-flex. Class-based to match
   .term-export-row and avoid attribute-vs-CSS specificity fights. */
.term-chrome-export {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    margin-right: 6px;
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent);
    border-radius: 4px;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: background var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                color var(--t-base) var(--ease);
}
/* Float-terminal scoping: this is the original use-case for the
   chrome-export icon. Hero terminal also uses this class but has its
   own visibility rules (only shown when [data-minimized="true"]) —
   scoping the .is-visible rule here prevents the hero chrome icon
   from appearing in maximized mode. */
.float-term-chrome .term-chrome-export.is-visible { display: inline-flex; }
.term-chrome-export:hover {
    background: color-mix(in oklab, var(--accent) 18%, transparent);
    border-color: var(--accent);
    color: var(--text);
}
.term-chrome-export i { font-size: 0.7rem; }
/* Share button — accent on hover so it reads as the active affordance. */
[data-doc-share]:hover, [data-modal-share]:hover { color: var(--accent); }

/* ─── Share/copy toast — single instance, bottom-centred. ─── */
.slx-toast {
    position: fixed;
    left: 50%;
    bottom: var(--space-5);
    transform: translateX(-50%) translateY(8px);
    z-index: 1000;
    padding: 8px 16px;
    background: color-mix(in oklab, var(--bg-deepest) 92%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 40%, var(--border));
    border-radius: 999px;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.02em;
    box-shadow: 0 12px 30px rgba(0,0,0,0.45);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms var(--ease), transform 200ms var(--ease);
}
.slx-toast[hidden] { display: none; }
.slx-toast[data-show="true"] {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

/* ─── Share popover — small anchored menu opened by [data-doc-share]
   / [data-modal-share]. Single shared instance, positioned in JS. ─── */
.share-pop {
    position: fixed;
    z-index: 1100;
    min-width: 180px;
    padding: 4px;
    background: color-mix(in oklab, var(--bg-deepest) 94%, transparent);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    box-shadow: 0 14px 40px rgba(0,0,0,0.55);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    display: flex;
    flex-direction: column;
    gap: 1px;
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
    transform-origin: top right;
    transition: opacity 160ms var(--ease), transform 160ms var(--ease);
    pointer-events: none;
}
.share-pop[hidden] { display: none; }
.share-pop[data-show="true"] {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}
.share-pop-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: transparent;
    border: 0;
    border-radius: 6px;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-align: left;
    cursor: pointer;
    transition: background 120ms var(--ease), color 120ms var(--ease);
}
.share-pop-item:hover,
.share-pop-item:focus-visible {
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    color: var(--accent);
    outline: none;
}
.share-pop-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 5px;
    background: color-mix(in oklab, var(--accent) 12%, var(--bg-elev-2));
    color: var(--accent);
    font-size: 0.78rem;
    font-weight: 600;
    flex-shrink: 0;
}
/* Slightly bump the chrome share icon so the share-nodes glyph reads
   as an obvious affordance rather than the tiny minimize/restore type. */
[data-doc-share] .fa-share-nodes,
[data-modal-share] .fa-share-nodes { font-size: 0.86rem; }

/* ─── Per-section share button — sits next to the section-num eyebrow.
   Quiet by default, accent on hover, never bigger than the num itself. */
.section-share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-left: var(--space-2);
    padding: 0;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-faint);
    font-size: 0.78rem;
    cursor: pointer;
    vertical-align: middle;
    transition: color var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease),
                background var(--t-base) var(--ease);
}
.section-share-btn:hover,
.section-share-btn:focus-visible {
    color: var(--accent);
    border-color: var(--border-accent);
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    outline: none;
}

/* ──── Hero terminal body content ────
   This is the scrollable region that holds the static bio + the live
   conversation. The container itself is height-locked (max-height in
   viewport units); content is pinned to the BOTTOM via flex column +
   margin-top:auto on the first child. Result:
     · On load: bio sits at the bottom, prompt right below it.
     · On chat: new entries appear above the prompt; the bio scrolls
       UP and out of the visible area. The hero-terminal frame never
       grows downward.
     · A scrollbar lets the user scroll back up to re-read the bio
       and earlier conversation.
   The legacy minimize animation (max-height 4000→0) still works
   because max-height: clamp(...) already gives us a hard ceiling, and
   the [data-minimized] override below sets max-height: 0. */
.term-body-content {
    /* Fills the remaining space inside .term-body and is the actual
       scroll container. flex:1 + min-height:0 is the canonical recipe
       for a flex child that needs internal scrolling.
       Content is pinned to the bottom via margin-top:auto on the first
       child — when content fits, bio sits at the bottom; when it
       overflows, the auto-margin collapses to 0 and the user sees a
       normal scrollbar with bio at top, newest entries at bottom. */
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    opacity: 1;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, var(--accent) 28%, transparent) transparent;
    transition:
        flex-basis 520ms cubic-bezier(.18, 1.0, .32, 1),
        opacity 280ms ease,
        margin 360ms ease,
        transform 480ms cubic-bezier(.18, 1.0, .32, 1);
    transform-origin: top center;
}
.term-body-content > :first-child {
    margin-top: auto;
}
.term-body-content::-webkit-scrollbar { width: 6px; }
.term-body-content::-webkit-scrollbar-thumb {
    background: color-mix(in oklab, var(--accent) 28%, transparent);
    border-radius: 3px;
}
.hero-terminal[data-minimized="true"] {
    /* Drop the locked height so the frame can shrink to just chrome + prompt. */
    height: auto;
}
.hero-terminal[data-minimized="true"] .term-body-content {
    flex: 0 0 0;
    height: 0;
    max-height: 0;
    opacity: 0;
    margin: 0;
    overflow: hidden;
    transform: scaleY(0.92);
}
.hero-terminal[data-minimized="true"] .term-body {
    flex: 0 0 auto;
    padding-bottom: var(--space-3);
}
.hero-terminal[data-minimized="true"] .term-cursor {
    margin-top: 0;
}
/* Minimized mode: anchor the save button to the left edge of the
   panel instead of floating it in the top-right of an empty area.
   Reduce padding so it doesn't introduce vertical bloat in the
   already-compact minimized state. The chrome bar on the right
   already handles min/max/close — putting the save action on the
   opposite side balances the panel visually. */
/* Minimized hero: swap the bottom save-conversation button for a
   chrome-bar download icon (sits next to min/max/close). The bottom
   button looked awkward in the compact minimized view; an icon in
   the title-bar area reads as a normal window action. */
.hero-terminal[data-minimized="true"] .term-export-row {
    display: none !important;
}
[data-hero-chrome-export] {
    display: none;
}
.hero-terminal[data-minimized="true"] [data-hero-chrome-export].is-visible {
    display: inline-flex;
}
.hero-terminal {
    transition: box-shadow 320ms ease;
}
.hero-terminal[data-minimized="true"] {
    box-shadow: var(--shadow-lift), 0 0 0 1px color-mix(in oklab, var(--accent) 18%, transparent);
}
.term-clock {
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.term-body {
    /* Tightened vertical padding so more bio content fits in the
       height-locked frame. Horizontal padding kept comfortable. */
    padding: var(--space-3) var(--space-5) var(--space-3);
    font-family: var(--font-mono);
    font-size: 0.92rem;
    line-height: 1.55;
    color: var(--text-muted);
    /* Fills the remaining vertical space below .term-chrome and lays
       out children (.term-body-content + .term-cursor) as a column.
       min-height:0 is required for the inner scroll region to actually
       overflow rather than expand its parent. */
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.term-cursor { flex-shrink: 0; }
@media (max-width: 640px) {
    .term-body { padding: var(--space-4) var(--space-3) var(--space-3); font-size: 0.85rem; }
    .term-chrome { font-size: 0.66rem; }
}

.term-line {
    margin: 0 0 var(--space-1);
}
.term-cmd {
    color: var(--text-muted);
    margin-top: var(--space-2);
}
.term-cmd:first-child { margin-top: 0; }
.term-prompt { color: var(--accent); font-weight: 500; }
.term-sep    { color: var(--text-faint); margin: 0 1px; }
.term-cwd    { color: var(--warm); }
.term-typed  { color: var(--text); font-weight: 500; }

.hero-terminal .hero-title {
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: clamp(2.2rem, 5.5vw, 4.2rem);
    line-height: 1.0;
    letter-spacing: -0.025em;
    color: var(--text);
    margin: var(--space-1) 0 var(--space-2);
    word-break: break-word;
}
.hero-terminal .hero-title-line { display: block; }
.hero-terminal .hero-title-line.italic {
    font-family: var(--font-mono);
    font-style: normal;
    font-weight: 500;
    color: var(--accent);
}
.hero-terminal .hero-title-line.italic::before {
    content: '> ';
    opacity: 0.6;
    color: var(--text-faint);
    font-weight: 400;
}

.term-meta {
    display: grid;
    /* Role gets more space than Location/Credentials so its 3-tag value
       doesn't crunch onto three lines. Wide rows (kv--wide) span all
       three columns. */
    grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr) minmax(0, 1.1fr);
    gap: var(--space-2) var(--space-4);
    margin: var(--space-2) 0;
    color: var(--text-muted);
    font-size: 0.84rem;
}
@media (max-width: 720px) {
    .term-meta { grid-template-columns: 1fr; }
}
.term-meta .kv {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-left: var(--space-2);
    border-left: 2px solid var(--border-accent);
}
.term-meta .kv i {
    font-style: normal;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 0.66rem;
    color: var(--text-faint);
}
.term-meta .kv b {
    color: var(--text);
    font-weight: 500;
}

.term-rule {
    color: var(--text-faint);
    opacity: 0.5;
    overflow: hidden;
    white-space: nowrap;
    margin: var(--space-2) 0;
    font-size: 0.78rem;
    user-select: none;
}

.hero-terminal .hero-tagline {
    font-family: var(--font-display);
    font-size: clamp(1.4rem, 2.6vw, 2rem);
    font-weight: 500;
    color: var(--text);
    margin: var(--space-2) 0 var(--space-1);
    line-height: 1.2;
    letter-spacing: -0.02em;
}
/* Emphasis is colour + weight only — no swapping into a serif/wonky
   italic. Keeps the tone tech-clean to match the terminal aesthetic. */
.hero-terminal .hero-tagline em {
    font-family: inherit;
    font-style: normal;
    font-weight: 600;
    color: var(--accent);
}

.hero-terminal .hero-lede {
    font-family: var(--font-body);
    font-size: 0.96rem;
    line-height: 1.55;
    color: var(--text-muted);
    /* Allowed to fill more of the box width — comfortable, not cramped. */
    max-width: none;
    margin: 0 0 var(--space-2);
}
.hero-terminal .hero-lede em { color: var(--text); font-style: italic; }
.hero-terminal .hero-lede strong { color: var(--accent); font-weight: 500; }

.term-log {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: var(--space-1) 0 var(--space-2);
    font-size: 0.86rem;
    color: var(--text-muted);
}
.term-log li {
    padding-left: var(--space-3);
    position: relative;
}
.term-ts {
    color: var(--accent);
    font-weight: 500;
    margin-right: 6px;
}
.term-num {
    color: var(--text);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.term-num sup { font-size: 0.7em; color: var(--accent); }
.term-key { color: var(--warm); }

.hero-terminal .hero-socials {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
    margin: var(--space-1) 0 var(--space-2);
    padding-left: var(--space-3);
}
.hero-terminal .hero-socials a {
    border: 0;
    background: transparent;
    padding: 0;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.86rem;
    letter-spacing: 0;
    text-transform: none;
    border-bottom: 1px solid transparent;
}
.hero-terminal .hero-socials a:hover {
    color: var(--accent-hover);
    border-bottom-color: var(--accent);
    background: transparent;
    transform: none;
}

.term-cursor { margin-top: var(--space-2); }
.term-caret {
    color: var(--accent);
    animation: termBlink 1s steps(2, jump-none) infinite;
}
@keyframes termBlink {
    50% { opacity: 0; }
}

/* ──── Discoverability pulse ────
   Until the user types anything, the prompt line softly glows so it's
   obvious the terminal is interactive. The class is set by JS on page
   load and removed on first interaction. */
.hero-terminal[data-hero-pulse="true"] .term-cursor {
    border-radius: 6px;
    /* Pull the pulse band flush to the parent's side padding so the
       teal ring spans the full input zone width. The internal padding
       matches so the prompt content stays in the same horizontal
       position; only the highlight box extends. */
    padding: 4px var(--space-3);
    margin-left: calc(var(--space-3) * -1);
    margin-right: calc(var(--space-3) * -1);
    /* Quiet accent ring + tiny tinted background — enough to draw
       the eye without lighting up the whole terminal. */
    background-color: color-mix(in oklab, var(--accent) 3%, transparent);
    box-shadow:
        inset 0 0 0 1px color-mix(in oklab, var(--accent) 18%, transparent);
    animation: termPromptPulse 2.4s ease-in-out infinite;
}
@keyframes termPromptPulse {
    0%, 100% {
        background-color: color-mix(in oklab, var(--accent) 3%, transparent);
        box-shadow:
            inset 0 0 0 1px color-mix(in oklab, var(--accent) 18%, transparent);
    }
    50% {
        background-color: color-mix(in oklab, var(--accent) 7%, transparent);
        box-shadow:
            inset 0 0 0 1px color-mix(in oklab, var(--accent) 32%, transparent);
    }
}
/* Make the placeholder fully readable while pulsing — it's the call
   to action, not ghost text. */
.hero-terminal[data-hero-pulse="true"] .term-input::placeholder {
    opacity: 1;
    color: color-mix(in oklab, var(--accent) 75%, var(--text));
    font-style: normal;
}

/* ──── Interactive prompt ──── */
.term-interactive {
    display: flex;
    align-items: baseline;
    flex-wrap: nowrap;
    gap: 0;
    border: 0;
    padding: 0;
    margin: var(--space-2) 0 0;
    cursor: text;
    text-align: left;
    justify-content: flex-start;
}
/* Inside the hero terminal, lift the input prompt's background a
   touch above the body so the input zone reads as a distinct band
   rather than the same charcoal as everything above. Extra top
   margin gives breathing room between 'links --all' and the input.
   Tighter side padding (was --space-5 = 32px each side) gives the
   input zone enough horizontal room that the full placeholder fits
   without truncation. The negative side margins still pull the band
   flush to the parent's edges. */
.hero-terminal .term-interactive {
    background: color-mix(in oklab, var(--bg-elev-1) 55%, transparent);
    padding: 12px var(--space-3);
    margin: var(--space-5) calc(var(--space-5) * -1) calc(var(--space-3) * -1);
    border-top: 1px solid var(--border);
}
.term-input-wrap {
    position: relative;
    flex: 1 1 auto;
    display: inline-block;
    min-width: 0;
}
.term-input {
    width: 100%;
    background: transparent;
    border: 0;
    outline: 0;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: inherit;
    line-height: inherit;
    padding: 0;
    margin: 0;
    caret-color: var(--accent);
    text-align: left;
}
.term-input::placeholder {
    color: var(--text-faint);
    opacity: 0.55;
    font-style: italic;
}
/* Manual blinking caret sits at the LEFT edge of the input area —
   shown only when input is empty AND not focused (idle state). */
.term-input-wrap > .term-caret {
    position: absolute;
    left: 0;
    top: 0;
    pointer-events: none;
    color: var(--accent);
}
.term-input:focus ~ .term-caret,
.term-input:not(:placeholder-shown) ~ .term-caret { display: none; }

/* ──── In-window conversation (primary read surface) ────
   .term-output flows naturally inside .term-body-content. The parent
   handles the scroll + height-lock; this container is just a list of
   entries that grows as the user chats. */
.term-output {
    font-family: var(--font-mono);
    font-size: 0.84rem;
    line-height: 1.5;
}
.term-output:empty { margin: 0; padding: 0; }
.term-output:not(:empty) { margin: var(--space-3) 0 0; }
.term-output .log-entry + .log-entry {
    margin-top: 6px;
}
.term-output .log-entry {
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    animation: termInFadeIn 280ms ease-out;
}
.term-output .log-echo { color: var(--text-muted); }
.term-output .log-result {
    color: var(--text);
    padding-left: var(--space-2);
    border-left: 2px solid color-mix(in oklab, var(--accent) 35%, transparent);
}
.term-output .log-result.term-error {
    color: #ff8a85;
    border-left-color: rgba(255, 138, 133, 0.4);
}
.term-output .log-thinking {
    color: var(--accent);
    opacity: 0.55;
    font-style: italic;
    animation: termInFadeIn 200ms forwards, logPulse 1.4s ease-in-out infinite 200ms;
}

@keyframes termInFadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ──── Background ghost log — hidden by default, revealed when terminal is minimized ──── */
.hero-log {
    position: absolute;
    inset: 0;
    z-index: 1;                     /* above hero-bg canvas (0), below hero-inner (2) */
    pointer-events: none;
    padding: 8vh 4vw 12vh 2.5vw;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;      /* newest entries hug the bottom */
    gap: 8px;
    overflow: hidden;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-faint);
    opacity: 0;
    transition: opacity 480ms ease;
    /* fade out at top so older entries dissolve as they scroll up */
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 100%);
            mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 100%);
}
/* Reveal background ghost only when the hero terminal is minimized */
.hero:has([data-hero-terminal][data-minimized="true"]) .hero-log {
    opacity: 1;
}
.hero-log .log-entry {
    line-height: 1.45;
    white-space: pre-wrap;
    word-break: break-word;
    max-width: 720px;
}
.hero-log .log-echo { color: color-mix(in oklab, var(--text-faint) 90%, transparent); }
.hero-log .log-result {
    color: color-mix(in oklab, var(--accent) 60%, var(--text-faint));
    padding-left: var(--space-3);
    border-left: 1px solid color-mix(in oklab, var(--accent) 18%, transparent);
}
.hero-log .log-result.term-error {
    color: #ff8a85;
    opacity: 0.7;
    border-left-color: rgba(255, 138, 133, 0.28);
}
.hero-log .log-thinking { display: none; }

@keyframes logPulse {
    0%, 100% { opacity: 0.32; }
    50%      { opacity: 0.6; }
}

/* When the input is locked (visual quota reached), tint it red-ish */
.term-input[data-locked="true"] {
    color: var(--text-faint);
    caret-color: #ff8a85;
}
.term-input[data-locked="true"]::placeholder { color: #ff8a85; opacity: 0.6; }

@media (max-width: 720px) {
    .hero-log { padding: 4vh 5vw; font-size: 0.72rem; }
    .hero-log .log-entry { max-width: 100%; }
}

/* ──── System log — idle heartbeat in the corner ──── */
.system-log {
    position: fixed;
    /* Sub-view pill now occupies the bottom-left corner; lift the
       indexer ticker above it. */
    bottom: 70px;
    left: var(--space-3);
    z-index: 80;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    color: var(--text-faint);
    letter-spacing: 0.04em;
    pointer-events: none;
    opacity: 0;
    transition: opacity 380ms ease;
    max-width: 50vw;
    text-shadow: 0 0 6px rgba(0,0,0,0.4);
}
.system-log.is-visible { opacity: 0.55; }
@media (max-width: 720px) { .system-log { display: none; } }

/* ──────────────────────────────────────────────────────
   Floating mini terminal — persistent Gemini access on scroll
   ────────────────────────────────────────────────────── */

.float-term {
    position: fixed;
    bottom: var(--space-3);
    right: calc(var(--space-3) + 28px);     /* clear of section-dots rail */
    z-index: 95;
}

.float-term-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px 8px 12px;
    background: color-mix(in oklab, var(--bg-deepest) 80%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 28%, transparent);
    border-radius: 999px;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    cursor: pointer;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: 0 6px 22px rgba(0,0,0,0.32), 0 0 0 1px rgba(255,255,255,0.02);
    transition: transform 220ms ease, background 220ms, border-color 220ms;
}
.float-term-toggle:hover {
    transform: translateY(-1px);
    background: color-mix(in oklab, var(--bg-deepest) 88%, transparent);
    border-color: color-mix(in oklab, var(--accent) 50%, transparent);
}
.float-term-icon {
    color: var(--accent);
    font-weight: 600;
    letter-spacing: -0.04em;
}

.float-term[data-open="true"] .float-term-toggle {
    display: none;
}

.float-term-panel {
    width: min(520px, 92vw);
    background: color-mix(in oklab, var(--bg-deepest) 78%, transparent);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    overflow: hidden;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    box-shadow: 0 12px 40px rgba(0,0,0,0.55);
    display: flex;
    flex-direction: column;
    transform-origin: bottom right;
    animation: floatTermIn 280ms cubic-bezier(.18, 1.0, .32, 1);
}
.float-term-panel[hidden] { display: none; }

@keyframes floatTermIn {
    from { opacity: 0; transform: translateY(12px) scale(0.94); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.float-term-chrome { flex-shrink: 0; }

.float-term-output {
    flex: 1 1 auto;
    max-height: 280px;
    min-height: 60px;
    overflow-y: auto;
    padding: var(--space-3);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    line-height: 1.5;
    display: flex;
    flex-direction: column;
    gap: 6px;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, var(--accent) 28%, transparent) transparent;
}
.float-term-output:empty { padding: 0; min-height: 0; }
.float-term-output::-webkit-scrollbar { width: 5px; }
.float-term-output::-webkit-scrollbar-thumb {
    background: color-mix(in oklab, var(--accent) 28%, transparent);
    border-radius: 3px;
}
.float-term-output .log-entry {
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    animation: termInFadeIn 240ms ease-out;
}
.float-term-output .log-echo { color: var(--text-muted); }
.float-term-output .log-result {
    color: var(--text);
    padding-left: var(--space-2);
    border-left: 2px solid color-mix(in oklab, var(--accent) 35%, transparent);
}
.float-term-output .log-result.term-error {
    color: #ff8a85;
    border-left-color: rgba(255, 138, 133, 0.4);
}
.float-term-output .log-thinking {
    color: var(--accent);
    opacity: 0.55;
    font-style: italic;
    animation: termInFadeIn 200ms forwards, logPulse 1.4s ease-in-out infinite 200ms;
}

.float-term-form {
    padding: var(--space-2) var(--space-3) var(--space-3);
    margin: 0;
    border-top: 1px solid color-mix(in oklab, var(--border) 60%, transparent);
    font-size: 0.84rem;
}

@media (max-width: 720px) {
    .float-term {
        right: var(--space-3);
        bottom: var(--space-3);
    }
    .float-term-panel {
        width: calc(100vw - 2 * var(--space-3));
    }
    .float-term-output { max-height: 50vh; }
}

/* On mobile the header has its own ask-patrick trigger, so hide the
   floating chip's toggle — but keep the panel functional so opening
   from the header still slides up at the bottom of the viewport. */
@media (max-width: 900px) {
    .float-term-toggle { display: none; }
}

/* When terminal variant is active, drop the right-side glyph & widen content */
[data-hero-variant="terminal"] .hero-inner {
    grid-template-columns: 1fr;
    max-width: var(--content);
}
[data-hero-variant="terminal"] .hero-content,
[data-hero-variant="terminal"] .hero-glyph {
    display: none;
}

/* ──────────────────────────────────────────────────────
   Hero variants — split / manifesto / terminal
   ────────────────────────────────────────────────────── */

/* Variant A — split */
[data-hero-variant="split"] .hero-terminal { display: none; }
[data-hero-variant="split"] .hero-content { display: flex; }
[data-hero-variant="split"] .hero-glyph { display: flex; }
[data-hero-variant="split"] .hero-inner { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); }

/* Variant B — manifesto (centered, big type, full-bleed nodes) */
[data-hero-variant="manifesto"] .hero-terminal { display: none; }
[data-hero-variant="manifesto"] .hero-content { display: flex; }
[data-hero-variant="manifesto"] .hero-inner {
    grid-template-columns: 1fr;
    text-align: center;
    max-width: 980px;
}
[data-hero-variant="manifesto"] .hero-content {
    align-items: center;
    margin: 0 auto;
}
[data-hero-variant="manifesto"] .hero-glyph { display: none; }
[data-hero-variant="manifesto"] .hero-title {
    font-size: clamp(3.5rem, 12vw, 9rem);
}
[data-hero-variant="manifesto"] .hero-lede {
    margin: 0 auto;
}
[data-hero-variant="manifesto"] .hero-bg canvas { opacity: 0.65; }

/* ──────────────────────────────────────────────────────
   Doc viewer window (CV / Resume) — terminal-themed modal
   ────────────────────────────────────────────────────── */

.doc-window {
    position: fixed;
    inset: 0;
    /* Above .viz-overlay (260) so paper viewers triggered FROM a
       chart paper-card render on top of the chart, not behind it. */
    z-index: 280;
    /* Just a tiny dim — desktop-popup feel. */
    background: rgba(0, 0, 0, 0.20);
    display: flex;
    align-items: flex-start;
    justify-content: flex-start;
    /* Default — CV opens upper-left-ish; resume cascades down-right.
       Each one gets its own coordinates so opening both doesn't stack
       them on top of each other. */
    padding: 8vh 0 0 11vw;
    animation: docFadeIn 200ms ease-out;
}
.doc-window[data-doc-active="cv"]     { padding: 6vh 0 0 9vw; }
.doc-window[data-doc-active="resume"] { padding: 11vh 0 0 17vw; }
.doc-window[hidden] { display: none; }

.doc-window-frame {
    width: min(1000px, 86vw);
    height: 84vh;
    background: color-mix(in oklab, var(--bg-deepest) 92%, transparent);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lift), 0 30px 80px rgba(0,0,0,0.55);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    animation: docSlideIn 280ms cubic-bezier(.18, 1.2, .32, 1);
    transform-origin: top left;
}

.doc-window-chrome { flex-shrink: 0; }

.doc-win-btn {
    background: transparent;
    border: 0;
    cursor: pointer;
    padding: 2px 6px;
    transition: opacity 180ms, color 180ms;
    font-family: var(--font-mono);
    font-size: 0.78rem;
}
.doc-win-btn:hover { opacity: 1; color: var(--text); }
.doc-win-btn.term-win-close:hover { color: #ff5f57; opacity: 1; }
/* Download button — anchor element styled the same as the chrome buttons,
   appears only when a draft is open. */
.doc-win-download {
    text-decoration: none;
    color: var(--accent);
    opacity: 0.75;
    margin-right: 6px;
    border-radius: 4px;
}
.doc-win-download:hover {
    opacity: 1;
    color: var(--text);
    background: color-mix(in oklab, var(--accent) 18%, transparent);
}

.doc-window-body {
    flex: 1;
    overflow: auto;
    background: #14141a;
    min-height: 0;
}
.doc-window-body iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    background: #14141a;
}

/* ──── Rendered doc content (CV / Resume) ──── */
.doc-content {
    padding: var(--space-3) var(--space-4) var(--space-4);
    font-family: var(--font-mono);
    color: var(--text-muted);
    font-size: 0.8rem;
    line-height: 1.5;
    max-width: 860px;
    margin: 0 auto;
}
.doc-content .term-cmd { margin: 0 0 var(--space-2); }
.doc-content .term-rule { margin: var(--space-2) 0; opacity: 0.35; }

.doc-header { margin: var(--space-2) 0 var(--space-2); }
.doc-name {
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: clamp(1.3rem, 2.4vw, 1.7rem);
    color: var(--text);
    margin: 0 0 4px;
    letter-spacing: -0.01em;
}
.doc-contact {
    display: flex;
    flex-direction: column;
    gap: 2px;
    color: var(--text-muted);
    font-size: 0.76rem;
    margin: 0;
    line-height: 1.45;
}
.doc-contact em { font-style: normal; color: var(--accent); }
.doc-stamp {
    color: var(--text-faint);
    font-size: 0.68rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.doc-section { margin: 0 0 var(--space-3); }
.doc-section-title {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 0.72rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 var(--space-2);
    padding-bottom: 4px;
    border-bottom: 1px dashed color-mix(in oklab, var(--border) 70%, transparent);
}
.doc-section-title--sub {
    color: var(--warm);
    margin-top: var(--space-2);
    border-bottom-style: dotted;
    opacity: 0.85;
}

.doc-prose {
    margin: 0 0 6px;
    color: var(--text-muted);
    font-size: 0.82rem;
    line-height: 1.55;
}
.doc-prose em { color: var(--text); font-style: italic; }

.doc-entry {
    display: grid;
    grid-template-columns: 104px 1fr;
    gap: var(--space-2);
    margin: 0 0 8px;
    padding-left: 10px;
    border-left: 2px solid color-mix(in oklab, var(--accent) 22%, transparent);
}
.doc-entry--job { padding-bottom: 4px; }
.doc-entry-body { min-width: 0; }

.doc-year {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--accent);
    font-weight: 500;
    letter-spacing: 0.03em;
    line-height: 1.4;
    padding-top: 1px;
    white-space: nowrap;
}

.doc-title {
    color: var(--text);
    font-weight: 500;
    margin: 0 0 2px;
    font-size: 0.84rem;
    line-height: 1.4;
}
.doc-title em {
    color: var(--warm);
    font-style: normal;
    font-weight: 400;
}
.doc-place {
    color: var(--text-faint);
    font-weight: 400;
    font-size: 0.76rem;
    margin-left: 4px;
}

.doc-detail {
    color: var(--text-faint);
    font-size: 0.72rem;
    margin: 0;
    font-style: italic;
}

.doc-thesis {
    color: var(--text-muted);
    font-style: italic;
    font-size: 0.78rem;
    margin: 0 0 2px;
    padding-left: 8px;
    border-left: 1px solid color-mix(in oklab, var(--accent) 18%, transparent);
    line-height: 1.5;
}

.doc-bullets {
    list-style: none;
    margin: 4px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.doc-bullets li {
    position: relative;
    padding-left: 14px;
    font-size: 0.78rem;
    color: var(--text-muted);
    line-height: 1.5;
}
.doc-bullets li::before {
    content: '›';
    position: absolute;
    left: 4px;
    color: var(--accent);
    opacity: 0.7;
}

.doc-skill-label {
    color: var(--warm);
    font-size: 0.76rem;
    margin: var(--space-2) 0 6px;
}
.doc-skill-label em { font-style: italic; }

.doc-skill-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.doc-skill-list li {
    display: grid;
    grid-template-columns: 104px 1fr;
    gap: var(--space-2);
    align-items: baseline;
    font-size: 0.78rem;
    color: var(--text-muted);
}
.doc-skill-level {
    font-family: var(--font-mono);
    font-size: 0.64rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--accent);
    border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
    border-radius: 10px;
    padding: 0 7px;
    text-align: center;
    justify-self: start;
    line-height: 1.6;
}
.doc-skill-level--ai {
    color: var(--warm);
    border-color: color-mix(in oklab, var(--warm) 35%, transparent);
}

.doc-footnote {
    color: var(--text-faint);
    font-size: 0.68rem;
    line-height: 1.5;
    margin: var(--space-2) 0 0;
    padding: 6px 10px;
    border-left: 2px dotted color-mix(in oklab, var(--border) 80%, transparent);
    font-style: italic;
}

.doc-pub {
    display: grid;
    grid-template-columns: 56px 1fr;
    gap: var(--space-2);
    margin: 0 0 6px;
    font-size: 0.78rem;
    line-height: 1.45;
}
.doc-pub .doc-year { font-size: 0.7rem; padding-top: 2px; }
.doc-pub-cite { margin: 0; color: var(--text-muted); }
.doc-pub-cite em { color: var(--accent); font-style: italic; }
.doc-authors { color: var(--text); }

.doc-tag {
    color: var(--text-faint);
    font-size: 0.68rem;
    margin-left: 6px;
    letter-spacing: 0.04em;
}

.doc-award-list, .doc-talk-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.doc-award-list li, .doc-talk-list li {
    position: relative;
    padding: 2px 0 2px 112px;
    font-size: 0.78rem;
    color: var(--text-muted);
    line-height: 1.5;
}
.doc-award-list li > .doc-year,
.doc-talk-list li > .doc-year {
    position: absolute;
    left: 0;
    top: 2px;
    width: 104px;
}
.doc-award-list li em, .doc-talk-list li em {
    color: var(--warm);
    font-style: italic;
}
.doc-award-list .term-num, .doc-talk-list .term-num {
    color: var(--accent);
    font-weight: 500;
    margin-left: 6px;
}

.doc-end {
    margin-top: var(--space-3);
    opacity: 0.7;
}

@media (max-width: 720px) {
    .doc-content { padding: var(--space-3); font-size: 0.82rem; }
    .doc-entry, .doc-pub, .doc-skill-list li {
        grid-template-columns: 1fr;
        gap: 4px;
    }
    .doc-award-list li, .doc-talk-list li {
        padding-left: 0;
        padding-top: 6px;
    }
    .doc-award-list li > .doc-year,
    .doc-talk-list li > .doc-year {
        position: static;
        display: block;
        width: auto;
        margin-bottom: 2px;
    }
    .doc-year { font-size: 0.72rem; }
}

.doc-window[data-minimized="true"] .doc-window-body { display: none; }
.doc-window[data-minimized="true"] .doc-window-frame {
    height: auto;
    width: min(420px, 70vw);
}
.doc-window[data-minimized="true"] {
    align-items: flex-end;
    padding: 0 0 3vh 4vw;
}

@keyframes docFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes docSlideIn {
    from { opacity: 0; transform: translateY(-18px) scale(0.96); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

@media (max-width: 720px) {
    .doc-window { padding: 3vh 4vw 0; }
    .doc-window-frame { width: 100%; height: 88vh; }
}

/* ──────────────────────────────────────────────────────
   Mail compose window
   ────────────────────────────────────────────────────── */

.mail-window-frame { width: min(720px, 86vw); height: auto; max-height: 84vh; }

.mail-window-body {
    overflow: auto;
    padding: var(--space-4) var(--space-5);
    font-family: var(--font-mono);
    color: var(--text-muted);
    background: transparent;
}

.mail-form { display: flex; flex-direction: column; gap: var(--space-2); }

.mail-form .term-cmd { margin: 0 0 var(--space-2); }

.mail-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px 0;
    border-bottom: 1px dashed color-mix(in oklab, var(--border) 60%, transparent);
}
.mail-row--static { color: var(--accent); }

.mail-label {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-faint);
    min-width: 78px;
    text-transform: lowercase;
    letter-spacing: 0.04em;
}

.mail-static {
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 0.92rem;
}

.mail-input {
    flex: 1;
    background: transparent;
    border: 0;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.92rem;
    padding: 4px 0;
    outline: 0;
}
.mail-input::placeholder { color: var(--text-faint); opacity: 0.6; }
.mail-input:focus { color: var(--text); }
.mail-row:focus-within { border-bottom-color: var(--accent); }

.mail-form .term-rule { margin: var(--space-2) 0; }

.mail-body-label {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    color: var(--text-faint);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    padding-left: 2px;
    margin-top: var(--space-2);
}

.mail-textarea {
    width: 100%;
    min-height: 180px;
    background: color-mix(in oklab, var(--bg-deepest) 50%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 6px);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.92rem;
    line-height: 1.55;
    padding: var(--space-3);
    resize: vertical;
    outline: 0;
    transition: border-color 180ms;
}
.mail-textarea:focus { border-color: var(--accent); }
.mail-textarea::placeholder { color: var(--text-faint); opacity: 0.6; }

.mail-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-3);
}

.mail-send {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--accent);
    color: var(--bg-deepest);
    border: 0;
    border-radius: 999px;
    padding: 8px 18px;
    font-family: var(--font-mono);
    font-size: 0.86rem;
    font-weight: 500;
    cursor: pointer;
    letter-spacing: 0.02em;
    transition: background 180ms, transform 180ms, box-shadow 180ms;
    box-shadow: 0 4px 16px var(--accent-glow);
}
.mail-send:hover {
    background: var(--accent-hover);
    transform: translateY(-1px);
    box-shadow: 0 6px 20px var(--accent-glow);
}
.mail-send span:last-child { transition: transform 180ms; }
.mail-send:hover span:last-child { transform: translateX(3px); }

.mail-hint {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--text-faint);
}

.mail-status {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--accent);
    margin: var(--space-2) 0 0;
    min-height: 1em;
}
.mail-status[data-state="error"] { color: #ff5f57; }

/* ──────────────────────────────────────────────────────
   Short-viewport overrides — must live AFTER the rules
   they override (cascade: later wins at equal specificity).
   The matching @media (max-height: 900px) block earlier in
   the file handles snap-mode disable + section padding +
   typography. The hero-terminal and .hero rules are defined
   later in the file, so their overrides have to live here.
   ────────────────────────────────────────────────────── */
@media (max-height: 900px) {
    /* Hero terminal default (84vh / max 960px) is too tall on a 900px
       viewport — its bottom edge collides with the fixed sub-view +
       ask-patrick.exe pills. Shrink + tighten hero padding so the
       terminal sits clear of the bottom rail. */
    .hero {
        padding-top: clamp(64px, 8vh, 96px);
        padding-bottom: 88px;
    }
    .hero-terminal {
        height: clamp(440px, 65vh, 640px);
    }
}
