/*
 * The page over the tree: logo, blurb, reel, the work timeline and the footer. Type
 * and colour follow the original site (archive-obvc9ceg/mainhome.css).
 *
 * #hero is fixed over the canvas and ignores input; tree-hero.js translates
 * #hero-track by the scene offset tree-scroll.js broadcasts, so the page moves with
 * the tree as the camera descends.
 */

/* Keep iOS Safari from boosting text sizes on its own (it starts doing so in wide
   blocks of copy, like the bio, once the page allows pinch-zoom). */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

#hero {
    position: fixed;
    inset: 0;
    z-index: 10002;
    overflow: hidden;
    pointer-events: none;
    font-family: 'Cousine', monospace;
    color: #000;
}

#hero-track {
    position: absolute;
    inset: 0 0 auto 0;
    will-change: transform;
}

/* Hidden until the web fonts are in, then faded in: the head (logo, blurb, reel)
   first, the timeline and footer just after (see "reveal" in tree-hero.js). The
   washes aren't faded, so the tint over the tree is there from the start. */
#hero-head,
#hero-page {
    opacity: 0;
    transition: opacity .9s cubic-bezier(.25, .1, .25, 1);
}

#hero-page {
    transition-delay: .15s;
}

#hero.ready #hero-head,
#hero.ready #hero-page {
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    #hero-head,
    #hero-page {
        transition: none;
    }
}

#hero a,
#hero button,
#hero video {
    pointer-events: auto;
}

/* WASHES ///////////////////////////////////// */

/* A tint over the splats so the type reads (no blur): strongest at the top, easing
   off through and past the fold (240vh), so it scrolls away gradually, with a
   grayscale pass under it.

   Every fade here is eased (smootherstep, sampled at 9 stops) rather than linear.
   The tint carries a faint grain that dithers away 8-bit banding in the long white
   fade. Generated by a script: change the curve by regenerating rather than
   hand-editing the stops. */
.wash {
    /* White tint, fading out down the wash. */
    --tint: .5;
    /* 1 = opaque white at the strongest edge, easing down to --tint; 0 = flat --tint. */
    --opaque-top: 1;
    /* Tint colour, as r,g,b. */
    --tint-rgb: 255,255,255;
    /* 1 = fully black and white under the tint, 0 = original colour. */
    --grayscale: 1;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    pointer-events: none;
}

/* Phones: no grayscale pass in the washes (a backdrop filter, costly there); just
   the tint gradient. */
html.phone .wash::before {
    display: none;
}

/* At the top of the page: strongest at the top, fading out down through the fold. */
#hero-wash {
    height: 240vh;
}

/* Behind the footer: the same wash flipped, strongest at the bottom and fading out
   upward, without the opaque edge: a flat 70% black tint all the way down, under
   white footer text. tree-hero.js
   sets its height to the footer's plus a fade above it. */
.wash.flipped {
    --opaque-top: 0;
    --tint: .7;
    --tint-rgb: 0,0,0;
    --grayscale: 0;
    top: auto;
    /* A few px past the page's bottom, which can land a fraction of a pixel above the
       window's, so no sliver of untinted scene shows under it. */
    bottom: -6px;
    transform: scaleY(-1);
}

/* First in paint order: desaturates the splats under the tint, so
   the washed area reads black and white. Eases back to colour toward the bottom. */
.wash::before {
    content: '';
    position: absolute;
    inset: 0;
    -webkit-backdrop-filter: grayscale(var(--grayscale));
    backdrop-filter: grayscale(var(--grayscale));
    -webkit-mask-image: linear-gradient(rgba(0,0,0,1.000) 55.0%, rgba(0,0,0,0.984) 60.6%, rgba(0,0,0,0.896) 66.2%, rgba(0,0,0,0.725) 71.9%, rgba(0,0,0,0.500) 77.5%, rgba(0,0,0,0.275) 83.1%, rgba(0,0,0,0.104) 88.8%, rgba(0,0,0,0.016) 94.4%, rgba(0,0,0,0.000) 100.0%);
    mask-image: linear-gradient(rgba(0,0,0,1.000) 55.0%, rgba(0,0,0,0.984) 60.6%, rgba(0,0,0,0.896) 66.2%, rgba(0,0,0,0.725) 71.9%, rgba(0,0,0,0.500) 77.5%, rgba(0,0,0,0.275) 83.1%, rgba(0,0,0,0.104) 88.8%, rgba(0,0,0,0.016) 94.4%, rgba(0,0,0,0.000) 100.0%);
}

