/* ══════════════════════════════════════════════════════════════
   PAGES — what the sub-pages (Work, Tools, Shop) need on top of
   site.css.

   Kept separate so shared/site.css stays a verbatim copy of the
   original index.html style block.

   The page heading is NOT restyled here: it reuses .hero and
   .hero-headline straight from site.css, so it is the same hero
   the home page has, minus the floating objects. Only the bits
   that don't exist on the home page live below.
════════════════════════════════════════════════════════════════ */

/* The navbar labels are styled in site.css (.nav-title) — every nav
   item is live text, so nothing extra is needed here for them. */

:root {
  /* site.css writes #F4F1ED as a literal in fifteen places — the fill
     behind case-study tiles and media frames. It never got a variable
     name, and site.css is kept as a verbatim copy, so the name is
     given here instead. Same colour, now referable. */
  --color-frame: #F4F1ED;
}

/* Nothing on the site glows permanently. The gradient is a hover
   response everywhere — navbar pills, contact buttons, headings —
   and that stays true for "Get in touch" too. An always-on version
   of it was tried on both the navbar item and the closing heading
   and taken back out. */

/* ── Page heading ──────────────────────────────────────────────
   .hero on the home page is `height: calc(100vh - 160px)` and holds
   the ring-cards. Here the same shell is used with nothing floating
   in it, so the headline sits alone in the open space. */

.hero--page .hero-cards { display: none; }

/* The home page reveals .hero-headline via JS (it adds .ready in sync
   with document.fonts.ready, to avoid a flash of unwrapped text).
   These pages have no such entrance, so the markup carries .ready
   and the headline is simply visible. */

/* ── Section under the heading ─────────────────────────────────*/

.page-section {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 var(--space-lg) var(--space-4xl);
}

/* ── Empty slot ────────────────────────────────────────────────
   A placeholder for work not added yet. It sits in the same two
   offset columns as the real cards (.cards-col from site.css), so
   it takes a card's exact footprint — 400 wide, 480 tall, the body
   height without the footer line — and the zigzag rhythm holds.

   Clearly not a project though: no thumbnail, no tags, a dashed
   edge and one quiet label. */

.slot {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--card-w);
  height: var(--card-h);
  flex-shrink: 0;
  border: 1px dashed var(--color-muted-50);
  background: transparent;
}

.slot-label {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 16px;
  line-height: 120%;
  text-transform: capitalize;
  color: var(--color-muted-50);
}
.slot-label::before { content: '/ '; }

/* ── The typeface itself ───────────────────────────────────────
   Declared up here, above everything that uses it, so a rewrite of
   any one section can't take it out from under the rest. It went
   missing exactly that way once.

   A subset of the shipping font: basic Latin only, no extras, no
   static weights, no licence. 3.7KB against the 153KB that is sold,
   with the wght axis intact so the testers interpolate for real. */
@font-face {
  font-family: 'DotsMono001 Specimen';
  src: url('/shared/fonts/DotsMono001-Specimen.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
}

/* ══════════════════════════════════════════════════════════════
   SHOP GRID

   Modelled on the Articles index at wearemotto.com/blog — a tag
   filter, a search field, and a three-across grid whose cards are
   a tag over a thumbnail with the title beneath.

   Everything else is this site's own: the palette from site.css,
   Switzer at 18px capitalize for titles (matching .card-b-title
   on the work cards), the --space scale, and square corners.

   Filtering hides and shows cards in place rather than reloading,
   which is what makes Motto's version feel instant.
════════════════════════════════════════════════════════════════ */

.shop {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 var(--space-lg) var(--space-4xl);
}

/* ── Filter bar ────────────────────────────────────────────── */

.shop-bar {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding-bottom: var(--space-md);
  margin-bottom: var(--space-xl);
  border-bottom: 1px solid var(--color-muted-15);
}

/* Figma: 4px between tags. */
.shop-tags { display: flex; gap: 4px; flex-wrap: wrap; }

/* Filter tag — Figma "Frame 1707483872".
   Auto-layout row, 10px padding, 5px radius, 32px tall. That height
   is 10 + 12 + 10, where the 12 is the cap-height text box produced
   by leading-trim: both / text-edge: cap. Neither property ships in
   a browser yet, so the pill is given the finished height directly
   and the label is centred in it — same result, no hack needed.

   Idle has no background; the background is the hover state. */
.shop-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  font-family: var(--font-sans);      /* Switzer */
  font-style: normal;
  font-weight: 400;
  font-size: 16px;
  line-height: 120%;
  /* Figma specified -0.01em; the site standardised on 0 with the
     navbar, so Switzer's own spacing is used untouched everywhere. */
  letter-spacing: 0;
  text-transform: capitalize;
  color: var(--color-muted);
  cursor: pointer;
  transition: background var(--dur-sm) ease, color var(--dur-sm) ease;
}

.shop-tag:hover { background: rgba(163, 160, 155, 0.1); }

/* Selected keeps the background but darkens the label, so "selected"
   and "hovered" don't render identically — the spec only defines the
   first two states. */
.shop-tag[aria-pressed="true"] {
  background: rgba(163, 160, 155, 0.1);
  color: var(--color-ink);
}

/* Search — after the field on cursor.com/blog: a fully rounded pill
   with a hairline border and a filled ground, so it reads as somewhere
   to type rather than as another button. Measured there: radius
   effectively infinite, 1px border of the ink at 10%, background
   rgb(242,241,237) — which is within a point of this site's own
   --color-frame (#F4F1ED), so the palette needs nothing new.

   Height stays 32px to sit level with the filter tags. */
.shop-search {
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: var(--color-frame);
  /* No outline — the filled ground is what says "type here". Hover and
     focus deepen that fill instead of drawing an edge. */
  transition: background var(--dur-sm) ease;
}
.shop-search:hover { background: rgba(163, 160, 155, 0.18); }
.shop-search:focus-within { background: rgba(163, 160, 155, 0.28); }

.shop-search svg {
  flex: none;
  display: block;
  color: var(--color-muted);
  transition: color var(--dur-sm) ease;
}
.shop-search:focus-within svg { color: var(--color-ink); }

/* A search input is one of the few elements browsers still style
   themselves — Chrome swaps in a system UI font unless every part of
   the font shorthand is declared, which is why the placeholder was
   coming out in the wrong face. `font` sets all of them at once, and
   `appearance: none` drops the rest of the native treatment. */
