/* ===========================================================================
   Project pages, prototype.

   Loaded after the landing's own stylesheet (../landing-showcase/style.css),
   which supplies the tokens, the type scale, the project pictures, the top
   bar, the recap carousel and the contact row. This file adds only what a
   project page has and the landing does not: the opening, the story, the
   demo frame and the "See more" panel.

   The same performance rules as the landing apply: no backdrop-filter, no
   filters, no gradients on anything that scales, and motion only on
   transform and opacity.
=========================================================================== */

html {
  /* The panel locks page scroll; reserving the scrollbar's gutter means the
     page does not shift sideways when it does. */
  scrollbar-gutter: stable;
}

html.pp-locked {
  overflow: hidden;
}

.topbar__name {
  text-decoration: none;
}

.topbar__name:hover {
  color: var(--fg);
}

/* The back link already carries its own arrow, pointing the other way. */
.btn--back::after {
  content: none;
}

/* ---------------------------------------------------------------------------
   Opening: the project's landing panel, as the top of its own page. It
   assembles once on arrival (no scrolling needed), from the same direction
   the panel entered from on the landing.
--------------------------------------------------------------------------- */

.pp-hero {
  position: relative;
  isolation: isolate;
  /* The arrival slides pieces in from the side (64px on projects 1 and 2).
     Unclipped, that brief overhang made a phone widen the whole page to fit
     it, and the page stayed wider (and scrollable sideways) afterwards.
     `clip` rather than `hidden`: it does not create a scroll container. */
  overflow-x: clip;
  display: flex;
  align-items: center;
  min-height: min(52rem, calc(100svh - var(--topbar-h, 3.75rem)));
  padding-block: clamp(2.5rem, 7vh, 5rem);
}

/* A soft wash of the project's colour behind the opening. Static and never
   scaled, so it is painted once. */
.pp-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(55% 60% at 78% 40%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 70%),
    radial-gradient(40% 50% at 10% 90%, color-mix(in srgb, var(--accent) 6%, transparent), transparent 70%);
  pointer-events: none;
}

.pp-hero__inner {
  width: 100%;
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

.pp-hero .panel__title {
  font-size: clamp(2.5rem, 1.2rem + 3vw, 3.75rem);
}

.pp-hero .panel__stats {
  margin-top: 2rem;
}

/* Two columns (projects 1 and 2), mirrored like the landing panels. */
@media (min-width: 900px) {
  .pp-hero--cols .pp-hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  }

  .pp-hero--viz-first.pp-hero--cols .pp-hero__viz {
    order: -1;
  }

  /* Rows (projects 3 and 4): the headline block, with the figures at its
     right, above or below a wide picture. */
  .pp-hero--rows .pp-hero__text {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: clamp(2rem, 5vw, 4rem);
    align-items: end;
  }

  .pp-hero--rows .panel__kicker,
  .pp-hero--rows .panel__title,
  .pp-hero--rows .panel__hook {
    grid-column: 1;
  }

  .pp-hero--rows .panel__title {
    max-width: none;
  }

  .pp-hero--rows .panel__hook {
    max-width: 52ch;
  }

  .pp-hero--rows .panel__stats {
    grid-column: 2;
    grid-row: 1 / span 3;
    align-self: end;
    margin-top: 0;
  }

  .pp-hero--rows.pp-hero--viz-first .pp-hero__viz {
    order: -1;
  }
}

/* The finale (project 5): centred, the picture under the words. */
.pp-hero--finale .pp-hero__inner {
  max-width: 48rem;
  justify-items: center;
  text-align: center;
}

.pp-hero--finale .panel__kicker,
.pp-hero--finale .panel__stats {
  justify-content: center;
}

.pp-hero--finale .panel__title {
  max-width: none;
}

.pp-hero--finale .panel__hook {
  margin-inline: auto;
  text-wrap: balance;
}

.pp-hero--finale .stat {
  align-items: center;
}

.pp-hero--finale .pp-hero__viz {
  width: min(100%, 34rem);
  text-align: left;
}

/* The pictures at full size. The landing sizes these under `.panel`, which
   also carries its pinned-stage layout, so the page repeats just the sizing. */
.pp-hero .viz {
  padding: 2.75rem 1.25rem 1.25rem;
}

.pp-hero .viz--line,
.pp-hero .viz--market {
  aspect-ratio: 578 / 414;
  max-height: 72vh;
}

.pp-hero .viz--flow {
  container-type: size;
  height: clamp(220px, 32vh, 320px);
}

.pp-hero .viz--chat {
  min-height: clamp(15rem, 34vh, 20rem);
}

