/* Deliberately not the adult product's palette. R-1/R-2 separate the brands everywhere a
   person can see, and a shared visual identity is as much a link as a shared domain. */
:root {
  --bg: #0d0e12;
  --surface: #16181f;
  --line: #272a34;
  --text: #f2f3f7;
  --muted: #9aa0b0;
  --accent: #ffd84d;
  --accent-ink: #1a1400;
  --danger: #ff6b6b;
  --radius: 14px;
  /* Fixed rather than intrinsic so the hero can subtract it and land its button above the
     fold on the nose, instead of overhanging by whatever the brand line happens to measure. */
  --topbar-h: 53px;
  /* One container for the whole site: the header, the hero, the pair of sections under it and
     the catalog grid all measure this, so every left edge on the page is the same left edge.
     Change it here and they move together. */
  --page-w: 1160px;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f7fa;
    --surface: #ffffff;
    --line: #e3e5ec;
    --text: #14161c;
    --muted: #5d6475;
    --accent: #ffcb1f;
    --accent-ink: #1a1400;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* A column so the footer sits at the bottom of a short page instead of floating halfway up
     it -- the result page a share link lands on is the short one, and it is the page a stranger
     sees first. */
  display: flex;
  flex-direction: column;
  min-height: 100svh;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.topbar {
  border-bottom: 1px solid var(--line);
}

/* The header's own container. It shares the site measure so the logo starts on the same left
   edge as the painting under it instead of hugging the window. */
.bar {
  display: flex;
  align-items: center;
  gap: 16px;
  height: var(--topbar-h);
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 0 20px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-right: auto;
  text-decoration: none;
}

/* Attribute-sized images need this or the presentational height wins over the box we asked
   for -- the same defect that made the catalog cards render as portraits. */
.brand img,
.footer-brand img { display: block; }

/* The head is drawn in a gilt disc, the way it is on the avatar. The mark's own box already
   leaves room for the circle -- its artwork reaches 441 of the 495 units from centre -- so
   inscribing it needs padding for breathing room, not for fit. */
.brand-mark {
  width: 36px;
  height: 36px;
  padding: 3px;
  border: 2px solid var(--accent);
  border-radius: 50%;
  background: var(--surface);
}

.brand-wordmark { width: 100px; height: auto; }

.nav {
  display: flex;
  gap: 22px;
  font-size: 15px;
}

.nav a {
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
}

.nav a:hover { color: var(--text); }

/* Three bars drawn from one element: a border above and below the span, and the span itself in
   the middle. Nothing to keep in sync when the colour changes. */
.burger {
  display: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.burger span,
.burger span::before,
.burger span::after {
  display: block;
  width: 20px;
  height: 2px;
  margin: 0 auto;
  background: var(--text);
  transition: transform 140ms ease, opacity 140ms ease;
}

.burger span::before,
.burger span::after {
  content: "";
  position: relative;
}

.burger span::before { top: -6px; }
.burger span::after { top: 4px; }

.burger--open span { background: transparent; }
.burger--open span::before { transform: translateY(6px) rotate(45deg); }
.burger--open span::after { transform: translateY(-4px) rotate(-45deg); }

/* Below 760px the links become a panel under the bar. They stay in the DOM at every width --
   only their box changes -- so the internal links are always crawlable. */
@media (max-width: 759px) {
  .burger { display: block; }

  .nav {
    position: absolute;
    top: var(--topbar-h);
    left: 0;
    right: 0;
    z-index: 3;
    flex-direction: column;
    gap: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    font-size: 17px;
  }

  .nav:not(.nav--open) { display: none; }
  .nav a { padding: 14px 20px; color: var(--text); }
  .nav a + a { border-top: 1px solid var(--line); }
}

.topbar { position: relative; }

.page {
  max-width: 560px;
  margin: 0 auto;
  /* No bottom padding: every page ends in the footer now, and the footer brings its own
     margin. The 56px this used to reserve was dead space between the last block and the rule. */
  padding: 20px 20px 0;
}

/* The template page alone opts out of the column: its painting is full-bleed and its own
   sections carry their padding. The result and catalog pages keep the original container. */
.page--hero {
  max-width: none;
  /* Zeroed, not inherited: `margin: 0 auto` on a flex item absorbs the free space rather than
     stretching, which capped this page at the width of the hero inside it. */
  margin: 0;
  padding: 0;
}

.stage { position: relative; }

/* Mobile: the painting is the whole screen and the button sits on it. Nothing a visitor needs
   in order to act is below the fold, which is what the previous stacked layout got wrong. */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: calc(100svh - var(--topbar-h));
}

.hero .stage { position: absolute; inset: 0; }

.hero .stage > * {
  height: 100%;
  aspect-ratio: auto;
  border: 0;
  border-radius: 0;
}

.intro { padding-top: 22px; }

.hero .intro {
  position: relative;
  z-index: 1;
  padding: 0 20px 16px;
}

/* Mobile only: the painting behind the copy is atmosphere, not the subject -- the before/after
   strip is what a visitor actually reads, and an undimmed full-bleed crop competes with it. Put
   on .preview rather than .stage so it never dims the live camera feed. */
.hero .preview { position: relative; }

.hero .preview::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to top, rgba(13, 14, 18, 0.9) 22%, rgba(13, 14, 18, 0.66) 62%, rgba(13, 14, 18, 0.42));
}

