/* ==========================================================================
   Work section (/work/ and /work/rhovive/), the "Specimen" design (2026-10-25).
   Loaded after styles.css on those two pages only.
   With motion allowed, full-length screenshots of rhovive.com scroll inside a window
   (and a phone) in step with the page. Only the site's own tokens, fonts and components;
   Rhovive's colors appear only inside its screenshots.
   Without JavaScript, or with reduced motion, every screenshot is a short still crop
   (its own small file), and the long screenshots are never downloaded. work.js puts in
   the long ones (data-long) only when motion is allowed, once they've loaded.
   The header's "Work" styles (aria-current="true", the "Call" breakpoint) live in
   styles.css, since the header is on every page.
   ========================================================================== */

/* Copy that describes the motion swaps for a still version when there is none.
   work.js adds .motion-live to the root when it runs with motion allowed. */
.live-only { display: none; }
.motion-live .live-only { display: block; }
.motion-live .still-only { display: none; }
/* A screen too short for the pinned card (still-pin) or the specimen (still-spec) gets
   that part's still version, so its copy swaps back too (data-live names the part) */
.still-pin [data-live="pin"].live-only,
.still-spec [data-live="spec"].live-only { display: none; }
.still-pin [data-live="pin"].still-only,
.still-spec [data-live="spec"].still-only { display: block; }

/* ---------- Frames: a browser window and a phone ---------- */

.win {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1.5px solid var(--ink);
  border-radius: var(--radius);
  overflow: hidden;
}
.win__bar,
.spec__bar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  height: 2.5rem;
  padding-inline: 0.7rem;
  background: var(--cream);
  border-bottom: 1.5px solid var(--ink);
  font-size: 0.8125rem;
  line-height: 1;
  white-space: nowrap;
}
.win__url {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  min-width: 0;
  padding: 0.45em 0.9em 0.45em 0.75em;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink);
  font-weight: 600;
}
.win__url::before { content: ""; flex: none; width: 0.5em; height: 0.5em; border-radius: 50%; background: var(--clay); }
/* not live yet: a plain dot, the same size as the clay one */
.win__url--open::before { background: var(--line); }
.win__view {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: var(--white);
}
/* A full-length screenshot: as wide as its window, as tall as the whole page */
.shot { display: block; width: 100%; height: auto; max-width: none; }

/* A scrollbar that shows how far down the page the window is (live version only) */
.win__rail { display: none; position: absolute; z-index: 1; top: 6px; bottom: 6px; right: 5px; width: 4px; border-radius: 2px; background: rgb(21 21 21 / 0.12); }
.win__thumb { position: absolute; top: 0; left: 0; right: 0; height: 12%; border-radius: 2px; background: rgb(21 21 21 / 0.55); will-change: transform; }

/* The phone: an ink body around a screen with the phone screenshot's own proportions.
   Sizes are in cqi (the phone's width), so it keeps its shape at any size. */
.phone { container-type: inline-size; }
.phone__body {
  position: relative;
  padding: 3.4cqi;
  background: var(--ink);
  border-radius: 15cqi;
  box-shadow: 0 30px 50px -30px rgb(21 21 21 / 0.55);
}
.phone__body::before {
  content: "";
  position: absolute;
  z-index: 2;
  top: 6.4cqi;
  left: 50%;
  width: 26cqi;
  height: 7cqi;
  border-radius: 999px;
  background: var(--ink);
  transform: translateX(-50%);
}
.phone__screen {
  position: relative;
  overflow: hidden;
  aspect-ratio: 390 / 844;
  border-radius: 11.6cqi;
  background: var(--white);
}
/* Without motion each phone shows a still crop that starts where its stretch starts; with
   motion, work.js swaps in the full-length screenshot and moves it with a transform. */

/* ==========================================================================
   Work page
   ========================================================================== */

.work-intro { padding-block: clamp(3.5rem, 2rem + 6vw, 9rem) clamp(3rem, 1.5rem + 5vw, 7rem); }
.work-intro h1 {
  margin: 0;
  font-size: clamp(2.75rem, 1rem + 7.2vw, 6rem);
  line-height: 0.94;
  letter-spacing: -0.035em;
}
.work-intro__lead {
  margin: clamp(1.75rem, 1rem + 2.5vw, 3.5rem) 0 0;
  max-width: 30rem;
}
.work-intro__site { font-weight: 600; color: var(--ink); }
@media (min-width: 60em) {
  /* the lead sits under the headline's second half, Baunfire style */
  .work-intro__lead { margin-left: auto; margin-right: clamp(0rem, 6vw, 7rem); }
}

