/* Shared styles for the Home, Feed, and case-study previews. */
@font-face {
        font-family: "Raveo";
        src: url("./assets/fonts/raveo/fonts/variable/RaveoVF.woff2") format("woff2");
        font-style: normal;
        font-weight: 100 900;
        font-display: swap;
      }

      @font-face {
        font-family: "Departure Mono";
        src: url("./assets/fonts/DepartureMono-1.500/DepartureMono-Regular.woff2") format("woff2");
        font-style: normal;
        font-weight: 400;
        font-display: swap;
      }

      @font-face {
        font-family: "LT Superior Serif";
        src: url("./assets/fonts/superior serif/LTSuperiorSerif-Regular.otf") format("opentype");
        font-style: normal;
        font-weight: 400;
        font-display: swap;
      }

      :root {
        --surface-primary: #fff;
        --paper: var(--surface-primary);
        --ink: #111;
        --quiet: #68655f;
        --rule: #d9d8d3;
        --surface: #f1f1ed;
        --text-primary: #1a1a1a;
        --text-tertiary: #cfcfcf;
        --surface-secondary: #f7f7f7;
        --highlight: #ffdb00;
        --tab-inline-padding: 8px;
        --gutter: clamp(1.25rem, 3.4vw, 3.75rem);
        --work-indent: clamp(var(--gutter), 17vw, 20rem);
        --claim-indent: clamp(0rem, 20vw, 19rem);
        /* Band sits short of the claim title's line: enough to read as an
           indent, not enough to line up with it. */
        --band-indent: clamp(var(--gutter), 7.7vw, 9rem);
        /* Card corner radius, reused as the title's indent so the text starts
           where the plate's straight edge does. */
        --card-radius: 1rem;
        /* How far the case-study lede's platforms and title sit in from the
           page edges. */
        --lede-indent: clamp(1.5rem, 7vw, 6.25rem);
        --section-space: clamp(4.5rem, 7vw, 7rem);
        --display: "Raveo", Arial, Helvetica, sans-serif;
        --sans: "Raveo", Arial, Helvetica, sans-serif;
        --mono: "Departure Mono", monospace;
        --type-meta: clamp(0.8125rem, calc(0.78rem + 0.16vw), 0.875rem);
        --type-ui: clamp(0.875rem, calc(0.82rem + 0.25vw), 1rem);
        --type-body: clamp(1rem, calc(0.94rem + 0.3vw), 1.125rem);
        --type-lead: clamp(1.125rem, calc(1.02rem + 0.5vw), 1.375rem);
        --type-identity: clamp(1.125rem, calc(1rem + 0.5vw), 1.375rem);
        --type-statement: clamp(2rem, calc(1.4rem + 2.5vw), 3.5rem);
        --type-project: clamp(2.25rem, calc(1.5rem + 3vw), 3.5rem);
        --type-display: clamp(3rem, calc(2rem + 4vw), 5.25rem);
      }

      * {
        box-sizing: border-box;
      }

      .study-heading {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 1.5rem;
        padding: clamp(2rem, 5vw, 5rem) var(--gutter) clamp(2rem, 4vw, 4rem);
      }

      .study-heading h1 {
        margin: 0;
        font: 400 var(--type-display)/1.04 var(--display);
        letter-spacing: -0.04em;
      }

      .study-heading p {
        margin: 0;
        font-size: var(--type-body);
        color: var(--quiet);
      }

      .study-gallery {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 4px;
        padding-inline: 4px;
      }

      @media (min-width: 651px) {
        body[data-page="case-study"] {
          --study-width: min(calc(100vw - var(--gutter) * 2), calc((100svh - 5rem) * 2644 / 1924));
        }

        .study-heading,
        .study-gallery {
          width: var(--study-width);
          margin-inline: auto;
          padding-inline: 0;
        }
      }

      .study-image {
        display: block;
        min-width: 0;
        overflow: hidden;
        border-radius: 16px;
        cursor: pointer;
      }

      .study-image--wide {
        grid-column: 1 / -1;
      }

      .study-image img {
        display: block;
        width: 100%;
        height: auto;
      }

      .study-image:focus-visible {
        outline-offset: -5px;
      }

      html:has(body[data-page="case-study"]) {
        scrollbar-gutter: stable;
      }

      html:has(body[data-page="home"]) {
        scrollbar-gutter: stable;
      }

      html:has(.image-lightbox[open]) {
        overflow: hidden;
      }

      .image-lightbox {
        position: fixed;
        inset: 0;
        width: 100%;
        max-width: none;
        height: 100%;
        max-height: none;
        margin: 0;
        padding: 32px;
        overflow: hidden;
        border: 0;
        background: transparent;
        cursor: pointer;
      }

      .image-lightbox[open] {
        display: grid;
        place-items: center;
      }

      /* Pointer only where a click closes it: the backdrop. The visual itself
         isn't a target, so it keeps the regular cursor. */
      .image-lightbox img,
      .image-lightbox video {
        cursor: default;
      }

      /* Static, not transitioned: a frozen transition would leave it clear. */
      .image-lightbox::backdrop {
        background: rgb(17 17 17 / 55%);
      }

      .image-lightbox > img,
      .video-lightbox > video {
        display: block;
        width: auto;
        height: auto;
        max-width: calc(100vw - 64px);
        max-height: calc(100svh - 64px);
        border-radius: 16px;
        transform-origin: top left;
      }

      /* On the study page the expanded visual stops short of the viewport. These
         are lifted windows, and a window blown up to fill the screen reads as
         empty space around its content rather than as a bigger screenshot. The
         percentage tracks the dialog's own padding, so the phone rule holds. */
      body[data-page="study"] .image-lightbox > img,
      body[data-page="study"] .video-lightbox > video {
        max-width: min(1200px, 100%);
      }

      .image-lightbox-close {
        position: absolute;
        top: 10px;
        right: 16px;
        z-index: 1;
        border: 0;
        padding: 5px 8px;
        border-radius: 4px;
        background: var(--paper);
        color: var(--ink);
        font: 400 14px/1.2 var(--sans);
        cursor: pointer;
      }

      @media (max-width: 650px) {
        .study-heading {
          align-items: flex-start;
          flex-direction: column;
          gap: 0.75rem;
        }

        .study-gallery { grid-template-columns: minmax(0, 1fr); }
        .study-image { border-radius: 10px; }
        .image-lightbox { padding: 16px; }
        .image-lightbox > img,
        .video-lightbox > video {
          max-width: calc(100vw - 32px);
          max-height: calc(100svh - 80px);
          border-radius: 10px;
        }
      }

      html {
        scroll-behavior: smooth;
      }

      body {
        margin: 0;
        background: var(--paper);
        color: var(--ink);
        font-family: var(--sans);
        font-size: var(--type-body);
        line-height: 1.5;
        -webkit-font-smoothing: antialiased;
      }

      a {
        color: inherit;
        text-decoration-thickness: 1px;
        text-underline-offset: 0.2em;
      }

      a:focus-visible {
        outline: 2px solid var(--ink);
        outline-offset: 5px;
      }

      .site-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1.5rem;
        padding: 1rem var(--gutter) 1.5rem;
        border-bottom: 1px solid var(--rule);
      }

      .identity-email {
        font-size: var(--type-identity);
        letter-spacing: -0.02em;
        line-height: 1.2;
        color: var(--ink);
        text-decoration: none;
      }

      .identity-email:hover {
        text-decoration: underline;
        text-decoration-thickness: 1px;
        text-underline-offset: 0.2em;
      }

      .site-nav-header {
        position: sticky;
        top: 0;
        z-index: 10;
        padding: 1rem calc(var(--gutter) - var(--tab-inline-padding));
        background: var(--surface-primary);
      }

      .identity {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 0.1rem;
      }

      .identity strong,
      .identity .local-time {
        font-size: var(--type-identity);
        letter-spacing: -0.02em;
        line-height: 1.2;
      }

      .identity strong {
        font-weight: 520;
      }

      .identity-home {
        color: inherit;
        text-decoration: none;
      }

      .identity-home:hover {
        text-decoration: underline;
        text-decoration-thickness: 1px;
        text-underline-offset: 0.2em;
      }

      .site-nav {
        font-size: var(--type-ui);
        font-weight: 520;
        line-height: 1.4;
      }

      .project-meta,
      .footer-meta {
        font-size: var(--type-meta);
        line-height: 1.4;
      }

      .project-meta,
      .footer-meta {
        color: var(--quiet);
      }

      .identity .local-time {
        color: var(--text-tertiary);
      }

      .project-meta {
        font-family: var(--mono);
      }

      .site-nav {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
      }

      .nav-tab {
        display: inline-flex;
        align-items: center;
        min-height: 2rem;
        padding: 4px var(--tab-inline-padding);
        border-radius: 4px;
        font-size: 14px;
        color: var(--text-tertiary);
        background: transparent;
        text-decoration: none;
        transition: color 160ms ease, background-color 160ms ease, transform 160ms ease;
      }

      .nav-tab:hover {
        color: var(--text-primary);
        background: var(--surface-secondary);
        transform: translateY(-1px);
      }

      .nav-tab[aria-current="page"],
      .nav-tab[aria-current="page"]:hover {
        color: var(--text-primary);
        background: transparent;
        text-decoration: none;
        transform: none;
      }

      .nav-tab:focus-visible {
        color: var(--text-primary);
      }

      .hero {
        display: grid;
        grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
        align-items: end;
        padding: clamp(4.5rem, 7vw, 7.5rem) var(--gutter) clamp(6rem, 12vw, 10rem);
      }

      .hero h1 {
        grid-column: 2;
        margin: 0;
        font-family: var(--display);
        font-size: clamp(3.5rem, 6.5vw, 6.25rem);
        font-weight: 500;
        letter-spacing: -0.04em;
        line-height: 1.03;
        transform: translateX(2rem);
      }

      .hero-quiet,
      .claim-quiet {
        color: var(--text-tertiary);
      }

      /* Highlighter fill on the closing word: guglieri.com/work's yellow, but
         a 16px corner rather than a pill. */
      .hero-mark {
        /* Equal 0.14em gap around the ink: above the x-height (0.53em) and
           below the g's descender (0.21em). Raveo's box is 0.97em up and
           0.25em down; a 0.62 line-height takes 0.3em off each side, so the
           top lands at 0.67em, and the bottom padding brings the bottom to
           0.35em below the baseline. */
        display: inline-block;
        line-height: 0.62;
        padding: 0 0.12em 0.4em;
        border-radius: 16px;
        background: var(--highlight);
        color: inherit;
      }

      /* 404: the hero statement alone, tall enough that the footer sits low. The
         code rides above the title on the same column and offset. */
      .hero.hero--notfound {
        min-height: calc(100svh - 12rem);
        grid-template-rows: 1fr auto;
      }

      .notfound-code {
        grid-column: 2;
        align-self: end;
        margin: 0 0 1.5rem;
        transform: translateX(2rem);
        font-size: var(--type-lead);
        color: var(--quiet);
      }

      /* For a word with an ascender and no descender ("work"): the same 0.14em
         gap, measured from the ascender (0.73em) and the baseline instead of
         the x-height and the g. The box then fits inside the line, so a mark
         mid-sentence doesn't push the next line down. */
      .hero-mark.hero-mark--ascender {
        padding: 0.2em 0.12em 0.09em;
      }

      /* The highlighted word is the way home. */
      a.hero-mark {
        text-decoration: none;
        transition: background-color 0.15s ease;
      }

      a.hero-mark:hover {
        background: #f2cb00;
      }

      a.hero-mark:focus-visible {
        outline: 2px solid var(--ink);
        outline-offset: 4px;
      }

      /* Indented, but short of the band's line. Title runs the full measure;
         the supporting copy drops into the second column, as the reference does. */
      .claim {
        display: grid;
        grid-template-columns: minmax(0, 0.75fr) minmax(0, 1fr);
        padding: clamp(6rem, 12vw, 10rem) var(--gutter) 0;
      }

      /* Indent is its own value so the title can move without dragging the copy. */
      .claim-role {
        grid-column: 1 / -1;
        margin: 0 0 0 var(--claim-indent);
        font-family: var(--display);
        font-size: clamp(2.75rem, 5vw, 4.75rem);
        font-weight: 500;
        letter-spacing: -0.03em;
        line-height: 1.1;
      }

      /* Keeps the role on its own line. */
      .claim-line {
        display: block;
      }

      .claim-detail {
        grid-column: 2;
        max-width: 46ch;
        margin: clamp(3.5rem, 6vw, 6rem) 0 0;
        font-size: var(--type-lead);
        font-weight: 400;
        line-height: 1.5;
        color: var(--quiet);
      }

      .claim-detail p {
        margin: 0;
      }

      .claim-detail p + p {
        margin-top: clamp(1.25rem, 2vw, 1.75rem);
      }

      .claim-detail strong {
        font-weight: 520;
        color: var(--ink);
      }

      /* Wider gap above than a normal section break: the claim has to finish
         before the evidence starts. Bottom padding keeps the last row off the
         footer rule below. */
      .contribution {
        padding: clamp(7rem, 12vw, 11.5rem) var(--gutter) var(--section-space);
      }

      /* Case cards follow guglieri.com/work, measured at 1440: three square
         plates 4px apart, a 16px radius, and a small bold caption. The page
         keeps its own gutter so the row lines up with the claim and footer. */
      .case-grid {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        align-items: start;
        gap: clamp(2rem, 3.5vw, 3.5rem) 4px;
      }

      /* Single column: the result line is indented with a margin, not a second
         track. A grid-column on it would spawn an implicit column and shrink
         the plate. */
      .case-card {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        align-content: start;
        gap: 0;
        color: inherit;
        text-decoration: none;
      }

      .case-card-plate {
        display: block;
        overflow: hidden;
        aspect-ratio: 1 / 1;
        margin-bottom: 0.75rem;
        border-radius: var(--card-radius);
        background: var(--surface);
      }

      .case-card-plate img,
      .case-card-plate video {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        object-position: center;
        transition: transform 420ms cubic-bezier(0.2, 0.8, 0.2, 1);
      }

      /* Otherwise the clip swallows the click meant for the card's link. */
      .case-card-plate video {
        pointer-events: none;
      }

      .case-card:hover .case-card-plate img,
      .case-card:hover .case-card-plate video {
        transform: scale(1.02);
      }

      /* Backdrop plus window, the same two layers as the study hero and the same
         62% share, so the card reads as a miniature of the page it opens.
         Doubled class: the plate's img sizing sits earlier but at equal weight. */
      .case-card-plate.case-card-plate--composed {
        display: grid;
        place-items: center;
        background: url("./assets/cases/desktop-art-hero.webp?v=2") center / cover no-repeat;
      }

      /* Much wider share than the hero's 62%: the plate is square and the
         window is not, so anything less leaves it a strip in the middle. */
      .case-card-plate.case-card-plate--composed img {
        width: 92%;
        height: auto;
        object-fit: contain;
        filter: drop-shadow(0 0.5rem 1rem rgb(17 17 17 / 0.28));
      }

      /* Two cards in a row: each plate is its study's hero in miniature, the
         same 16:9 backdrop and foreground at the same share, so the card and
         the page it opens are one image. */
      .case-grid.case-grid--two {
        grid-template-columns: repeat(2, minmax(0, 1fr));
      }

      .case-grid--two .case-card-plate {
        aspect-ratio: 16 / 9;
      }

      .case-grid--two .case-card-plate.case-card-plate--composed img {
        width: 62%;
      }

      .case-card-plate.case-card-plate--composed.case-card-plate--mobile {
        background-image: url("./assets/cases/mobile-art-hero.webp?v=2");
      }

      .case-grid--two .case-card-plate.case-card-plate--mobile img {
        width: 52.8%;
        height: auto;
      }

      .case-card-plate--pending {
        border: 1px dashed var(--rule);
        background: var(--surface);
      }

      .case-card-heading {
        display: block;
        padding-left: 0.5rem;
      }

      /* Block, or the body's taller line box pushes it off the plate. */
      .case-card-title {
        display: block;
        font-family: var(--sans);
        font-size: 0.75rem;
        font-weight: 700;
        line-height: 1.2;
        color: var(--ink);
      }

      .case-card:focus-visible {
        outline: 2px solid var(--ink);
        outline-offset: 6px;
      }

      .case {
        padding-bottom: clamp(4rem, 7vw, 7rem);
      }

      .case-heading {
        display: grid;
        grid-template-columns: 2.5rem minmax(0, 1fr);
        align-items: baseline;
        column-gap: 0.5rem;
        margin-bottom: clamp(1.25rem, 2vw, 2rem);
      }

      .case-heading .project-meta {
        color: var(--quiet);
      }

      .case-heading h3 {
        margin: 0;
        font-family: var(--display);
        font-size: clamp(1.5rem, 2.4vw, 2.25rem);
        font-weight: 400;
        letter-spacing: -0.02em;
        line-height: 1.1;
      }

      .case-result {
        grid-column: 2;
        max-width: 46ch;
        margin: 0.5rem 0 0;
        font-size: var(--type-body);
        line-height: 1.5;
        color: var(--quiet);
      }

      .case-visual {
        margin-left: 3rem;
      }

      .case-visual span {
        display: block;
        border: 1px dashed var(--rule);
        border-radius: 0.5rem;
        background: var(--surface);
      }

      /* 01: quantity is the argument. */
      .case-visual--many {
        display: grid;
        grid-template-columns: repeat(10, minmax(0, 1fr));
        gap: 0.5rem;
      }

      .case-visual--many span {
        aspect-ratio: 9 / 16;
      }

      /* 02: a sequence, read left to right. */
      .case-visual--sequence {
        display: grid;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: clamp(1.5rem, 3vw, 3rem);
      }

      .case-visual--sequence span {
        aspect-ratio: 9 / 16;
      }

      /* 03: the moment, then the prompt. */
      .case-visual--pairs {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: clamp(1.5rem, 3vw, 3rem);
      }

      .case-visual--pairs > div {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.5rem;
      }

      .case-visual--pairs span {
        aspect-ratio: 9 / 16;
      }

      /* Opening band: indented, with no bottom padding so the gap below it is
         exactly what .claim sets.
         Doubled class so it outranks the .flagship-visual padding shorthand below. */
      .flagship-visual.flagship-visual--opener {
        padding-left: var(--band-indent);
        padding-bottom: 0;
        scroll-padding-left: var(--band-indent);
      }

      /* Work entry: title left, copy and chips right, then a full-width drag band. */
      .flagship + .flagship {
        padding-top: clamp(5rem, 8vw, 8rem);
      }

      .role-intro {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.65fr);
        align-items: start;
        gap: 0 clamp(2rem, 6vw, 7rem);
        margin-left: var(--work-indent);
        padding: 0 var(--gutter) clamp(2.5rem, 4vw, 4rem) 0;
      }

      .role-intro h2 {
        margin: 0;
        font-family: var(--display);
        font-size: clamp(2rem, 3.2vw, 3.25rem);
        font-weight: 520;
        letter-spacing: -0.025em;
        line-height: 1.1;
      }

      .role-intro h2 a {
        text-decoration-thickness: 1px;
      }

      .role-intro p {
        max-width: 46ch;
        margin: 0;
        font-size: var(--type-lead);
        line-height: 1.5;
      }

      .role-chips {
        display: flex;
        flex-wrap: wrap;
        gap: 0.625rem;
        margin: clamp(1.5rem, 2.5vw, 2.25rem) 0 0;
        padding: 0;
        list-style: none;
      }

      .role-chips li {
        padding: 0.5rem 1.125rem;
        border: 1px solid var(--rule);
        border-radius: 999px;
        font-size: var(--type-ui);
        line-height: 1.4;
      }

      .flagship-visual {
        display: flex;
        gap: clamp(1rem, 2vw, 2rem);
        overflow-x: auto;
        padding: 0 var(--gutter) 1rem var(--work-indent);
        scroll-padding-left: var(--work-indent);
        overscroll-behavior-x: contain;
        scrollbar-width: none;
      }

      .flagship-visual {
        user-select: none;
      }

      .flagship-visual[data-dragging] {
        cursor: grabbing;
      }

      .flagship-visual img,
      .flagship-visual video {
        -webkit-user-drag: none;
      }

      .flagship-visual::-webkit-scrollbar {
        display: none;
      }

      .flagship-visual > a,
      .flagship-visual > .gallery-video-trigger {
        display: block;
        flex: 0 0 auto;
      }

      /* Sizes to its own recording, so a band can mix clips of different ratios. */
      .gallery-video-trigger {
        width: auto;
        height: auto;
        padding: 0;
        border: 0;
        background: transparent;
        line-height: 0;
        cursor: pointer;
      }

      .gallery-video-trigger video {
        pointer-events: none;
      }
      .flagship-visual img,
      .flagship-visual video {
        display: block;
        width: auto;
        height: clamp(20rem, 43vw, 42rem);
        border-radius: 1rem;
      }

      .flagship-visual [data-expand-image],
      .flagship-visual [data-expand-image] img {
        cursor: pointer;
      }

      body[data-page="home"] .image-lightbox,
      body[data-page="home"] .image-lightbox > img,
      body[data-page="home"] .video-lightbox > video {
        cursor: default;
      }

      .flagship-visual [data-expand-image]:focus-visible,
      .gallery-video-trigger:focus-visible {
        outline: 2px solid var(--ink);
        outline-offset: 4px;
      }

      .flagship-wide-video {
        position: relative;
        width: min(100%, calc((100svh - 8rem) * 2116 / 1440));
        margin-top: clamp(2rem, 4vw, 4rem);
        margin-inline: auto;
      }

      .flagship-wide-video video {
        display: block;
        width: 100%;
        max-width: 100%;
        height: auto;
        max-height: calc(100svh - 8rem);
        margin-inline: auto;
      }

      .flagship-wide-video canvas {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
      }

      .quality-player-controls:not([hidden]) {
        position: absolute;
        inset: auto 0 0;
        display: flex;
        align-items: center;
        gap: 1rem;
        padding: 0.75rem 1rem;
        background: rgb(17 17 17 / 85%);
        color: white;
        opacity: 0;
        transition: opacity 150ms ease;
      }

      .flagship-wide-video:hover .quality-player-controls,
      .flagship-wide-video:focus-within .quality-player-controls,
      .flagship-wide-video[data-paused="true"] .quality-player-controls {
        opacity: 1;
      }

      .quality-player-controls button {
        border: 0;
        padding: 0.25rem;
        background: transparent;
        color: inherit;
        font: inherit;
        font-size: 14px;
        cursor: pointer;
      }

      .quality-player-controls input {
        flex: 1;
        min-width: 0;
        accent-color: white;
      }

      .flagship-wide-video:fullscreen {
        display: grid;
        place-items: center;
        width: 100%;
        height: 100%;
        margin: 0;
        background: #111;
      }

      .flagship-wide-video:fullscreen video {
        width: auto;
        max-height: 100%;
      }

      .flagship-wide-video:fullscreen canvas {
        object-fit: contain;
      }

      .chapters {
        padding-top: clamp(2rem, 4vw, 4rem);
      }

      .chapter {
        padding: 0 var(--gutter) clamp(4rem, 7vw, 7rem);
      }

      .chapter-heading {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(20rem, 0.8fr);
        align-items: end;
        column-gap: clamp(2rem, 7vw, 8rem);
        padding-bottom: clamp(1.5rem, 3vw, 3rem);
      }

      .chapter-heading .project-meta {
        grid-column: 1 / -1;
        margin-bottom: 0.6rem;
      }

      .chapter-heading h3 {
        margin: 0;
        font-family: var(--display);
        font-size: clamp(2.75rem, 5.2vw, 5.5rem);
        font-weight: 400;
        letter-spacing: -0.025em;
        line-height: 1.04;
      }

      .chapter-heading p {
        max-width: 34rem;
        margin: 0;
        font-size: var(--type-lead);
        line-height: 1.45;
      }

      .chapter-visual {
        min-height: clamp(24rem, 58vw, 52rem);
        background: var(--surface);
      }

      .chapter-visual--screens {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: clamp(1.5rem, 5vw, 5rem);
        overflow: hidden;
        padding: clamp(2rem, 5vw, 5rem);
      }

      .chapter-visual--screens img {
        display: block;
        width: auto;
        max-width: 42%;
        max-height: clamp(24rem, 50vw, 46rem);
        object-fit: contain;
        box-shadow: 0 1rem 3rem rgb(17 17 17 / 0.07);
      }

      .chapter-visual--pending {
        display: grid;
        place-items: center;
        color: var(--quiet);
        font-size: var(--type-meta);
      }

      .previous-work {
        padding: clamp(3rem, 7vw, 7rem) var(--gutter) 0;
        border-top: 1px solid var(--rule);
      }

      .previous-work h2 {
        margin: 0;
        font-size: var(--type-statement);
        font-weight: 400;
        letter-spacing: -0.025em;
      }

      .projects {
        overflow: clip;
      }

      .project {
        display: flex;
        flex-direction: column;
        background: var(--paper);
        color: var(--ink);
        transform: scale(1);
        transform-origin: center top;
        transition: transform 420ms cubic-bezier(0.2, 0.8, 0.2, 1);
      }

      .project:hover {
        transform: scale(0.965);
      }

      .project-header {
        display: grid;
        grid-template-columns: minmax(0, 1.2fr) minmax(20rem, 0.8fr);
        gap: clamp(3rem, 7vw, 8rem);
        align-items: end;
        min-height: clamp(12rem, 16vw, 16rem);
        padding: clamp(2rem, 3vw, 3.5rem) clamp(var(--gutter), 14vw, 16rem) clamp(3rem, 5vw, 5rem);
      }

      .project h2 {
        margin: 0;
        font-family: var(--display);
        font-size: clamp(3.5rem, 5.2vw, 6rem);
        font-weight: 400;
        letter-spacing: -0.02em;
        line-height: 1.04;
      }

      .project h2 a {
        text-decoration: none;
      }

      .project-date {
        margin: 0.65rem 0 0;
        font-size: clamp(1.2rem, 1.8vw, 1.75rem);
        line-height: 1.25;
      }

      .project-summary {
        max-width: 36rem;
        margin: 0;
        font-size: var(--type-lead);
        line-height: 1.5;
      }

      .project-visual {
        display: block;
        overflow: hidden;
        background: var(--surface);
      }

      .project-visual img {
        display: block;
        width: 100%;
        height: auto;
      }

      .project + .project {
        margin-top: clamp(2.5rem, 5vw, 5rem);
      }

      .about {
        display: grid;
        grid-template-columns: minmax(10rem, 0.62fr) minmax(0, 2fr);
        gap: 2rem;
        padding: var(--section-space) var(--gutter);
        border-top: 1px solid var(--rule);
      }

      .about p {
        max-width: 30ch;
        margin: 0;
        font-family: var(--display);
        font-size: var(--type-statement);
        font-weight: 400;
        letter-spacing: -0.025em;
        line-height: 1.08;
      }

      .about a {
        display: inline-block;
        margin-top: 1.5rem;
        font-size: var(--type-lead);
      }

      /* Follows guglieri.com/work: a quiet two-line invitation with the address
         dropped back underneath it, and the profile links bold in a second
         column. Both sides share the same 32px Raveo setting, so weight is the
         only thing separating them. */
      .site-footer {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: clamp(2rem, 4vw, 3.5rem) 2rem;
        padding: clamp(3.5rem, 6.5vw, 6rem) var(--gutter);
        border-top: 1px solid var(--rule);
      }

      /* The study page ends on a full-bleed band, so the footer rule would
         otherwise butt straight against the Integrations artwork. Hold it off
         by roughly the footer's own top padding. */
      body[data-page="study"] .site-footer {
        margin-top: clamp(3.5rem, 7vw, 6.5rem);
      }

      .footer-reach,
      .footer-links {
        margin: 0;
        padding: 0;
        list-style: none;
        font-size: clamp(1.5rem, 2.25vw, 2rem);
        line-height: 1.2;
        letter-spacing: -0.02em;
      }

      .footer-reach p {
        margin: 0;
        font-weight: 400;
      }

      /* The address sits at the reference's 30% so the invitation reads first;
         hovering brings it back to full ink. */
      .footer-reach a {
        color: rgb(17 17 17 / 0.3);
        font-weight: 400;
        text-decoration: none;
        transition: color 160ms ease;
      }

      .footer-reach a:hover,
      .footer-reach a:focus-visible {
        color: var(--ink);
      }

      .footer-links a {
        color: var(--ink);
        font-weight: 800;
        text-decoration: none;
      }

      .footer-links a:hover,
      .footer-links a:focus-visible {
        text-decoration: underline;
        text-underline-offset: 0.12em;
      }


      @media (max-width: 850px) {
        :root {
          --work-indent: var(--gutter);
          --band-indent: var(--gutter);
        }

        /* Mobile keeps the identity band to the name and email alone. With the
           clock's second line gone, the band's padding evens out so the single
           line sits centred. */
        .local-time {
          display: none;
        }

        .site-header {
          padding-block: 1rem;
        }

        .hero,
        .about {
          grid-template-columns: 1fr;
        }

        .hero {
          padding-top: 2rem;
          padding-bottom: 2rem;
        }

        .hero h1 {
          grid-column: 1;
          font-size: clamp(2.25rem, 9vw, 3rem);
          transform: none;
        }

        /* On a phone the statement leads: top of the screen, space below. */
        .hero.hero--notfound {
          min-height: calc(100svh - 10rem);
          grid-template-rows: auto auto 1fr;
          padding-top: 4rem;
        }

        .notfound-code {
          grid-column: 1;
          transform: none;
        }

        /* No alignment play at phone width: the claim loses its indent and
           second column. Blocks centre; the text inside stays left-aligned. */
        .claim {
          grid-template-columns: 1fr;
        }

        .claim-role {
          margin-left: 0;
        }

        .claim-detail {
          grid-column: 1;
          margin-inline: auto;
        }

        .project-header {
          grid-template-columns: 1fr;
          gap: 2rem;
          padding-inline: var(--gutter);
        }

        .chapter-heading {
          grid-template-columns: 1fr;
          gap: 1rem;
        }

        .role-intro {
          grid-template-columns: 1fr;
          row-gap: 1.25rem;
        }

        .flagship-visual img,
        .flagship-visual video {
          height: 60vw;
        }

        .chapter-heading .project-meta {
          margin-bottom: 0;
        }

        .chapter-visual--screens {
          min-height: 24rem;
          padding: 2rem 1rem;
          gap: 1rem;
        }

        .chapter-visual--screens img {
          max-width: 46%;
          max-height: 32rem;
        }

      }

      @media (max-width: 520px) {
        .site-footer {
          grid-template-columns: 1fr;
        }
      }

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

        .nav-tab {
          transition: none;
        }

        .nav-tab:hover {
          transform: none;
        }

        .project {
          transition: none;
        }

        .project:hover {
          transform: none;
        }
      }