.shop-search input {
  -webkit-appearance: none;
  appearance: none;
  width: clamp(96px, 14vw, 170px);
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font: normal 400 16px/120% var(--font-sans);
  letter-spacing: 0;
  color: var(--color-ink);
}
.shop-search input::placeholder { color: var(--color-muted); opacity: 1; }
.shop-search input:focus { outline: none; }
/* The browser's own clear button doesn't match anything here. */
.shop-search input::-webkit-search-cancel-button,
.shop-search input::-webkit-search-decoration { -webkit-appearance: none; display: none; }

/* ── Grid ──────────────────────────────────────────────────── */

.shop-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-xl) var(--space-lg);
}

.shop-card { position: relative; min-width: 0; }
.shop-card[hidden] { display: none; }

/* Whole card clickable, without nesting interactive elements. */
.shop-card-link::after { content: ''; position: absolute; inset: 0; z-index: 3; }

/* Every thumbnail is square, so the grid keeps one rhythm whatever
   a product turns out to be. */
.shop-card-media {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--color-frame);
  overflow: hidden;
}
.shop-card-media > img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* The category is no longer printed on the thumbnail — it lives on
   the product page. It stays in the card's data attributes, which is
   all the filter ever read it from. */

/* ── Live type thumbnail ───────────────────────────────────────
   The typeface card previews itself rather than showing a picture of
   itself: real text in the subset face, walking up the weight axis
   while the pointer is over the card. White on black, matching the
   product artwork. */
.shop-card-media--type {
  background: #000000;
  /* Shared by the sample and the slider, so the slider can be exactly
     as tall as the block of letters without measuring anything. */
  --sample-size: clamp(34px, 5.6vw, 68px);
  --sample-lh: 1.35;
  --sample-lines: 3;
}

.shop-card-type {
  position: absolute;
  inset: 0;
  display: grid;
  /* The block sits in the middle of the thumbnail; the lines inside it
     stay flush left. Extra padding on the left clears the weight
     slider that runs down that edge. */
  place-items: center;
  padding: 0 var(--space-md) 0 calc(var(--space-md) + 20px);
  white-space: pre-line;          /* the sample is three lines */
  text-align: left;
  font-family: 'DotsMono001 Specimen', ui-monospace, monospace;
  font-size: var(--sample-size);
  line-height: var(--sample-lh);
  color: #FFFFFF;
  font-variation-settings: 'wght' 250;
  /* No transition. The weight is being driven directly by the pointer,
     so easing it only puts the letters behind the hand. */
  user-select: none;
}

/* Grain — the same noise the sphere card uses, screened so it reads as
   white speckle over the black.

   The sphere card runs it at 0.12, but that card sits on a light
   ground. Screened onto pure black the noise comes through at its own
   value — mid grey, about 50% — so 0.12 of that lands near RGB 15 and
   is invisible. Hence --grain: it is a knob, not a constant.

   Left unanimated: the sphere card's shifts every 0.35s, which is
   lively on one canvas and restless across a whole grid. */
.shop-card-media {
  --grain: 0.34;
}

.shop-card-media .card-grain {
  z-index: 1;
  opacity: var(--grain);
  mix-blend-mode: screen;
  animation: none;
}

/* ── Weight read-out ───────────────────────────────────────────
   Driven by the hover rather than dragged: the marker travels the
   same 250→800 the letters do, on the same curve, so it reads as the
   cause of what the type is doing. No numbers — the position alone
   says where on the axis you are.

   There is no drawn track — the left edge of the frame is the slider.
   The marker rides it from the very bottom to the very top, following
   the pointer: low is light, high is bold. So the slider isn't
   reporting the hover, it *is* the control. */
.shop-weight {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 2;
  pointer-events: none;
}

.shop-weight-track {
  position: relative;
  width: 1px;
  height: 100%;
  background: none;                /* the frame edge stands in for it */
}

/* Triangle pointing left, at the edge it marks. Flush to the frame. */
.shop-weight-knob {
  position: absolute;
  left: 0;
  bottom: 25%;                     /* resting at 250 on a 100–900 axis */
  width: 0;
  height: 0;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  border-right: 11px solid #FFFFFF;
  transform: translateY(50%);
  /* No transition: the marker is under the hand, and any easing here
     reads as lag rather than smoothing. */
}

/* Placeholder card for a product not released yet. */
.shop-card--soon .shop-card-media {
  border: 1px dashed var(--color-muted-50);
  background: transparent;
}
.shop-card--soon .shop-card-type {
  color: var(--color-muted-50);
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 16px;
  text-transform: capitalize;
}

/* ── Card footer — mirrors .card-b-footer on the work cards ──── */