.pp-hero .chat {
  max-width: 42rem;
  gap: 0.6rem;
}

.pp-hero .chat__q,
.pp-hero .chat__a {
  font-size: 1rem;
}

.pp-hero .chat__typing {
  height: calc(1rem * 1.35 + 1.1rem);
}

/* The arrival. Each piece starts offset along its project's entry direction
   and settles, staggered, in about a second and a half. Only transform and
   opacity move, once, so it costs nothing after it ends. */
.pp-hero[data-enter="right"] { --dx: 64px; --dy: 0px; }
.pp-hero[data-enter="left"] { --dx: -64px; --dy: 0px; }
.pp-hero[data-enter="bottom"] { --dx: 0px; --dy: 48px; }
.pp-hero[data-enter="top"] { --dx: 0px; --dy: -32px; }
.pp-hero[data-enter="finale"] { --dx: 0px; --dy: 24px; --s: 0.97; }

@media (prefers-reduced-motion: no-preference) {
  .pp-hero .panel__kicker,
  .pp-hero .pp-part {
    animation: pp-arrive 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
  }

  .pp-hero .panel__kicker { animation-delay: 0.08s; }
  .pp-hero .panel__title { animation-delay: 0.2s; }
  .pp-hero .panel__hook { animation-delay: 0.34s; }
  .pp-hero .panel__stats { animation-delay: 0.48s; }
  .pp-hero .pp-hero__viz { animation-delay: 0.56s; animation-duration: 1.1s; }
}

@keyframes pp-arrive {
  from {
    opacity: 0;
    transform: translate(var(--dx, 0px), var(--dy, 24px)) scale(var(--s, 1));
  }
}

/* ---------------------------------------------------------------------------
   The story: two paragraphs, set to be read.
--------------------------------------------------------------------------- */

.pp-story {
  padding-block: clamp(3rem, 9vh, 5.5rem) clamp(2rem, 6vh, 3.5rem);
}

.pp-story__body {
  max-width: 62ch;
  display: grid;
  gap: 1.35rem;
}

.pp-story__body p {
  font-size: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem);
  line-height: 1.7;
  color: var(--fg-2);
}

.pp-story__body p:first-child {
  color: var(--fg);
}

/* ---------------------------------------------------------------------------
   Try it yourself
--------------------------------------------------------------------------- */

.pp-demo {
  padding-block: clamp(2rem, 6vh, 3.5rem);
}

.pp-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.pp-pill__dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--accent);
}

.seq-live .pp-pill__dot {
  animation: pp-pulse 2.2s ease-in-out infinite;
}

@keyframes pp-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

/* The live demo keeps its own chrome (DemoFrame: window bar, simulation
   badge, note). Only the no-JavaScript still needs framing here. */
.pp-demo__still {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--line-strong));
  border-radius: 1rem;
}

/* The demo's own note under its frame sits on this page's ground. */
.pp-demo__live > section > p {
  margin-top: 0.9rem;
}

/* ---------------------------------------------------------------------------
   The "See more" call to action
--------------------------------------------------------------------------- */

.pp-more {
  padding-block: clamp(1.5rem, 5vh, 3rem) clamp(1rem, 3vh, 2rem);
}

.pp-more__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem 2rem;
}

.pp-more__inner > div {
  flex: 1 1 20rem;
}

/* The band itself is a framed strip in the project's colour. */
.pp-more .wrap {
  padding-block: clamp(1.5rem, 4vw, 2.25rem);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line-strong));
  border-radius: 1.25rem;
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
}

.pp-more__title {
  font-size: clamp(1.375rem, 1rem + 1vw, 1.75rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--fg);
}

.pp-more__lede {
  margin-top: 0.4rem;
  color: var(--fg-3);
}

.pp-more__open {
  padding: 0.85rem 1.4rem;
  font-size: 0.9375rem;
  /* The page's own colour, not the site's: this is the one action on the
     page that belongs to the project. */
  color: var(--fg);
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  box-shadow: 0 10px 30px -16px color-mix(in srgb, var(--accent) 90%, transparent);
  cursor: pointer;
}

.pp-more__open:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 32%, transparent);
}

/* ---------------------------------------------------------------------------
   The technical panel
--------------------------------------------------------------------------- */