/* Last in paint order, so the tint sits on top of the splats. Opaque white at
   the very top, easing down to --tint by about a fifth of the way (eased stops), plus
   grain; the eased mask then fades it out. */
.wash::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.06'/%3E%3C/svg%3E"),
        linear-gradient(
            rgba(var(--tint-rgb),calc(var(--tint) + (1 - var(--tint)) * var(--opaque-top))) 0%,
            rgba(var(--tint-rgb),calc(var(--tint) + (1 - var(--tint)) * var(--opaque-top))) 4%,
            rgba(var(--tint-rgb),calc(var(--tint) + (1 - var(--tint)) * .85 * var(--opaque-top))) 8%,
            rgba(var(--tint-rgb),calc(var(--tint) + (1 - var(--tint)) * .5 * var(--opaque-top))) 13%,
            rgba(var(--tint-rgb),calc(var(--tint) + (1 - var(--tint)) * .15 * var(--opaque-top))) 18%,
            rgba(var(--tint-rgb),var(--tint)) 22%);
    -webkit-mask-image: linear-gradient(rgba(0,0,0,1.000) 30.0%, rgba(0,0,0,0.984) 38.8%, rgba(0,0,0,0.896) 47.5%, rgba(0,0,0,0.725) 56.2%, rgba(0,0,0,0.500) 65.0%, rgba(0,0,0,0.275) 73.8%, rgba(0,0,0,0.104) 82.5%, rgba(0,0,0,0.016) 91.2%, rgba(0,0,0,0.000) 100.0%);
    mask-image: linear-gradient(rgba(0,0,0,1.000) 30.0%, rgba(0,0,0,0.984) 38.8%, rgba(0,0,0,0.896) 47.5%, rgba(0,0,0,0.725) 56.2%, rgba(0,0,0,0.500) 65.0%, rgba(0,0,0,0.275) 73.8%, rgba(0,0,0,0.104) 82.5%, rgba(0,0,0,0.016) 91.2%, rgba(0,0,0,0.000) 100.0%);
}


/* HEAD ///////////////////////////////////// */

#hero-head {
    position: relative;
    padding: 3vw 0 0 4vw;
}

#hero-logo {
    display: block;
    width: 20em;
    height: 3em;
    background: url(site/logo_static.gif) left center / contain no-repeat;
    border: none !important;
}

#hero-logo:hover {
    background-image: url(site/logo.gif);
}

/* Above the reel. Set in Martian Mono (variable) for the cursor ripple in
   tree-hero.js: the text is split into one .c span per character, each in a fixed
   slot (--cell, measured mid-range) so widening a glyph never reflows the line. Words are kept whole with nowrap so lines still
   break between words. Only the characters take the pointer, so the empty parts of
   the paragraph don't block hovering the reel underneath. */
#hero-blurb {
    --cell: .6em;
    position: relative;
    z-index: 2;
    margin: 4vh 0 0 -.08em;
    font-family: 'Martian Mono', 'Cousine', monospace;
    font-size: clamp(2.2rem, 4vw, 5.5rem);
    line-height: 1.15em;
    font-variation-settings: 'wdth' 105, 'wght' 300;
    color: #111;
    pointer-events: none;
}

#hero-blurb .line {
    display: block;
    white-space: nowrap;
}

#hero-blurb .w {
    white-space: nowrap;
}

/* White outline, as on the old site, on a copy of the text laid exactly under the
   letters (see tree-hero.js). Transparent fill, so only the stroke draws; its inner
   half is hidden by the letters on top, so the visible outline is half the stroke.
   The ripple swells it per character. */
#hero-blurb .blurb-outline,
#hero-blurb .blurb-swell {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    color: transparent;
}

#hero-blurb .blurb-outline {
    -webkit-text-stroke: 2px #fff;
}

/* The hover swell: solid white stroke, width set per character by the ripple, cut
   into a halftone by the mask: two offset dot grids, which together make the classic
   45-degree screen. --halftone is the grid spacing, --dot the dot radius. The layer is
   padded out by --reach (and shifted back) so fat strokes past the text box aren't
   clipped by the mask. */