.shop-card-foot {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

/* What the thing is, under its name — the way a foundry lists a
   release. Carries the description the title used to hold after a
   dash. */
.shop-card-sub {
  margin-top: 2px;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 14px;
  line-height: 120%;
  text-transform: capitalize;
  color: var(--color-muted-50);
}

.shop-card-title {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 18px;
  line-height: 120%;
  text-transform: capitalize;
  color: var(--color-muted);
  margin: 0;
  /* Motto caps titles at two lines so every row stays level. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color var(--dur-sm) ease;
}
.shop-card:hover .shop-card-title { color: var(--color-ink); }

.shop-card-price {
  flex-shrink: 0;
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 18px;
  line-height: 120%;
  color: var(--color-muted-50);
}

.shop-empty {
  grid-column: 1 / -1;
  padding: var(--space-2xl) 0;
  text-align: center;
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 16px;
  color: var(--color-muted-50);
}

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

@media (max-width: 600px) {
  .shop       { padding: 0 var(--space-md) var(--space-3xl); }
  .shop-grid  { grid-template-columns: 1fr; gap: var(--space-lg); }
  .shop-bar   { gap: var(--space-sm); }
  .shop-search { margin-inline-start: 0; width: 100%; }
  .shop-search input { width: 100%; }
}

/* ══════════════════════════════════════════════════════════════
   PRODUCT PAGE

   After the GT Haptik specimen at grillitype.com, in this site's own
   terms: the thumbnail opens, then a field to type in, then every
   weight on its own editable line, then what it is, then the gallery,
   then three ways onward, then the closing line.

   The page is dark throughout — the typeface is white on black, and
   the page is built around it rather than around the site's paper.
════════════════════════════════════════════════════════════════ */

.product {
  max-width: 1440px;
  margin: 0 auto;
  /* The Shop band is hidden on a product, so this is the first thing in
     the document and the fixed 60px navbar would sit on it. Clear the
     navbar first, then give the artwork room to breathe. */
  padding: calc(var(--navbar-h) + clamp(40px, 5vw, 72px)) var(--space-lg) var(--space-2xl);
}

/* ── 1. Opening thumbnail ──────────────────────────────────────*/

/* Figma frame "9": 1600x900, padding 120, gap 120, black.
   Sizes are in cqw against the frame, so it scales as one piece.

   The container is the outer box and the padding lives on an inner
   one, deliberately. cqw resolves against the container's CONTENT
   box, so padding declared on the container itself would shrink the
   very basis it is measured from, and every number would come out
   about 16% short. */
.pd-hero {
  container-type: inline-size;
  position: relative;
  aspect-ratio: 16 / 9;
  background: #000000;
  overflow: hidden;
  color: #FFFFFF;
  font-family: 'DotsMono001 Specimen', ui-monospace, monospace;
  --grain: 0.30;
}

.pd-hero-inner {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  height: 100%;
  gap: 7.5cqw;                   /* 120 / 1600 */
  padding: 7.5cqw;
  box-sizing: border-box;
}

.pd-hero-name {
  flex: none;
  font-size: 11.5cqw;            /* 184 */
  font-variation-settings: 'wght' 400;
  line-height: 140%;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.pd-hero-col {
  flex: 1 1 auto;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  min-width: 0;
}

.pd-hero-desc {
  width: 100%;
  font-size: 1.625cqw;           /* 26 */
  font-variation-settings: 'wght' 300;
  line-height: 170%;
}

.pd-hero-cue {
  width: 100%;
  font-size: 4.3125cqw;          /* 69 */
  font-variation-settings: 'wght' 900;
  line-height: 176%;
  text-align: right;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ── 2. Tester ─────────────────────────────────────────────────*/

.pd-tester {
  margin-top: var(--space-xl);
  border: 1px solid var(--color-muted-15);
}

.pd-tester-bar {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--color-muted-15);
}

.pd-label {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 14px;
  text-transform: capitalize;
  color: var(--color-muted-50);
}
.pd-control { display: flex; align-items: center; gap: 10px; }
.pd-control:last-of-type { margin-inline-start: auto; }
.pd-control input { width: 130px; accent-color: var(--color-ink); }
.pd-control output {
  min-width: 5ch;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--color-muted);
}

.spec-field {
  display: block;
  width: 100%;
  min-height: 240px;
  padding: clamp(20px, 4vw, 48px);
  border: 0;
  background: transparent;
  color: var(--color-ink);
  font-family: 'DotsMono001 Specimen', ui-monospace, monospace;
  font-size: 88px;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.spec-field:focus { outline: none; }

/* ── 3. Every weight, one editable line each ───────────────────
   Grilli's move: the specimen lines are the control. Type into one
   and the sentence changes across the whole family, which is the
   only way to compare weights on your own words. */

.pd-weights { margin-top: var(--space-2xl); }

.pd-weight {
  padding: var(--space-md) 0;
  border-top: 1px solid var(--color-muted-15);
}
.pd-weight:last-child { border-bottom: 1px solid var(--color-muted-15); }

.pd-weight-tag {
  display: block;
  margin-bottom: 6px;
  font-family: var(--font-sans);
  font-size: 13px;
  text-transform: capitalize;
  color: var(--color-muted-50);
}

.pd-weight-line {
  font-family: 'DotsMono001 Specimen', ui-monospace, monospace;
  font-size: clamp(24px, 3.6vw, 46px);
  line-height: 1.25;
  color: var(--color-ink);
  overflow-wrap: anywhere;
}
.pd-weight-line:focus { outline: none; }

/* ── 4. What it is ─────────────────────────────────────────────*/

.pd-info { margin-top: var(--space-2xl); }

/* Text on the left, the numbers on the right, both starting at the same
   line. Two real columns rather than one narrow one and a boxed panel,
   so the middle of the page is no longer empty. */
.pd-info-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: var(--space-xl);
  align-items: start;
  margin-top: var(--space-lg);
}
.pd-body {
  max-width: 48ch;
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  color: var(--color-muted-50);
}
/* The paragraph that carries the idea leads, and the one that qualifies
   it sits back. Without this the section ran 18 / 16 / 14 and read as
   one flat grey block. */
.pd-body--lead {
  max-width: 42ch;
  font-size: 22px;
  line-height: 1.45;
  color: var(--color-muted);
}
.pd-body + .pd-body { margin-top: var(--space-md); }

/* Hairline per row, no container: the rules are the structure. */
.pd-specs { margin: 0; }
.pd-spec {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
  padding: 10px 0;
  border-bottom: 1px solid var(--color-muted-15);
  font-family: var(--font-sans);
  font-size: 14px;
}
.pd-spec:first-child { border-top: 1px solid var(--color-muted-15); }
.pd-spec dt { color: var(--color-muted-50); }
.pd-spec dd { color: var(--color-ink); text-align: right; }

/* Price and button on one full-width line under a rule, so the ask is
   the last thing in the section instead of a card floating beside it. */
.pd-take {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-muted-15);
}
.pd-price {
  font-family: var(--font-serif);
  font-size: clamp(52px, 5.5vw, 76px);
  line-height: 1;
  color: var(--color-muted);
}
.pd-price small {
  margin-inline-start: 0.3em;
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 15px;
  color: var(--color-muted-50);
}
.pd-note {
  flex: 1 0 100%;
  max-width: 58ch;
  margin-inline-start: auto;
  text-align: right;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.5;
  color: var(--color-muted-50);
}

/* ── 5. Gallery ────────────────────────────────────────────────*/

.pd-gallery {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-md);
  margin-top: var(--space-2xl);
}
.pd-shot { margin: 0; background: #000000; overflow: hidden; }
.pd-shot--wide { grid-column: 1 / -1; }
.pd-shot img { width: 100%; height: auto; display: block; }

/* ── 6. Three ways onward ──────────────────────────────────────*/


.pd-more-head {
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--color-muted-15);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 18px;
  text-transform: capitalize;
  color: var(--color-muted);
}
.pd-more-head span { color: var(--color-muted-50); margin-inline-end: 0.4em; }




/* ── 7. The closing line — the move a case study makes ─────────*/

.pd-top {
  display: block;
  margin-top: var(--space-3xl);
  text-align: center;
  font-family: var(--font-serif);
  font-size: clamp(40px, 7vw, 80px);
  line-height: 1.05;
  color: var(--color-muted);
  text-decoration: none;
  transition: color var(--dur-sm) ease;
}
.pd-top:hover { color: var(--color-ink); }

@media (max-width: 900px) {
  .pd-info      { grid-template-columns: 1fr; }
  .spec-field   { font-size: 52px; min-height: 180px; }
}

