

:root {
    /* --- Palette: unchanged from the original -------------------------- */
    --c-black: #000000;
    --c-beige: #cec5be;
    --c-brown: #9e7352;
    --c-green: #042a17;
    --c-accent: #ffeda3;
    --c-grad-a: #8c52ff;
    --c-grad-b: #ff914d;

    --c-beige-72: rgba(206, 197, 190, 0.72);
    --c-beige-24: rgba(206, 197, 190, 0.24);
    --c-black-70: rgba(0, 0, 0, 0.7);
    --c-black-24: rgba(0, 0, 0, 0.24);
    --c-accent-72: rgba(255, 237, 163, 0.72);

    /* Hero texture. Turn these down to make it subtler, up to make it
       louder, without touching the background rule itself. */
    --tech-dot: rgba(206, 197, 190, 0.14);
    --tech-line: rgba(206, 197, 190, 0.05);
    --tech-scan: rgba(206, 197, 190, 0.022);

    /* --- Semantic (default = black panel) ------------------------------- */
    --ground: var(--c-black);
    --ink: var(--c-beige);
    --muted: var(--c-beige-72);
    --rule: var(--c-beige-24);
    --slam: var(--c-beige);

    /* --- Type: the original families ------------------------------------ */
    --sans: "Montserrat", Arial, sans-serif;
    --serif: "DM Serif Display", Georgia, serif;

    --step--1: 0.75rem;
    --step-0: clamp(0.8125rem, 0.7857rem + 0.1339vw, 0.9375rem);
    --step-1: clamp(0.9375rem, 0.9018rem + 0.1786vw, 1.0625rem);
    --step-2: clamp(1.0625rem, 0.9911rem + 0.3571vw, 1.3125rem);
    --step-3: clamp(1.375rem, 1.1964rem + 0.8929vw, 2rem);
    --display: clamp(3.25rem, 1.4464rem + 9.0179vw, 9rem);

    /* Story body copy scales harder than the general scale, because that
       section has the room to spend. */
    --story-lead: clamp(1.0625rem, 0.9375rem + 0.625vw, 1.5rem);
    --display-s: clamp(2.25rem, 1.5357rem + 3.5714vw, 4.75rem);

    --leading-display: 0.82;
    --leading-body: 1.75;
    --tracking-display: -0.035em;
    --tracking-label: 0.08em;
    --measure: 62ch;

    /* --- Space ----------------------------------------------------------- */
    --space-3xs: 0.25rem;
    --space-2xs: 0.5rem;
    --space-xs: 0.75rem;
    --space-s: 1rem;
    --space-m: clamp(1.25rem, 1.1607rem + 0.4464vw, 1.5rem);
    --space-l: clamp(1.75rem, 1.5714rem + 0.8929vw, 2.375rem);
    --space-xl: clamp(2.5rem, 2.1429rem + 1.7857vw, 3.75rem);
    --space-2xl: clamp(4rem, 3.2857rem + 3.5714vw, 6.5rem);

    --gutter: clamp(1.125rem, 0.7679rem + 1.7857vw, 2.375rem);
    --width-wide: 87.5rem;

    /* --- Shape: one radius language, and it is none --------------------- */
    --radius: 0;
    --rule-hair: 1px;
    --rule-thick: 2px;
    --rule-heavy: 3px;
    --seam: 0.5rem;
    --offset: 0.5rem;

    --z-progress: 200;
    --z-skip: 300;

    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --dur-fast: 110ms;
    --dur-base: 240ms;
    --dur-slow: 640ms;
}

/* =============================================================================
   1. BASE
   ========================================================================== */

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

* {
    margin: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-block-start: var(--space-l);
    scrollbar-width: none;      /* Firefox, Chrome 121+ */
    -ms-overflow-style: none;   /* old Edge/IE */
}
html::-webkit-scrollbar {
  display: none;              /* Safari, older Chrome */
}

body {
    min-height: 100svh;
    background: var(--c-black);
    color: var(--c-beige);
    font-family: var(--sans);
    font-size: var(--step-1);
    line-height: var(--leading-body);
    overflow-wrap: break-word;
    -webkit-font-smoothing: antialiased;
}

a {
    color: inherit;
    text-decoration: none;
}

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

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

code {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 0.92em;
    padding: 0 0.3em;
    background: var(--c-black-24);
}