#hero-blurb .blurb-swell {
    --halftone: 4px;
    --dot: .5px;
    --reach: 80px;
    top: calc(var(--reach) * -1);
    left: calc(var(--reach) * -1);
    padding: var(--reach);
    -webkit-text-stroke: 0 #fff;
    -webkit-mask-image:
        radial-gradient(circle, #000 var(--dot), transparent calc(var(--dot) + .5px)),
        radial-gradient(circle, #000 var(--dot), transparent calc(var(--dot) + .5px));
    mask-image:
        radial-gradient(circle, #000 var(--dot), transparent calc(var(--dot) + .5px)),
        radial-gradient(circle, #000 var(--dot), transparent calc(var(--dot) + .5px));
    -webkit-mask-size: var(--halftone) var(--halftone);
    mask-size: var(--halftone) var(--halftone);
    -webkit-mask-position: 0 0, calc(var(--halftone) / 2) calc(var(--halftone) / 2);
    mask-position: 0 0, calc(var(--halftone) / 2) calc(var(--halftone) / 2);
}

#hero-blurb .blurb-outline .c,
#hero-blurb .blurb-swell .c {
    pointer-events: none;
}

#hero-blurb .blurb-swell + span {
    position: relative;
}

/* The padding (cancelled by the margin, so layout is unchanged) gives descenders
   and heavy strokes room inside each letter's box at the ripple's peak. */
#hero-blurb .c {
    display: inline-flex;
    justify-content: center;
    width: var(--cell);
    padding: .15em 0;
    margin: -.15em 0;
    pointer-events: auto;
    transform-origin: 50% 70%;
    transition:
        font-variation-settings .35s cubic-bezier(.16,1,.3,1),
        transform .35s cubic-bezier(.16,1,.3,1),
        -webkit-text-stroke-width .35s cubic-bezier(.16,1,.3,1);
}

/* Full sentence for screen readers; the split characters are aria-hidden. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* REEL ///////////////////////////////////// */

/* 16:9, centred on the page behind the blurb, tilted 2deg. tree-hero.js sets top so
   the tilted top edge lands on the middle of the blurb's last line; the value here is
   only a fallback until it measures. Rotating about the centre lifts the highest
   corner by width / 2 * sin(2deg) ~= width * .0175, which the script accounts for.
   Hover straightens it, scales it up slightly, lifts it above the blurb, and shows
   the expand control. */
#hero-reel {
    --reel-width: min(54vw, 96vh);
    position: absolute;
    top: calc(3vw + var(--reel-width) * .0175);
    left: calc(50% - var(--reel-width) / 2);
    z-index: 1;
    width: var(--reel-width);
    aspect-ratio: 16 / 9;
    background: #000;
    box-shadow: 0 20px 60px rgba(0,0,0,.3);
    pointer-events: auto;
    transform: rotate(2deg);
    transform-origin: 50% 50%;
    transition: transform .6s cubic-bezier(.16,1,.3,1), box-shadow .6s ease;
}

#hero-reel:not(.expanded):not(.flying):hover {
    z-index: 3;
    transform: rotate(0deg) scale(1.06);
    box-shadow: 0 40px 140px rgba(0,0,0,.45);
}

#hero-reel video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Filling the window. tree-hero.js moves the reel to <body> for this (the hero track
   is transformed, which would trap position: fixed) and animates in and out of it.
   .flying is the in-between state on the way back, pinned to the inline spot. */
#hero-reel.expanded,
#hero-reel.flying {
    position: fixed;
    right: auto;
    z-index: 10011;
    transform: none;
    transition: none;
}

#hero-reel.expanded {
    inset: 0;
    width: min(100vw, 100vh * 16 / 9);
    height: min(100vh, 100vw * 9 / 16);
    margin: auto;
    box-shadow: none;
}

#reel-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10010;
    background: #000;
    opacity: 0;
    visibility: hidden;
    transition: opacity .45s ease, visibility 0s linear .45s;
}

#reel-backdrop.open {
    opacity: 1;
    visibility: visible;
    transition: opacity .45s ease;
}

/* REEL CONTROLS ///////////////////////////////////// */

/* Pixel icons from make-icons.py: white with the black hairline outline baked into
   the svg, shown as plain background images. No masks, filters or hover styles: an
   earlier mask + drop-shadow version flickered on hover. Sized at 1px per svg unit
   (2px for the big expand icon) to stay crisp. */
.reel-icon {
    display: block;
    padding: 0;
    border: none;
    background: var(--icon) center / 100% 100% no-repeat;
    cursor: pointer;
    pointer-events: auto;
}

#reel-expand {
    --icon: url(site/icons/expand.svg);
    position: absolute;
    top: 50%;
    left: 50%;
    width: 70px;
    height: 70px;
    margin: -35px 0 0 -35px;
    opacity: 0;
    transition: opacity .3s ease;
}

