/* About page only. Loaded after site.css.

   The horizontal run is built to the reference's own geometry, taken from its
   published stylesheet rather than estimated from screenshots — every value
   marked [ref] below is the one that site uses. Its layout is a *timeline*: a
   dashed rule sits at exactly 50vh and runs the whole length of the run, each
   panel hangs its title above that rule and its copy below, and the panels are
   60vw wide so two are always in view.

   Three things are deliberately not copied:
     · the typeface — Commit Mono stays, per the site;
     · the header — the site's own nav stays;
     · the palette — the reference is hard #000/#fff, and this site's tokens
       carry a light mode the reference has no equivalent for. The dark theme's
       #10100e/#ffffe3 sit within a couple of percent of it anyway.

   Everything here is written vertical-first: the plain stack below is what a
   phone, a reduced-motion setting, a narrow window or a failed script gets,
   and it is a complete page on its own. The run is layered on top of it under
   `html.is-horizontal`, which about.js sets once it decides to take over. */

/* The hand from the home page's annoy prompt, declared again here for the
   signature — the original lives in home.css, which this page does not load.
   Same arrangement contact.css already makes for the same reason. */
@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;
}

/* styles.css still carries the pre-shell about page, and `.about` on <body>
   there caps the document at 1350px and centres it. The header is
   position:absolute against the viewport, so it stays full-bleed while the
   content narrows and the two stop lining up. */
.site.about {
    max-width: none;
    margin: 0;
    padding: 0;
}

.about-page {
    position: relative;
    width: 100%;
}

/* ------------------------------------------------------------------- Type */

/* [ref] .o-title — 38px above 641px wide, 33px below, tracked 4px, light.
   Weight 300 in the source; Commit Mono ships 400 and 700, so 400 stands in.
   Qualified by .site because the shell's reset (`.site h1, .site h2 {
   font-size: 100% }`) carries an element in the selector and would otherwise
   outrank a bare class here however late this file loads. */
.site .about-title {
    margin: 0;
    font-size: 33px;
    font-weight: 400;
    letter-spacing: 4px;
    line-height: 1.15;
    /* styles.css still carries the pre-shell about page, whose own .about-title
       lowercases and recolours. Both are restated here rather than the class
       renamed, because this is the name the markup wants. */
    text-transform: inherit;
    color: inherit;
}

/* [ref] .o-subtitle — 18px, tracked 2px, .4rem above and 2rem below, on a
   10px root: 4px and 20px */
.site .about-subtitle {
    margin: 4px 0 20px;
    font-size: 18px;
    font-weight: 400;
    letter-spacing: 2px;
    line-height: 1.15;
}

/* [ref] .c-section_para — 1.5vh, line-height 2, tracked 2px. Sized off the
   viewport's *height* because every panel is one screen tall, so the copy
   scales with the space it has rather than with the width. */
.about-para p {
    margin: 0;
    font-size: 1.5vh;
    line-height: 2;
    letter-spacing: 2px;
}

.about-para p + p {
    margin-top: 1em;
}

/* The projects and places named in the prose. Underlined at rest rather than
   on hover: everywhere else on the site a link is a whole row or card and
   announces itself by position, but these sit mid-sentence and have nothing
   else to mark them. */
.site .about-link {
    color: inherit;
    text-decoration: none;
    cursor: none;
    border-bottom: 1px solid var(--color-faint);
    transition: border-color 0.35s ease;
}

.site .about-link:hover {
    border-bottom-color: currentColor;
}

/* Reads as a link the way the project rows do — a word with a rule under it
   that fills in on hover, rather than a button */
.site .about-cta {
    display: inline-block;
    margin-top: 2em;
    margin-right: 2em;
    position: relative;
    padding-bottom: 0.3em;
    color: var(--color-dim);
    text-decoration: none;
    cursor: none;
    letter-spacing: 2px;
    transition: color 0.35s ease;
}

.about-cta::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: right center;
    transition: transform 0.5s cubic-bezier(0.65, 0, 0.35, 1);
}

.site .about-cta:hover {
    color: var(--color-primary);
}

.about-cta:hover::after {
    transform: scaleX(1);
    transform-origin: left center;
}

/* The name, in the hand from the home page's annoy prompt. It stands in for the
   plain title rather than sitting under it — the page said "Jerome Craig" twice
   otherwise — so it is the h1, and the margin is a heading's: none above, a gap
   to the subtitle below. */
.site .about-signature {
    /* line-height 1 on a hand this loose leaves the descender of the g hanging
       outside its own line box, so the gap below has to clear the glyph rather
       than the box — hence more room under than a heading would normally take. */
    margin: 0 0 0.34em;
    font-family: just-me-again, cursive;
    font-size: 3.2em;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    color: var(--color-primary);
}

/* ------------------------------------------------------ Panels, stacked */