.work-grid {
  display: grid;
  gap: clamp(5rem, 3rem + 6vw, 9rem) clamp(2.5rem, 0.5rem + 5vw, 6.5rem);
  margin-block: 0;
  padding: 0 0 clamp(5rem, 3rem + 8vw, 11rem);
  list-style: none;
}
/* Two columns from 60em (tablets get one large card per row) */
@media (min-width: 60em) {
  .work-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  /* the right column starts lower */
  .work-grid > .card:nth-child(2) { margin-top: clamp(9rem, 1rem + 19vw, 21rem); }
}

.card { position: relative; }
.card__media { position: relative; container-type: inline-size; }
.card__win { aspect-ratio: 4 / 5; box-shadow: 0 40px 70px -50px rgb(21 21 21 / 0.55); transition: box-shadow 0.6s var(--ease); }

/* Hover: the window lifts toward you, the phone rises more (depth), the bar offers the project page.
   work.js eases --px/--py (cursor) and --h (hover) so it never snaps. */
.card__win { transform: translate3d(calc(var(--px, 0) * -6px), calc(var(--py, 0) * -6px - var(--h, 0) * 8px), 0); }
.card__phone .phone { transform: translate3d(calc(var(--px, 0) * 12px), calc(var(--py, 0) * 10px - var(--h, 0) * 22px), 0); }
.card:hover .card__win { box-shadow: 0 60px 90px -50px rgb(21 21 21 / 0.6); }
.win__go {
  display: inline-flex;
  gap: 0.4em;
  color: var(--clay-ink);
  font-weight: 600;
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity 0.35s ease, transform 0.5s var(--ease);
}
.card:hover .win__go,
.card:focus-within .win__go { opacity: 1; transform: none; }
@media (max-width: 30em) { .win__go { display: none; } }

/* The phone tucked over the window's lower right corner (never past the page gutter) */
.card__phone {
  position: absolute;
  z-index: 2;
  right: max(-7%, 6px - var(--gutter));
  bottom: -9%;
  width: 26%;
}