@media (prefers-color-scheme: light) {
  .hero .preview::after {
    background: linear-gradient(to top, rgba(247, 247, 250, 0.92) 22%, rgba(247, 247, 250, 0.7) 62%, rgba(247, 247, 250, 0.45));
  }
}

/* The scrim is a pseudo-element rather than a wrapper so it can overhang the top of the text
   block and fade into the painting instead of cutting across it. */
.hero .intro::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: 0;
  right: 0;
  bottom: 0;
  top: -140px;
  background: linear-gradient(to top, var(--bg) 26%, rgba(13, 14, 18, 0.82) 58%, rgba(13, 14, 18, 0));
  pointer-events: none;
}

@media (prefers-color-scheme: light) {
  .hero .intro::before {
    background: linear-gradient(to top, var(--bg) 26%, rgba(247, 247, 250, 0.88) 58%, rgba(247, 247, 250, 0));
  }
}

.preview, .camera {
  margin: 0;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
}

.preview img, .camera video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Mirrored preview only. The captured frame is drawn unmirrored on the canvas. */
.camera { position: relative; }
.camera video { transform: scaleX(-1); }

.shutter {
  position: absolute;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  width: 66px;
  height: 66px;
  border-radius: 50%;
  border: 4px solid rgba(255, 255, 255, 0.9);
  background: rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(6px);
  cursor: pointer;
}

.shutter:active { transform: translateX(-50%) scale(0.94); }

.working {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  display: grid;
  place-content: center;
  gap: 14px;
  justify-items: center;
  color: var(--muted);
}

/* An author-stylesheet `display` beats the user-agent's `[hidden] { display: none }`, so the
   rule above was showing the spinner to every visitor before they had done anything. .camera
   escapes this only because it never sets `display` at all — which is luck, not design, so the
   guard covers both. */
.working[hidden], .camera[hidden], .preview[hidden], .intro[hidden] { display: none; }

.spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  animation: spin 0.9s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}

.kicker {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}

h1 {
  margin: 0 0 8px;
  font-size: 25px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.lede { margin: 0 0 12px; color: var(--muted); }

.payoff { margin: 0 0 12px; }

.payoff-pair {
  display: flex;
  align-items: center;
  gap: 10px;
  /* Capped so the strip stays evidence rather than becoming the subject -- the full-bleed
     painting behind it is already carrying the picture. */
  max-width: 320px;
}

.payoff-pair img {
  display: block;
  width: 100%;
  height: auto;
  flex: 1;
  /* An image's automatic minimum size is its intrinsic width, so without this the two 720px
     sources refuse to shrink and the pair overflows the screen instead of splitting it. */
  min-width: 0;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--surface);
}

.payoff-arrow {
  flex: none;
  color: var(--accent);
  font-size: 20px;
  line-height: 1;
}

.payoff figcaption {
  margin-top: 6px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--muted);
}

.actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.cta {
  display: block;
  width: 100%;
  padding: 16px;
  border: 0;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 17px;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.ghost {
  display: block;
  width: 100%;
  padding: 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text);
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
}

/* Separator between the two ways in. A rule either side rather than a bare word, so the two
   buttons read as alternatives rather than as a sequence. */
.or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.or::before, .or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.error { color: var(--danger); margin: 12px 0 0; }

/* A spent allowance is not a fault. Three lines of danger red on the one screen where we then
   ask for money reads as "you did something wrong", and the sentence under it is an offer. */