/* Feed layout; uses the same tokens and shared shell. */
      .feed-intro {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 2rem;
        padding: clamp(2.5rem, 4vw, 4rem) var(--gutter) clamp(1.75rem, 3vw, 3rem);
        border-bottom: 1px solid var(--rule);
      }

      .feed-intro h1 {
        margin: 0;
        font-size: var(--type-display);
        font-weight: 400;
        letter-spacing: -0.035em;
        line-height: 1;
      }

      .feed {
        display: grid;
        grid-template-columns: repeat(12, minmax(0, 1fr));
        grid-auto-rows: clamp(3rem, 4.6vw, 4.5rem);
        gap: clamp(0.75rem, 1.2vw, 1.25rem);
        padding: clamp(0.75rem, 1.2vw, 1.25rem) var(--gutter) clamp(5rem, 8vw, 8rem);
      }

      figure {
        min-width: 0;
        min-height: 0;
        margin: 0;
      }

      .tile-link {
        display: grid;
        grid-template-rows: minmax(0, 1fr) auto;
        height: 100%;
        text-decoration: none;
      }

      .tile-frame {
        display: grid;
        width: 100%;
        overflow: hidden;
        place-items: center;
        background: var(--surface);
        min-height: 0;
      }

      .tile-frame img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: contain;
        transition: transform 180ms ease;
      }

      .tile-link:hover .tile-frame img { transform: scale(1.01); }

      figcaption {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        padding: 0.65rem 0 0.25rem;
        font-size: var(--type-ui);
        line-height: 1.4;
      }

      .tile-1 { grid-column: 1 / 9; grid-row: 1 / 7; }
      .tile-2 { grid-column: 9 / 13; grid-row: 1 / 10; }
      .tile-3 { grid-column: 1 / 5; grid-row: 7 / 10; }
      .tile-4 { grid-column: 5 / 9; grid-row: 7 / 10; }
      .tile-5 { grid-column: 1 / 5; grid-row: 10 / 19; }
      .tile-6 { grid-column: 5 / 13; grid-row: 10 / 16; }
      .tile-7 { grid-column: 5 / 9; grid-row: 16 / 19; }
      .tile-8 { grid-column: 9 / 13; grid-row: 16 / 19; }
      .tile-9 { grid-column: 1 / 13; grid-row: 19 / 25; }


      .feed-count,
      .tile-type {
        font-size: var(--type-meta);
        color: var(--quiet);
      }

      @media (max-width: 850px) {
        .feed-intro {
          padding-top: 2.5rem;
        }

        .feed {
          grid-auto-rows: auto;
        }

        .tile-1,
        .tile-2,
        .tile-3,
        .tile-4,
        .tile-5,
        .tile-6,
        .tile-7,
        .tile-8,
        .tile-9 {
          grid-column: 1 / -1;
          grid-row: auto;
        }

        .tile-frame img {
          height: auto;
        }
      }

      @media (prefers-reduced-motion: reduce) {
        .tile-frame img {
          transition: none;
        }
      }