.pp-sheet {
  /* The site's diagram component names its colours the app's way
     (--color-surface…); map them onto this prototype's tokens. */
  --color-ground: var(--ground);
  --color-surface: var(--surface);
  --color-surface-2: var(--surface-2);
  --color-fg: var(--fg);
  --color-fg-3: var(--fg-3);
  --color-iris: var(--iris);

  width: min(58rem, calc(100vw - 2 * var(--gutter)));
  max-width: none;
  max-height: min(88vh, 60rem);
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--accent) 38%, var(--line-strong));
  border-radius: 1.25rem;
  background: var(--surface);
  color: var(--fg-2);
  box-shadow:
    0 50px 120px -40px rgb(0 0 0 / 0.85),
    0 0 90px -50px color-mix(in srgb, var(--accent) 70%, transparent);
}

.pp-sheet[open] {
  display: flex;
  flex-direction: column;
}

/* A plain dim, not a blur: blurring the whole page behind the panel is the
   single most expensive thing to ask of a weak GPU. */
.pp-sheet::backdrop {
  background: rgb(5 6 14 / 0.78);
}

@media (prefers-reduced-motion: no-preference) {
  .pp-sheet[open] {
    animation: pp-sheet-in 0.45s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .pp-sheet[open]::backdrop {
    animation: pp-fade-in 0.3s ease-out;
  }

  .pp-sheet.is-closing {
    animation: pp-sheet-out 0.24s ease-in forwards;
  }

  .pp-sheet.is-closing::backdrop {
    animation: pp-fade-out 0.24s ease-in forwards;
  }

  /* No second stagger for the panel's contents: they arrive with the panel.
     Measured with the CPU throttled 4x, fading the blocks in one by one on
     top of the panel's own entrance was the biggest single cost of opening
     it (the median frame went from 33ms to 17ms without it). */
}

@keyframes pp-sheet-in {
  from { opacity: 0; transform: translateY(28px) scale(0.97); }
}

@keyframes pp-sheet-out {
  to { opacity: 0; transform: translateY(16px) scale(0.98); }
}

@keyframes pp-fade-in {
  from { opacity: 0; }
}

@keyframes pp-fade-out {
  to { opacity: 0; }
}


.pp-sheet__head {
  position: relative;
  flex: 0 0 auto;
  padding: 1.6rem 4.5rem 1.35rem 1.75rem;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
}

/* A thin band of the project's colour along the top edge. */
.pp-sheet__head::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 20%, transparent));
}

.pp-sheet__kicker {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

.pp-sheet__title {
  margin-top: 0.4rem;
  font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  color: var(--fg);
}

.pp-sheet__close {
  position: absolute;
  top: 1.1rem;
  right: 1.1rem;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: var(--fg-2);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.pp-sheet__close:hover {
  color: var(--fg);
  border-color: color-mix(in srgb, var(--accent) 60%, var(--line-strong));
}

.pp-sheet__body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.75rem 1.75rem 2.25rem;
}

/* Role, running since, repo: one quiet row. */
/* One column per item: role and running, plus the repo when it is public. */
.pp-meta {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 1.25rem;
}

.pp-meta dt,
.pp-block__title,
.pp-num__label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.pp-num__label {
  font-family: var(--font-sans);
  font-size: 0.875rem;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.4;
}

.pp-meta dd {
  margin: 0.4rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--fg-2);
}

.pp-block {
  margin-top: 2.25rem;
}

.pp-block__title {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.pp-block__title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 60%, transparent), transparent);
}

/* Built with: one line per tool, name then what it is for. */
.pp-tools {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.6rem;
}

.pp-tools li {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 0.7rem;
  background: var(--surface-2);
}

.pp-tool__name {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--accent);
}

.pp-tool__name::after {
  content: " \2014";
  color: var(--fg-3);
  font-weight: 500;
}

.pp-tool__what {
  font-size: 0.875rem;
  line-height: 1.4;
  color: var(--fg-2);
}

/* How it fits together: the site's own diagram. */
.pp-diagram {
  margin: 0;
}

.pp-diagram__scroll {
  overflow-x: auto;
  padding: 0.75rem;
  border: 1px solid var(--line);
  border-radius: 0.9rem;
  background: color-mix(in srgb, var(--ground) 60%, var(--surface));
}

.pp-diagram svg {
  display: block;
  width: 100%;
  min-width: 44rem;
  height: auto;
}

.pp-diagram figcaption {
  margin-top: 0.7rem;
  font-size: 0.8125rem;
  color: var(--fg-3);
}

/* By the numbers */
/* --cols is set per page: one per card, two for a wide one. */
.pp-nums {
  display: grid;
  grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr));
  gap: 0.75rem;
}

/* The headline figure: twice as wide, so it reads on one line. */
.pp-num--wide {
  grid-column: span 2;
}

.pp-num--wide .pp-num__value {
  white-space: nowrap;
}