.card__text { margin-top: clamp(1.75rem, 1rem + 2vw, 2.75rem); }
.card__title {
  margin: 0 0 0.45rem;
  font-family: var(--serif);
  font-size: clamp(2.1rem, 1.4rem + 2.4vw, 3.4rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.card--invite .card__title { font-size: clamp(1.9rem, 1.3rem + 2vw, 2.9rem); }
.card__title a,
.card__link { color: inherit; text-decoration: none; }
/* The whole card is the link's click area; the link text itself stays short and clear */
.card__title a::after,
.card__link::after { content: ""; position: absolute; inset: -0.5rem; z-index: 3; }
.card__title a:hover,
.card:hover .card__title a { color: var(--clay-ink); }
.card__kind { margin: 0; color: var(--ink-2); }
/* Tags: a drawn slash between them (no text, so screen readers don't say "slash"). Each
   slash sits in front of its tag, and the list's left edge is clipped, so a line never
   starts or ends on one. */
.card__tags {
  --sep: 1.4em;
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0;
  margin: 0.8rem 0 0 calc(-1 * var(--sep));
  padding: 0;
  list-style: none;
  font-size: var(--step--1);
  font-weight: 600;
  clip-path: inset(-0.5em -0.5em -0.5em var(--sep));
}
.card__tags li { display: inline-flex; align-items: center; white-space: nowrap; }
.card__tags li::before {
  content: "";
  flex: none;
  width: 1.5px;
  height: 0.9em;
  margin-inline: calc((var(--sep) - 1.5px) / 2);
  background: var(--gray);
  transform: rotate(20deg);
}
.card__cta { margin: 1rem 0 0; }
.card__link {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding-block: 0.15em;
  font-weight: 600;
  border-bottom: 1.5px solid currentColor;
}
.card__arrow { display: inline-block; transition: transform 0.35s var(--ease); }
.card:hover .card__link { color: var(--clay-ink); }
.card:hover .card__arrow { transform: translateX(5px); }
/* Keyboard focus shows on the window too, since the whole card is the target */
.card:has(:focus-visible) .card__win { outline: 3px solid var(--clay-ink); outline-offset: 5px; }

/* The invitation: a homepage for the visitor's business, set in the site's own type, with
   the magnifier eye looking it over. Finished by default; with motion allowed it writes
   itself in, word by word, as the card passes (the pending look exists only under
   .cards-ready, which work.js sets). */
.card__win .invite { container-type: inline-size; background: var(--cream); }
.mock {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 5.5cqi 8cqi;
}
.mock__nav {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 4cqi;
  width: 100%;
  margin: 0;
  padding-bottom: 4cqi;
  border-bottom: 1px solid var(--line);
  font-size: 3cqi;
  line-height: 1.2;
}
.mock__brand { font-family: var(--serif); font-size: 4.6cqi; font-weight: 600; letter-spacing: -0.02em; color: var(--ink); }
.mock__dot { color: var(--clay); }
.mock__links { display: flex; gap: 4.5cqi; color: var(--ink-2); font-weight: 600; }
.mock__h {
  margin: 14cqi 0 0;
  font-family: var(--serif);
  font-size: 9.6cqi;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.mock__line { display: block; white-space: nowrap; }
.mock__h b { font-weight: 700; color: var(--clay); }
.mock__p { max-width: 60%; margin: 5cqi 0 0; font-size: 3.7cqi; line-height: 1.45; color: var(--ink-2); }
.mock__cta { margin: 6.5cqi 0 0; }
.mock__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 2.5cqi 4.4cqi;
  border-radius: 1.2cqi;
  background: var(--ink);
  color: var(--cream);
  font-size: 3.4cqi;
  font-weight: 600;
  line-height: 1.1;
}
.mock__arrow { color: var(--clay-light); }
.invite .seeker.invite__eye {
  position: absolute;
  right: 5cqi;
  bottom: 5cqi;
  width: 38cqi;
  margin: 0;
}
.cards-ready .mock [data-part],
.cards-ready .mock [data-part] .mock__dot,
.cards-ready .mock [data-part] .mock__arrow { transition: color 0.6s ease, background-color 0.6s ease; }
.cards-ready .mock [data-part]:not(.is-on),
.cards-ready .mock [data-part]:not(.is-on) .mock__dot,
.cards-ready .mock [data-part]:not(.is-on) .mock__arrow { color: var(--line); }
.cards-ready .mock .mock__btn:not(.is-on) { background: var(--line); color: var(--white); }
.cards-ready .mock .mock__btn:not(.is-on) .mock__arrow { color: var(--white); }

/* Image reveal (Baunfire style): each window wipes up into place as it scrolls in.
   Only set up by work.js, so without it everything is simply there. */
.cards-ready .card__reveal {
  transition: clip-path 1.25s var(--ease), transform 1.25s var(--ease);
  clip-path: inset(-15% -15% -25% -15%);
}
.cards-ready .card.is-pending .card__reveal { clip-path: inset(100% -15% -25% -15%); transform: translateY(56px); }
.cards-ready .card__phone { transition: opacity 0.8s ease 0.45s, translate 1.1s var(--ease) 0.45s; }
.cards-ready .card.is-pending .card__phone { opacity: 0; translate: 0 48px; }
.cards-ready .card__text > * { transition: opacity 0.7s ease, translate 0.9s var(--ease); transition-delay: calc(0.25s + var(--t, 0) * 0.08s); }
.cards-ready .card.is-pending .card__text > * { opacity: 0; translate: 0 14px; }
.card__text > :nth-child(2) { --t: 1; }
.card__text > :nth-child(3) { --t: 2; }

/* Live (work.js, motion allowed): the Rhovive card pins, centered in the screen, while
   its homepage plays through; the runway after it (--run) is how long it stays. The window
   keeps the column's width but gives up some height on short screens, so the name and tags
   stay in view with it (under about 560px tall the floor drops too; a screen that still
   can't hold the whole card gets no pin, see work.js). On two columns the invitation starts
   much lower (--offset) and scrolls past the pinned card. */
.cards-live .card--rhovive .card__pin { position: sticky; top: var(--pin-top, 4.5rem); }
.cards-live .card--rhovive::after { content: ""; display: block; height: var(--run, 0px); }
.cards-live .card--rhovive .card__win { aspect-ratio: auto; height: min(125cqi, max(min(18rem, 100svh - 10rem), 100svh - 17rem)); }
.cards-live .win__rail { display: block; }
@media (min-width: 60em) {
  .cards-live .work-grid > .card--invite { margin-top: var(--offset, clamp(9rem, 1rem + 19vw, 21rem)); }
}

/* ==========================================================================
   Case study
   ========================================================================== */

.cs-open { padding-block: clamp(2rem, 1rem + 3vw, 4rem) clamp(3rem, 2rem + 4vw, 6rem); text-align: center; }
.cs-back { margin: 0 0 clamp(1.5rem, 0.5rem + 3vw, 3.5rem); text-align: left; }
.cs-open h1 {
  margin: 0 0 0.12em;
  font-size: clamp(4rem, 1.2rem + 10vw, 8rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
}
.cs-open__line {
  max-width: 24em;
  margin: 0 auto;
  font-size: var(--step-2);
  line-height: 1.3;
  color: var(--ink-2);
  text-wrap: balance;
}
.cs-meta {
  display: grid;
  gap: var(--s5);
  max-width: 58rem;
  margin: clamp(2.5rem, 1.5rem + 3vw, 4rem) auto 0;
  padding-top: var(--s5);
  border-top: 1px solid var(--ink);
}
.cs-meta dt { margin-bottom: 0.4rem; font-size: var(--step--1); font-weight: 600; color: var(--ink-2); }
.cs-meta dd { margin: 0; font-weight: 600; line-height: 1.45; text-wrap: balance; }
.cs-meta ul { margin: 0; padding: 0; list-style: none; }
/* the spelled-out names after "SEO and AI search": a gloss, so it doesn't outweigh the list */
.cs-meta__gloss { font-weight: 400; color: var(--ink-2); }
.cs-meta__visit { display: inline-flex; align-items: center; gap: 0.35em; min-height: 44px; margin-top: -0.6rem; }
@media (min-width: 48em) {
  .cs-meta { grid-template-columns: 1.3fr 1fr 1fr; gap: 0; }
  .cs-meta > div { padding-inline: var(--s4); }
  .cs-meta > div + div { border-left: 1px solid var(--line); }
}

/* ---------- The specimen ---------- */

.spec-sec { padding-block: clamp(1rem, 0.5rem + 2vw, 3rem) clamp(4rem, 3rem + 5vw, 8rem); }
.spec-head { max-width: 46rem; margin: 0 auto clamp(2.5rem, 1.5rem + 3vw, 4.5rem); text-align: center; }
.spec-head .lead { margin: 0 auto; }
.spec-after { margin: clamp(3rem, 2rem + 3vw, 5rem) 0 0; text-align: center; }
/* The specimen and its notes keep to the site's container */
.spec-wide { width: min(100% - (2 * var(--gutter)), var(--container)); margin-inline: auto; }

/* Still version (no JavaScript, or reduced motion): one crop of the screenshot per note,
   each its own small file (a laptop crop, and a phone crop under 60em via <picture>).
   The crop always gets the wider track, on either side. */
.spec__stage { display: none; }
/* Once work.js is running with motion (.motion-live) the live version takes over, so the
   crops stay hidden (and, being lazy, aren't downloaded) unless work.js falls back to the
   still version (.is-still). Tied to the class, not to (scripting: enabled), so a work.js
   that fails to load or throws still leaves the stills on screen. */
.motion-live .spec:not(.is-still) .spec-note__shot { display: none; }
.spec__notes { display: grid; gap: clamp(4rem, 2.5rem + 5vw, 8rem); max-width: 68rem; margin: 0 auto; padding: 0; list-style: none; }
.spec-note { display: grid; gap: var(--s4) clamp(2rem, 1rem + 4vw, 5rem); align-items: center; }
.spec-note__body h3 {
  margin: 0 0 0.5em;
  font-family: var(--serif);
  font-size: clamp(1.6rem, 1.25rem + 1.2vw, 2.25rem);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.025em;
  text-wrap: balance;
}
.spec-note__body p { margin: 0; color: var(--ink-2); text-wrap: pretty; }
.spec-crop {
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1.5px solid var(--ink);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 30px 60px -45px rgb(21 21 21 / 0.55);
}
.spec-crop__view { position: relative; overflow: hidden; }
.spec-crop__view picture { display: block; }
.spec-crop__view img { display: block; width: 100%; height: auto; max-width: none; }
@media (min-width: 48em) {
  .spec-note { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
  .spec-note:nth-child(even) { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
  .spec-note:nth-child(even) .spec-note__shot { order: 2; }
}
@media (min-width: 60em) {
  /* the comparison table gets the widest crop, so its rows stay readable */
  .spec-note:nth-child(2) { grid-template-columns: minmax(0, 1fr) minmax(0, 1.65fr); }
}
@media (max-width: 59.99em) {
  /* phones and tablets: the crops come from the phone screenshot */
  .spec-crop { width: min(78%, 17rem); margin-inline: auto; border-radius: 28px; border: 7px solid var(--ink); }
  .spec-crop .spec__bar { display: none; }
}

/* Live version (work.js adds .is-live): one sticky frame; the page inside follows the
   scroll. The track's height, the notes' places and the brackets are set by work.js. */
.spec {
  --frame-h: min(80svh, 56rem);
  --frame-w: 56%;
  --note-w: calc(100% - var(--frame-w) - 4.5rem);
}
.spec.is-live { position: relative; }
.spec.is-live .spec__stage {
  display: block;
  position: sticky;
  top: calc((100svh - var(--frame-h)) / 2);
  height: var(--frame-h);
}
.spec.is-live .spec__notes { position: absolute; inset: 0; display: block; max-width: none; margin: 0; pointer-events: none; }
.spec.is-live .spec-note { position: absolute; top: 0; display: block; width: var(--note-w); pointer-events: auto; }
.spec.is-live .spec-note__shot { display: none; }

.spec__frame {
  position: relative;
  display: flex;
  flex-direction: column;
  width: var(--frame-w);
  height: 100%;
  margin-inline: auto;
  background: var(--white);
  border: 1.5px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 0 50px 90px -60px rgb(21 21 21 / 0.6);
}
.spec__frame > .spec__bar { border-radius: calc(var(--radius) - 1.5px) calc(var(--radius) - 1.5px) 0 0; }
.spec__view {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  border-radius: 0 0 calc(var(--radius) - 1.5px) calc(var(--radius) - 1.5px);
  background: var(--white);
}
/* The page: the laptop screenshot, or the phone screenshot in two tiles (WebP can't be
   taller than 16,383px). work.js makes the tiles from the page's data-d / data-m. */
.spec__page { display: block; will-change: transform; }
.spec__tile { display: block; width: 100%; height: auto; max-width: none; }

/* A scrollbar that tells you how far down rhovive.com you are */
.spec__rail { position: absolute; z-index: 1; top: 6px; bottom: 6px; right: 5px; width: 5px; border-radius: 3px; background: rgb(21 21 21 / 0.1); }
.spec__thumb { position: absolute; top: 0; left: 0; right: 0; height: 12%; border-radius: 3px; background: rgb(21 21 21 / 0.55); will-change: transform; }

/* Marks: a clay bracket on the frame's edge spanning the section the note talks about */
.spec__mark {
  position: absolute;
  z-index: 2;
  top: 0;
  height: 0;
  left: calc(100% + 10px);
  width: 8px;
  border: 2px solid var(--clay);
  border-left: 0;
  border-radius: 0 3px 3px 0;
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
}
.spec__mark.is-on { opacity: 1; }
.spec-note__body { position: relative; }

/* Laptops and up: notes arrive from below in the margin, and while a note's line is level
   with its section in the frame, a clay line reaches from the note to that section's bracket */
@media (min-width: 60em) {
  .spec.is-live .spec-note { opacity: 0; translate: 0 28px; transition: opacity 0.7s ease, translate 0.9s var(--ease); }
  .spec.is-live .spec-note.is-seen { opacity: 1; translate: 0 0; }
  .spec.is-live .spec-note__body::before,
  .spec.is-live .spec-note__body::after {
    content: "";
    position: absolute;
    background: var(--clay);
  }
  /* the line, from the note to the frame (it draws itself toward the frame) */
  .spec.is-live .spec-note__body::before {
    top: 0.95rem;
    right: calc(100% + 0.9rem);
    width: var(--lead-w, 2rem);
    height: 2px;
    transform: scaleX(0);
    transform-origin: right center;
    transition: transform 0.6s var(--ease);
  }
  /* the dot where it meets the frame's bracket */
  .spec.is-live .spec-note__body::after {
    top: calc(0.95rem - 3px);
    right: calc(100% + 0.9rem + var(--lead-w, 2rem) - 4px);
    width: 8px;
    height: 8px;
    border-radius: 50%;
    transform: scale(0);
    transition: transform 0.35s var(--ease);
  }
  .spec.is-live .spec-note.is-active .spec-note__body::before { transform: scaleX(1); }
  .spec.is-live .spec-note.is-active .spec-note__body::after { transform: scale(1); transition-delay: 0.35s; }
  .spec.is-live .spec-note__body h3 { font-size: clamp(1.4rem, 1.1rem + 0.7vw, 1.75rem); }
}

/* 60em to 80em: the frame on the left, every note on the right */
@media (min-width: 60em) and (max-width: 79.99em) {
  .spec__frame { margin-left: 0; }
  .spec.is-live .spec-note { right: 0; }
}

/* Wide screens: the frame in the middle, notes alternating in both margins, all inside the
   site's container. A left note's heading sits against its line; its text keeps a straight
   left edge on the container. */
@media (min-width: 80em) {
  .spec {
    --note-w: 16.5rem;
    --frame-w: calc(100% - 2 * (var(--note-w) + 3.5rem));
  }
  .spec.is-live .spec-note[data-side="left"] { left: 0; }
  .spec.is-live .spec-note[data-side="left"] h3 { text-align: right; }
  .spec.is-live .spec-note[data-side="right"] { right: 0; }
  .spec.is-live .spec-note[data-side="left"] .spec-note__body::before { right: auto; left: calc(100% + 0.9rem); transform-origin: left center; }
  .spec.is-live .spec-note[data-side="left"] .spec-note__body::after { right: auto; left: calc(100% + 0.9rem + var(--lead-w, 2rem) - 4px); }
  .spec__mark[data-side="left"] { left: auto; right: calc(100% + 10px); border: 2px solid var(--clay); border-right: 0; border-radius: 3px 0 0 3px; }
}

/* Phones and tablets: the frame becomes a phone near the top of the screen, and each note
   is a card that slides up into the space under it and stays there while its section rests.
   A note only shows while its section is the one in the phone, so it never covers it. */
@media (max-width: 59.99em) {
  .spec { --frame-h: min(57svh, 36rem); }
  .spec.is-live .spec__stage { top: var(--stage-top, 4svh); }
  .spec__frame {
    width: calc((var(--frame-h) - 16px) * 0.4621 + 16px);
    padding: 8px;
    background: var(--ink);
    border: 0;
    border-radius: 36px;
  }
  .spec__frame::before {
    content: "";
    position: absolute;
    z-index: 3;
    top: 15px;
    left: 50%;
    width: 26%;
    height: 16px;
    border-radius: 999px;
    background: var(--ink-soft);
    transform: translateX(-50%);
  }
  .spec__frame > .spec__bar { display: none; }
  .spec__view { border-radius: 28px; }
  /* a status bar the notch sits in, so the page never slides under the notch */
  .spec__view::before { content: ""; position: absolute; z-index: 2; top: 0; left: 0; right: 0; height: 26px; background: var(--ink); }
  .spec__rail { display: none; }
  .spec__mark { left: calc(100% + 8px); width: 8px; }
  .spec.is-live .spec-note {
    position: sticky;
    top: var(--note-top, 66svh);
    width: min(100%, 22rem);
    margin-inline: auto;
    padding: 1rem 1.15rem 1.1rem;
    background: var(--cream);
    border: 1.5px solid var(--ink);
    border-radius: var(--radius);
    box-shadow: 0 22px 40px -26px rgb(21 21 21 / 0.5);
    opacity: 0;
    transition: opacity 0.35s ease;
  }
  .spec.is-live .spec-note.is-active { opacity: 1; }
  .spec.is-live .spec-note__body h3 { margin-bottom: 0.35em; font-size: 1.35rem; }
  .spec.is-live .spec-note__body p { font-size: 0.9375rem; line-height: 1.5; }
}

/* ---------- Phones section ---------- */

.phones { display: grid; gap: clamp(3rem, 2rem + 4vw, 5rem); margin: 0; padding: 0; list-style: none; }
.phones__fig { margin: 0; }
.phones .phone { width: min(100%, 17.5rem); margin-inline: auto; }
.phone--dark .phone__body,
.phone--dark .phone__body::before { background: var(--ink-soft); }
.phone--dark .phone__body { box-shadow: inset 0 0 0 1.5px rgb(255 255 255 / 0.2), 0 40px 60px -40px rgb(0 0 0 / 0.8); }
.phones figcaption { max-width: 17.5rem; margin: var(--s4) auto 0; color: var(--on-dark-2); font-size: 1rem; line-height: 1.55; }
@media (min-width: 48em) {
  .phones { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; gap: clamp(1.5rem, 4vw, 5rem); }
  .phones > li:nth-child(2) { margin-top: clamp(4rem, 9vw, 9rem); }
  .phones > li:nth-child(3) { margin-top: clamp(1.5rem, 3.5vw, 3.5rem); }
}
@media (max-width: 47.99em) {
  /* a zigzag: phone and caption side by side, swapping sides each row */
  .phones__fig { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s4); align-items: center; }
  .phones .phone { width: 100%; }
  .phones figcaption { margin: 0; }
  .phones > li:nth-child(even) .phone { order: 2; }
}

/* ---------- How it introduces itself to Google ---------- */

.serp { max-width: 46rem; margin: 0 auto; }
.serp__card {
  padding: clamp(1.25rem, 0.8rem + 2vw, 2.25rem) clamp(1.25rem, 0.8rem + 2.5vw, 2.75rem);
  background: var(--white);
  border: 1.5px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 0 30px 60px -45px rgb(21 21 21 / 0.5);
}
.serp__site { display: grid; margin: 0 0 0.75rem; font-size: 0.875rem; line-height: 1.4; }
.serp__name { font-weight: 600; }
.serp__url { color: var(--ink-2); overflow-wrap: anywhere; }
.serp__title,
.serp__desc { position: relative; }
.serp__title { margin: 0 0 0.4rem; font-size: clamp(1.15rem, 1rem + 0.7vw, 1.45rem); font-weight: 600; line-height: 1.3; color: var(--clay-ink); }
.serp__desc { margin: 0; color: var(--ink-2); font-size: 1rem; line-height: 1.6; }
/* The same clay brackets as the specimen: one on each line of the card, and the same
   bracket in front of the note about it, so each note reads as "this bracket, this line" */
.serp__mark,
.serp__notes p::before {
  position: absolute;
  width: 7px;
  border: 2px solid var(--clay);
  border-right: 0;
  border-radius: 3px 0 0 3px;
}
.serp__mark { top: 0.1em; bottom: 0.1em; left: calc(-1 * clamp(0.75rem, 0.4rem + 1.4vw, 1.6rem)); }
.serp__notes {
  display: grid;
  align-items: start;
  gap: var(--s3) var(--s6);
  margin-top: var(--s5);
  color: var(--ink-2);
  font-size: 1rem;
}
.serp__notes p { position: relative; margin: 0; padding-left: 1.15rem; }
.serp__notes p::before { content: ""; top: 0.25em; bottom: 0.25em; left: 0; }
@media (min-width: 48em) { .serp__notes { grid-template-columns: 1fr 1fr; } }

/* ---------- Built with: the code behind the store ----------
   Kept general on purpose (owner, 2026-10-25): the languages only, no file names, counts or
   component names. The heading and one line sit beside a ruled list of the five languages
   (ink rule on top, like the project page's opening row). */

.built { display: grid; gap: clamp(2rem, 1rem + 3vw, 3rem); align-items: start; }
@media (min-width: 60em) { .built { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(3rem, 1rem + 4vw, 6rem); } }
.built__head { margin-bottom: 0; }

.built__langs { margin: 0; border-top: 1px solid var(--ink); }
.built__langs > div {
  display: grid;
  grid-template-columns: 7.25rem minmax(0, 1fr);
  gap: var(--s4);
  align-items: baseline;
  padding-block: 1.05rem;
  border-bottom: 1px solid var(--line);
}
.built__langs dt {
  font-family: var(--serif);
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.02em;
}
.built__langs dd { margin: 0; color: var(--ink-2); line-height: 1.55; text-wrap: pretty; }
/* Phones: the name above its line, so the text gets the full width */
@media (max-width: 30em) {
  .built__langs > div { grid-template-columns: minmax(0, 1fr); gap: 0.2rem; }
  .built__langs dt { font-size: 1.25rem; }
}

/* ---------- Reduced motion: everything rests ---------- */
@media (prefers-reduced-motion: reduce) {
  .card__win,
  .card__phone .phone { transform: none; }
  .win__go { transition: none; }
}