@media (max-width: 600px) {
  .product     { padding: 0 var(--space-md) var(--space-2xl); }
  .pd-gallery  { grid-template-columns: 1fr; }
  .spec-field  { font-size: 38px; min-height: 150px; }
  .pd-control:last-of-type { margin-inline-start: 0; }
}

/* ── Empty state for Tools / Shop ──────────────────────────────*/

.page-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-4xl) var(--space-lg);
  text-align: center;
}

.page-empty-note {
  max-width: 52ch;
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  color: var(--color-muted);
}

/* ══════════════════════════════════════════════════════════════
   SECONDARY FOOTER

   The page ends on one large line — the same move a case study makes
   with "Back to top": display type you arrive at, not a grid of
   columns to read. The ways to reach her sit under it as a quiet row
   of the site's own pills.

   The home page keeps its full contact section and passes
   SITE_FOOTER_SKIP_CONTACT, which drops the duplicate email and phone.
════════════════════════════════════════════════════════════════ */

.foot2 {
  margin-top: var(--space-3xl);
  padding-top: var(--space-2xl);
  border-top: 1px solid var(--color-muted-15);
  text-align: center;
}

.foot2-cta {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 var(--space-lg);
}

/* Sized like the hero headline, so the bottom of the page answers the
   top of it. */
.foot2-big {
  display: inline-block;
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(48px, 8vw, 88px);
  line-height: 1.05;
  color: var(--color-muted);
  text-decoration: none;
  transition: color var(--dur-sm) ease;
}
.foot2-big:hover { color: var(--color-ink); }

.foot2-sub {
  margin-top: var(--space-sm);
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 24px;
  line-height: 120%;
  letter-spacing: -0.02em;
  color: var(--color-muted);
}

.foot2-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-lg);
  max-width: 1440px;
  margin: var(--space-xl) auto 0;
  padding: 0 var(--space-lg);
}

.foot2-legal {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  max-width: 1440px;
  margin: var(--space-2xl) auto 0;
  padding: 0 var(--space-lg) var(--space-lg);
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--color-muted-50);
}

.foot2-top {
  text-decoration: none;
  color: var(--color-muted-50);
  transition: color var(--dur-sm) ease;
}
.foot2-top:hover { color: var(--color-ink); }

/* The accent strip the home page also ends on. */
.foot2-strip { height: 4px; background: var(--gradient-accent); }

/* ══════════════════════════════════════════════════════════════
   RESPONSIVE
════════════════════════════════════════════════════════════════ */

@media (max-width: 600px) {
  /* site.css collapses .cards-col to `display: contents` here, so the
     cards become direct children of .cards-inner and are sequenced by
     --card-order. Its rule only names .card-b, so the slots need the
     same treatment or they would jump to the end of the list. */
  .slot {
    order: var(--card-order, 0);
    width: 100%;
    height: auto;
    aspect-ratio: 400 / 480;
  }

  .page-section { padding: 0 var(--space-md) var(--space-3xl); }
  .foot2-cta,
  .foot2-row,
  .foot2-legal { padding-inline: var(--space-md); }
  .foot2-row   { gap: var(--space-md); }
  .foot2-sub   { font-size: 18px; }
  .slot { min-height: 180px; }
}

/* ══════════════════════════════════════════════════════════════
   DARK SHOP — a trial, not a decision

   Scoped to html[data-theme="dark"] and only ever set on the shop
   page, so nothing else on the site can be affected. Remove this
   block and the toggle in shop/index.html to take it back out.

   The palette is turned rather than replaced: the same warm greys,
   swapped end for end, so it still reads as this site and not as a
   generic dark theme.
════════════════════════════════════════════════════════════════ */

html[data-theme="dark"] {
  --color-bg:        #1A1917;   /* warm near-black, not a blue-black */
  --color-surface:   #232120;
  --color-frame:     #262422;   /* the ground behind a thumbnail     */
  --color-muted:     #908C86;   /* body text, readable on the dark   */
  --color-ink:       #F5F3F0;   /* emphasis — the light end now      */
  --color-ink-muted: #CFCAC4;
  --color-white:     #2E2B29;   /* "raised" surfaces, e.g. on focus  */
}

html[data-theme="dark"] body { background: var(--color-bg); }

/* The two hairlines were written against the light ground. */
html[data-theme="dark"] .shop-search:hover { background: rgba(245, 243, 240, 0.10); }
html[data-theme="dark"] .shop-search:focus-within { background: rgba(245, 243, 240, 0.16); }
html[data-theme="dark"] .shop-tag:hover,
html[data-theme="dark"] .shop-tag[aria-pressed="true"] { background: rgba(245, 243, 240, 0.08); }

/* No outline on the thumbnails. The black sits directly on the dark
   ground and the frame reads by its contents rather than by an edge. */

/* The cursor's SVGs are drawn in the ink colour, which disappears on
   the dark ground. Same override .cursor-tooltip--light already uses
   for dark cards, applied to the whole cursor here. */
html[data-theme="dark"] #cursor svg path   { stroke: #F5F3F0 !important; }
html[data-theme="dark"] #cursor svg circle,
html[data-theme="dark"] #cursor svg polygon,
html[data-theme="dark"] #cursor svg text   { fill: #F5F3F0 !important; }

/* ── Buttons on the dark ground ────────────────────────────────
   The accent gradient is built for the beige page; against the dark
   it turns into a colour cast that fights everything around it. The
   glow goes white here, and the label goes dark to sit on it. */
/* The glow is the only thing drawn — spread wider than the pill and
   blurred hard, so it reads as a soft mass of light rather than a
   shape with an edge. Blurring an opaque white leaves the middle
   fully opaque, which is what the dark label sits on; the falloff
   happens outside the text. */
html[data-theme="dark"] .contact-btn::before,
html[data-theme="dark"] .nav-link::before,
html[data-theme="dark"] .nav-logo::before {
  background: #FFFFFF;
  inset: -5px -14px;
  filter: blur(16px);
}

/* No background of its own — a filled pill on top of the glow is
   exactly the hard edge that gave it away. */
html[data-theme="dark"] .contact-btn:hover,
html[data-theme="dark"] .nav-link:hover,
html[data-theme="dark"] .nav-logo:hover {
  background: transparent;
  color: #1A1917;
}

html[data-theme="dark"] .contact-btn:hover .btn-label,
html[data-theme="dark"] .contact-btn:hover .btn-slash,
html[data-theme="dark"] .nav-link:hover .nav-title,
html[data-theme="dark"] .nav-logo:hover .nav-title {
  color: #1A1917;
  opacity: 1;
}