.about-track {
    display: flex;
    flex-direction: column;
}

.about-panel {
    position: relative;
    padding: 3em 0;
    border-top: 1px solid var(--hairline);
}

.about-panel:first-child {
    border-top: 0;
    padding-top: 0;
}

.about-panel-titles {
    margin-bottom: 2em;
}

.about-para p {
    max-width: 40em;
}

/* The rule is the horizontal run's spine and means nothing stacked */
.about-line,
.about-scroller,
.about-chapters {
    display: none;
}

/* ------------------------------------------------------------ Intro panel */

.about-panel--intro {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 3em;
}

.about-intro-text {
    flex: 1 1 20em;
}

.about-portrait {
    margin: 0;
    flex: 0 1 22em;
}

.about-portrait img {
    display: block;
    width: 100%;
    height: auto;
    -webkit-user-drag: none;
    user-select: none;
}

/* ============================================================ Horizontal */

/* Everything below applies only once about.js has taken the wheel. It leaves
   the document scroll authoritative — the scrollbar, the keyboard and the
   footer all still work — and maps scroll position onto the track's transform,
   so the run is a real scroll rather than a hijacked one. */

.is-horizontal .site.about main {
    padding: 0;
}

/* The shell's header is absolute, so it scrolls away with the document — which
   on every other page is the point, and here would take the navigation off
   screen a third of a panel into a run the whole page is spent inside. */
.is-horizontal .site-header {
    position: fixed;
}

.is-horizontal .about-viewport {
    position: sticky;
    top: 0;
    height: 100vh;
    overflow: hidden;
}

/* Block, not flex. As a flex item the track would be sized from its own
   max-content — the whole length of the run — and every percentage inside it
   would resolve against that instead of against the screen. */