:focus-visible {
    outline: var(--rule-heavy) solid var(--c-accent);
    outline-offset: var(--space-3xs);
}

.skip-link {
    position: absolute;
    z-index: var(--z-skip);
    inset-block-start: 0;
    inset-inline-start: 0;
    padding: var(--space-xs) var(--space-s);
    background: var(--c-accent);
    color: var(--c-black);
    font-weight: 800;
    transform: translateY(-120%);
}

.skip-link:focus {
    transform: none;
}

.visually-hidden:not(:focus):not(:active) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Thin raw scroll indicator — the only piece of chrome on the page. */
.progress {
    position: fixed;
    z-index: var(--z-progress);
    inset-block-start: 0;
    inset-inline: 0;
    height: var(--rule-heavy);
    background: var(--c-black-24);
}

.progress > span {
    display: block;
    width: 0%;
    height: 100%;
    background: var(--c-accent);
}

/* =============================================================================
   2. PANELS
   Section colours are the original's. The seam between them is now an
   explicit heavy rule rather than an implied colour change.
   ========================================================================== */

section {
    position: relative;
    padding-block: var(--space-2xl);
    padding-inline: var(--gutter);
    background: var(--ground);
    color: var(--ink);
    border-block-start: var(--seam) solid var(--c-black);
}

.hero {
    border-block-start: 0;
}

/* ===========================================================================
   HERO BACKGROUND — technology texture

   Still black. Three layers over it: a fine perfboard dot matrix, a faint
   major grid, and slow scanlines. Opacities are deliberately low so the
   texture reads as material rather than as pattern.

   TO GO BACK TO PLAIN BLACK: delete this one rule. Nothing else depends on
   it — the section already gets its black from the `section` rule above.
   =========================================================================== */

.hero {
    background-color: var(--c-black);
    background-image:
        /* perfboard: the dots a component would solder into */
        radial-gradient(
            var(--tech-dot) 1px,
            transparent 1.2px
        ),
        /* major grid, like a schematic sheet */
        linear-gradient(var(--tech-line) 1px, transparent 1px),
        linear-gradient(90deg, var(--tech-line) 1px, transparent 1px),
        /* CRT scanlines */
        repeating-linear-gradient(
            var(--tech-scan) 0 1px,
            transparent 1px 4px
        );
    background-size:
        1.25rem 1.25rem,
        7.5rem 7.5rem,
        7.5rem 7.5rem,
        100% 4px;
    background-position: center;
}

.story {
    --ground: var(--c-beige);
    --ink: var(--c-black);
    --muted: var(--c-black-70);
    --rule: var(--c-black-24);
    --slam: var(--c-black);
}

.projects {
    --ground: var(--c-brown);
    --ink: var(--c-black);
    /* Muted black fails 4.5:1 on this brown, so hierarchy here comes from
       size and weight instead of from opacity. */
    --muted: var(--c-black);
    --rule: var(--c-black-24);
    --slam: var(--c-black);
}

.contact {
    --ground: #174d3a;
    --ink: #ffeda3;
    --muted: rgba(255, 237, 163, 0.72);
    --rule: rgba(255, 237, 163, 0.45);
    --slam: #ffeda3;

    background-color: var(--ground);

    /*
     * Cutting-mat grid:
     * - large 1cm-style grid
     * - smaller 5mm-style grid
     */
    background-image:
        linear-gradient(
            rgba(255, 237, 163, 0.12) 1px,
            transparent 1px
        ),
        linear-gradient(
            90deg,
            rgba(255, 237, 163, 0.12) 1px,
            transparent 1px
        ),
        linear-gradient(
            rgba(255, 237, 163, 0.055) 1px,
            transparent 1px
        ),
        linear-gradient(
            90deg,
            rgba(255, 237, 163, 0.055) 1px,
            transparent 1px
        );

    background-size:
        5rem 5rem,
        5rem 5rem,
        1rem 1rem,
        1rem 1rem;

    background-position: center;
}

/* =============================================================================
   3. TYPE
   ========================================================================== */

.heading {
    font-family: var(--serif);
    font-size: var(--display);
    font-weight: 400;
    line-height: var(--leading-display);
    letter-spacing: var(--tracking-display);
    text-wrap: balance;
}