/* ── The toggle itself ─────────────────────────────────────────
   Deliberately plain: it is here to compare the two, not to ship. */
.theme-toggle {
  position: fixed;
  right: var(--space-md);
  bottom: var(--space-md);
  z-index: 1000;
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--color-muted-50);
  border-radius: 999px;
  background: var(--color-bg);
  font: normal 400 14px/120% var(--font-sans);
  color: var(--color-muted);
  cursor: pointer;
  transition: color var(--dur-sm) ease, border-color var(--dur-sm) ease;
}
.theme-toggle:hover { color: var(--color-ink); border-color: var(--color-ink); }

/* ══════════════════════════════════════════════════════════════
   MARQUEE TITLE

   A slim band that names the page, in place of the full-height hero.
   The unit repeats and drifts right to left, so the strip reads as one
   continuous line rather than a heading sitting in a box.

   The unit is the Figma "Shop/" frame: the word, then three
   overlapping rings. Every measurement below is written in em against
   the 96px the spec was drawn at, so the whole assembly scales with
   the type instead of coming apart at other sizes:

     word            161   × 69      96px Instrument Serif, -0.02em
     ring            52.97 × 74.37   4px border, no fill
     ring overlap    24                → frame 110.9 wide
     gap             8
     padding-right   16
     unit            295.9 × 74.37

   The track holds two identical sets and travels -50%, which lands the
   second set exactly where the first began. The item count can change;
   the -50% cannot.
════════════════════════════════════════════════════════════════ */

.mtitle {
  position: relative;
  /* The navbar is fixed and 60px tall and this sits at the very top of
     the document, so its height is cleared before any breathing room is
     measured — otherwise the artwork sits under the nav. */
  padding-top: calc(var(--navbar-h) + clamp(24px, 3vw, 48px));
  padding-bottom: clamp(16px, 2vw, 32px);
  padding-inline: var(--gutter, clamp(20px, 5vw, 60px));
  user-select: none;
}

/* Big, but never touching the edges — the products below are held in a
   column and a hero running to the bleed would not belong to them.

   The height is reserved by aspect-ratio rather than left to the file,
   so nothing under it jumps when 240KB of animation finishes arriving. */
.mtitle-gif {
  display: block;
  width: 100%;
  max-width: 1200px;
  margin-inline: auto;
  aspect-ratio: 2137 / 1206;
  height: auto;
  /* The artwork is drawn small inside a wide frame; letting it fill the
     box would blow it up past the headline sizes the rest of the page
     uses. Contained, it keeps the proportion it was drawn at. */
  object-fit: contain;
}

/* A GIF plays whatever anyone's settings say, so the still frame is the
   only way to honour the preference. The first frame is the word
   resolved, which is the one worth holding. */
@media (prefers-reduced-motion: reduce) {
  .mtitle-gif { content: url("/shop/assets/shop-hero-still.png"); }
}

/* ══════════════════════════════════════════════════════════════
   DARK SHOP — a trial, not a decision

   Scoped to html[data-theme="dark"] and only ever set on the shop
   page, so nothing else on the site can be affected. Remove this
   block and the toggle in shop/index.html to take it back out.

   The palette is turned rather than replaced: the same warm greys,
   swapped end for end, so it still reads as this site and not as a
   generic dark theme.
════════════════════════════════════════════════════════════════ */

html[data-theme="dark"] {
  --color-bg:        #1A1917;   /* warm near-black, not a blue-black */
  --color-surface:   #232120;
  --color-frame:     #262422;   /* the ground behind a thumbnail     */
  --color-muted:     #908C86;   /* body text, readable on the dark   */
  --color-ink:       #F5F3F0;   /* emphasis — the light end now      */
  --color-ink-muted: #CFCAC4;
  --color-white:     #2E2B29;   /* "raised" surfaces, e.g. on focus  */
}

html[data-theme="dark"] body { background: var(--color-bg); }

/* The two hairlines were written against the light ground. */
html[data-theme="dark"] .shop-search:hover { background: rgba(245, 243, 240, 0.10); }
html[data-theme="dark"] .shop-search:focus-within { background: rgba(245, 243, 240, 0.16); }
html[data-theme="dark"] .shop-tag:hover,
html[data-theme="dark"] .shop-tag[aria-pressed="true"] { background: rgba(245, 243, 240, 0.08); }

/* No outline on the thumbnails. The black sits directly on the dark
   ground and the frame reads by its contents rather than by an edge. */

/* The cursor's SVGs are drawn in the ink colour, which disappears on
   the dark ground. Same override .cursor-tooltip--light already uses
   for dark cards, applied to the whole cursor here. */
html[data-theme="dark"] #cursor svg path   { stroke: #F5F3F0 !important; }
html[data-theme="dark"] #cursor svg circle,
html[data-theme="dark"] #cursor svg polygon,
html[data-theme="dark"] #cursor svg text   { fill: #F5F3F0 !important; }

/* ── Buttons on the dark ground ────────────────────────────────
   The accent gradient is built for the beige page; against the dark
   it turns into a colour cast that fights everything around it. The
   glow goes white here, and the label goes dark to sit on it. */
/* The glow is the only thing drawn — spread wider than the pill and
   blurred hard, so it reads as a soft mass of light rather than a
   shape with an edge. Blurring an opaque white leaves the middle
   fully opaque, which is what the dark label sits on; the falloff
   happens outside the text. */
html[data-theme="dark"] .contact-btn::before,
html[data-theme="dark"] .nav-link::before,
html[data-theme="dark"] .nav-logo::before {
  background: #FFFFFF;
  inset: -5px -14px;
  filter: blur(16px);
}

/* No background of its own — a filled pill on top of the glow is
   exactly the hard edge that gave it away. */
html[data-theme="dark"] .contact-btn:hover,
html[data-theme="dark"] .nav-link:hover,
html[data-theme="dark"] .nav-logo:hover {
  background: transparent;
  color: #1A1917;
}

html[data-theme="dark"] .contact-btn:hover .btn-label,
html[data-theme="dark"] .contact-btn:hover .btn-slash,
html[data-theme="dark"] .nav-link:hover .nav-title,
html[data-theme="dark"] .nav-logo:hover .nav-title {
  color: #1A1917;
  opacity: 1;
}

/* ── The toggle itself ─────────────────────────────────────────
   Deliberately plain: it is here to compare the two, not to ship. */