/* Controls show while the pointer is active over the reel (.controls-on, set by
   tree-hero.js) and fade after a short idle or when the pointer leaves. */
#hero-reel.controls-on:not(.expanded):not(.flying) #reel-expand,
#reel-expand:focus-visible {
    opacity: 1;
}

#hero-reel.expanded #reel-expand,
#hero-reel.flying #reel-expand {
    display: none;
}

.reel-controls {
    position: absolute;
    right: 2.5%;
    bottom: 4%;
    display: flex;
    align-items: center;
    gap: 18px;
    opacity: 0;
    transition: opacity .3s ease;
}

#hero-reel.controls-on .reel-controls,
.reel-controls:focus-within {
    opacity: 1;
}

/* Phones use the system video player (tree-hero.js): none of the custom controls. */
html.phone #reel-expand,
html.phone .reel-controls {
    display: none;
}

/* Hide the cursor too while watching full screen and idle. */
#hero-reel.expanded:not(.controls-on) {
    cursor: none;
}

#reel-collapse {
    --icon: url(site/icons/collapse.svg);
    display: none;
    width: 35px;
    height: 35px;
}

#hero-reel.expanded #reel-collapse {
    display: block;
}

#reel-mute {
    --icon: url(site/icons/sound-off.svg);
    width: 41px;
    height: 35px;
}

#reel-mute.loud {
    --icon: url(site/icons/sound-on.svg);
}

/* Touch screens and phones: no controls on the inline reel (a tap expands it); once
   it's full screen, collapse and mute show and fade as usual. */
@media only screen and (max-width: 768px), (hover: none) {
    #hero-reel:not(.expanded) #reel-expand,
    #hero-reel:not(.expanded) .reel-controls {
        display: none;
    }

    /* 75% of the desktop icons. */
    #reel-collapse {
        width: 26px;
        height: 26px;
    }

    #reel-mute {
        width: 31px;
        height: 26px;
    }

    .reel-controls {
        gap: 14px;
    }
}


/* TIMELINE ///////////////////////////////////// */

/* The page below the hero; tree-hero.js sizes it. */
#hero-page {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    pointer-events: none;
}

/* Modules alternate either side of a rail down the middle (over the trunk). All the
   same width; tree-hero.js sets each one's top and a tilt of at most 1deg (--tilt).
   --gutter is the space between the rail and a module's inner edge. */
#timeline {
    --module-width: clamp(18rem, 30vw, 34rem);
    --gutter: 2.5vw;
    position: absolute;
    inset: 0;
}

/* The drawn tree (tree-hero.js, drawTree): trunk, a branch to each card, roots above
   the footer. One thin white line throughout; the trunk's top and the roots fade via
   gradients set inline (inline style, so they win over the stroke below). */
#timeline svg.tree {
    position: absolute;
    top: 0;
    left: 0;
    overflow: visible;
    pointer-events: none;
}

#timeline svg.tree path {
    fill: none;
    stroke: rgba(255,255,255,.75);
    stroke-width: 1;
    stroke-linecap: round;
}

#timeline .module {
    position: absolute;
    width: var(--module-width);
    box-sizing: border-box;
    color: #000;
    box-shadow: 0 20px 60px rgba(0,0,0,.28);
    pointer-events: auto;
    transform: rotate(var(--tilt, 0deg));
    transition: transform .5s cubic-bezier(.16,1,.3,1), box-shadow .5s ease;
}

/* Hover straightens the card and grows it for a closer look, anchored on its rail
   side so it expands outward, away from the middle. */
#timeline .module:hover {
    z-index: 2;
    transform: rotate(0deg) scale(var(--grow, 1.1));
    box-shadow: 0 40px 120px rgba(0,0,0,.42);
}

#timeline .module.left {
    right: calc(50% + var(--gutter));
    transform-origin: 100% 50%;
}

#timeline .module.right {
    left: calc(50% + var(--gutter));
    transform-origin: 0 50%;
}

#timeline.stacked .module:hover {
    --grow: 1.04;
    transform: scale(var(--grow));
}

#timeline .media {
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: #111;
}

#timeline .media video,
#timeline .media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Icons sit in a row on a light ground rather than filling the frame. */
#timeline .media.icons {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    align-items: center;
    gap: 4%;
    padding: 0 6%;
    background: #f1f1f1;
}

#timeline .media.icons img {
    height: auto;
    aspect-ratio: 1;
    object-fit: contain;
}