/* Descenders would otherwise be clipped by the reveal's overflow box. */
.heading .line {
    display: block;
    overflow: hidden;
    padding-block: 0.14em;
    margin-block: -0.14em;
}

.heading .line > span {
    display: block;
}

/* The heavy rule under each section heading. It is the brutalist device
   doing the work the original had no equivalent for. */
.services .heading::after,
.projects .heading::after {
    content: "";
    display: block;
    height: var(--rule-heavy);
    margin-block-start: var(--space-m);
    background: currentColor;
    transform: scaleX(var(--draw, 1));
    transform-origin: left;
    transition: transform var(--dur-slow) var(--ease-out);
}

.label {
    font-size: var(--step--1);
    font-weight: 800;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

/* =============================================================================
   4. COMPONENTS
   ========================================================================== */

/* --- GitHub band -----------------------------------------------------------
   Was: a contained pill with a gradient fill and two colour blocks peeling
   out on hover. Now: a full-bleed stripe that breaks the section gutter and
   doubles as the seam into the next section.

   It also behaves differently. The old button was a static target that
   animated on hover; this one is moving by default and stops when you point
   at it, so hovering is what makes it readable rather than what decorates
   it. The destination URL scrolls past in the band itself, so you know where
   you are going before you tap, and the hit area is the full width of the
   viewport instead of a 56px pill. */

.github-band {
    position: relative;
    display: flex;
    align-items: center;
    min-height: 4.5rem;
    overflow: hidden;
    margin-block-start: var(--space-2xl);
    margin-block-end: calc(var(--space-2xl) * -1);
    margin-inline: calc(var(--gutter) * -1);
    border-block: var(--rule-heavy) solid var(--c-black);
    background: linear-gradient(90deg, var(--c-grad-a), var(--c-grad-b));
    color: var(--c-black);
}

.github-band__track {
    display: flex;
    width: max-content;
}

.github-band__set {
    display: flex;
    align-items: center;
    gap: var(--space-m);
    padding-inline-end: var(--space-m);
    font-size: clamp(1.125rem, 0.875rem + 1.25vw, 2rem);
    font-weight: 800;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    white-space: nowrap;
}

.gb-icon {
    width: 1.25em;
    height: 1.25em;
    fill: currentColor;
    transition: transform var(--dur-base) var(--ease-out);
}

.gb-sep {
    opacity: 0.55;
}

/* The stop-to-read label, hidden until the band is pointed at or focused. */
.github-band__label {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--c-black);
    color: var(--c-beige);
    font-size: clamp(1.125rem, 0.875rem + 1.25vw, 2rem);
    font-weight: 800;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    transform: translateY(101%);
    transition: transform var(--dur-base) var(--ease-out);
}

.github-band:hover .github-band__label,
.github-band:focus-visible .github-band__label {
    transform: translateY(0);
}

.github-band:hover .gb-icon,
.github-band:focus-visible .gb-icon {
    transform: rotate(-14deg) scale(1.12);
}

/* Pressed: the label snaps to beige. A hard three-state flip, no easing. */
.github-band:active .github-band__label {
    background: var(--c-beige);
    color: var(--c-black);
}

/* --- Project button --------------------------------------------------------
   Square, and it carries a stretched hit area so the whole card is tappable. */

.project-button {
    align-self: flex-start;
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    margin-block-start: var(--space-m);
    padding: var(--space-2xs) var(--space-m);
    border: var(--rule-thick) solid var(--c-black);
    border-radius: var(--radius);
    background: var(--c-black);
    color: var(--c-beige);
    font-size: var(--step--1);
    font-weight: 800;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

.project-button::after {
    /* Stretched hit area: the whole card becomes the tap target, which
       matters far more on a phone than on a mouse. */
    content: "";
    position: absolute;
    inset: 0;
}

/* =============================================================================
   5. SECTIONS
   ========================================================================== */

/* --- Hero ------------------------------------------------------------------ */

.hero {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100svh;
}

.hero-content {
    width: min(var(--width-wide), 100%);
    margin-inline: auto;
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: center;
}

/* Greeting sits well below the name so the two read as different voices
   rather than one long headline. */
.hero-greeting {
    font-size: clamp(1.5rem, 1.1786rem + 1.6071vw, 2.625rem);
    font-style: italic;
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--muted);
}