.theme-toggle {
  position: fixed;
  right: var(--space-md);
  bottom: var(--space-md);
  z-index: 1000;
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--color-muted-50);
  border-radius: 999px;
  background: var(--color-bg);
  font: normal 400 14px/120% var(--font-sans);
  color: var(--color-muted);
  cursor: pointer;
  transition: color var(--dur-sm) ease, border-color var(--dur-sm) ease;
}
.theme-toggle:hover { color: var(--color-ink); border-color: var(--color-ink); }


/* ══════════════════════════════════════════════════════════════
   DITHERED GLOW — shop page only

   The glow keeps its gradient; what changes is that the falloff is
   drawn as a field of hard dots whose density carries it, instead of
   as smooth transparency. The work happens in the SVG filter defined
   at the top of shop/index.html — a CSS mask can't threshold, and a
   threshold is the whole trick.

   The blur lives in the filter now, so there is none here.

   Scoped to .page-shop; widen the selector to make it site-wide.
════════════════════════════════════════════════════════════════ */

.page-shop .contact-btn::before,
.page-shop .nav-link::before,
.page-shop .nav-logo::before {
  filter: url(#ditherGlow);
  inset: -8px -18px;
}

/* ── Hero artwork ──────────────────────────────────────────────
   The exported SVG rather than live text: it is the artwork the
   product is sold under, and vector keeps it crisp at any size. */
.pd-hero-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ══════════════════════════════════════════════════════════════
   DRAW ONE — the 5×7 editor

   The typeface is drawn on a grid of circles, so the tool for
   proposing a character is that same grid. Each dot is a real
   button, so it is focusable and works from the keyboard, not only
   under a mouse.
════════════════════════════════════════════════════════════════ */

.pd-draw { margin-top: var(--space-3xl); }

.pd-draw-intro {
  max-width: 56ch;
  margin-top: var(--space-md);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  color: var(--color-muted);
}

.pd-draw-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-xl);
  align-items: start;
  margin-top: var(--space-lg);
}

.pd-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: clamp(6px, 1vw, 10px);
  padding: clamp(16px, 2.5vw, 28px);
  background: #000000;
  touch-action: none;              /* drag paints instead of scrolling */
}

.pd-dot {
  width: clamp(26px, 4.4vw, 44px);
  aspect-ratio: 1;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  cursor: pointer;
  transition: background var(--dur-sm) ease, transform var(--dur-sm) ease;
}
.pd-dot:hover { background: rgba(255, 255, 255, 0.32); }
.pd-dot[aria-pressed="true"] { background: #FFFFFF; }
.pd-dot:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 3px; }

.pd-draw-form { display: flex; flex-direction: column; gap: var(--space-md); }

.pd-field { display: flex; flex-direction: column; gap: 6px; }
.pd-field input {
  -webkit-appearance: none;
  appearance: none;
  height: 40px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: var(--color-frame);
  font: normal 400 16px/120% var(--font-sans);
  color: var(--color-ink);
  transition: background var(--dur-sm) ease;
}
.pd-field input::placeholder { color: var(--color-muted); opacity: 1; }
.pd-field input:focus { outline: none; background: rgba(245, 243, 240, 0.16); }

.pd-draw-actions {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  margin-top: var(--space-xs);
}

.pd-clear {
  border: 0;
  background: none;
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 14px;
  color: var(--color-muted-50);
  cursor: pointer;
  transition: color var(--dur-sm) ease;
}
.pd-clear:hover { color: var(--color-ink); }

@media (max-width: 900px) {
  .pd-draw-row { grid-template-columns: 1fr; gap: var(--space-lg); }
  .pd-grid { justify-self: start; }
}

/* ── Tester controls ───────────────────────────────────────────
   The two sliders sit together rather than pinned to opposite ends
   of the bar, so they read as one set of controls. */
.pd-tester-bar { gap: var(--space-md); }
.pd-control:last-of-type { margin-inline-start: 0; }
.pd-tester-bar > .pd-label:first-child { margin-inline-end: auto; }

/* Uppercase on/off, in the same pill language as the filter tags. */
.pd-toggle {
  height: 30px;
  padding: 0 12px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  font: normal 400 14px/120% var(--font-sans);
  text-transform: capitalize;
  color: var(--color-muted);
  cursor: pointer;
  transition: background var(--dur-sm) ease, color var(--dur-sm) ease;
}
.pd-toggle:hover { background: rgba(245, 243, 240, 0.08); }
.pd-toggle[aria-pressed="true"] {
  background: rgba(245, 243, 240, 0.14);
  color: var(--color-ink);
}

/* Share, quiet, beside the label. */
.pd-share {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  font: italic 400 14px/120% var(--font-sans);
  text-transform: capitalize;
  color: var(--color-muted-50);
  cursor: pointer;
  transition: background var(--dur-sm) ease, color var(--dur-sm) ease;
}
.pd-share:hover { background: rgba(245, 243, 240, 0.08); color: var(--color-ink); }

/* ── Floating call to action ───────────────────────────────────
   Light on the dark ground with a shadow under it, because at the
   point it appears nothing else says "this is a button". */
.pd-float {
  position: fixed;
  right: var(--space-lg);
  bottom: var(--space-lg);
  z-index: 900;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 22px;
  border-radius: 999px;
  background: #F5F3F0;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  font: normal 400 16px/1 var(--font-sans);
  text-transform: capitalize;
  color: #1A1917;
  text-decoration: none;
  transition: transform var(--dur-md) var(--ease-out, cubic-bezier(0.22,1,0.36,1)),
              box-shadow var(--dur-md) ease;
}
.pd-float:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
}
.pd-float-price {
  padding-inline-start: 10px;
  border-inline-start: 1px solid rgba(26, 25, 23, 0.22);
  color: rgba(26, 25, 23, 0.65);
}

/* ── Confirmation ──────────────────────────────────────────────*/

/* Matches the unlock gate in site.css, which is this site's one
   existing dialog: the page fades back into its own colour rather than
   dimming to black, an 18px card, a serif title and an italic line
   under it. Only the ground colour differs, because this page is dark. */




/* ── Discover more, with thumbnails ────────────────────────────*/


/* Submit is a real button element, so the shared pill needs its
   native chrome cleared before it looks like the others. */
/* A <button> carrying .contact-btn needs its native chrome cleared,
   but NOT its font reset: 'font: inherit' is a shorthand, so it was
   overwriting the family, style and size .contact-btn had just set and
   the label came out in the wrong face. Clear only what the browser
   adds. */
button.contact-btn {
  border: 0;
  background: none;
  cursor: pointer;
}