.pp-num {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-end;
  padding: 1rem 1rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 0.8rem;
  background: color-mix(in srgb, var(--accent) 6%, var(--surface-2));
}

.pp-num__value {
  margin: 0 0 0.5rem;
  font-size: clamp(1.375rem, 1rem + 0.8vw, 1.75rem);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--accent);
}

@media (max-width: 859.98px) {
  .pp-nums {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 639.98px) {
  .pp-tools {
    grid-template-columns: 1fr;
  }

  .pp-meta {
    grid-auto-flow: row;
    grid-template-columns: 1fr;
    gap: 0.9rem;
  }

  /* On a phone the panel becomes a full-screen sheet that slides up. */
  .pp-sheet {
    width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    margin: 0;
    border: 0;
    border-radius: 0;
  }

  .pp-sheet__head {
    padding: 1.25rem 4rem 1.1rem 1.25rem;
  }

  .pp-sheet__body {
    padding: 1.25rem 1.25rem 2rem;
  }
}

@media (max-width: 639.98px) and (prefers-reduced-motion: no-preference) {
  .pp-sheet[open] {
    animation: pp-sheet-up 0.42s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .pp-sheet.is-closing {
    animation: pp-sheet-down 0.26s ease-in forwards;
  }
}

@keyframes pp-sheet-up {
  from { transform: translateY(100%); }
}

@keyframes pp-sheet-down {
  to { transform: translateY(100%); }
}

/* ---------------------------------------------------------------------------
   Narrow screens: the opening stacks, the picture goes under the words.
--------------------------------------------------------------------------- */

@media (max-width: 899.98px) {
  .pp-hero {
    min-height: 0;
  }

  .pp-hero .viz--line,
  .pp-hero .viz--market {
    aspect-ratio: auto;
    height: 260px;
  }
}

/* Project 5's exchange is three short lines; the tall frame the other chat
   uses left a dead band under it (the landing's finale frame is 13rem too). */
.pp-hero--finale .viz--chat {
  min-height: 13rem;
}

/* A phone has room for both page actions only if they tighten up. */
@media (max-width: 439.98px) {
  .topbar .btn {
    padding: 0.45rem 0.7rem;
    font-size: 0.75rem;
  }

  .topbar .btn::after {
    content: none;
  }
}

/* The second copy of the other four, inside the carousel's first set (see
   gen-pages.cjs): only the drift needs it. */
html:not(.seq-live) .marquee__pad {
  display: none !important; /* beats its inline display: contents */
}

/* ---------------------------------------------------------------------------
   Scrollbars in the site's palette. The panel's own scroll area had the
   browser's default light scrollbar, a bright slab against the dark panel.
   A slim thumb in the project's colour on a transparent track, the page's
   scrollbar in the ground's tones, and dark native controls everywhere else
   (color-scheme, set in the landing's stylesheet).
--------------------------------------------------------------------------- */

.pp-sheet__body,
.pp-diagram__scroll {
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--accent) 55%, var(--surface-2)) transparent;
}

.pp-sheet__body:hover {
  scrollbar-color: color-mix(in srgb, var(--accent) 80%, var(--surface-2)) transparent;
}

/* Safari and older Chromium, which ignore scrollbar-color. */
.pp-sheet__body::-webkit-scrollbar,
.pp-diagram__scroll::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.pp-sheet__body::-webkit-scrollbar-track,
.pp-diagram__scroll::-webkit-scrollbar-track {
  background: transparent;
}

.pp-sheet__body::-webkit-scrollbar-thumb,
.pp-diagram__scroll::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 55%, var(--surface-2)) padding-box;
}

/* ---------------------------------------------------------------------------
   The scroll cue

   A chevron at the foot of the screen, over the dark band under the opening.
   It is hidden until page.js adds .is-on, two seconds in, so it reads as an
   invitation once the arrival has finished rather than as part of it, and it
   goes on the first scroll.

   Pinned to the viewport rather than to the opening: on a phone the opening
   is taller than the screen, so its own foot is out of sight.
--------------------------------------------------------------------------- */
.pp-cue {
  position: fixed;
  bottom: clamp(0.75rem, 2.4vh, 1.6rem);
  left: 50%;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 4rem;
  height: 4rem;
  /* Decoration: never in the way of what is behind it. */
  pointer-events: none;
  opacity: 0;
  transform: translateX(-50%);
  transition: opacity 0.6s ease;
}

.pp-cue.is-on {
  opacity: 1;
}

/* The accent glow behind the chevron, which is what makes it read as the
   project's own colour rather than as browser chrome, over a soft disc of the
   page's own background: on a phone the opening's picture reaches the foot of
   the screen, and the chevron would otherwise sit on top of it. */