/* Surname is drawn, not filled. The solid line above it and the hollow line
   below give the block its contrast without adding a second colour. */
.hero-outline {
    color: inherit;
}

@supports (-webkit-text-stroke: 1px currentColor) {
    .hero-outline {
        color: transparent;
        -webkit-text-stroke: var(--rule-thick) var(--c-beige);
    }

    .hero-dot {
        -webkit-text-stroke-width: 0;
    }
}

/* The one spot of accent in the hero, tying it to the sticker and the
   contact block. */
.hero-dot {
    color: var(--c-accent);
}

.hero-subtitle {
    position: relative;
    max-width: 32ch;
    margin-inline: auto;
    margin-block-start: var(--space-l);
    padding-block-start: var(--space-m);
    color: var(--muted);
    font-family: var(--serif);
    font-size: var(--step-2);
    font-style: italic;
}

/* A short accent dash instead of a full-width rule — the full rule made the
   subtitle read as a footer to the name rather than an aside. */
.hero-subtitle::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 50%;
    width: 3rem;
    height: var(--rule-heavy);
    background: var(--c-accent);
    transform: translateX(-50%);
}

.scroll-cue {
    align-self: center;
    margin-block-start: var(--space-l);
    color: var(--muted);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

/* --- Story -----------------------------------------------------------------
   The section fills the viewport and distributes its three parts down it,
   the way the original did. At desktop the body splits into two columns so
   the paragraph and the quote occupy the full width instead of stacking in
   a narrow centred rail. */

.story {
    /* lifts the section, and so the overhanging sticker, above the black
       block that comes after it in the DOM */
    z-index: 1;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.story-content {
    width: min(var(--width-wide), 100%);
    margin-inline: auto;
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--space-xl);
    text-align: center;
}

.story-content > p {
    max-width: var(--measure);
    margin-inline: auto;
    margin-block: 0;
    font-size: var(--story-lead);
    line-height: var(--leading-body);
}

/* The pull quote is a sticker: back to its original size, tilted, pushed
   off-centre, and hung past the bottom edge so it sits on the black of the
   next section. --tilt is a variable so the entrance animation can reuse the
   same angle instead of fighting it. */
.story blockquote {
    --tilt: -2.5deg;
    position: relative;
    z-index: 1;
    max-width: 34ch;
    margin-inline: auto;
    /* clears the section's bottom padding, then 2.5rem more onto the black */
    margin-block: 0 calc((var(--space-2xl) + 2.5rem) * -1);
    padding: var(--space-m);
    border: var(--rule-heavy) solid var(--c-black);
    background: var(--c-accent);
    color: var(--c-black);
    box-shadow: var(--offset) var(--offset) 0 var(--c-black);
    font-family: var(--serif);
    font-size: var(--step-3);
    font-style: italic;
    line-height: 1.2;
    text-align: start;
    transform: rotate(var(--tilt));
}

.story cite {
    display: block;
    margin-block-start: var(--space-s);
    padding-block-start: var(--space-2xs);
    border-block-start: var(--rule-thick) solid currentColor;
    font-family: var(--sans);
    font-size: var(--step--1);
    font-style: normal;
    font-weight: 800;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

/* --- Services --------------------------------------------------------------- */

.services-inner {
    width: min(var(--width-wide), 100%);
    margin-inline: auto;
}

.services-list {
    margin-block-start: var(--space-xl);
    border-block-end: var(--rule-hair) solid var(--rule);
}

.service {
    position: relative;
    padding-block: var(--space-m);
    border-block-start: var(--rule-hair) solid var(--rule);
}

.service h3 {
    font-family: var(--sans);
    font-size: var(--step-2);
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

.service p {
    max-width: var(--measure);
    margin-block-start: var(--space-2xs);
    color: var(--muted);
    font-size: var(--step-1);
}

/* --- Projects --------------------------------------------------------------- */

.projects-content {
    width: min(var(--width-wide), 100%);
    margin-inline: auto;
}

.project-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-xl);
    margin-block-start: var(--space-2xl);
}

.project {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    overflow: hidden;
    padding: var(--space-m);
    border: var(--rule-heavy) solid var(--ink);
    background: var(--ground);
    box-shadow: var(--offset) var(--offset) 0 var(--slam);
    transition:
        transform var(--dur-fast) linear,
        box-shadow var(--dur-fast) linear;
}

/* Hard state flip: no fade, so it reads as a switch rather than decoration. */
.project:hover,
.project:focus-within {
    background: var(--c-black);
    color: var(--c-beige);
    transform: translate(
        calc(var(--offset) / 2),
        calc(var(--offset) / 2)
    );
    box-shadow: calc(var(--offset) / 2) calc(var(--offset) / 2) 0 var(--slam);
}

.project:hover .project-button,
.project:focus-within .project-button {
    background: var(--c-beige);
    border-color: var(--c-beige);
    color: var(--c-black);
}

.project h3 {
    font-family: var(--serif);
    font-size: var(--step-3);
    font-weight: 400;
    line-height: 1;
    letter-spacing: var(--tracking-display);
}

.project > div > p {
    max-width: 40ch;
    margin-block-start: var(--space-s);
    font-size: var(--step-1);
}

.project-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3xs);
    margin-block-start: var(--space-m);
}

