/* Contact page only. Loaded after site.css.
   Layout follows the reference's contact hero: a small eyebrow, a statement
   heading with one word set apart, a paragraph under it, and a filled pill
   beside a quieter arrow link — all in the left half of the grid, floated in a
   full viewport of space. Nothing sits below it. */

/* The accent word's face. Declared again here rather than shared, because the
   home page's copy lives in home.css and this page does not load it — the
   @font-face is cheap and the woff2 is fetched once either way. */
@font-face {
    font-family: just-me-again;
    src: url('fonts/just-me-again-down-here-latin-400-normal.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

/* The cycling word runs hotter than the page around it: pure white on the dark
   background, pure black on the cream. Not --color-primary, which is a warm
   off-white (#ffffe3) and an off-black (#23241a) — against a paragraph already
   set in those at half strength, the full-strength version of the same hue
   reads as the same colour turned up rather than as a different one. */
.site.contact {
    --swap: #ffffff;
}

:root[data-theme="light"] .site.contact {
    --swap: #000000;
}

/* Matches the home hero's height so the footer stays below the fold and the
   block sits in open space the way the reference's does. Centred rather than
   bottom-aligned: there is no character sharing the row here. */
.contact-hero {
    min-height: calc(100svh - var(--base-padding-y) * 4 - 2em);
    align-content: center;
}

/* The words keep the left half and the character takes the right. Held to six
   columns rather than spanning the grid: the cast below is explicitly placed
   across rows 1–4, and a twelve-column block cannot auto-place into a row it
   would overlap — it gets pushed underneath the character instead. */
.contact-eyebrow {
    grid-column: 1 / span 6;
    margin: 0 0 1.5em;
    color: var(--color-faint);
}

/* Scoped with .site so the size outranks the `.site h1` reset in site.css,
   the same reason .hero-lede is scoped. */
.site .contact-heading {
    grid-column: 1 / span 6;
    margin: 0 0 1.2em;
    font-size: clamp(1.5em, 3.4vw, 3em);
    font-weight: 700;
    line-height: 1.1;
    max-width: 13em;
}

/* The reference drops its accent word into a serif italic. This page has no
   serif, so it borrows the hand from the home page's annoy prompt — the one
   informal voice the site already owns. Set slightly larger and pulled back on
   the baseline, since the hand runs small against the mono at the same size. */
.contact-accent {
    font-family: just-me-again, cursive;
    font-style: normal;
    font-weight: 400;
    font-size: 1.35em;
    line-height: 0.8;
    text-transform: none;      /* .site sets uppercase on everything */
    white-space: nowrap;
    vertical-align: -0.06em;
}

.site .contact-body {
    grid-column: 1 / span 6;
    margin: 0 0 3em;
    color: var(--color-dim);
    max-width: 34em;
}

/* ------------------------------------------------------------- Word swap */

/* The paragraph's last word cycles. This block is the resting state and the
   fallback in one: without contact.js, and under reduced motion, the words
   simply sit inline as a comma list — they each name something worth
   discussing, so losing two of them to a stalled script would cost meaning. */
.contact-swap {
    color: var(--swap);
}

/* Separators stay the paragraph's colour, so the highlight reads as three
   words rather than one long orange run. They are real text rather than
   generated content because this is the degraded path — `::after` commas would
   be lost the moment anyone copied the sentence. */
.contact-swap-sep {
    color: var(--color-dim);
}

/* Live: the words stack in one grid cell and cross-fade. inline-grid rather
   than absolute positioning so the container keeps a real baseline — it is
   mid-sentence, and an absolutely positioned stack would need its height and
   vertical alignment faked. */
.contact-swap.is-live {
    display: inline-grid;
    justify-items: start;
    /* Only once a single word is on show. The fallback list is long enough to
       want a line break on a narrow screen. */
    white-space: nowrap;
    /* contact.js sets the width to the word on show, so the full stop after it
       travels instead of the line reflowing. Delayed to start once the outgoing
       word has gone, so the stop moves against an empty slot. */
    transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.22s;
}

/* The two words share one grid cell, so they must never be visible together —
   overlapped mid-fade they read as one garbled hybrid word. The outgoing word
   is given the slot to itself for 0.25s, and the incoming one waits that out
   before it starts. Clipping the cell instead would be shorter, but `overflow`
   other than visible moves an inline-grid's baseline to its bottom margin edge,
   and this sits mid-sentence. */
.contact-swap.is-live .contact-swap-word {
    grid-area: 1 / 1;
    opacity: 0;
    transform: translateY(0.35em);
    transition: opacity 0.25s ease,
                transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.contact-swap.is-live .contact-swap-word.is-current {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.3s ease 0.28s,
                transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.28s;
}

/* The comma list only exists for the fallback */
.contact-swap.is-live .contact-swap-sep { display: none; }

.contact-actions {
    grid-column: 1 / span 6;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2em;
}

/* --------------------------------------------------------------- Pill CTA */

.site .contact-cta {
    display: inline-flex;
    align-items: center;
    padding: 0.85em 1.8em;
    border: 1px solid var(--color-primary);
    border-radius: 999px;
    background: var(--color-primary);
    color: var(--color-background);
    text-decoration: none;
    cursor: none;
    transition: background-color 0.35s ease, color 0.35s ease;
}

/* Hollows out rather than dimming — the fill is the only solid shape on the
   page, so fading it would read as a disabled state */
.site .contact-cta:hover {
    background: transparent;
    color: var(--color-primary);
}

/* -------------------------------------------------------------- Arrow link */

.site .contact-alt {
    display: inline-flex;
    align-items: center;
    gap: 0.9em;
    color: var(--color-dim);
    text-decoration: none;
    cursor: none;
    transition: color 0.35s ease;
}

.site .contact-alt:hover {
    color: var(--color-primary);
}

.contact-arrow {
    width: 2.8em;
    height: auto;
    display: block;
    overflow: visible;
    transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.contact-alt:hover .contact-arrow {
    transform: translateX(0.4em);
}

/* -------------------------------------------------------------------- Cast */

/* The character, then a + and a ? — the heading's question again, in pictures.
   Driven by contact-character.js. */
.contact-cast {
    /* Explicitly placed across the four rows the text blocks occupy, so it
       stands beside all of them rather than under them. Spanned by count
       because the section has no row template — a fifth block in the left
       column means widening this. */
    grid-column: 7 / span 6;
    grid-row: 1 / span 4;
    align-self: center;

    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(0.5em, 1.2vw, 1.5em);
    height: min(66svh, 34em);

    /* Nothing in here is clickable, and the character's own hit testing does
       not exist on this page — the CTAs are the only targets and they sit left */
    pointer-events: none;
}

.contact-figure {
    height: 100%;
    flex: 0 1 46%;
}

.contact-figure canvas {
    display: block;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 1.4s ease;
    /* contact-character.js draws into a framebuffer a third of this size; the
       blow-up has to stay nearest-neighbour or the PSX pixels turn to mush */
    image-rendering: pixelated;
}

.contact-figure canvas.is-loaded { opacity: 1; }

/* Same hand as the heading's accent word, so the three read as one remark */
.contact-mark {
    font-family: just-me-again, cursive;
    /* Large: this hand draws well below its nominal size, so the em value runs
       far ahead of what lands on screen. Sized against the character beside it
       rather than against the body copy. */
    font-size: clamp(5em, 11vw, 12em);
    line-height: 1;
    text-transform: none;      /* .site sets uppercase on everything */
    color: var(--color-primary);

    /* Around chest height rather than the middle of the box — the character
       stands on the floor of the frame, so its own centre sits low */
    align-self: center;
    margin-top: -0.55em;

    opacity: 0;
    transform: translateY(0.2em);
}

/* Only once there is a character to stand beside. contact-character.js bails
   out under reduced motion, on coarse pointers and below 1024px, so in all
   three cases is-live never lands and the marks correctly never show. */
.contact-cast.is-live .contact-mark {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.9s ease, transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}

.contact-cast.is-live .contact-mark:nth-of-type(1) { transition-delay: 0.25s; }
.contact-cast.is-live .contact-mark:nth-of-type(2) { transition-delay: 0.45s; }

/* -------------------------------------------------------------- Breakpoints */

/* [ref] the section grid drops to 4 columns at 1024px */
@media only screen and (max-width: 1024px) {
    .contact-eyebrow,
    .site .contact-heading,
    .site .contact-body,
    .contact-actions { grid-column: 1 / span 4; }

    /* The measure is already set by the four-column span down here; keeping the
       13em cap on top of it would stack the heading in a narrow well */
    .site .contact-heading { max-width: none; }

    .contact-actions {
        gap: 1.4em;
        align-items: flex-start;
        flex-direction: column;
    }

    /* contact-character.js bails out at this same width, so the marks would be
       left standing on their own */
    .contact-cast { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .contact-arrow { transform: none; }
}