.error--notice { color: var(--text); }

.reassure {
  margin: 12px 0 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
.hint { color: var(--muted); margin: 10px 0 0; text-align: center; font-size: 14px; }

/* The named networks, under the Share button rather than instead of it. `navigator.share`
   covers phones; on desktop the button degrades to a clipboard write, which leaves the person
   to go somewhere and paste -- and that walk is where a share is lost. Anchors, not widgets:
   nothing is loaded from any of these hosts, so none of them sees the visitor until the
   visitor chooses to go. */
.socials {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
}

.social {
  display: flex;
  align-items: center;
  justify-content: center;
  /* 40px square: the smallest tap target that is not a mis-tap on a phone. */
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--muted);
  text-decoration: none;
}

.social:hover, .social:focus-visible {
  border-color: var(--muted);
  color: var(--text);
}

/* The second exit from the spent allowance. Set at the weight of .reassure and not of .cta: the
   first exit -- share one, somebody opens it, the allowance comes back -- is the loop, and the
   paid door must not out-shout the free one on the screen where both are offered. */
.ask {
  margin: 8px 0 0;
  text-align: center;
  font-size: 14px;
  color: var(--muted);
}

/* The answer to a round trip the person deliberately started, so it sits at the top of the page
   they came back to rather than somewhere they have to go looking. */
.patron-note {
  /* The hero's main has no padding of its own, so the gutter has to come from here or the note
     sits edge to edge on a phone. */
  max-width: min(var(--page-w), calc(100% - 40px));
  margin: 16px auto 0;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font-size: 14px;
}

.patron-note--ok { border-color: var(--accent); }

.ask-signin { color: var(--muted); }

.patron-clean { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }

/* The paid clean copy.
 *
 * This was a text link at the weight of a caption, on the argument that the loop -- "Make your
 * own" and "Share" -- compounds while a sale does not, so the upsell must not compete with the
 * two buttons. Half of that argument was about PLACEMENT and still holds: the card sits below
 * both buttons and nothing has moved above them. The other half was about volume, and it was
 * wrong -- somebody who had already decided to buy had to hunt for the door. A card, with its
 * own edge and its own button, is findable without being first.
 *
 * The number to watch afterwards is clean_unlocked against the k-factor: if shares per result
 * fall while sales rise, this card is taking from the loop rather than from nothing. */
.offer {
  margin: 20px 0 0;
  padding: 16px 16px 18px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: center;
}

.offer-title {
  margin: 0 0 6px;
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
}

.offer-note {
  margin: 0 0 14px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--muted);
}

/* The one button on the page that is not the loop, so it carries the price in its own label:
   a call to action that hides what it costs until the next screen is a call to action people
   learn not to press. */
.cta--buy { font-size: 16px; }
.cta:disabled { opacity: 0.55; cursor: default; }

/* Payment landed. The border goes quiet and the button stays loud -- at this point there is
   exactly one thing left to do and the card exists only to carry it. */
.offer--done { border-color: var(--line); }