.is-horizontal .about-track-clip {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

.is-horizontal .about-track {
    flex-direction: row;
    align-items: stretch;
    height: 100%;
    will-change: transform;
}

/* [ref] .c-timeline_section — 60vw of screen, one screen tall, 2px apart */
.is-horizontal .about-panel {
    flex: 0 0 auto;
    width: 60vw;
    height: 100%;
    margin: 0 2px;
    padding: 0;
    border-top: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* [ref] .o-section:first-child */
.is-horizontal .about-panel:first-child {
    padding-left: 10vw;
}

/* [ref] .o-section:last-child — the run ends on a wide right margin rather
   than against the edge of the screen */
.is-horizontal .about-panel:last-child {
    padding-right: 20vw;
}

/* ------------------------------------------------------------- The spine */

/* [ref] .c-timeline_section_line — a dashed rule half the panel wide, pinned
   to its right edge at the vertical middle, then scaled 2× across from that
   edge so it covers the whole panel with dashes twice the length. Panel to
   panel they meet, and the run reads as one continuous line. */
.is-horizontal .about-line {
    display: block;
    position: absolute;
    z-index: 3;
    top: 50%;
    right: 0;
    left: auto;
    width: 50%;
    height: 2px;
    border-bottom: 1px dashed var(--color-primary);
    transform: scaleX(2) scaleY(0.7);
    transform-origin: 100% 50%;
}

/* [ref] the end caps: a 50vh tick centred on the rule at the start of the
   first panel that carries it, and at the end of the last */
.is-horizontal .about-panel--line .about-line::before,
.is-horizontal .about-panel--line:last-of-type .about-line::after {
    content: '';
    position: absolute;
    display: block;
    width: 1px;
    height: 50vh;
    top: -25vh;
    left: -3px;
    background: var(--color-primary);
    transform: scaleX(0.5);
    transform-origin: center;
}

/* [ref] .c-timeline~.c-timeline …:before — only the first one gets a tick */
.is-horizontal .about-panel--line ~ .about-panel--line .about-line::before {
    display: none;
}

.is-horizontal .about-panel--line:last-of-type .about-line::after {
    left: auto;
    right: -3px;
}

/* --------------------------------------------------- Above and below it */

/* [ref] .c-about_txt_titles / .c-about_txt_content — the title block sits on
   the rule from above, the copy hangs 2vh under it, both inset 15% of the
   panel. Absolute, so neither one's length can push the other off the line. */
.is-horizontal .about-panel-titles {
    position: absolute;
    bottom: 50vh;
    left: 0;
    width: 100%;
    margin: 0;
    padding: 0 15%;
}

.is-horizontal .about-panel-content {
    position: absolute;
    top: 52vh;
    left: 0;
    width: 100%;
    padding: 0 15%;
}

.is-horizontal .about-para p {
    max-width: none;
}

/* The panels are clipped by the viewport, not the window, so the shared reveal
   in site.js fires as each one is carried in from the right. Its lift is taken
   off here: the rule and the copy are positioned against the panel, so
   translating the panel would drag that panel's stretch of the timeline out of
   line with its neighbours' for the length of the fade. */
.is-horizontal .about-panel.reveal {
    transform: none;
}

/* site.js staggers the reveal by writing transition-delay onto each element
   inline. A panel waiting to be revealed has no `transition` of its own yet,
   so transition-property is still `all` — and that inline delay then defers
   *every* property, including the box model, by up to a third of a second.
   The panels change shape wholesale the moment about.js sets is-horizontal,
   and they must do it at once rather than trickling into place. */
.about-panel.reveal {
    transition-property: opacity, transform;
}

/* [ref] .o-title at 641px and up */
.is-horizontal .site .about-title {
    font-size: 38px;
}

/* The subtitle sits under the title and above the rule, so its lower margin
   is the gap to the line rather than to the copy */
.is-horizontal .site .about-subtitle {
    margin: 4px 0 20px;
}

/* ------------------------------------------------------------ Intro panel */

/* [ref] .c-about_txt--intro is 60vw with a 10vw right inset; the image is
   50vh tall, pulled 5vw left of the section's own padding and set 12vw off
   the text. The panel is as wide as those add up to. */
.is-horizontal .about-panel--intro {
    width: auto;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0;
}

/* [ref] margin is `0 12vw 0 -5vw` there, which starts the picture at 5vw and
   leaves it under the chapter list; the reference clears it with a parallax
   offset that has the image sitting about 19vw in at rest. This run snaps
   rather than scrolling freely, so there is no parallax to inherit and the
   rest position is set directly. */
.is-horizontal .about-portrait {
    flex: 0 0 auto;
    height: 50vh;
    margin: 0 12vw 0 9vw;
}

/* [ref] .o-img — sized off its height, width follows the picture */
.is-horizontal .about-portrait img {
    height: 100%;
    width: auto;
    max-height: 90vh;
    max-width: none;
}

.is-horizontal .about-intro-text {
    position: relative;
    flex: 0 0 auto;
    width: 60vw;
    padding: 0 10vw 0 0;
}

/* ---------------------------------------------------- Chapters and mark */

/* [ref] .c-innernav — fixed, centred on the rule, 2vw from the edge.
   The reference adds a 4vw inner gutter, which pushes the list to 6vw and puts
   it across the panel's text column, which starts at 9vw. It gets away with
   that because its scroll is free and a panel is never *parked* underneath;
   this run snaps, so the same list would sit on the same words permanently.
   The gutter is the one measurement here dropped for that reason: at 2vw the
   list ends short of 9vw and the column is clear at every stop. */
.is-horizontal .about-chapters {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 2vw;
    top: 50%;
    transform: translateY(-50%);
    z-index: 10;
    padding: 10px 0 0;
}

/* [ref] .c-innernav_link */
.site .about-chapter {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 2px;
    margin: 6px 0;
    text-align: left;
    text-transform: uppercase;
    color: var(--color-primary);
    opacity: 0.5;
    cursor: none;
    transition: opacity 0.35s ease;
}

.site .about-chapter:hover,
.site .about-chapter.is-current {
    opacity: 1;
}

/* [ref] .o-scroller — 100 × 11.5 at 5vh off the bottom, 70px left of centre */
.is-horizontal .about-scroller {
    display: block;
    position: absolute;
    bottom: 5vh;
    left: calc(50vw - 70px);
    width: 100px;
    height: 11.5px;
    z-index: 4;
    color: var(--color-faint);
}

.about-scroller svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* -------------------------------------------------------- Section sketches */

/* The hand-drawn objects that belong to Craft, Drive, Games and Lately. The
   artwork is injected by sketch-draw.js from about-sketches.js; everything
   about how it draws on is here.

   The draw is a cascade, not a loop. Every <path> the build emits carries a
   --c custom property — its chunk index along the direction that sketch is
   drawn in — so one class on the container turns into 48 staggered steps and
   the whole sequence is handed to the compositor. Keep STEP and FADE in
   sketch-draw.js in step with these two numbers: it waits out the total before
   letting the hammer swing. */
.about-sketches {
    --sketch-step: 28ms;
    --sketch-fade: 90ms;
    /* 48 chunks × step, plus the last one's fade — the length of a full draw,
       and what the wipe fallback is matched to. sketch-draw.js waits this out
       before letting the hammer swing. */
    --sketch-draw: 1434ms;
    display: flex;
    align-items: flex-end;
    gap: 1.6em;
    margin: 0 0 2.2em;
}

.about-sketch {
    display: block;
    height: var(--h-stacked);
    color: var(--color-primary);
    /* Each sketch is given its own compositor layer. The sideways run moves the
       whole track by transform on every scroll frame, and these are the most
       complex geometry on the site — left in the track's own layer they are
       re-rasterized as it goes, which is enough to lock the page up. Promoted,
       they are rasterized once and then only moved. */
    transform: translateZ(0);
}

.about-sketch svg {
    display: block;
    width: auto;
    height: 100%;
    overflow: visible;
}

/* The second arm of the pair is the same drawing, turned over */
.about-sketch--flip svg {
    transform: scaleX(-1);
}

/* Undrawn until the container is told otherwise. The delay is what sequences
   it — every path shares one transition and differs only in --c. */
.about-sketch svg path {
    opacity: 0;
}

.about-sketch.is-drawn svg path {
    opacity: 1;
    transition: opacity var(--sketch-fade) linear;
    transition-delay: calc(var(--c, 0) * var(--sketch-step));
}

/* The fallback, for drawings the build could not chunk — see clean-sketches.mjs.
   A trace states holes as counter-rotating rings inside the same <path>, so a
   drawing that is one connected silhouette (the hammer) is one shape plus its
   own holes and cannot be split into strokes without the holes filling in.
   Those are wiped across instead of stroked in. Stepped rather than smooth, so
   it lands in bands like strokes rather than sliding like a bar — and on the
   brushed <> mark, which is also too few shapes to stagger, left to right is
   how it would have been drawn anyway. */
.about-sketch svg[data-reveal="wipe"] {
    clip-path: inset(0 100% 0 0);
}

.about-sketch svg[data-reveal="wipe"] path {
    opacity: 1;
}

@keyframes about-sketch-wipe {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0 0 0); }
}