/* Case-study page. Follows the guglieri.com/work/composer rhythm measured at
   1440: title row, full-bleed hero, offset intro with credits, then visual
   groups introduced by a small two-line label. Visuals sit near the viewport
   edge and 4px apart, so the page reads as a showcase, not an article.
   Scoped to the page so the dormant .study-heading / .study-gallery rules
   above stay out of the way. */
      html:has(body[data-page="study"]) {
        scrollbar-gutter: stable;
      }

      body[data-page="study"] {
        --study-gutter: clamp(1rem, 1.2vw, 1.25rem);
        /* Shared by the lede and the copy sections so the title starts on the
           same x as the copy below it. */
        --study-columns: minmax(0, 0.87fr) minmax(0, 1fr);
      }

      /* Platforms left, title on the copy column so the two share a left edge. */
      .study-lede {
        display: grid;
        grid-template-columns: var(--study-columns);
        column-gap: 1.5rem;
        align-items: baseline;
        padding: clamp(3rem, 5vw, 5.5rem) var(--study-gutter) clamp(2rem, 3.5vw, 3.5rem);
      }

      .study-platforms {
        display: flex;
        align-items: baseline;
        gap: 0.75rem;
        margin-left: var(--lede-indent);
        color: var(--text-tertiary);
      }

      .study-platforms svg {
        display: block;
        width: clamp(1.125rem, 1.5vw, 1.375rem);
        height: auto;
        fill: currentColor;
      }

      /* Weight matches the reference's Raveo setting (wght 800); it carries the
         title at display size where 500 reads thin. */
      .study-lede h1 {
        grid-column: 2;
        margin: 0;
        font-family: var(--display);
        font-size: clamp(2.5rem, 4.6vw, 4.25rem);
        font-weight: 800;
        letter-spacing: -0.04em;
        line-height: 1.06;
        text-align: left;
      }

      .study-year {
        grid-column: 3;
        font-size: var(--type-lead);
        line-height: 1.2;
        color: var(--text-tertiary);
      }

      .study-hero {
        padding-inline: var(--study-gutter);
      }

      /* Backdrop and window are separate layers rather than one baked image, so
         the backdrop stays a one-line change. Window sits at 62% of the frame,
         the proportion the reference uses. */
      .study-plate.study-plate--hero {
        display: grid;
        place-items: center;
        aspect-ratio: 16 / 9;
        background: url("./assets/cases/desktop-art-hero.webp?v=2") center / cover no-repeat;
      }

      /* The window PNG carries its own rounded corners, so the shadow has to
         follow the alpha; box-shadow would trace the square border box.
         Doubled class: the .study-plate img width sits later in the file. */
      .study-plate.study-plate--hero img {
        width: 62%;
        height: auto;
        filter: drop-shadow(0 1.5rem 2.5rem rgb(17 17 17 / 0.3));
      }

      /* Three phones at 60% of the plate's height, with room around them. A
         percentage height doesn't resolve in the plate's grid, so it is set as
         the width that gives it: 60% x 9/16 x the strip's 4151/2652. */
      .study-plate.study-plate--hero.study-plate--hero-phones img {
        width: 52.8%;
        height: auto;
      }

      /* Left column stays empty; the copy takes the right, as the reference does. */
      .study-intro {
        display: grid;
        grid-template-columns: var(--study-columns);
        column-gap: 1.5rem;
        padding: clamp(4rem, 7vw, 7rem) var(--study-gutter) clamp(2rem, 3.2vw, 3.25rem);
      }

      .study-intro > div {
        grid-column: 2;
        /* Runs the column, stopping short of the visuals' right edge. */
        max-width: calc(100% - 2rem);
      }

      .study-intro p {
        margin: 0;
        font-family: var(--display);
        font-size: clamp(1.25rem, 1.9vw, 1.75rem);
        font-weight: 400;
        letter-spacing: -0.02em;
        line-height: 1.3;
      }

      .study-intro p + p {
        margin-top: clamp(1rem, 1.6vw, 1.5rem);
      }

      /* Same offset column as the opening copy, used as a break between groups. */
      .study-intro--break {
        padding-block: clamp(4.5rem, 8vw, 8rem);
      }

      /* Set as a standfirst: rule above, tighter measure, and the lead sentence
         carrying the weight. Editorial without reaching for a serif. */
      /* A left break may run on into the right column rather than stopping at
         the narrow left one. The cap is in rem, not ch: ch here resolves at
         the box's body size, not the copy's, and 44ch held the copy to about
         27 characters a line. */
      .study-intro--break > div {
        grid-column: 1 / -1;
        margin-left: var(--lede-indent);
        border-top: 1px solid var(--rule);
        padding-top: clamp(1.5rem, 2.4vw, 2.25rem);
        max-width: 39rem;
      }

      /* Two steps only: everything is quiet except what the bold picks out. */
      .study-intro--break p {
        font-size: clamp(1.5rem, 2.4vw, 2.125rem);
        letter-spacing: -0.03em;
        line-height: 1.2;
        color: var(--quiet);
      }

      /* Steps back from the lead without going as pale as the hero's quiet grey,
         which is set for display sizes. */
      .break-quiet,
      .study-intro--break strong.break-quiet {
        color: var(--quiet);
      }

      /* Chapter title: opens a major section of the study. One step under the
         page title in the same bold Raveo, on the labels' left edge, with a
         rule inset equally from both sides closing it off. */
      /* Margin, not padding, carries the indent so the rule starts where the
         title does, and the same inset on the right keeps it centred. */
      .study-chapter {
        margin: 0 calc(var(--study-gutter) + var(--lede-indent));
        padding: clamp(5rem, 9vw, 8.5rem) 0 clamp(1rem, 1.6vw, 1.5rem);
        border-bottom: 1px solid var(--rule);
      }

      .study-chapter h2 {
        margin: 0;
        font-family: var(--display);
        font-size: clamp(2rem, 3.4vw, 3rem);
        font-weight: 800;
        letter-spacing: -0.04em;
        line-height: 1.06;
        color: var(--ink);
      }

      /* The chapter's rule stands in for the first break's own, and the
         break sits closer since the title has already opened the section. */
      .study-chapter + .study-intro--break {
        padding-top: clamp(2rem, 3.5vw, 3.5rem);
      }

      .study-chapter + .study-intro--break > div {
        border-top: 0;
        padding-top: 0;
      }

      .study-chapter + .study-intro--break .break-step {
        top: 0;
      }

      /* A break that continues the one before it: the pair reads as one beat,
         so the second drops most of the section's top padding. */
      .study-intro--break + .study-intro--break {
        padding-top: clamp(1.5rem, 2.5vw, 2.5rem);
      }

      .study-intro--break:has(+ .study-intro--break) {
        padding-bottom: 0;
      }

      /* No rule of its own: it continues the break above, not a new one. */
      .study-intro--break + .study-intro--break > div {
        border-top: 0;
      }

      /* Step number for a paired break, hung in the margin left of the copy on
         its first line, at the copy's own size and quiet tone. */
      .study-intro--break > div {
        position: relative;
      }

      .break-step {
        position: absolute;
        top: clamp(1.5rem, 2.4vw, 2.25rem);
        right: 100%;
        margin-right: clamp(1rem, 2vw, 1.75rem);
        font-size: clamp(1.5rem, 2.4vw, 2.125rem);
        letter-spacing: -0.03em;
        line-height: 1.2;
        color: var(--quiet);
        font-variant-numeric: tabular-nums;
      }

      /* Breaks sit on the left column by default; this one returns to the copy
         column, on the same left edge as the opening copy. */
      .study-intro--break.study-intro--right > div {
        grid-column: 2;
        margin-left: 0;
      }


      /* The break already carries the space; the label below shouldn't add more. */
      .study-intro--break + .study-note {
        margin-top: 0;
        padding-top: 0;
      }

      .study-intro--break strong {
        font-weight: 800;
        color: var(--ink);
      }

      /* Doubled up so it outranks the .study-intro p display size above. */
      .study-intro .study-credits {
        margin: clamp(2rem, 3vw, 2.75rem) 0 0;
        font-family: var(--sans);
        font-size: 0.875rem;
        font-weight: 400;
        letter-spacing: normal;
        line-height: 1.6;
        color: var(--quiet);
      }

      .study-credits span {
        display: block;
      }

      /* Two lines: what the group is, then what it shows. Tied to the visual
         below it, spaced away from the one above. */
      .study-note {
        padding: clamp(3.5rem, 6vw, 5.5rem) var(--study-gutter) 4px
          calc(var(--study-gutter) + var(--lede-indent));
      }

      .study-note strong {
        display: block;
        font-size: var(--type-meta);
        font-weight: 800;
        line-height: 1.4;
        color: var(--ink);
      }

      .study-note span {
        display: block;
        font-size: var(--type-meta);
        line-height: 1.4;
        color: var(--quiet);
      }

      .study-row {
        display: grid;
        grid-template-columns: repeat(var(--row-columns, 1), minmax(0, 1fr));
        /* Visuals of unequal height hang from a common top edge; stretching
           would show the plate's own background under the shorter ones. */
        align-items: start;
        gap: 4px;
        padding: 0 var(--study-gutter) 4px;
      }

      .study-row--two { --row-columns: 2; }

      /* Two visuals of different proportions sharing one frame, so the row keeps
         a flat bottom edge: the taller one is cropped rather than letterboxed. */
      .study-row--even {
        --row-aspect: 2086 / 1344;
      }

      .study-row--even .study-plate {
        aspect-ratio: var(--row-aspect);
      }

      /* Only the still is cropped to the frame. Cropping a video would clip it
         again, and a clipped video shifts colour as the compositor switches
         paths mid-scroll — so each row's frame matches its own recording. */
      .study-row--even .study-plate img {
        height: 100%;
        object-fit: cover;
      }

      /* The integrations recording is a rectangular crop with no alpha, so its
         plate draws the corner. The window beside it carries its radius baked
         into the PNG, and this matches it at the row's display width. */
      .study-row--integrations .study-plate--lift {
        border-radius: 0.5rem;
      }

      /* The landing recording is cropped to the browser window, whose rounded
         corners (32px at 1928 x 1132) are still in the frame; the plate clips
         to them. The phones beside it centre on the window's height. */
      .study-row--landing {
        align-items: center;
      }

      /* Two groups, not one row of four: the phones sit a phone-gap apart
         (baked into the PNG), and a much wider gap separates them from the
         window. Columns in the two visuals' ratios (1928/1132, 2958/1927),
         so the phones stand exactly as tall as the window. */
      .study-row.study-row--band.study-row--landing {
        grid-template-columns: 1.703fr 1.535fr;
        column-gap: clamp(2.5rem, 6vw, 6rem);
      }

      @media (max-width: 520px) {
        .study-row.study-row--band.study-row--landing {
          grid-template-columns: minmax(0, 1fr);
        }
      }

      .study-row--landing .study-plate--lift {
        border-radius: 1.66% / 2.83%;
      }

      .study-row--three { --row-columns: 3; }

      /* Phones on tiles of their own rather than one band: the unbanded row's
         flat plates, 4px apart in a 2x2 grid, each screen centred and kept
         whole. Landscape tiles, so the grid stays short at full width. Sized
         by width so the img box is the phone itself, which the lightbox
         animates from; 24% puts the phone at about 78% of the tile height. */
      .study-row--tiles {
        --row-columns: 2;
        --phone: 24%;
      }

      .study-row--tiles .study-plate {
        display: grid;
        place-items: center;
        aspect-ratio: 16 / 10;
      }

      .study-row--tiles .study-plate img {
        width: var(--phone);
        filter: drop-shadow(0 0.75rem 1.5rem rgb(17 17 17 / 0.18));
      }

      /* A tile holding a short sequence: the phones composed into one
         transparent image, so they expand together. The image carries a gap
         of 1/12 of a phone between each, so n phones span (13n - 1) / 12
         phone widths and every phone stays at the single-phone size. */
      .study-row--tiles .study-plate--duo img {
        width: calc(var(--phone) * 25 / 12);
      }

      .study-row--tiles .study-plate--trio img {
        width: calc(var(--phone) * 38 / 12);
      }

      .study-row--tiles .study-plate--quartet img {
        width: calc(var(--phone) * 51 / 12);
      }

      /* A row with four phones in one tile: smaller phones throughout the row
         so the four fit and its neighbouring tile matches. */
      .study-row--tiles-tight {
        --phone: 21%;
      }

      /* One tile across the row, the height of a 16:10 tile. Its image is a
         strip of App Store screenshots sized to the phones' 78% height. */
      .study-row--tiles .study-plate--wide {
        grid-column: 1 / -1;
        aspect-ratio: 32 / 10;
      }

      .study-row--tiles .study-plate--wide img {
        width: 72%;
      }

      /* A sequence alone on a wide tile: the tile spans two, so the phones
         keep the half-width tiles' phone size. */
      .study-row--tiles .study-plate--wide.study-plate--duo img {
        width: calc(var(--phone) * 25 / 24);
      }

      .study-row--tiles .study-plate--wide.study-plate--trio img {
        width: calc(var(--phone) * 38 / 24);
      }

      .study-row--tiles .study-plate--wide.study-plate--quartet img {
        width: calc(var(--phone) * 51 / 24);
      }

      .study-row--tiles .study-plate--wide.study-plate--single img {
        width: calc(var(--phone) / 2);
      }

      /* A phone's feature callouts, set as labels under its tile rather than
         as bubbles on it. */
      .study-note-pair.study-note-pair--four {
        grid-template-columns: repeat(4, minmax(0, 1fr));
      }

      .study-row--tiles + .study-note-pair--four .study-note {
        padding-top: clamp(1.25rem, 2vw, 1.75rem);
      }

      @media (max-width: 850px) {
        .study-note-pair.study-note-pair--four {
          grid-template-columns: repeat(2, minmax(0, 1fr));
        }
      }

      /* A phone recording (transparent WebM) on a wide tile, at the same
         phone size. */
      .study-row--tiles .study-plate video {
        width: var(--phone);
        filter: drop-shadow(0 0.75rem 1.5rem rgb(17 17 17 / 0.18));
      }

      .study-row--tiles .study-plate--wide video {
        width: calc(var(--phone) / 2);
        filter: drop-shadow(0 0.75rem 1.5rem rgb(17 17 17 / 0.18));
      }

      /* Godot Tech studies: backdrops stay the flat plate until their art is
         chosen. */
      .study-plate.study-plate--hero.study-plate--unpainted {
        background: var(--surface);
      }

      /* One painting across a row of tiles: each tile shows its own part of
         it, and the gaps cut through it. The painting covers the whole row,
         centred, and each tile offsets it by its column. Measured in cqw of
         the row, the tiles' container. Defaults fit two 16:10 tiles and a
         3.2:1 crop; --mural-cols, --tile-ratio and --mural-ratio change them
         (one wide tile, three 4:5 moments). */
      .study-row--mural {
        container-type: inline-size;
        --mural-cols: 2;
        --tile-ratio: 1.6;
        --mural-ratio: 3.2;
        --mural-gap: 4px;
        gap: var(--mural-gap);
        --tile-w: calc((100cqw - var(--mural-gap) * (var(--mural-cols) - 1)) / var(--mural-cols));
        --tile-h: calc(var(--tile-w) / var(--tile-ratio));
        --mural-w: max(100cqw, var(--tile-h) * var(--mural-ratio));
        --mural-x: calc((var(--mural-w) - 100cqw) / 2);
        --mural-y: calc((var(--mural-w) / var(--mural-ratio) - var(--tile-h)) / 2);
      }

      .study-row--mural .study-plate {
        --col: 0;
        background: var(--mural) no-repeat;
        background-size: var(--mural-w) auto;
        background-position:
          calc((var(--mural-x) + var(--col) * (var(--tile-w) + var(--mural-gap))) * -1)
          calc(var(--mural-y) * -1);
      }

      .study-row--mural .study-plate:nth-child(2) { --col: 1; }
      .study-row--mural .study-plate:nth-child(3) { --col: 2; }

      .study-row--mural-wide {
        --mural-cols: 1;
        --tile-ratio: 3.2;
      }

      .study-row--mural-moments {
        --mural-cols: 3;
        --tile-ratio: 0.8;
        --mural-ratio: 2.398;
      }

      /* Results under a group of tiles: plain type, no plate. Figures at the
         break copy's size, quiet lead-in and bold number the way a break mixes
         the two; labels set like the section sublines. Starts on the labels'
         left edge. */
      /* Bottom padding closes the section: the results end it, so the next
         break needs more room than it would after a row of visuals. */
      .study-metrics {
        padding: clamp(3rem, 5vw, 4.5rem) var(--study-gutter) clamp(2rem, 4vw, 3.5rem)
          calc(var(--study-gutter) + var(--lede-indent));
      }

      .study-metrics-list {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 2rem;
      }

      .study-metric {
        margin: 0;
      }

      .study-metric strong {
        display: block;
        font-size: clamp(1.5rem, 2.4vw, 2.125rem);
        font-weight: 800;
        letter-spacing: -0.03em;
        line-height: 1.2;
        color: var(--ink);
      }

      .study-metric strong .metric-quiet {
        font-weight: 400;
        color: var(--quiet);
      }

      .study-metric > span {
        display: block;
        margin-top: 0.375rem;
        font-size: var(--type-meta);
        line-height: 1.4;
        color: var(--quiet);
      }

      @media (max-width: 520px) {
        .study-metrics-list {
          grid-template-columns: 1fr;
          gap: 1.25rem;
        }
      }

      /* Labels for two tiles side by side: the same two-line label, one over
         each tile, each held in from its own tile's edge by the lede indent
         so the pair lines up with a single label's left edge. */
      .study-note-pair {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 4px;
        padding: 0 var(--study-gutter);
      }

      /* Tighter above than a new section's label: the pair continues the
         group started by the tile above it. */
      .study-note-pair .study-note {
        padding: clamp(2rem, 3.4vw, 3rem) 0 4px var(--lede-indent);
      }

      /* Labels living inside their tiles' row, one per column: auto-placement
         puts the notes on the first grid row and the tiles under them, so the
         desktop reads exactly like the old separate pair — and mobile can
         reorder each label directly above its own tile. */
      .study-row--paired .study-note {
        padding: clamp(2rem, 3.4vw, 3rem) 0 0 var(--lede-indent);
      }

      /* The row's own gap already provides the 4px the pair's bottom padding
         used to. Mural columns count from the plates' new child positions. */
      .study-row--mural.study-row--paired .study-plate:nth-child(3) { --col: 0; }
      .study-row--mural.study-row--paired .study-plate:nth-child(4) { --col: 1; }
      .study-row--mural.study-row--paired-three .study-plate:nth-child(4) { --col: 0; }
      .study-row--mural.study-row--paired-three .study-plate:nth-child(5) { --col: 1; }
      .study-row--mural.study-row--paired-three .study-plate:nth-child(6) { --col: 2; }

      .study-plate {
        display: block;
        min-width: 0;
        overflow: hidden;
        border-radius: 1rem;
        background: var(--surface);
      }

      .study-plate img,
      .study-plate video {
        display: block;
        width: 100%;
        height: auto;
      }

      /* Rounding a video by clipping its parent drops it off the compositor's
         fast path, and scrolling then flips it between paths — which shifts the
         colour. Round the video itself instead. */
      .study-plate video {
        border-radius: inherit;
      }

      /* Keyed to the element, not its parent: the lightbox moves this same video
         into the dialog, and a rule scoped to the plate would stop applying
         there — leaving the two states on different paths, which is the colour
         change you see on expand and collapse. */
      [data-gallery-video] {
        transform: translateZ(0);
        backface-visibility: hidden;
      }

      a.study-plate,
      button.study-plate {
        cursor: pointer;
      }

      /* A plate that is also the recording's expand trigger. */
      button.study-plate {
        width: 100%;
        padding: 0;
        border: 0;
        font: inherit;
        line-height: 0;
        appearance: none;
      }

      button.study-plate video {
        pointer-events: none;
      }

      a.study-plate:focus-visible,
      button.study-plate:focus-visible {
        outline: 2px solid var(--ink);
        outline-offset: 4px;
      }

      /* One backdrop under the whole group; the windows sit on top of it. */
      .study-row.study-row--band {
        margin-inline: var(--study-gutter);
        padding: clamp(2.5rem, 4.5vw, 4.5rem) clamp(1.5rem, 3vw, 3rem);
        gap: clamp(1rem, 2vw, 2rem);
        border-radius: var(--card-radius);
        background: var(--surface);
      }

      /* Same band, artwork instead of a flat tint. The piece is a token, so a
         band swaps art by setting --band-art rather than by a new rule. */
      .study-row.study-row--band.study-row--art {
        background: var(--band-art, url("assets/cases/latest-backdrop.webp")) center / cover;
      }

      .study-row--art-team {
        --band-art: url("assets/cases/team-backdrop.webp");
      }

      /* Bands stacked inside one group read as separate beats, not a mosaic. */
      .study-row--band + .study-row--band {
        margin-top: clamp(0.75rem, 1.5vw, 1.25rem);
      }

      /* A single visual sitting on the band rather than filling it, so the
         artwork reads as a backdrop and the clip stays a comfortable size. */
      .study-row--inset {
        justify-items: center;
      }

      .study-row--inset .study-plate,
      .study-row--inset button.study-plate {
        width: 60%;
      }

      /* The email cards are already a composed card on a white page, so they
         read heavy at full column width. Sitting them back a little lets the
         band's artwork carry more of the row. */
      .study-row--mails {
        justify-items: center;
      }

      .study-row--mails .study-plate {
        width: 85%;
      }

      /* Opaque screens on a band: they carry no chrome and no alpha, so the
         plate's own radius and a box-shadow stand in for a window. */
      .study-plate--lift {
        box-shadow: 0 0.75rem 1.5rem rgb(17 17 17 / 0.16);
      }

      /* Windows lifted off their gradient backdrops: no plate of their own, and
         the shadow follows the PNG's alpha the way the hero's does. */
      .study-plate.study-plate--window {
        background: none;
        border-radius: 0;
        overflow: visible;
      }

      .study-plate.study-plate--window img {
        filter: drop-shadow(0 0.75rem 1.5rem rgb(17 17 17 / 0.22));
      }

      .study-plate--pending {
        display: grid;
        place-items: center;
        aspect-ratio: 16 / 10;
        border: 1px dashed var(--rule);
        color: var(--quiet);
        font-size: var(--type-meta);
      }

      /* Discovery's cause and effect: three 4:5 tiles, each a phone at the
         trigger with its intro sheet rising over it (see below). */
      .study-row--moments {
        --row-columns: 3;
      }

      .study-plate.study-plate--moment {
        display: flex;
        flex-direction: column;
        align-items: center;
        aspect-ratio: 4 / 5;
        padding-top: clamp(1.25rem, 2.2vw, 2rem);
      }
      /* The polished moment: the user's own screen at the trigger (a framed
         phone mock), then the app's scrim and the intro sheet rising over it,
         clipped to the phone's screen. The screen box is measured off the
         mock: 1167 x 2532 at (72, 60) in a 1311 x 2652 frame, corner radius
         138 (the frame every mobile mock shares). The flag rings the trigger
         the case-study doc names. With no motion the tile holds its end state. */
      .study-plate--moment-live {
        --app-accent: #556cff;
        --app-tint: #e8e9ff;
      }

      .moment-stage {
        display: flex;
        flex: 1;
        justify-content: center;
        width: 100%;
        min-height: 0;
        padding-bottom: clamp(1.25rem, 2.2vw, 2rem);
      }

      .moment-phone {
        position: relative;
        display: block;
        height: 100%;
        aspect-ratio: 1311 / 2652;
        line-height: 0;
        filter: drop-shadow(0 0.75rem 1.5rem rgb(17 17 17 / 0.18));
      }

      .study-plate .moment-phone > img {
        width: 100%;
        height: 100%;
      }

      .moment-screen {
        position: absolute;
        top: 2.262%;
        left: 5.492%;
        width: 89.016%;
        height: 95.475%;
        overflow: hidden;
        border-radius: 11.825% / 5.45%;
      }

      /* Each tile places its ring over its own trigger, in percentages of
         the screen box, through --flag-x, --flag-y, --flag-w and --flag-h. */
      .moment-flag {
        position: absolute;
        top: var(--flag-y);
        left: var(--flag-x);
        width: var(--flag-w);
        height: var(--flag-h);
        border-radius: 999px;
        box-shadow: 0 0 0 2px var(--app-accent), 0 0 0 6px rgb(85 108 255 / 0.2);
        opacity: 0;
      }

      .moment-scrim {
        position: absolute;
        inset: 0;
        background: rgb(17 17 17 / 0.16);
      }

      .study-plate .moment-sheet-img {
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        height: auto;
      }

      .moment-phone:focus-visible {
        outline: 2px solid var(--ink);
        outline-offset: 4px;
      }

      /* One loop: a beat on the plain transcript, the default name flags,
         then the scrim and sheet come up, hold, and everything resets. */
      @media (prefers-reduced-motion: no-preference) {
        .is-playing .moment-flag {
          animation: moment-flag 8s ease-in-out infinite;
        }

        .is-playing .moment-scrim {
          animation: moment-scrim 8s ease-in-out infinite;
        }

        .is-playing .moment-sheet-img {
          animation: moment-sheet 8s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
        }
      }

      @keyframes moment-flag {
        0%, 8% { opacity: 0; transform: scale(1.06); }
        14%, 34% { opacity: 1; transform: scale(1); }
        40%, 100% { opacity: 0; transform: scale(1); }
      }

      @keyframes moment-scrim {
        0%, 32% { opacity: 0; }
        42%, 88% { opacity: 1; }
        96%, 100% { opacity: 0; }
      }

      @keyframes moment-sheet {
        0%, 34% { transform: translateY(100%); }
        46%, 88% { transform: translateY(0); }
        97%, 100% { transform: translateY(100%); }
      }
      /* Labels over three tiles, and results aligned under them. */
      .study-metrics.study-metrics--tiles {
        padding-left: var(--study-gutter);
      }

      .study-metrics--tiles .study-metrics-list {
        gap: 4px;
      }

      .study-metrics--tiles .study-metric {
        padding-left: var(--lede-indent);
      }

      .study-outro {
        padding: clamp(4rem, 7vw, 7rem) var(--study-gutter);
      }

      @media (max-width: 850px) {
        .study-lede {
          grid-template-columns: 1fr;
          align-items: flex-start;
          row-gap: 1.25rem;
        }

        /* No alignment play at phone width: indents drop, capped blocks centre,
           and the text inside stays left-aligned. */
        .study-lede h1 {
          grid-column: 1;
          margin: 0;
          font-size: clamp(2rem, 7vw, 2.75rem);
        }

        .study-platforms {
          margin-left: 0;
        }

        .study-year {
          grid-column: auto;
        }

        .study-intro {
          grid-template-columns: 1fr;
        }

        .study-intro > div {
          grid-column: 1;
          margin-inline: auto;
        }

        .study-intro--break > div {
          margin-inline: auto;
        }

        .study-note {
          padding-left: var(--study-gutter);
        }

        .study-note-pair .study-note {
          padding-left: 0;
        }

        .study-metrics--tiles .study-metric {
          padding-left: 0;
        }

        /* Never two tiles side by side on mobile: every row stacks, and each
           paired label reorders to sit directly above its own tile. */
        .study-row--two,
        .study-row--three,
        .study-row--tiles,
        .study-row--moments { --row-columns: 1; }

        .study-row.study-row--band.study-row--landing {
          grid-template-columns: minmax(0, 1fr);
        }

        .study-row--paired .study-note:nth-child(1) { order: 1; }
        .study-row--paired .study-plate:nth-child(3) { order: 2; }
        .study-row--paired .study-note:nth-child(2) { order: 3; }
        .study-row--paired .study-plate:nth-child(4) { order: 4; }

        .study-row--paired-three .study-note:nth-child(1) { order: 1; }
        .study-row--paired-three .study-plate:nth-child(4) { order: 2; }
        .study-row--paired-three .study-note:nth-child(2) { order: 3; }
        .study-row--paired-three .study-plate:nth-child(5) { order: 4; }
        .study-row--paired-three .study-note:nth-child(3) { order: 5; }
        .study-row--paired-three .study-plate:nth-child(6) { order: 6; }

        .study-row--paired .study-note {
          padding-left: 0;
        }

        /* Stacked mural tiles each show the painting's centre crop. */
        .study-row--mural { --mural-cols: 1; }

        .study-row--mural .study-plate:nth-child(2),
        .study-row--mural .study-plate:nth-child(3),
        .study-row--mural.study-row--paired .study-plate:nth-child(4),
        .study-row--mural.study-row--paired-three .study-plate:nth-child(5),
        .study-row--mural.study-row--paired-three .study-plate:nth-child(6) { --col: 0; }

        /* Less frame at phone size, or the window gets unreadable. */
        .study-plate.study-plate--hero img {
          width: 82%;
        }

        .case-grid,
        .case-grid.case-grid--two {
          grid-template-columns: 1fr;
        }
      }

      @media (max-width: 520px) {
        .study-row--two,
        .study-row--three { --row-columns: 1; }
      }

      @media (prefers-reduced-motion: reduce) {
        .case-card-plate img,
        .case-card-plate video {
          transition: none;
        }

        .case-card:hover .case-card-plate img,
        .case-card:hover .case-card-plate video {
          transform: none;
        }
      }