.project-tags span {
    padding: 0 var(--space-2xs);
    border: var(--rule-hair) solid currentColor;
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

.raspberry-pi {
    position: absolute;

    width: clamp(18rem, 32vw, 38rem);

    left: 5vw;
    bottom: -12vw;

    transform: rotate(-12deg);

    z-index: 2;
    pointer-events: none;
}

/* --- Contact ---------------------------------------------------------------- */

.contact {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100svh;
}

.contact-content {
    width: min(var(--width-wide), 100%);
    margin-inline: auto;
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: center;
}

.contact-actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-m);
    margin-block-start: var(--space-xl);
}

.email {
    position: relative;
    display: inline-block;
    max-width: 100%;
    padding-block-end: var(--space-3xs);
    font-family: var(--serif);
    font-size: var(--step-3);
    font-style: italic;
    overflow-wrap: anywhere;
}

/* The original's underline swipe, kept — it was the best moment on the page. */
.email::after {
    content: "";
    position: absolute;
    inset-block-end: 0;
    inset-inline: 0;
    height: var(--rule-heavy);
    background: currentColor;
    transform: scaleX(0);
    transform-origin: right;
    transition: transform var(--dur-base) var(--ease-out);
}

.email:hover::after,
.email:focus-visible::after {
    transform: scaleX(1);
    transform-origin: left;
}

.copy-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2xs);
    min-height: 2.875rem;
    padding: var(--space-2xs) var(--space-m);
    border: var(--rule-thick) solid var(--ink);
    border-radius: var(--radius);
    color: var(--ink);
    font-size: var(--step--1);
    font-weight: 800;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    cursor: pointer;
    transition:
        transform var(--dur-fast) linear,
        box-shadow var(--dur-fast) linear;
    box-shadow: var(--offset) var(--offset) 0 var(--slam);
}

.copy-button:hover {
    transform: translate(
        calc(var(--offset) / 2),
        calc(var(--offset) / 2)
    );
    box-shadow: calc(var(--offset) / 2) calc(var(--offset) / 2) 0 var(--slam);
}

.copy-button:active {
    transform: translate(var(--offset), var(--offset));
    box-shadow: 0 0 0 var(--slam);
}

.copy-icon {
    width: 1.1rem;
    height: 1.1rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
}