/* Under the text: plain opaque white. */
#timeline .copy {
    padding: 1.1em 1.4em 1.4em;
    background: #fff;
}

#timeline .period {
    margin: 0 0 .5em;
    font-size: .75em;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #444;
}

#timeline h3 {
    margin: 0;
    font-size: 1.5em;
    font-weight: 400;
    line-height: 1.2;
}

#timeline .sub {
    margin: .3em 0 0;
    font-size: .95em;
    color: #222;
}

/* Abbreviations in the cards (e.g. VPG) show their expansion on hover, instantly
   and in the site's black label style, rather than as a slow native tooltip. */
#timeline abbr[data-tip] {
    position: relative;
    text-decoration: underline dotted rgba(0,0,0,.4);
    text-underline-offset: .2em;
    cursor: help;
}

#timeline abbr[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    left: 50%;
    bottom: calc(100% + .5em);
    padding: .3em .6em;
    font-size: .85em;
    white-space: nowrap;
    color: #fff;
    background: #000;
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, .3em);
    transition: opacity .15s ease, transform .15s ease;
}

#timeline abbr[data-tip]:hover::after {
    opacity: 1;
    transform: translate(-50%, 0);
}

#timeline .text {
    margin: .7em 0 0;
    font-family: 'Noticia Text', serif;
    font-size: .95em;
    line-height: 1.55;
    color: #555;
}

/* Narrow windows: one centred column, no rail. */
#timeline.stacked {
    --module-width: 88vw;
}

/* The tree's roots, drawn in the footer above its wash but under its text. The same
   line as the tree; tree-hero.js positions it and sets each root's fade. */
#hero-footer svg.roots {
    position: absolute;
    overflow: visible;
    pointer-events: none;
    z-index: 1;
}

#hero-footer svg.roots path {
    fill: none;
    stroke: rgba(255,255,255,.75);
    stroke-width: 1;
    stroke-linecap: round;
}

#timeline.stacked .module {
    left: 6vw;
}

/* EXPERIENCE, over the stacked cards; the site's heading style (the resume's square). */
#timeline .subhead {
    display: none;
}

#timeline.stacked .subhead {
    display: block;
    position: absolute;
    left: 6vw;
    margin: 0;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #000;
}

#timeline.stacked .subhead::before {
    content: '';
    display: inline-block;
    width: .6em;
    height: .6em;
    margin: 0 .55em .08em 0;
    background: #000;
}

/* FOOTER ///////////////////////////////////// */

/* Full width at the bottom of the page, over the scene, on the top's wash
   flipped (.wash.flipped): the about section (photo, bio, skillset in small type,
   headings with the resume's square), then a bar with the links and credit. */
#hero-footer {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 3em 4vw 1.6em;
    color: #fff;
    pointer-events: auto;
}

/* Content above the footer's wash and roots, capped and centred so very wide
   windows keep the ideal spacing rather than stretching it (the wash and roots
   still run full width). */
#hero-footer > :not(.wash):not(.roots) {
    position: relative;
    z-index: 2;
    max-width: 78rem;
    margin-left: auto;
    margin-right: auto;
}

/* Wide: photo | bio | skillset, the bio centred in the space between. Medium: bio |
   skillset, no photo. Narrow: one column, no photo (see the media queries). The photo is a fixed-size square, never sized from the text, so the
   columns can't feed back on each other. */
#hero-footer .about {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 26rem);
    grid-template-areas: "photo bio skills";
    align-items: start;
    gap: 2.8em;
    font-size: .85em;
    line-height: 1.55;
}

#hero-footer .photo {
    grid-area: photo;
    display: block;
    width: clamp(10rem, 16vw, 16rem);
    height: auto;
    aspect-ratio: 1;
    object-fit: cover;
}

/* Flush with the trailing edge of the footer content (left on phones). */
#hero-footer .skills {
    grid-area: skills;
    justify-self: end;
}

#hero-footer .heading {
    margin: 0 0 .9em;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

#hero-footer .heading::before {
    content: '';
    display: inline-block;
    width: .6em;
    height: .6em;
    margin: 0 .55em .08em 0;
    background: #fff;
}

#hero-footer .bio {
    grid-area: bio;
    justify-self: center;
    max-width: 34rem;
}

/* Medium widths: no photo; bio | skillset side by side. */
@media only screen and (max-width: 1100px) {
    #hero-footer .about {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "bio skills";
    }

    #hero-footer .photo {
        display: none;
    }

    #hero-footer .bio {
        justify-self: start;
    }
}