@media (max-width: 600px) {
  .pd-float { right: var(--space-md); bottom: var(--space-md); padding: 12px 18px; font-size: 15px; }
}

/* The grain layer is shared, so every surface that wants it has to be
   named — it was only reaching the shop cards. */
.pd-hero .card-grain {
  z-index: 1;
  opacity: var(--grain, 0.30);
  mix-blend-mode: screen;
  animation: none;
}

/* Sliders sit together. The label holds the left, the controls group
   to its right, and Caps rides with them. */
.pd-tester-bar > .pd-label:first-child { margin-inline-end: var(--space-md); }

.pd-control { gap: 8px; }
.pd-control + .pd-control { margin-inline-start: var(--space-md); }

.mtitle[hidden] { display: none; }

/* ── Share, at page level ──────────────────────────────────────
   It shares the product, not the tester, so it sits above the frame
   rather than inside a control bar for something else. */
.pd-topbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-sm);
}

/* ── The buy button, in the panel ──────────────────────────────
   A standing 5% white fill so it reads as a button before anyone
   hovers it. On this ground an unfilled pill just looks like text. */
.pd-buy .contact-btn {
  background: rgba(255, 255, 255, 0.05);
  transition: background var(--dur-sm) ease, color var(--dur-sm) ease;
}

/* ── Floating call to action ───────────────────────────────────
   Squared off with a soft corner rather than a pill, and taller, so
   it carries weight against the page. No price: the panel states it,
   and repeating it here made the button read as a receipt. */
.pd-float {
  padding: 20px 28px;
  border-radius: 8px;
  font-size: 17px;
}
.pd-float-price { display: none; }

@media (max-width: 600px) {
  .pd-float { padding: 16px 22px; font-size: 15px; }
}

/* FunType's thumbnail is a live preview, the same iframe the work card
   runs. Inert, so the click still lands on the card around it. */

/* The editor sits in its own frame rather than straight on the page,
   the way the tester above it does — it is a tool, not a paragraph. */
.pd-draw {
  padding: var(--space-lg);
  border: 1px solid var(--color-muted-15);
}
.pd-draw .pd-more-head { border-bottom: 0; padding-bottom: 0; }
.pd-draw-intro { margin-top: var(--space-xs); }

/* ── Share ─────────────────────────────────────────────────────
   Not a new button: the 32x32 icon button a project overlay already
   carries, restated here so it survives on the dark ground. Same box,
   same glyph, same quiet-to-ink hover. */
.pd-share {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: var(--color-muted);
  cursor: pointer;
  transition: color var(--dur-sm) ease;
}
.pd-share svg { display: block; flex-shrink: 0; }
.pd-share:hover,
.pd-share.is-copied { background: none; color: var(--color-ink); }

/* Caps is a .btn-flat now; only the pressed state is its own. Stated
   after that block so it wins, and with the class doubled so it also
   beats .btn-flat:hover while the toggle is on. */
.btn-flat.pd-toggle[aria-pressed="true"],
.btn-flat.pd-toggle[aria-pressed="true"]:hover {
  background: rgba(255, 255, 255, 0.24);
  color: var(--color-ink);
}

/* ── The two sliders ───────────────────────────────────────────
   The same marker the shop thumbnail uses: a bare line, and a solid
   triangle whose tip touches it. Native chrome off on both engines,
   so the browser's own pill doesn't come back. */
.pd-control input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 130px;
  height: 18px;
  margin: 0;
  background: none;
  accent-color: auto;
  cursor: pointer;
}
.pd-control input[type="range"]::-webkit-slider-runnable-track {
  height: 1px;
  border: 0;
  background: var(--color-muted-50);
}
.pd-control input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 13px;
  height: 9px;
  border: 0;
  /* Relative to the track, so the flat top is 9px up and the tip
     lands exactly on the line. */
  margin-top: -9px;
  background: var(--color-ink);
  clip-path: polygon(50% 100%, 0 0, 100% 0);
}
.pd-control input[type="range"]::-moz-range-track {
  height: 1px;
  border: 0;
  background: var(--color-muted-50);
}
.pd-control input[type="range"]::-moz-range-thumb {
  width: 13px;
  height: 9px;
  border: 0;
  border-radius: 0;
  background: var(--color-ink);
  clip-path: polygon(50% 100%, 0 0, 100% 0);
  transform: translateY(-5px);
}

/* ══════════════════════════════════════════════════════════════
   BUTTON — the flat one

   Second button in the shared language, alongside the outlined
   .contact-btn that site.css owns. This one carries no border: a
   panel of white at 10%, a 4px corner, and muted text. Quiet enough
   to repeat, and it never draws an outline the eye has to read
   around.

   Figma: 28 tall, padding 8/16, gap 4, radius 4, 16px/120%,
   -0.01em, capitalize, #A3A09B.

   The spec sets leading-trim: both / text-edge: cap, which no
   browser implements. 8 + 12 (cap height) + 8 = 28, so the height is
   stated outright and the label centred in it — same result, and it
   holds whatever the font's own line box happens to be.
   ══════════════════════════════════════════════════════════════ */
.btn-flat {
  display: inline-flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: 4px;
  height: 28px;
  padding: 0 16px;
  border: 0;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.1);
  font: normal 400 16px/120% var(--font-sans);
  letter-spacing: -0.01em;
  text-transform: capitalize;
  /* The spec value, stated outright: --color-muted is remapped darker
     under the dark theme, which is the wrong direction on this ground. */
  color: #A3A09B;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-sm) ease, color var(--dur-sm) ease;
}
.btn-flat:hover { background: rgba(255, 255, 255, 0.18); color: var(--color-ink); }
.btn-flat:active { background: rgba(255, 255, 255, 0.24); }

/* Same button, scaled whole, for the one place on a page that is the
   point of the page. Every number doubled except the corner, which
   would stop reading as the same shape. */
.btn-flat--lg {
  height: 56px;
  padding: 0 32px;
  gap: 8px;
  border-radius: 8px;
  font-size: 20px;
}

/* ── Why the arrow was dropping to its own line ─────────────────
   Everything in the frame is in cqw, so the widths hold their ratio
   at any size — and at every size the row came out about a pixel
   too wide for "Take a look ↓" to fit. The extra pixel is CSS's:
   letter-spacing is added AFTER the last glyph of a line, which
   Figma does not do. Cancelling that trailing space on both spaced
   lines gives the width back, and nowrap makes sure the cue is
   treated as the one unit it is. */
.pd-hero-cue,
.pd-hero-name {
  margin-inline-end: -0.04em;
}
.pd-hero-cue { white-space: nowrap; }