.contact footer {
    width: min(var(--width-wide), 100%);
    margin-inline: auto;
    margin-block-start: var(--space-xl);
    padding-block-start: var(--space-s);
    border-block-start: var(--rule-thick) solid var(--rule);
    text-align: center;
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

.contact footer span {
    font-weight: 800;
}

/* =============================================================================
   6. MOTION

   Entrance animations are gated on .reveal-ready, a class the script adds
   only once the observer is wired. Nothing on this page is hidden by CSS
   alone, so a script that fails to load or throws leaves the page readable.

   Kill switch: put data-reveal-off on <html> to skip entrances entirely.
   Opt-in only. Without JS, or with reduced motion, the page renders in its
   finished state — nothing is ever left hidden.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
    .reveal-ready [data-reveal="lines"] .line > span {
        transform: translateY(105%);
    }

    .reveal-ready [data-reveal="lines"].is-in .line > span {
        transform: none;
        transition: transform var(--dur-slow) var(--ease-out);
    }

    .reveal-ready [data-reveal="lines"].is-in .line:nth-child(2) > span {
        transition-delay: 100ms;
    }

    .reveal-ready .hero [data-reveal="lines"].is-in .line:nth-child(3) > span {
        transition-delay: 200ms;
    }

    /* Heading rules draw across rather than fading in. */
    .reveal-ready [data-reveal="lines"] {
        --draw: 0;
    }

    .reveal-ready [data-reveal="lines"].is-in {
        --draw: 1;
    }

    .motion .services .heading::after,
    .motion .projects .heading::after,
    .motion .contact .heading::after {
        transition-delay: 260ms;
    }

    /* Blocks wipe in from the left. A wipe, not a fade — it matches the
       drawn rules and keeps the whole page on one motion idea. */
    .reveal-ready .service,
    .reveal-ready .project {
        clip-path: inset(0 100% 0 0);
        transition: clip-path var(--dur-slow) var(--ease-out);
    }

    .reveal-ready .service.is-in,
    .reveal-ready .project.is-in {
        clip-path: inset(0 0 0 0);
    }

    /* The sticker gets slapped on instead of wiped. It can't use the wipe:
       clip-path crops the element's own box shadow, which would have eaten
       the offset that makes it read as a sticker. */
    .reveal-ready .story blockquote {
        transform: rotate(var(--tilt)) scale(0.88);
        transition: transform var(--dur-slow) var(--ease-out);
    }

    .reveal-ready .story blockquote.is-in {
        transform: rotate(var(--tilt)) scale(1);
    }

    .reveal-ready .service:nth-of-type(2) {
        transition-delay: 80ms;
    }

    .reveal-ready .service:nth-of-type(3) {
        transition-delay: 160ms;
    }

    .motion .github-band__track {
        animation: band-scroll 22s linear infinite;
    }

    .motion .github-band:hover .github-band__track,
    .motion .github-band:focus-visible .github-band__track {
        animation-play-state: paused;
    }

    .motion .scroll-cue {
        animation: nudge 2.4s var(--ease-out) infinite;
    }
}

@keyframes band-scroll {
    to {
        transform: translateX(-50%);
    }
}

@keyframes nudge {
    0%,
    70%,
    100% {
        transform: translateY(0);
    }
    80% {
        transform: translateY(0.4rem);
    }
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    /* The band's reveal snaps instead of sliding. */
    .github-band__label {
        transition: none;
    }
}

/* =============================================================================
   7. BREAKPOINTS
   ========================================================================== */

/* --- 600px ---------------------------------------------------------------- */

@media (min-width: 37.5em) {
    .contact-actions {
        flex-direction: row;
        justify-content: center;
        gap: var(--space-l);
    }

    .project-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: var(--space-l);
    }

    .service {
        display: grid;
        grid-template-columns: minmax(0, 14rem) minmax(0, 1fr);
        column-gap: var(--space-l);
        align-items: start;
        padding-block: var(--space-l);
    }

    .service p {
        margin-block-start: 0;
    }

    /* Off-centre: it hugs the right rather than sitting on the axis. */
    .story blockquote {
        margin-inline-start: auto;
        margin-inline-end: 0;
    }
}

/* --- 1000px --------------------------------------------------------------- */

@media (min-width: 62.5em) {
    /* Heading full width, then paragraph and quote side by side. Row two
       takes the remaining height, so the quote becomes a tall panel rather
       than a small box floating in dead space. */
    .story-content {
        display: grid;
        grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr);
        column-gap: var(--space-2xl);
        row-gap: var(--space-xl);
    }

    .story .heading {
        grid-column: 1 / -1;
    }

    .story-content > p {
        grid-column: 1;
        max-width: 58ch;
        margin-inline: 0;
        align-self: center;
        text-align: start;
    }

    .story blockquote {
        --tilt: -3.5deg;
        grid-column: 2;
        justify-self: end;
        align-self: end;
    }

    .services-inner {
        display: grid;
        grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
        column-gap: var(--space-2xl);
        align-items: start;
    }

    .services-heading .heading {
        position: sticky;
        inset-block-start: var(--space-xl);
    }

    .services-list {
        margin-block-start: 0;
    }

    .service {
        grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
    }

    .project-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .project {
        padding: var(--space-l);
    }
}