/* Copyright © 2026 Patrick Angle. All rights reserved. */

/* Where a page's own arrangement departs from the shared components in `elements.css`: the hero, the drawn phones, the
 * timeline, the resume, and the contact form. */

@layer components {
    /* Home */

    /* `isolation` is load-bearing: the slabs below sit at `z-index: -1`, and without a stacking context here they
       would paint behind the page background instead of behind the hero. `overflow: clip` takes the place of the
       wrapper the design used to keep them from bleeding into the ticker underneath, and is what ends the photograph
       at the ticker rather than letting it run the length of the page.

       The photograph is the hero's own background rather than a layer of its own, which is what keeps the slabs and
       the portrait over it: an element paints its background before even its negative-z-index children. The scrim over
       it is the page's own ground color, and it is what the headline reads against — the sky and the pale cliff carry
       paper-colored display type at about 1.2:1 on their own. It thins through the middle, where the photograph has
       the beach to show and the type has already cleared the sky. */
    home-hero {
        align-content: center;
        background-image:
            linear-gradient(
                to bottom,
                color-mix(in srgb, var(--page-background-color) 68%, transparent),
                color-mix(in srgb, var(--page-background-color) 54%, transparent) 45%,
                color-mix(in srgb, var(--page-background-color) 66%, transparent)
            ),
            image-set(url("../images/Home-Coast.jpg") 1x, url("../images/Home-Coast@2x.jpg") 2x);
        background-position: center;
        background-size: cover;
        isolation: isolate;
        min-block-size: 100dvh;
        overflow: clip;
        padding-block: max(var(--header-clearance-block-size), clamp(9rem, 18vh, 14rem)) clamp(4rem, 8vh, 7rem);
        position: relative;
    }

    home-hero::before,
    home-hero::after {
        border-radius: 2rem;
        content: "";
        position: absolute;
        z-index: -1;
    }

    /* No two brand hues clear even 1.5:1 against each other at these saturations, so the slabs have to miss the
       headline outright rather than try to contrast with it.

       This one sits bottom-right, behind the portrait, and appears only in the two-column layout — that is the only
       arrangement where the headline is confined to the left column and the right side belongs to the photo. */
    home-hero::before {
        background-color: var(--color-green);
        block-size: 18rem;
        display: none;
        inline-size: 30rem;
        inset-block-end: -9rem;
        inset-inline-end: -11rem;
        rotate: -9deg;
    }

    @media (width >= 64rem) {
        home-hero::before {
            display: block;
        }
    }

    /* Its right edge tracks the content column's left edge rather than the viewport's, so it keeps the same clearance
       from the headline at every width and slides off-canvas once the column runs full-bleed. */
    home-hero::after {
        background-color: var(--color-purple);
        block-size: 5rem;
        inline-size: 14rem;
        inset-block-start: 30%;
        inset-inline-end: calc(100% - max(50% - var(--content-max-inline-size) / 2, 0px) - var(--gutter-inline-size) + 2rem);
        rotate: -18deg;
    }

    /* At phone widths the slabs are large enough relative to the viewport to land under the headline and the
       portrait. The portrait and its own color block carry the hero there instead. */
    @media (width < 52rem) {
        home-hero::before,
        home-hero::after {
            display: none;
        }
    }

    home-hero content-split {
        align-items: center;
        gap: clamp(2rem, 4vw, 3.5rem);
        grid-template-columns: minmax(0, 1fr);
    }

    /* The portrait is last in the DOM so a narrow screen reads kicker → headline → photo. */
    @media (width >= 64rem) {
        home-hero content-split {
            column-gap: clamp(1.5rem, 2.5vw, 2.5rem);
            grid-template-columns: minmax(0, 1fr) 26rem;
        }
    }

    home-hero content-stack {
        container-type: inline-size;
        gap: clamp(1.5rem, 3vw, 2.5rem);
    }

    /* Sized against the copy column rather than the viewport: that column loses width to the portrait beside it, and
       only a container unit keeps the longest line filling it without running into the photo. */
    home-hero page-headline h1 {
        font-size: clamp(2.25rem, 13.4cqi, 5.5rem);
    }

    home-hero heading-line:nth-child(even) {
        margin-inline-start: clamp(0rem, 4vw, 3.5rem);
    }

    /* Same width as the two-column rule above, because it is the same fact: once the photo no longer fits beside the
       copy it sits under it, and the copy has the whole column to itself. The headline's size is set by its longest
       line, so every shorter one falls short of the right edge — beside the portrait that shortfall is what the photo
       and the slabs stand in, and with nothing there the block reads as hanging off the left. Centring splits the
       difference to both sides, and leaves the indent nothing to do: against a centered line it is not a stagger. */
    @media (width < 64rem) {
        home-hero page-headline h1 {
            text-align: center;
        }

        home-hero heading-line:nth-child(even) {
            margin-inline-start: 0;
        }

        /* The kicker goes with them — left alone at the far edge of a wide column it reads as a label that got left
           behind rather than as the top of the stack. Its tilt pivots from the middle here rather than from its first
           letter, so the rotation keeps it on the axis it was just centered on. */
        home-hero page-kicker {
            align-self: center;
            transform-origin: 50% 100%;
        }
    }

    /* Portraits — each on an offset flat color block, the specimen's way of backing an image. */

    portrait-figure {
        display: block;
        position: relative;
    }

    portrait-figure::before {
        aspect-ratio: 4 / 5;
        border-radius: var(--block-corner-radius);
        content: "";
        inset: 0 0 auto;
        position: absolute;
        z-index: -1;
    }

    portrait-figure img {
        block-size: auto;
        border-radius: var(--block-corner-radius);
        inline-size: 100%;
    }

    /* The cap is what the two-column layout's own track is, so it only ever bites in the one-column layout, where the
       portrait is the whole of its row: `100%` fills the content column on a phone rather than leaving a stripe of
       ground beside the photo, and the cap still holds the picture back at the widths in between. Those are the widths
       the auto margins are for — stacked under a centered headline, a photo narrower than the column has to sit on the
       same axis. Beside the copy the track is the cap exactly, so there is no free space and the margins do nothing.
       The width is set rather than capped because auto margins on a grid item suppress its stretch, and a merely
       capped figure would fall back to the intrinsic width of the picture instead of filling the column. */
    home-hero portrait-figure {
        inline-size: min(26rem, 100%);
        margin-inline: auto;
        rotate: 2.5deg;
    }

    home-hero portrait-figure::before {
        background-color: var(--color-red);
        rotate: -5deg;
    }

    page-masthead portrait-figure {
        max-inline-size: min(22rem, 100%);
        rotate: -2.5deg;
    }

    page-masthead portrait-figure::before {
        background-color: var(--page-accent-color);
        rotate: 5deg;
    }

    page-masthead content-split {
        align-items: center;
        gap: clamp(2.5rem, 5vw, 4.5rem);
        grid-template-columns: minmax(0, 1fr);
    }

    @media (width >= 58rem) {
        page-masthead content-split {
            grid-template-columns: minmax(0, 1fr) 22rem;
        }
    }

    /* Product pages — Stagehand and OnStage */

    product-hero {
        padding-block-start: max(var(--header-clearance-block-size), clamp(9rem, 16vh, 13rem));

        /* The gel strip is the hero's bottom edge rather than a sibling below it, so the copy's own end padding sets
           the distance and the row gap the other page elements share would only push the band away from it. */
        row-gap: 0;
    }

    product-hero > content-stack {
        --stack-gap: clamp(1.25rem, 3vw, 2rem);

        padding-block-end: clamp(3rem, 7vw, 5rem);
    }

    /* Nine letters on one line, so the size tracks the viewport directly rather than through a `rem` floor that would
       overflow narrow screens; `flex-wrap` catches whatever the cap can't. Laying the words out as flex items drops
       the spaces between them, so the gap puts a word space back — a single-word headline never sees it. */
    product-hero h1 {
        column-gap: 0.22em;
        display: flex;
        flex-wrap: wrap;
        font-size: clamp(3rem, 10.5vw, 11rem);
    }

    /* The phones are the heavier column, so this split centers them against the copy rather than hanging both from a
       shared top edge. */
    content-split:has(phone-pair) {
        align-items: center;
        gap: clamp(2.5rem, 6vw, 5rem);
    }

    phone-pair {
        display: flex;
        gap: clamp(0.75rem, 2vw, 1.5rem);
        justify-content: center;
    }

    /* A screen capture in its hardware's bezel. The two devices differ only in how much bezel and how round a corner,
       so each names those two values and the drawing is shared. */

    :is(device-phone, device-tablet) {
        background-color: var(--ink-color);
        border-radius: var(--device-corner-radius);
        box-shadow: 0 1.5rem 3.5rem rgb(0 0 0 / 0.45);
        display: block;
        padding: var(--device-bezel-width);
    }

    :is(device-phone, device-tablet) img {
        block-size: auto;
        border-radius: calc(var(--device-corner-radius) - var(--device-bezel-width));
        display: block;
        inline-size: 100%;
    }

    device-phone {
        --device-bezel-width: 0.85rem;
        --device-corner-radius: 2.25rem;

        flex: 0 1 15rem;
        max-inline-size: 15rem;
    }

    device-phone:nth-child(odd) {
        rotate: -4deg;
    }

    device-phone:nth-child(even) {
        rotate: 5deg;
        translate: 0 1.5rem;
    }

    device-tablet {
        --device-bezel-width: 1.1rem;
        --device-corner-radius: 1.9rem;

        margin-inline: auto;
        max-inline-size: min(44rem, 100%);
        rotate: -1.5deg;
    }

    /* Colophon */

    page-timeline {
        border-inline-start: 3px solid var(--page-rule-color);
        display: flex;
        flex-direction: column;
        gap: clamp(2rem, 4vw, 3rem);
        padding-inline-start: clamp(1.25rem, 3vw, 2.5rem);
    }

    timeline-entry {
        display: grid;
        gap: 0.75rem 2rem;
        grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
        position: relative;
    }

    timeline-entry::before {
        background-color: var(--page-accent-color);
        block-size: 0.9rem;
        border-radius: 50%;
        content: "";
        inline-size: 0.9rem;
        inset-block-start: 0.4rem;
        inset-inline-start: calc(-1 * clamp(1.25rem, 3vw, 2.5rem) - 0.45rem - 1.5px);
        position: absolute;
    }

    entry-date {
        color: var(--page-accent-color);
        display: block;
        font-size: 0.85rem;
        letter-spacing: 0.14em;
        text-transform: uppercase;
    }

    @media (width < 46rem) {
        timeline-entry {
            grid-template-columns: minmax(0, 1fr);
        }
    }

    /* About */

    content-split > page-prose {
        font-size: clamp(1.05rem, 1rem + 0.5vw, 1.3rem);
    }

    content-split > page-prose p {
        max-inline-size: 38ch;
    }

    /* Resume */

    /* `min(…, 100%)` keeps the track from forcing the page wider than the viewport on a phone, where 24rem exceeds
       the content column. */
    resume-list {
        display: grid;
        gap: 2rem 2.5rem;
        grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
    }

    resume-entry {
        border-inline-start: 6px solid var(--resume-entry-color, var(--page-accent-color));
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
        padding-inline-start: clamp(1rem, 2.5vw, 1.75rem);
    }

    resume-entry:nth-child(5n + 1) { --resume-entry-color: var(--color-yellow); }
    resume-entry:nth-child(5n + 2) { --resume-entry-color: var(--color-teal); }
    resume-entry:nth-child(5n + 3) { --resume-entry-color: var(--color-pink); }
    resume-entry:nth-child(5n + 4) { --resume-entry-color: var(--color-green); }
    resume-entry:nth-child(5n + 5) { --resume-entry-color: var(--color-purple); }

    entry-meta {
        color: var(--resume-entry-color, var(--page-accent-color));
        display: flex;
        flex-wrap: wrap;
        font-size: 0.85rem;
        gap: 0.5rem 1rem;
        letter-spacing: 0.12em;
        text-transform: uppercase;
    }

    entry-dates {
        color: var(--page-muted-text-color);
    }

    /* The role names the job the company heading above it was held in, so it is a heading of its own. It sits at the
       quiet end of the scale, which it declares on itself rather than inheriting the entry's. */
    resume-entry h4 {
        --heading-font-size: 0.95rem;
        --heading-axis-settings: "SCTR" 40, "JMBL" 120, "COUT" 110;
        --heading-letter-spacing: 0.1em;
        --heading-line-height: var(--body-line-height);
        --heading-text-transform: uppercase;
    }

    data-table {
        display: block;
        overflow-x: auto;
    }

    /* A table wide enough to need scrolling stops mid-word wherever its scroller ends, and ending at the gutter reads
       as a column that got cut off rather than as one there is more of. Running the scroller to the edges of the
       screen is the difference: the gutter comes back as the scroller's own padding, so the first column still lines
       up with the copy above it and the last one now runs off the screen, which is where a reader expects to push.
       Above this width the content column is wider than the table's own minimum and there is nothing to scroll. */
    @media (width < 46rem) {
        data-table {
            margin-inline: calc(-1 * var(--gutter-inline-size));
            padding-inline: var(--gutter-inline-size);
        }
    }

    data-table table {
        border-collapse: collapse;
        inline-size: 100%;
        min-inline-size: 38rem;
        text-align: start;
    }

    data-table :is(th, td) {
        border-block-end: 2px solid var(--page-rule-color);
        padding: 0.85rem 1rem 0.85rem 0;
        text-align: start;
        vertical-align: baseline;
    }

    data-table thead th {
        color: var(--page-accent-color);
        font-size: 0.8rem;
        letter-spacing: 0.14em;
        text-transform: uppercase;
    }

    data-table tbody th {
        font-size: 1.15rem;
        font-variation-settings: "SCTR" 25, "JMBL" 140, "COUT" 100;
    }

    data-table tbody td {
        color: var(--page-muted-text-color);
    }

    /* The qualification that closes the skills section stands alone under the grid rather than joining it. */
    block-grid + collage-block {
        margin-block-start: 1.5rem;
        max-inline-size: 34rem;
    }

    /* Knowledge base */

    /* A support article is one column of running text whose lists and figures belong to the same measure its paragraphs
       already keep, so the article names the measure once and everything inside it inherits the limit. Its headings
       label a step rather than announce a section, so they sit at the card end of the scale. */
    page-section > page-prose {
        --heading-font-size: 1.35rem;
        --heading-axis-settings: "SCTR" 70, "JMBL" 185, "COUT" 125;
        --heading-letter-spacing: normal;
        --heading-line-height: 1.15;
        --heading-text-transform: none;
        --prose-max-inline-size: 38rem;

        max-inline-size: var(--prose-max-inline-size);
    }

    page-section > page-prose :is(h2, h3):not(:first-child) {
        margin-block-start: 0.9rem;
    }

    /* A screenshot with nothing drawn around it: these are windows and panels rather than whole devices, and several
       arrive as PNGs whose own drop shadow is part of the picture. */
    screen-figure {
        display: block;
        max-inline-size: min(38rem, 100%);
    }

    screen-figure img {
        block-size: auto;
        border-radius: var(--block-corner-radius);
        inline-size: 100%;
    }

    /* Contact */

    contact-form {
        display: block;
    }

    contact-form form {
        display: flex;
        flex-direction: column;
        gap: 1.25rem;
    }

    contact-form form > p:first-child {
        font-size: 1.15rem;
        max-inline-size: 34ch;
    }

    form-field {
        display: flex;
        flex-direction: column;
        gap: 0.4rem;
    }

    form-field label {
        font-size: 0.8rem;
        letter-spacing: 0.14em;
        text-transform: uppercase;
    }

    /* The asterisk is drawn from the control's own `required` rather than typed into the label, so the marker and the
       constraint can never disagree. */
    form-field:has([required]) label::after {
        color: var(--page-accent-color);
        content: " *";
    }

    form-field :is(input, textarea) {
        background-color: color-mix(in srgb, var(--ink-color) 22%, transparent);
        border: 2px solid color-mix(in srgb, var(--page-text-color) 40%, transparent);
        border-radius: 0.9rem;
        padding: 0.7rem 0.9rem;
        resize: vertical;
    }

    form-field :is(input, textarea):focus {
        border-color: var(--page-accent-color);
        outline: none;
    }

    form-submit {
        display: block;
    }

    form-submit button {
        align-items: center;
        background-color: var(--page-accent-color);
        border: 2px solid transparent;
        border-radius: var(--chip-corner-radius);
        color: var(--page-accent-text-color);
        cursor: pointer;
        display: inline-flex;
        gap: 0.6rem;
        letter-spacing: 0.06em;
        padding: 0.7em 1.4em;
        text-transform: uppercase;
        transition: rotate 200ms ease, scale 200ms ease;
    }

    form-submit button:hover {
        rotate: -1.5deg;
        scale: 1.03;
    }

    /* Error page */

    /* The ghost light: the lamp a theatre leaves burning in the middle of an empty stage so that whoever opens up next
       can find the edge of it. It is a bare bulb in principle and almost never in practice — a company that keeps one
       for a season ends up putting a shade on it, and the sillier that shade looks clamped to the work-light stand
       underneath, the more it looks like the real thing.

       The shade is also what makes the fixture drawable. Light out of a shade is two shapes — a cone going down and a
       pool where it lands — where a bare filament is a falloff curve that never reads as anything but a blurred
       circle, however many rings of blur are stacked behind it. */

    /* Every measurement in the fixture is an `em`, so the whole lamp — shade, fringe, pole, foot, cable, and the light
       it throws — scales from the one `font-size` below and nothing else has to be touched to resize it. The element
       carries no text, so the font size is doing no other work here. */
    ghost-light {
        --shade-inline-size: 9.5em;
        --shade-block-size: 4.2em;
        --finial-block-size: 0.9em;
        --lamp-color: #FFD9A6;
        --shade-color: #E23E63;
        --shade-stripe-color: #FFB3C0;
        --shade-trim-color: #FFC721;
        --stand-color: #4C4653;

        block-size: 17em;
        display: block;
        font-size: 1rem;
        inline-size: 13em;
        margin-inline: auto;
        position: relative;
    }

    /* The cone the shade throws, opening from the width of its bottom rim down to the deck.
       `screen` is what makes it light rather than paint: a warm translucent wash laid flat over a near-black ground
       mixes to brown and reads as a solid object hanging off the lamp, where the same color screened onto that ground
       adds to it and reads as air. The mask then takes the hard sides off, since spill has edges only in a smoke
       machine. */
    ghost-light::before {
        animation: ghost-light-breathe 5s ease-in-out infinite alternate;
        background-image: linear-gradient(
            to bottom,
            color-mix(in srgb, var(--lamp-color) 38%, transparent),
            color-mix(in srgb, var(--lamp-color) 11%, transparent) 62%,
            transparent 96%
        );
        clip-path: polygon(14% 0, 86% 0, 100% 100%, 0 100%);
        content: "";
        inset: calc(var(--finial-block-size) + var(--shade-block-size) - 0.15em) 0 1.1em;
        mask-image: linear-gradient(to right, transparent, #000 26%, #000 74%, transparent);
        mix-blend-mode: screen;
        position: absolute;
    }

    /* Where that cone lands. Without it the light stops in mid-air and the fixture reads as a sticker. */
    ghost-light::after {
        animation: ghost-light-breathe 5s ease-in-out infinite alternate;
        background-image: radial-gradient(
            ellipse 42% 38% at 50% 55%,
            color-mix(in srgb, var(--lamp-color) 46%, transparent),
            color-mix(in srgb, var(--lamp-color) 12%, transparent) 58%,
            transparent 78%
        );
        block-size: 2.6em;
        content: "";
        inset-block-end: -0.4em;
        inset-inline: 0;
        mix-blend-mode: screen;
        position: absolute;
    }

    /* The shade. Its own box holds only the finial — the body and the fringe hang off it, so that the clip-path the
       body needs to be a trapezoid never reaches the fringe below it. */
    light-shade {
        background-image: radial-gradient(
            circle 0.3em at 50% 0.42em,
            var(--shade-trim-color) 68%,
            transparent 70%
        );
        /* The box has to be given a size of its own: its two pseudo-elements are positioned out of the flow, so left
           to itself it would collapse to nothing and take the finial painted on it along too. */
        block-size: calc(var(--finial-block-size) + var(--shade-block-size) + 0.85em);
        display: block;
        inset-block-start: 0;
        inset-inline: calc((13em - var(--shade-inline-size)) / 2);
        position: absolute;
    }

    /* The body: ticking stripes, a trapezoid, and a wash down the inside that puts the bulb behind the fabric rather
       than in front of it. */
    light-shade::before {
        background-image:
            linear-gradient(
                to bottom,
                rgb(0 0 0 / 0.26),
                rgb(0 0 0 / 0.02) 44%,
                color-mix(in srgb, var(--lamp-color) 60%, transparent)
            ),
            repeating-linear-gradient(
                90deg,
                var(--shade-color) 0 0.62em,
                var(--shade-stripe-color) 0.62em 1.24em
            );
        block-size: var(--shade-block-size);
        clip-path: polygon(27% 0, 73% 0, 100% 100%, 0 100%);
        content: "";
        inset-block-start: var(--finial-block-size);
        inset-inline: 0;
        position: absolute;
    }

    /* The fringe, scalloped by repeating one circle along the shade's bottom edge, with the hot line of the bulb
       showing between the tassels. */
    light-shade::after {
        background-color: var(--shade-trim-color);
        block-size: 0.85em;
        content: "";
        inset-block-start: calc(var(--finial-block-size) + var(--shade-block-size) - 0.05em);
        inset-inline: 0;
        mask-image: radial-gradient(circle 0.34em at 50% 0, #000 96%, transparent 100%);
        mask-repeat: repeat-x;
        mask-size: 0.68em 100%;
        position: absolute;
    }

    /* The stand: a plain steel work-light pole, which is the joke the shade is sitting on. */
    light-stand {
        background-image: linear-gradient(
            90deg,
            #201D25,
            var(--stand-color) 42%,
            #6E6676 52%,
            #201D25
        );
        display: block;
        inline-size: 0.45em;
        inset-block: calc(var(--finial-block-size) + var(--shade-block-size)) 1.1em;
        inset-inline-start: calc(50% - 0.225em);
        position: absolute;
    }

    /* The foot, wide and flat and sitting in its own light. */
    light-stand::before {
        background-image: linear-gradient(to bottom, #57505F, #221F28);
        block-size: 0.75em;
        border-radius: 50%;
        content: "";
        inline-size: 5em;
        inset-block-end: -0.5em;
        inset-inline-start: calc(50% - 2.5em);
        position: absolute;
    }

    /* The cable, running off the base and out of the light the way it does on every stand that ever stood on a stage. */
    light-stand::after {
        border-block-end: 2px solid #2A2731;
        border-end-start-radius: 1.6em;
        border-inline-start: 2px solid #2A2731;
        block-size: 1.4em;
        content: "";
        inline-size: 3.4em;
        inset-block-end: -0.3em;
        inset-inline-start: 0.2em;
        position: absolute;
    }

    @keyframes ghost-light-breathe {
        from {
            opacity: 0.82;
        }

        to {
            opacity: 1;
        }
    }

    /* The error page is the only page that carries a ghost light, which is enough to name it — it needs no class of
       its own, and no content file has to know that this is the page the rule is for. */

    body:has(ghost-light) page-footnote {
        max-inline-size: 34rem;
    }

    /* Beside the copy the lamp has to carry its side of the masthead against display type several times its size, so it
       grows with the viewport rather than staying the size it is when it sits under a phone's worth of text.

       The track is `auto` rather than a width of its own: a fixed track wide enough for the lamp at its largest would
       still be that wide at the breakpoint, where the headline needs every pixel it can get and the fixture is at its
       smallest. Letting the track take the fixture's own width means the copy is never charged for room the lamp is
       not using. */
    @media (width >= 58rem) {
        body:has(ghost-light) page-masthead content-split {
            grid-template-columns: minmax(0, 1fr) auto;
        }

        ghost-light {
            font-size: clamp(1rem, 1.9vw, 1.75rem);
        }
    }

    /* Beside the copy the page reads left to right like every other masthead on the site. Once the lamp drops beneath
       it there is nothing to the right of anything, and the column centers on the fixture instead — the same trade the
       home hero makes at the width where its own two columns become one. */
    @media (width < 58rem) {
        body:has(ghost-light) :is(page-masthead h1, page-prose, page-section) {
            text-align: center;
        }

        body:has(ghost-light) page-prose p {
            margin-inline: auto;
        }

        body:has(ghost-light) page-kicker {
            align-self: center;
            transform-origin: 50% 100%;
        }

        body:has(ghost-light) page-section {
            justify-items: center;
        }

        /* The row fills the column whether or not its buttons do, so centring the box leaves them ranged left against
           it — the buttons have to be told where to sit inside the row as well. */
        body:has(ghost-light) button-row {
            justify-content: center;
        }
    }

    /* The word the sentence turns on, eroding and recovering on the cutout axis. The typeface can say it twice — once
       with the word and once with the shape of it — and this is the one page with a reason to let it. */
    body:has(ghost-light) page-masthead h1 em {
        animation: ghost-light-erode 5s ease-in-out infinite alternate;
    }

    @keyframes ghost-light-erode {
        from {
            font-variation-settings: "SCTR" 105, "JMBL" 215, "COUT" 150;
        }

        to {
            font-variation-settings: "SCTR" 145, "JMBL" 250, "COUT" 300;
        }
    }
}