/* Sphere's wordmark, sized against the tile the way the home-page card
   sizes it against its own. Inert, like the media under it. */

/* ── Selection ─────────────────────────────────────────────────
   The browser's blue belongs to the browser. This is the pink the
   site already uses on the sphere, lightened so dark ink sits on it
   comfortably. ::selection does not inherit, so the ::-moz- twin is
   spelled out rather than grouped: one unknown pseudo-element in a
   selector list drops the whole rule. */
::selection {
  background: #FFB9F7;
  color: #1A1917;
  text-shadow: none;
}
::-moz-selection {
  background: #FFB9F7;
  color: #1A1917;
  text-shadow: none;
}

/* ══════════════════════════════════════════════════════════════
   NOTIFICATION CARD

   Figma: 354.66 x 153, padding 8, rgba(46,46,46,.95) behind a
   rgba(89,89,89,.5) hairline, 12px corner. Inside it a 110.66-wide
   #373737 panel at 8px holding the glyph, and a 226-wide column with
   24 padding and 16 gap.

   leading-trim / text-edge are draft properties no browser implements,
   so the two text blocks carry the heights the spec gives them (9 and
   62) with the line box centred inside — same result, and it survives
   whatever line box the font brings.
   ══════════════════════════════════════════════════════════════ */
.pd-toast {
  /* Centred on the screen, not tucked in a corner: it is the answer to
     something you just did, so it belongs where you were looking. */
  position: fixed;
  left: 50%;
  top: 50%;
  z-index: 1200;
  box-sizing: border-box;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  padding: 8px;
  width: 354.66px;
  height: 153px;
  background: rgba(46, 46, 46, 0.95);
  border: 1px solid rgba(89, 89, 89, 0.5);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  cursor: pointer;
  /* Enters from below and settles. The transition is on the class, so
     nothing animates while it is still hidden. */
  opacity: 0;
  /* The centring translate has to be carried through both states, or
     the card would slide out of centre as it settles. */
  transform: translate(-50%, -50%) scale(0.97);
  transition: opacity var(--dur-md) ease, transform var(--dur-md) var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1));
}
.pd-toast.is-in { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.pd-toast[hidden] { display: none; }

/* Left panel: the glyph that was sent. */
.pd-toast-glyph {
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: stretch;
  flex: none;
  box-sizing: border-box;
  width: 110.66px;
  padding: 24px;
  background: #373737;
  border-radius: 8px;
}

/* 5 across, 7 down, filling the 62.66 x 87 the spec leaves inside the
   padding. The cells are all but square, so the nodes come out round
   and near-touching — the same construction as the typeface. */
.pd-toast-dots {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: repeat(7, 1fr);
  width: 62.66px;
  height: 87px;
}
.pd-toast-dots i {
  border-radius: 50%;
  background: transparent;
}
.pd-toast-dots i.on { background: #F8F6F3; }

.pd-toast-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  flex: none;
  box-sizing: border-box;
  width: 226px;
  align-self: stretch;
  padding: 24px;
  gap: 16px;
}
.pd-toast-eyebrow {
  display: flex;
  align-items: center;
  height: 9px;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 12px;
  line-height: 140%;
  text-transform: capitalize;
  color: #A3A09B;
}
.pd-toast-line {
  display: flex;
  align-items: center;
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 36px;
  line-height: 100%;
  color: #A3A09B;
}

/* On a narrow screen the fixed width would hang off the edge. */
@media (max-width: 420px) {
  .pd-toast { width: calc(100vw - 24px); }
  .pd-toast-text { width: auto; flex: 1; }
}

/* ══════════════════════════════════════════════════════════════
   REVIEW — /shop/?p=dots-mono-001&review=<key>

   The product page's own language — its ground, its hairlines, the
   flat button — set as a table, because this is read as a list of
   decisions rather than browsed as a gallery. The drawing is redrawn
   the way the notification card draws it: 5 across, 7 down, on the
   card's tile. The colours of that tile are stated outright for the
   same reason they are there: --color-muted goes the wrong way under
   the dark theme.
   ══════════════════════════════════════════════════════════════ */
.rv-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--color-muted-15);
}
.rv-title {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(36px, 5vw, 56px);
  line-height: 1;
  color: var(--color-ink);
}
.rv-count {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--color-muted-50);
}
.rv-note {
  max-width: 60ch;
  margin: var(--space-md) 0 var(--space-lg);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--color-muted-50);
}

.rv-tablewrap { overflow-x: auto; }
.rv-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--color-muted);
}
.rv-table th {
  padding: 0 12px 10px;
  border-bottom: 1px solid var(--color-muted-15);
  font-weight: 400;
  font-size: 12px;
  text-align: left;
  text-transform: capitalize;
  white-space: nowrap;
  color: var(--color-muted-50);
}
.rv-table td {
  padding: 14px 12px;
  border-bottom: 1px solid var(--color-muted-15);
  vertical-align: middle;
}
.rv-table th:first-child, .rv-table td:first-child { padding-left: 0; }
.rv-table th:last-child,  .rv-table td:last-child  { padding-right: 0; }
.rv-table[hidden] { display: none; }

.rv-art {
  display: inline-grid;
  place-items: center;
  padding: 10px;
  background: #373737;
  border-radius: 8px;
}
.rv-dots {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: repeat(7, 1fr);
  width: 50px;
  height: 70px;
}
.rv-dots i { border-radius: 50%; background: rgba(255, 255, 255, 0.06); }
.rv-dots i.on { background: #F8F6F3; }

.rv-char {
  display: block;
  font-family: var(--font-serif);
  font-size: 30px;
  line-height: 1;
  color: var(--color-ink);
  word-break: break-word;
}
.rv-kind {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  color: var(--color-muted-50);
}
.rv-when { font-size: 13px; white-space: nowrap; color: var(--color-muted-50); }

.rv-empty {
  margin: var(--space-xl) 0;
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.3;
  color: var(--color-muted-50);
}
.rv-empty.is-err { color: #FFB9F7; }
.rv-empty[hidden] { display: none; }

/* Narrow: each row becomes a small card, the column names travelling
   with their cells. */
@media (max-width: 760px) {
  .rv-table thead { display: none; }
  .rv-table, .rv-table tbody, .rv-table tr, .rv-table td { display: block; }
  .rv-table tr { padding: var(--space-md) 0; border-bottom: 1px solid var(--color-muted-15); }
  .rv-table td { padding: 6px 0; border: 0; }
  .rv-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    color: var(--color-muted-50);
  }
}