.patreon-link {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Takes the slack, so the footer is pushed down rather than the content stretched. */
main { flex: 1 0 auto; }

/* Every section below the fold, on every page: one measure, one rule above it, one voice. */
.block {
  max-width: 640px;
  margin: 36px auto 0;
  padding: 24px 20px 0;
  border-top: 1px solid var(--line);
  color: var(--muted);
}

.block h2 {
  margin: 0 0 14px;
  font-size: 15px;
  color: var(--text);
}

.block h3 {
  margin: 18px 0 4px;
  font-size: 15px;
  color: var(--text);
}

.block p { margin: 0 0 10px; font-size: 15px; }

/* Two of the sections below the fold answer different halves of the hero: the provenance
   belongs to the painting on the left, the three steps belong to the buttons on the right.
   On a narrow screen there are no halves, so this is transparent and the blocks stay ordinary
   siblings with their own rules above them. */
.pair { display: contents; }
.block-more { margin: 16px 0 0; font-size: 15px; }
.block-more a { color: var(--accent); text-decoration: none; }
.provenance { font-size: 13px; }
.provenance a { color: var(--muted); }

/* The category pages: a list on the catalog, an inline row on a template page. Both are plain
   links in the served markup -- these are the internal links that decide whether a hub gets
   crawled at all, so nothing about them may depend on script running. */
.collections { margin: 0; padding: 0; list-style: none; display: grid; gap: 12px; }
.collections li { font-size: 15px; color: var(--muted); }
.collections a { color: var(--accent); text-decoration: none; font-weight: 600; }
.collections a:hover { text-decoration: underline; }
.collections-inline { margin: 10px 0 0; font-size: 13px; color: var(--muted); }
.collections-inline a { color: var(--accent); text-decoration: none; }
.collections-inline a:hover { text-decoration: underline; }

/* The three steps. Numbered by the markup as well as by the badge: this is an ordered list
   because the order is the whole claim -- there is no fourth step. */
.steps {
  display: grid;
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.steps li {
  display: grid;
  grid-template-columns: 30px 1fr;
  grid-template-rows: auto auto;
  column-gap: 12px;
  align-items: baseline;
}

.step-n {
  grid-row: 1 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 14px;
  font-weight: 700;
}

.steps h3 { margin: 0 0 2px; }
.steps p { margin: 0; }

/* The rest of the catalog: two rows that scroll sideways rather than a grid that grows down
   the page. Eight paintings fit a screen; forty will not, and the section under a painting is
   somewhere to glance, not a second catalog to scroll past. */
.rail { position: relative; }

.thumbs {
  display: grid;
  /* Columns are generated, rows are fixed at two, so the items fill downwards first and the
     overflow goes sideways. minmax lets a short rail stretch to the full width and a long one
     keep each painting legible and scroll instead. */
  grid-auto-flow: column;
  grid-template-rows: repeat(2, auto);
  /* A ceiling as well as a floor: left to stretch, four columns across a 1112px container made
     each painting 266px and the strip taller than the section it belongs to. */
  grid-auto-columns: minmax(140px, 170px);
  gap: 12px;
  overflow-x: auto;
  /* So a sideways swipe on the rail does not turn into the browser's back gesture. */
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* No bar: the cut-off column is the affordance on a phone, and the arrows are on a desktop. */
  scrollbar-width: none;
}

.thumbs::-webkit-scrollbar { display: none; }

.thumb { scroll-snap-align: start; text-decoration: none; color: var(--muted); }

.thumb img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: 10px;
}

/* Overlaid on the ends of the rail rather than placed beside it: beside it they would eat into
   the measure on every screen, including the ones where the rail does not scroll at all. */
.rail-arrow {
  position: absolute;
  top: calc(25% - 18px);
  z-index: 1;
  width: 36px;
  height: 36px;
  padding: 0 0 3px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.rail-arrow:hover { border-color: var(--muted); }

/* A phone scrolls it with a thumb. The arrows are for the pointer that cannot. */
@media (max-width: 759px) {
  .rail-arrow { display: none; }
}
.rail-arrow--prev { left: -8px; }
.rail-arrow--next { right: -8px; }

.thumb span {
  display: block;
  margin-top: 6px;
  font-size: 13px;
  line-height: 1.3;
}

.qa-list { display: grid; gap: 16px; }
.qa h3 { margin: 0 0 4px; }
.qa p { margin: 0; }

.footer {
  margin-top: 40px;
  border-top: 1px solid var(--line);
}

.footer-inner {
  max-width: 640px;
  margin: 0 auto;
  padding: 24px 20px 32px;
  color: var(--muted);
}

.footer-brand { display: inline-block; }
.footer-brand .brand-mark { width: 32px; height: 32px; padding: 2px; }

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin: 12px 0;
  font-size: 15px;
}

.footer-nav a { color: var(--muted); text-decoration: none; }
.footer-nav a:hover { color: var(--text); }
.footer-note { margin: 0; font-size: 13px; }

.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-top: 22px;
}

/* The catalog is a gallery, and a gallery of two columns on a 1500px screen is a column of
   thumbnails with a window's worth of nothing beside it. Only the grid takes the full
   container: the sentences under it keep their own measure, because a line of body text that
   runs 1000px wide is unreadable however much room there is for it. */
.page--wide { max-width: var(--page-w); }

/* Desktop: the same content, side by side. A 560px column on a 1500px screen left the painting
   small and the page mostly empty, and a full-height hero on a landscape window would have put
   the button off the bottom instead of the fold. */