.pp-cue::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(closest-side, color-mix(in srgb, var(--accent) 30%, transparent), transparent),
    radial-gradient(closest-side, color-mix(in srgb, var(--ground) 82%, transparent) 58%, transparent);
}

/* Two lines meeting in a point, the same shape as the landing's cue. */
.pp-cue__chevron {
  width: 1.2rem;
  height: 1.2rem;
  border-right: 2.5px solid var(--accent);
  border-bottom: 2.5px solid var(--accent);
  border-radius: 0 0 3px 0;
  /* Half the chevron's height, so the shape sits optically centred. */
  margin-top: -0.42rem;
  transform: rotate(45deg);
}

@media (prefers-reduced-motion: no-preference) {
  .pp-cue.is-on .pp-cue__chevron {
    animation: pp-cue-nudge 1.9s ease-in-out infinite;
  }
}

@keyframes pp-cue-nudge {
  0%,
  100% {
    transform: rotate(45deg) translate(0, 0);
    opacity: 0.65;
  }
  50% {
    transform: rotate(45deg) translate(3px, 3px);
    opacity: 1;
  }
}

/* ===========================================================================
   Phones: the project page

   The landing's stylesheet carries the phone layer for everything both pages
   share (the bar, the figures, the pictures, the swipe row that replaces the
   carousel). This is what a project page adds: its opening, the cue under it,
   and the demo.
=========================================================================== */

@media (max-width: 779.98px) {
  /* The opening is the landing card, larger. On a phone it does not also
     have to fill the screen: with the picture under the words it ran past
     the fold anyway, and the empty band it added above the title just
     pushed the words further down. */
  .pp-hero {
    min-height: 0;
    padding-block: clamp(1.75rem, 5vh, 2.75rem) clamp(2rem, 6vh, 3rem);
  }

  /* About a third of the screen, like the landing's pictures. */
  .pp-hero .viz--line,
  .pp-hero .viz--market,
  .pp-hero .viz--bars,
  .pp-hero .viz--flow {
    aspect-ratio: auto;
    height: min(32vh, 220px);
  }

  .pp-hero .viz--chat {
    min-height: 0;
  }

  .pp-hero--finale .viz--chat {
    min-height: 0;
  }

  /* No cue here. It exists because the opening fills a desktop screen, so
     without it the page can be taken for all there is; on a phone the
     opening ends inside the first screen and the next heading is already
     showing under it. Floating over that heading, it was only in the way. */
  .pp-cue {
    display: none;
  }

  .pp-story {
    padding-block: clamp(2.25rem, 7vh, 3.25rem) clamp(1.5rem, 5vh, 2.25rem);
  }

  /* Same as the landing's mono labels: 12px is the floor on a hand-held
     screen. */
  .pp-pill {
    font-size: 0.75rem;
  }

  /* A tool and what it does, one under the other. Side by side, a long name
     ("Model-agnostic LLM layer") took most of the width and left its own
     description wrapping in a column three words wide. */
  .pp-tools li {
    display: block;
  }

  .pp-tool__name {
    display: block;
    margin-bottom: 0.15rem;
  }

  .pp-tool__name::after {
    content: none;
  }

  /* The "See more" band stacks: a full-width button under its own heading is
     a thumb's target, not a link at the end of a line. */
  .pp-more__inner {
    display: grid;
    gap: 1rem;
  }

  .pp-more__open {
    width: 100%;
    min-height: 48px;
    justify-content: center;
  }

  /* The sheet covers the screen edge to edge, so it is the one thing on the
     page that has to keep clear of the hardware itself: its head and the
     close button in it sit under a notch otherwise, and its last line under
     the home indicator. */
  .pp-sheet__head {
    padding-top: calc(1.25rem + env(safe-area-inset-top));
  }

  .pp-sheet__close {
    top: calc(1rem + env(safe-area-inset-top));
    width: 2.75rem;
    height: 2.75rem;
  }

  .pp-sheet__body {
    padding-bottom: calc(2rem + env(safe-area-inset-bottom));
  }

  /* The diagram is 44rem wide at its narrowest and cannot usefully shrink
     further — the boxes carry words. It scrolls sideways here, so it says
     so, and its right edge fades instead of ending in a hard cut that reads
     as the picture being broken. */
  .pp-diagram--wide .pp-diagram__scroll {
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
  }

  .pp-diagram--wide::after {
    content: "Swipe the diagram sideways";
    display: block;
    margin-top: 0.5rem;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    color: var(--fg-3);
  }
}