.about-sketch.is-drawn svg[data-reveal="wipe"] {
    animation: about-sketch-wipe var(--sketch-draw) steps(26, end) both;
}

/* Sizes are per sketch rather than uniform: these are different drawings at
   different aspect ratios, and matching their heights would leave the mountain
   enormous and the <> mark tiny. Height drives, width follows. */
.about-sketch--computer   { --h-stacked: 7em;   --h: 18vh; }
.about-sketch--hammer     { --h-stacked: 6.2em; --h: 16vh; }
.about-sketch--muscle     { --h-stacked: 6.2em; --h: 16vh; }
.about-sketch--controller { --h-stacked: 5.4em; --h: 14vh; }
/* Solid brush strokes next to two fine line drawings, so it is set smaller than
   its height alone would suggest — matched on weight, not on size */
.about-sketch--div        { --h-stacked: 2.2em; --h: 5.5vh; }
.about-sketch--tiger      { --h-stacked: 8.5em; --h: 22vh; }
/* Alone in its panel and a wide, low drawing, so it is given a little more
   than its height alone would suggest */
.about-sketch--mountain   { --h-stacked: 5.4em; --h: 14vh; }

/* The swing. Paired keyframe stops rather than an eased sweep, so each angle is
   held and then snapped out of — a hammer at work, not a metronome. It is only
   started once the drawing has finished landing; see sketch-draw.js. */
@keyframes about-sketch-swing {
    0%, 49.9%  { transform: rotate(-25deg); }
    50%, 100%  { transform: rotate(25deg); }
}

.about-sketch--swing.is-swinging svg {
    /* Down the handle, so it pivots where a hand would hold it */
    transform-origin: 82% 88%;
    animation: about-sketch-swing 1.1s infinite;
}

/* Horizontal: the sketches sit in the empty upper right of the panel, resting
   just above the rule, opposite the title — which is anchored to the same line
   from the left. Both are absolute, so neither can push the other off it. */
.is-horizontal .about-sketches {
    position: absolute;
    bottom: 50vh;
    right: 15%;
    left: auto;
    z-index: 2;
    margin: 0 0 16px;
    gap: 2.2vw;
    max-width: 62%;
    pointer-events: none;
}

.is-horizontal .about-sketch {
    height: var(--h);
}

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

/* Below this the page never goes horizontal — about.js checks the same number
   — so only the stacked layout needs tuning */
@media only screen and (max-width: 1024px) {
    .about-panel--intro {
        flex-direction: column;
        align-items: flex-start;
    }

    .about-portrait {
        flex-basis: auto;
        width: 100%;
        max-width: 22em;
    }

    .about-para p {
        font-size: 100%;
        letter-spacing: 1px;
        line-height: 1.8;
    }

    .site .about-signature {
        font-size: 2.6em;
    }
}

@media (prefers-reduced-motion: reduce) {
    .about-cta::after {
        transition: none;
    }

    /* The sketches are artwork, so they stay — they just arrive already drawn
       and the hammer does not swing. sketch-draw.js sets is-drawn up front
       under the same query rather than waiting on the observer. */
    .about-sketch svg path {
        opacity: 1;
        transition: none;
    }

    .about-sketch svg[data-reveal="wipe"],
    .about-sketch.is-drawn svg[data-reveal="wipe"] {
        clip-path: none;
        animation: none;
    }

    .about-sketch--swing.is-swinging svg {
        animation: none;
        transform-origin: initial;
    }
}