@media (min-width: 760px) {
  .hero {
    display: grid;
    /* Not 1fr 1fr. The right-hand column's content has its own natural width -- the buttons are
       capped and the text wraps long before the column ends -- so equal halves left it visibly
       short of its own right edge and threw the whole composition off centre. Sizing the column
       to what goes in it and centring the pair puts the empty space on both sides instead. */
    grid-template-columns: minmax(0, 620px) minmax(0, 440px);
    justify-content: center;
    align-items: center;
    gap: 44px;
    min-height: 0;
    max-width: var(--page-w);
    margin: 0 auto;
    padding: 32px 24px 0;
  }

  .hero .stage { position: static; }

  .hero .stage > * {
    height: auto;
    aspect-ratio: 1 / 1;
    border: 1px solid var(--line);
    border-radius: var(--radius);
  }

  /* Same 24px as the hero, so the logo starts on the painting's left edge and the menu ends on
     the intro column's right edge rather than a few pixels past both. */
  .bar { padding: 0 24px; }
  .hero .intro { padding: 0; }
  .hero .intro::before { display: none; }
  .hero .preview::after { display: none; }
  .payoff-pair img { max-width: 160px; }
  /* Bound the whole block, not each control: capping the buttons alone left the "or" rule
     running wider than the things it separates. */
  .hero .actions { max-width: 360px; }
  .reassure { text-align: left; }
  /* Shares the hero's container so the body text starts on the same left edge as the painting
     rather than floating in its own centred column. */
  /* Deliberately narrower than the hero and centred, not stretched to match it. Aligning the
     rule with the painting's left edge looked right until you read the section: the text stops
     at its own measure long before the container does, so most of the right-hand half sat
     empty and the whole page read as pushed to the left. */
  .block { padding-left: 24px; padding-right: 24px; }

  .page--wide { padding-left: 24px; padding-right: 24px; }

  /* The sections that are not in the pair take the site container too, so every left edge on
     the page -- logo, painting, provenance, thumbnails, questions -- is the same left edge.
     Centring a 640px measure under a 1160px hero was defensible when the hero was the only wide
     thing on the page; with the pair above them it just read as a column that had slipped. */
  .page--hero > .block {
    max-width: var(--page-w);
    margin: 36px auto 0;
    padding-left: 24px;
    padding-right: 24px;
  }

  /* The container is wide now; the reading is not. A paragraph that runs the full 1112px is
     a paragraph nobody finishes. */
  .block p { max-width: 62ch; }

  /* Given the room, the questions read as four things rather than one list. */
  .qa-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px 44px; }

  /* The footer keeps the same left edge as everything above it. */
  .footer-inner { max-width: var(--page-w); padding-left: 24px; padding-right: 24px; }

  /* auto-fill rather than a fixed count, so the row fills whatever the container turns out to
     be instead of being right at exactly one window width. */
  .page--wide .grid {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 16px;
  }

  .pair {
    display: grid;
    /* The hero's own columns, gap and container, so each block lands under the half it is
       about rather than merely near it. */
    grid-template-columns: minmax(0, 620px) minmax(0, 440px);
    justify-content: center;
    align-items: start;
    gap: 44px;
    max-width: var(--page-w);
    margin: 0 auto;
    padding: 0 24px;
  }

  /* The cell sets the measure now. Left as auto margins they would re-centre inside their own
     column and drift away from the hero edges above them. */
  .pair .block { max-width: none; margin: 36px 0 0; padding-left: 0; padding-right: 0; }

  /* Placed by column rather than by source order: the markup order is the mobile one -- the
     steps first, because on a phone the buttons are what the visitor has just looked at. */
  /* Both on row one explicitly: the steps come first in the markup and take column two, which
     leaves the auto-placement cursor past column one, and the provenance drops to a row of its
     own under the wrong half of the page. */
  .pair .block--about { grid-column: 1; grid-row: 1; }
  .pair .block--how { grid-column: 2; grid-row: 1; }
}

/* Wider columns once there is room for them; the rail scrolls when they stop fitting. */
@media (min-width: 1040px) {
  .thumbs { grid-auto-columns: minmax(170px, 190px); gap: 16px; }
}

.card {
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
}

.card img {
  display: block;
  width: 100%;
  /* The markup carries width/height attributes as a layout hint, and a specified height beats
     `aspect-ratio` outright -- which is why these cards were rendering as tall portraits with
     the square crop never applied. Same defect class as the spinner: an author rule that looks
     right and is silently overridden. */
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

.card span {
  display: block;
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 600;
}