#hero-footer .bio p:not(.heading) {
    margin: 0 0 1em;
    font-family: 'Noticia Text', serif;
    color: #fff;
}

#hero-footer .bio p:last-child {
    margin-bottom: 0;
}

#hero-footer ul {
    margin: 0;
    padding: 0;
    list-style: none;
    columns: 2;
    column-gap: 1.5em;
}

#hero-footer li {
    margin: 0 0 .25em;
    break-inside: avoid;
    white-space: nowrap;
}

/* Styled as a heading, like SKILLSET; just spaced off the list. */
#hero-footer .refs {
    margin: 2em 0 0;
}

#hero-footer .bar {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: .5em 2em;
    margin-top: 2.4em;
    padding-top: 1.2em;
    border-top: 1px solid rgba(255,255,255,.2);
    color: rgba(255,255,255,.7);
}

#hero-footer .bar p {
    margin: 0;
}

/* The MY ROOTS heading's size (the about block's .85em), on one line. */
#hero-footer .links {
    font-size: .85em;
    line-height: 2em;
    white-space: nowrap;
}

#hero-footer .links a {
    color: #fff;
    text-decoration: none;
    border-bottom: solid 1px rgba(255,255,255,.4);
    transition: border .2s ease;
}

#hero-footer .links a:hover {
    border-bottom: solid 2px #fff;
}

#hero-footer .credit {
    margin-top: .2em;
    font-size: .9em;
    color: #fff;
    opacity: .7;
}

#hero-footer .credit a {
    color: #fff;
    text-decoration: none;
    border-bottom: solid 1px rgba(255,255,255,.4);
}

#hero-footer .credit a:hover {
    border-bottom-color: #fff;
}

/* MEDIA QUERIES ///////////////////////////////////// */

@media only screen and (max-width: 768px) {
    #hero-logo {
        width: 13em;
        height: 2em;
    }

    #hero-head {
        padding: 6vw 5vw 0;
    }

    /* Too narrow for the set lines: let them flow and wrap between words. */
    #hero-blurb .line {
        display: inline;
        white-space: normal;
    }

    #hero-blurb {
        font-size: 4.5vw;
    }

    #hero-blurb .blurb-outline,
    #hero-blurb .blurb-swell {
        display: none;
    }

    /* Exactly the hero's content width (100vw - 2 * 5vw padding), so auto margins
       centre it; wider, it overflowed to the right. */
    #hero-reel {
        --reel-width: 90vw;
        position: relative;
        top: auto;
        left: auto;
        margin: 2em auto 0;
        transform: none;
    }

    #hero-reel:hover {
        transform: rotate(0deg) scale(1.04);
    }

    #timeline .module h3 {
        font-size: 1.25em;
    }

    /* One column, bio then skillset, flush left; no photo on phones. */
    #hero-footer .about {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "bio"
            "skills";
        gap: 1.5em;
    }

    #hero-footer .photo {
        display: none;
    }

    #hero-footer .bio {
        justify-self: stretch;
    }


    #hero-footer .skills {
        justify-self: start;
    }
}

/* ROOTS TUNING PANEL ///////////////////////////////////// */

/* ?tune or R (tree-hero.js). Matches tree-scroll.js's camera panel, on the left. */
#roots-tune {
    position: fixed;
    top: 12px;
    left: 12px;
    z-index: 10012;
    width: 230px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
    padding: 12px 14px 14px;
    background: rgba(10,13,13,.9);
    border: 1px solid rgba(255,255,255,.14);
    font: 400 10px/1.5 'Cousine', ui-monospace, monospace;
    color: rgba(255,255,255,.8);
    user-select: none;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
}

#roots-tune h4 {
    margin: 0 0 10px;
    font-size: 10px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: rgba(255,255,255,.5);
}

#roots-tune label {
    display: block;
    margin-bottom: 8px;
}

#roots-tune .row {
    display: flex;
    justify-content: space-between;
}

#roots-tune .val {
    color: #64FF6F;
}

#roots-tune input[type=range] {
    width: 100%;
    margin: 2px 0 0;
    accent-color: #64FF6F;
}

#roots-tune button {
    width: 100%;
    margin-top: 4px;
    padding: 6px;
    background: rgba(255,255,255,.08);
    border: 1px solid rgba(255,255,255,.2);
    color: rgba(255,255,255,.85);
    font: inherit;
    letter-spacing: .1em;
    text-transform: uppercase;
    cursor: pointer;
}
