:root {
  /* ---- Overprint identity (see vault "Frontend-design review (whole site)") ----
     The product is screen-PRINTED; two overlapping spot inks make a third colour —
     which is the mashup mechanic. Cyan = inspiration A, magenta = inspiration B,
     violet = their overprint (the result / primary action). */
  --ink-paper: #0e0e13;       /* deep ink "paper" background */
  --ink-paper-soft: #16161f;
  --ink-cyan: #11c6d6;        /* spot ink A */
  --ink-magenta: #ff2e88;     /* spot ink B */
  --ink-violet: #6b3ce0;      /* the overprint where A + B overlap */
  --ink: #f4f2ef;             /* near-paper white text */
  --ink-haze: #a6a2b4;        /* muted / utility text */
  --paper-light: #f3eee4;     /* used only on tiny "garment label" surfaces */

  --font-display: "Bricolage Grotesque", system-ui, sans-serif;  /* readable display */
  --font-body: "Space Grotesk", system-ui, sans-serif;
  --font-mono: "Space Mono", ui-monospace, "Courier New", monospace;  /* print-shop data */

  /* Legacy aliases — existing components reference these until reworked. */
  --bg: var(--ink-paper);
  --bg-soft: var(--ink-paper-soft);
  --card: #1b1b27;
  --ink-soft: var(--ink-haze);
  --accent: var(--ink-magenta);
  --accent-2: var(--ink-violet);
  --accent-3: var(--ink-cyan);
  --ring: rgba(107, 60, 224, 0.5);
  --radius: 14px;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background:
    radial-gradient(1200px 600px at 12% -10%, rgba(17, 198, 214, 0.12), transparent 60%),
    radial-gradient(900px 500px at 100% 0%, rgba(255, 46, 136, 0.12), transparent 55%),
    var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ---------- Header / footer ---------- */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem clamp(1rem, 5vw, 4rem);
}
.brand {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.02em;
}
.brand-mark { color: var(--accent-3); }
.brand-accent { color: var(--accent); }
.site-nav a {
  color: var(--ink-soft);
  text-decoration: none;
  margin-left: 1.4rem;
  font-weight: 500;
  font-size: 0.95rem;
  transition: color 0.2s;
}
.site-nav a:hover { color: var(--ink); }

.site-main {
  flex: 1;
  width: min(1040px, 92vw);
  margin: 0 auto;
  padding: clamp(1rem, 4vw, 3rem) 0 4rem;
}

.site-footer {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 1.2rem clamp(1rem, 5vw, 4rem);
  color: var(--ink-soft);
  font-size: 0.82rem;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.site-footer .modes b { color: var(--accent-3); text-transform: uppercase; font-size: 0.72rem; }

/* Public footer — the policy links (ISP-022). Sits above the dev-state footer and,
   unlike it, is always visible. `margin-top: auto` pushes it to the bottom of the
   flex-column body so a short page (a 404, an empty cart) doesn't leave it floating. */
.public-footer {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.6rem 1.4rem;
  padding: 1.6rem clamp(1rem, 5vw, 4rem);
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  font-size: 0.82rem;
  color: var(--ink-haze);
}
.public-footer-links { display: flex; flex-wrap: wrap; gap: 0.35rem 1.1rem; }
.public-footer-links a {
  color: var(--ink-haze);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.public-footer-links a:hover,
.public-footer-links a:focus-visible { color: var(--ink); border-bottom-color: var(--ink-cyan); }
.public-footer-legal { margin: 0; }

/* ---------- Policy pages (Terms / Privacy / Returns / Contact) ----------
   A deliberately plain reading column: these are read, not browsed. The only brand
   cues are the display face on headings and a cyan rule under the title, so the
   page stays legible at length instead of competing with the store. */
.legal {
  max-width: 46rem;
  margin: 0 auto;
  padding: clamp(1rem, 4vw, 2.5rem) 0 clamp(2rem, 6vw, 4rem);
}
.legal-head { margin-bottom: 2.4rem; }
.legal-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-cyan);
  margin: 0 0 0.5rem;
}
.legal h1 {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 5vw, 2.7rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0 0 0.9rem;
  padding-bottom: 0.9rem;
  border-bottom: 2px solid var(--ink-violet);
}
.legal-summary { font-size: 1.05rem; line-height: 1.6; color: var(--ink); margin: 0 0 0.7rem; }
.legal-updated {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--ink-haze);
  margin: 0;
}
.legal-section { margin-bottom: 2.1rem; }
.legal-section h2 {
  font-family: var(--font-display);
  font-size: 1.18rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 0.7rem;
}
.legal-section p { line-height: 1.65; color: var(--ink-haze); margin: 0 0 0.9rem; }
.legal-section ul { margin: 0 0 1rem; padding-left: 1.15rem; }
.legal-section li { line-height: 1.6; color: var(--ink-haze); margin-bottom: 0.5rem; }
.legal-section li::marker { color: var(--ink-magenta); }
.legal a { color: var(--ink-cyan); }
.legal-foot {
  margin-top: 2.6rem;
  padding-top: 1.4rem;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.legal-foot p { color: var(--ink-haze); margin: 0 0 0.7rem; }
.legal-other { display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem; font-size: 0.9rem; }
.legal-other a { text-decoration: none; }
.legal-other a:hover { text-decoration: underline; }

/* ---------- Hero ----------
   A true hero: ONE gradient panel, two zones. LEFT column = copy pinned to the top +
   the primary CTA pinned to the bottom (flex space-between). RIGHT column = the
   "A × B → tee" example, vertically centred, with the chips up against the result tee.
   Re-stacks to one centred column on mobile (see media query at the end). */
.hero {
  --hero-panel: #15111f;  /* baked into the tee PNGs by scripts/hero_assets.py */
  position: relative;
  /* Full-bleed: break out of .site-main's centred column to span the whole viewport.
     The negative top margin cancels .site-main's top padding so the band butts straight
     up against the header, and the bottom margin gives the generator below some air. */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  margin-top: calc(-1 * clamp(1rem, 4vw, 3rem));
  margin-bottom: clamp(1.6rem, 4vw, 3rem);
  padding: clamp(2rem, 6vw, 4.5rem) 0;
  /* Overprint band: a cyan ink wash bleeding from the top-left, a magenta one from the
     bottom-right (the two inks), over the ink-paper. A faint halftone dot screen sits on
     top for the print-shop texture. */
  background:
    radial-gradient(1100px 620px at 16% -5%, rgba(17, 198, 214, 0.16), transparent 60%),
    radial-gradient(950px 560px at 90% 105%, rgba(255, 46, 136, 0.16), transparent 60%),
    var(--ink-paper);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
/* Halftone dot screen — the print texture, kept very faint so it reads as paper, not noise. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1.4px);
  background-size: 7px 7px;
  pointer-events: none;
}
/* Inner wrapper: keeps the hero content aligned with the rest of the page (matches
   .site-main's width) while the band spans the full viewport. */
.hero-inner {
  /* Span the band edge-to-edge with the same gutters as the header/footer, so the title
     and CTA sit flush-left under the logo rather than indented in a centred column. */
  position: relative;
  z-index: 1;   /* above the .hero::before halftone screen */
  margin: 0;
  padding: 0 clamp(1rem, 5vw, 4rem);
  display: grid;
  /* Left column sizes to the copy; the demo (equation + tee) takes the rest and centres
     itself within it — so the demo sits midway between the copy and the right edge. The CTA is
     a direct child (not nested in .hero-copy) so the mobile stack is pure source order; named
     areas put it back under the copy on the left here. */
  grid-template-columns: auto minmax(0, 1fr);
  /* row1 hugs the copy, row2 takes the rest — so the CTA sits straight under the copy while the
     demo, spanning both rows, extends downward. Both columns are TOP-aligned so the heading is
     level with the equation cards: on laptop widths the demo (equation + tee) is taller than the
     copy and wraps, and a centred demo would otherwise float its cards above the heading. */
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "copy demo"
    "cta  demo";
  column-gap: clamp(1.5rem, 4vw, 4rem);
  row-gap: clamp(1.1rem, 2.2vw, 1.7rem);   /* copy ↔ CTA; was .hero-copy's flex gap */
  align-items: start;
}
/* Left column: a tight copy stack — headline → subhead — at the top of its row. */
.hero-copy {
  grid-area: copy;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(1.1rem, 2.2vw, 1.7rem);
}
/* The demo: the "[box A] × [box B] → [tee]" sentence, centred horizontally in the space to the
   right of the copy and top-aligned so its first row (the equation cards) sits level with the
   heading rather than above it. */
.hero-demo {
  grid-area: demo;
  display: flex;
  align-items: center;
  justify-content: center;
  justify-self: center;
  align-self: start;
  gap: clamp(0.2rem, 0.6vw, 0.6rem);
  flex-wrap: wrap;
}
/* Value-prop kicker as a print-shop spec line: mono, muted, with a cyan registration tick.
   States the mechanic in words in <50ms without leaning on a gradient. */
.hero-eyebrow {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-haze);
}
.hero-eyebrow::before {
  content: "✛";   /* registration mark */
  color: var(--ink-cyan);
  font-size: 0.9em;
}
.hero-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.3rem, 4.2vw, 3.6rem);
  line-height: 1.04;
  letter-spacing: -0.015em;
  margin: 0;
  max-width: 16ch;
  text-wrap: balance;
}
.hero-sub {
  margin: 0;
  color: var(--ink-haze);
  font-family: var(--font-body);
  font-size: clamp(1.02rem, 1.5vw, 1.2rem);
  line-height: 1.5;
  max-width: 34ch;
}

/* ---------- Generator form ---------- */
/* Flat "print spec sheet" panel — squared, hairline border, with a CMY-style registration
   bar across the top (the three inks). No glass. */
.generator {
  position: relative;
  overflow: hidden;            /* clip the top registration bar to the rounded corner */
  background: var(--ink-paper-soft);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: clamp(1.5rem, 3vw, 2.2rem);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
}
.generator::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg,
    var(--ink-cyan) 0 33.34%, var(--ink-magenta) 33.34% 66.67%, var(--ink-violet) 66.67% 100%);
}

/* Daily-allowance shown as a small rotated INK STAMP in the top-right corner (mono, ink
   outline) — a print-shop tally, not an e-commerce ribbon. Keeps the #quota-banner /
   .quota-ribbon hooks so the live OOB swap still works. */
.quota-ribbon {
  position: absolute;
  top: 0.85rem;
  right: 0.9rem;
  pointer-events: none;
  z-index: 3;
}
.quota-ribbon-band {
  display: inline-block;
  transform: rotate(-4.5deg);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.26rem 0.5rem;
  border-radius: 3px;
  color: var(--ink-cyan);
  border: 1.5px solid var(--ink-cyan);
  background: rgba(17, 198, 214, 0.08);
}
.quota-ribbon.is-low .quota-ribbon-band {
  color: var(--ink-magenta);
  border-color: var(--ink-magenta);
  background: rgba(255, 46, 136, 0.08);
}
.quota-ribbon.is-exhausted .quota-ribbon-band {
  color: #ff5a5a;
  border-color: #ff5a5a;
  background: rgba(255, 60, 60, 0.1);
}
.inputs {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  gap: 1rem;
}
.field { display: flex; flex-direction: column; gap: 0.4rem; }
.field-label {
  font-family: var(--font-mono);
  font-size: 0.72rem; color: var(--ink-haze); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.1em;
}
.field input {
  background: var(--ink-paper);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 6px;
  padding: 0.85rem 1rem;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.05rem;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.field input::placeholder { color: #6f6e92; }
/* The second field sits under the corner ribbon — keep its text clear of the band so a
   long value can't slide beneath it. (On mobile the fields stack and the FIRST field is
   the one under the ribbon; handled in the breakpoint below.) */
.field--second input { padding-right: 2.9rem; }
.field input:focus {
  outline: none;
  border-color: var(--accent-2);
  box-shadow: 0 0 0 4px var(--ring);
}
.field-join {
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--ink-violet);
  padding-bottom: 0.7rem;
}
.actions { display: flex; gap: 0.8rem; margin-top: 1.3rem; flex-wrap: wrap; }
.btn-ghost .die { vertical-align: -3px; margin-right: 0.2rem; color: var(--accent-3); }

/* White-tee colour toggle (ISP-039) — the choice IS a garment colour, so each option shows
   an actual tee silhouette filled with that colour. The black tee is stroked so it reads on
   the dark paper (a flat black square vanished into it); the selected chip lifts onto a
   violet keyline + focus ring, exactly like the text inputs, with a mono print-shop label.
   Native radios stay in the DOM (visually hidden) to drive state + keyboard/focus + submit. */
.garment-toggle {
  border: 0; min-width: 0; padding: 0; margin: 1.2rem 0 0;
  display: flex; flex-direction: column; gap: 0.5rem;
}
.garment-toggle legend { padding: 0; }  /* the legend is a .field-label */
.garment-options { display: flex; gap: 0.6rem; }
/* Small content-sized rectangles (not stretched full-width): the 40px tee + a mono label
   side by side, snug. */
.garment-option {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.35rem 0.75rem 0.35rem 0.5rem;
  background: var(--ink-paper);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.garment-option:hover { border-color: rgba(255, 255, 255, 0.32); }
/* Hide the radio without removing it: stays focusable + submits, but the chip is the UI. */
.garment-option input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.garment-tee {
  flex: none;  /* fixed-size icon — never let the flex row shrink the tee */
  width: 52px; height: 49px;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.5));
  transition: transform 0.15s ease;
}
.garment-tee path { stroke-width: 1.5; stroke-linejoin: round; }
.garment-option--black .garment-tee path { fill: #17171e; stroke: rgba(255, 255, 255, 0.55); }
.garment-option--white .garment-tee path { fill: var(--paper-light); stroke: rgba(255, 255, 255, 0.25); }
.garment-name {
  font-family: var(--font-mono); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-haze); transition: color 0.2s;
}
/* Selected + keyboard focus read as the input-focus treatment: violet keyline + ring. */
.garment-option:has(input:checked) { border-color: var(--accent-2); }
.garment-option:has(input:checked),
.garment-option:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--ring); }
.garment-option:has(input:checked) .garment-name { color: var(--ink); }
.garment-option:has(input:checked) .garment-tee { transform: translateY(-2px) scale(1.05); }
@media (prefers-reduced-motion: reduce) {
  .garment-tee, .garment-option:has(input:checked) .garment-tee { transition: none; transform: none; }
}

.examples {
  margin: 1.3rem 0 0;
  color: var(--ink-soft);
  font-size: 0.92rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
}
.examples-lead { margin-right: 0.1rem; }
.example-chip {
  font-family: var(--font-mono);
  font-size: 0.84rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 4px;
  padding: 0.24rem 0.6rem;
  max-width: 100%;
  text-align: left;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
}
.example-chip:hover {
  background: rgba(17, 198, 214, 0.12);
  border-color: var(--ink-cyan);
  color: var(--ink-cyan);
}
.example-chip:active { transform: translateY(1px); }

/* ---------- Buttons ---------- */
.btn {
  font-family: var(--font-display);
  font-weight: 700;
  border: none;
  border-radius: 6px;
  padding: 0.85rem 1.5rem;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.2s, background 0.15s, opacity 0.2s;
}
.btn:active { transform: translateY(1px); }
/* Primary = the overprint violet ink (the "result" colour). */
.btn-primary {
  background: var(--ink-violet);
  color: #fff;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.16) inset, 0 8px 20px rgba(107, 60, 224, 0.3);
}
.btn-primary:hover {
  background: #7a4ae8;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 12px 26px rgba(107, 60, 224, 0.42);
}
/* Ghost = an inked outline. */
.btn-ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.2);
}
.btn-ghost:hover { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.32); }
.btn:disabled { opacity: 0.55; cursor: progress; }
/* Pick / add = the cyan ink. */
.btn-pick { width: 100%; background: var(--ink-cyan); color: #04201e; font-weight: 700; border: none; }
.btn-pick:hover { background: #2bd6e6; box-shadow: 0 8px 20px rgba(17, 198, 214, 0.3); }

/* Visible keyboard focus on every interactive control (WCAG 2.4.7) — the inputs already
   have a violet ring; mirror it on buttons, links and chips with a bright cyan outline. */
.btn:focus-visible,
.hero-cta:focus-visible,
.site-nav a:focus-visible,
.nav-cart:focus-visible,
.example-chip:focus-visible,
.modal-close:focus-visible {
  outline: 2px solid var(--ink-cyan);
  outline-offset: 2px;
}

/* ---------- Spinner ---------- */
.spinner { display: none; align-items: center; gap: 0.5rem; margin-top: 1.2rem; color: var(--ink-soft); }
.spinner.htmx-request { display: flex; }
.spinner p { margin: 0; }
.spinner .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent);
  animation: bounce 1s infinite ease-in-out;
}
.spinner .dot:nth-child(2) { animation-delay: 0.15s; background: var(--accent-2); }
.spinner .dot:nth-child(3) { animation-delay: 0.3s; background: var(--accent-3); }
@keyframes bounce { 0%, 80%, 100% { transform: scale(0.5); opacity: 0.5; } 40% { transform: scale(1); opacity: 1; } }

/* ---------- Results ---------- */
.results:empty { display: none; }
.results { margin-top: 2.4rem; animation: fade-in 0.4s ease; }
@keyframes fade-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

.brief-card {
  background: var(--ink-paper-soft);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: 1.4rem 1.6rem;
}
.brief-title { font-family: var(--font-display); font-size: 1.6rem; margin: 0 0 0.3rem; }
.brief-summary { color: var(--ink-soft); margin: 0 0 1rem; }
.brief-meta { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.palette { display: flex; gap: 0.4rem; }
.swatch { width: 26px; height: 26px; border-radius: 7px; border: 1px solid rgba(255, 255, 255, 0.2); }
.brief-mood { color: var(--ink-soft); font-style: italic; font-size: 0.9rem; }

.grid-heading { font-family: var(--font-display); font-size: 1.3rem; margin: 2.6rem 0 1.1rem; }
.design-grid {
  display: grid;
  /* auto-FIT (not fill) so 3 cards stretch to fill the row instead of leaving a
     phantom empty 4th column; larger min makes each tee bigger. */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.4rem;
}
.design-card {
  margin: 0;
  background: var(--ink-paper-soft);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.18s ease, box-shadow 0.2s, border-color 0.2s;
}
.design-card:hover { border-color: rgba(17, 198, 214, 0.5); }
.design-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
/* The tee floats (transparent PNG) on the .design-media stage — contain + centre it,
   exactly like the carousel's .proof-tee, so generated and premade tees match. */
.design-card img { width: 100%; height: 100%; object-fit: contain; display: block; }
.design-card form { padding: 0.8rem; }

/* Price block — mirrors the enlarged view's bar (label / $value / note). */
.card-price {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  column-gap: 0.6rem;
  padding: 0.8rem 0.8rem 0;
}
.card-price-label {
  font-family: var(--font-mono);
  font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-haze);
}
.card-price-value {
  grid-row: 1 / span 2; align-self: center;
  font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; color: var(--ink);
}
.card-price-note { font-size: 0.74rem; color: var(--ink-soft); }

/* Flip-to-the-other-garment nudge (ISP-149 part B). Only rendered when the design's backdrop
   suits the other tee colour. Sits between the price block and the actions: it's an offer, not
   an action bar, so it reads quieter than Add to cart and must never compete with it. */
.card-flip {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; flex-wrap: wrap;
  margin: 0; padding: 0.6rem 0.8rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(107, 60, 224, 0.10);   /* the violet overprint, held right back */
}
.card-flip-note { margin: 0; font-size: 0.78rem; color: var(--ink); min-width: 0; }
.btn-flip {
  background: transparent;
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.22);
  white-space: nowrap;
  padding: 0.45rem 0.8rem;
  font-size: 0.78rem;
}
.btn-flip:hover { background: rgba(255, 255, 255, 0.10); border-color: rgba(255, 255, 255, 0.34); }
@media (max-width: 420px) {
  .card-flip { flex-direction: column; align-items: stretch; }
  .btn-flip { width: 100%; }
}

/* Card actions: Add to cart (wide primary) + Share (compact secondary) share one
   row. Size + quantity are chosen in the cart drawer, not on the card. */
.card-actions { display: flex; flex-direction: row; align-items: stretch; gap: 0.6rem; padding: 0.8rem; }
.card-actions .card-action { padding: 0; margin: 0; display: flex; }
.card-actions .card-action .btn { width: 100%; }
.card-action-pick { flex: 1 1 auto; min-width: 0; }   /* Add to cart takes the width */
.card-action-share { flex: 0 0 auto; }                /* Share stays compact */
.btn-share {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.14);
  white-space: nowrap;
  padding: 0.85rem 1.1rem;
}
.btn-share:hover { background: rgba(255, 255, 255, 0.12); }

/* Daily usage counter + limit card */
.grid-headrow { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; flex-wrap: wrap; }

.limit-card {
  text-align: center; padding: 2.2rem 1.4rem;
  border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
}

/* ---------- Day history (flick back through today's batches, in the gallery widget) ---------- */
/* Eyebrow on the on-load gallery so revisited designs don't read as a fresh generation. */
.gallery-eyebrow {
  margin: 0 0 0.8rem;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-soft);
}
.history-pager {
  display: flex; align-items: center; justify-content: center; gap: 1rem;
  margin: 2rem auto 1.4rem; flex-wrap: wrap;  /* clear space above so it doesn't crowd the cards */
}
.history-nav[disabled] { opacity: 0.35; pointer-events: none; }
/* While a batch is still generating, the pending design slots and the pager share the
   #results container. A pager click swaps #results out and aborts the in-flight /design
   requests, so the not-yet-finished designs are lost. Disable the pager for that window;
   :has() drops the guard automatically once the last slot resolves (no .design-slot left). */
#results:has(.design-slot) .history-nav { opacity: 0.35; pointer-events: none; cursor: progress; }
.history-counter {
  font-family: var(--font-display); font-weight: 600; font-size: 1.05rem;
  color: var(--ink); white-space: nowrap;
}
.history-empty {
  text-align: center; padding: 3rem 1.4rem;
  border: 1px dashed rgba(255, 255, 255, 0.16); border-radius: 16px;
  background: rgba(255, 255, 255, 0.02);
}
.history-empty-msg { margin: 0 0 1.1rem; color: var(--ink-soft); font-size: 1.05rem; }
.limit-title { margin: 0 0 0.5rem; }
.limit-msg { margin: 0; color: var(--ink-soft); }

/* Garment-preview overlay (raw design -> mockup) */
/* The custom-tee preview floats (transparent PNG) on the SAME violet "stage" as the
   premade carousel cards (.proof-media): a brighter violet overprint halo over a lifted
   panel, so the near-black garment separates from the dark card instead of fading into
   it — and a generated tee reads as one gallery with the ready-made ones. */
.design-media {
  position: relative;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: 0.6rem;
  background:
    radial-gradient(66% 66% at 50% 44%, rgba(107, 60, 224, 0.30), transparent 72%),
    #201a2c;
}
/* While the tee mockup is loading we show a spinner on the same stage (no raw-design flash). */
.design-media-loading {
  display: flex;
  align-items: center;
  justify-content: center;
}
.mockup-pending,
.mockup-tag {
  position: absolute;
  font-size: 0.68rem;
  letter-spacing: 0.02em;
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
  backdrop-filter: blur(4px);
}
.mockup-tag { top: 0.55rem; left: 0.55rem; }
.mockup-pending { bottom: 0.55rem; left: 50%; transform: translateX(-50%); opacity: 0.9; }

/* Streaming placeholder slot while a design generates */
.design-slot {
  background: var(--card);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.slot-spinner { display: flex; gap: 0.4rem; }
.slot-spinner .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent);
  animation: bounce 1s infinite ease-in-out;
}
.slot-spinner .dot:nth-child(2) { animation-delay: 0.15s; background: var(--accent-2); }
.slot-spinner .dot:nth-child(3) { animation-delay: 0.3s; background: var(--accent-3); }

/* ---------- Modal ---------- */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(6, 6, 18, 0.72);
  backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; z-index: 50;
  animation: fade-in 0.2s ease;
}
.modal {
  position: relative;
  background: var(--ink-paper-soft);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  padding: 2rem;
  max-width: 460px; width: 100%;
  box-shadow: var(--shadow);
  text-align: center;
}
.modal-close {
  position: absolute; top: 0.8rem; right: 1rem;
  background: none; border: none; color: var(--ink-soft);
  font-size: 1.6rem; cursor: pointer; line-height: 1;
}
.modal-badge {
  display: inline-block;
  background: rgba(17, 198, 214, 0.12);
  color: var(--ink-cyan);
  border: 1px solid rgba(17, 198, 214, 0.4);
  font-family: var(--font-mono);
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  padding: 0.26rem 0.55rem; border-radius: 3px; margin-bottom: 0.9rem;
}
.modal-title { font-family: var(--font-display); font-size: 1.5rem; margin: 0 0 0.8rem; }
.modal-body { color: var(--ink-soft); line-height: 1.5; margin: 0 0 1.4rem; }
.modal-design img { width: 160px; height: 160px; object-fit: cover; border-radius: 6px; margin: 0 auto 1.2rem; display: block; }
.modal-flow { display: flex; gap: 0.4rem; justify-content: center; flex-wrap: wrap; margin-bottom: 1.4rem; }
.chip { font-family: var(--font-mono); font-size: 0.74rem; padding: 0.3rem 0.6rem; border-radius: 4px; background: rgba(255, 255, 255, 0.07); color: var(--ink-soft); }
.chip.done { background: rgba(17, 198, 214, 0.16); color: var(--ink-cyan); }

/* ---------- Click-to-enlarge lightbox ---------- */
/* Cue that the tee preview is clickable. */
.zoomable { cursor: zoom-in; }
.zoomable:focus-visible {
  outline: 2px solid var(--accent-3);
  outline-offset: -2px;
}

.lightbox-overlay {
  position: fixed; inset: 0;
  background: rgba(6, 6, 18, 0.88);
  backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(0.75rem, 3vw, 2.5rem);
  z-index: 100;
  cursor: zoom-out;
  animation: fade-in 0.2s ease;
  /* Keep scroll/zoom gestures inside the overlay — never let them reach (and move) the
     page behind it. Paired with a body scroll-lock in base.html while the overlay is open. */
  overflow: auto;
  overscroll-behavior: contain;
}
/* Pinch-zoom targets the image itself, not the whole page. */
.lightbox-img { touch-action: pinch-zoom; }
.lightbox-figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  max-width: min(96vw, 1000px);
  max-height: 92vh;
  cursor: default;
}
.lightbox-img {
  /* Show the full-resolution file, whole and uncropped, as large as the
     viewport allows — crisp on the 1000px mockup / 768px design. The bar below
     claims room, so cap the image a little under the figure height. */
  max-width: 100%;
  max-height: calc(92vh - 4.5rem);
  width: auto; height: auto;
  object-fit: contain;
  border-radius: 14px 14px 0 0;
  box-shadow: var(--shadow);
  align-self: center;
  /* Scroll-to-zoom (base.html) applies a translate/scale transform from the centre. */
  transform-origin: center center;
  will-change: transform;
  cursor: zoom-in;
}
/* Price + Add to cart bar pinned under the enlarged image. */
.lightbox-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1.1rem;
  background: rgba(31, 33, 56, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-top: none;
  border-radius: 0 0 14px 14px;
  box-shadow: var(--shadow);
}
.lightbox-price { display: flex; flex-direction: column; line-height: 1.2; text-align: left; }
.lightbox-price-label {
  font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink-soft);
}
.lightbox-price-value {
  font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; color: var(--ink);
}
.lightbox-price-note {
  font-size: 0.74rem; color: var(--ink-soft); margin-top: 0.15rem;
}
.lightbox-cart {
  font: inherit; font-weight: 700;
  background: #14c4b8; color: #04201e;
  border: 1px solid rgba(0, 210, 198, 0.85);
  border-radius: 12px;
  padding: 0.7rem 1.5rem;
  cursor: pointer;
  transition: background 0.15s, box-shadow 0.2s, transform 0.12s ease;
}
.lightbox-cart:hover { background: var(--accent-3); box-shadow: 0 8px 20px rgba(0, 210, 198, 0.3); }
.lightbox-cart:active { transform: translateY(1px); }
/* Actions group (Size + Share + Add to cart) on the right of the bar. */
.lightbox-actions { display: flex; gap: 0.6rem; align-items: center; }
.lightbox-size { display: flex; flex-direction: column; gap: 0.15rem; text-align: left; }
.lightbox-size-label {
  font-size: 0.62rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-soft);
}
.lightbox-size select {
  font: inherit; font-size: 0.9rem;
  background: var(--bg-soft); color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 12px;
  padding: 0.55rem 0.6rem; cursor: pointer;
}
.lightbox-size select:focus { outline: none; border-color: var(--accent-2); box-shadow: 0 0 0 3px var(--ring); }
.lightbox-share {
  font: inherit; font-weight: 700;
  background: rgba(255, 255, 255, 0.08);
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 12px;
  padding: 0.7rem 1.2rem;
  cursor: pointer; white-space: nowrap;
  transition: background 0.15s, transform 0.12s ease;
}
.lightbox-share:hover { background: rgba(255, 255, 255, 0.16); }
.lightbox-share:active { transform: translateY(1px); }
.lightbox-close {
  position: fixed; top: 1rem; right: 1.2rem;
  width: 2.4rem; height: 2.4rem;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  color: var(--ink);
  font-size: 1.6rem; line-height: 1; cursor: pointer;
  transition: background 0.15s;
}
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }

/* ---------- Share: modal copy row + reminder ---------- */
.copy-row { display: flex; gap: 0.5rem; margin: 0 0 1.2rem; }
.copy-input {
  flex: 1; min-width: 0;
  font: inherit; font-size: 0.92rem;
  color: var(--ink);
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  padding: 0.7rem 0.9rem;
}
.copy-input:focus { outline: 2px solid var(--ring); outline-offset: 1px; }
.copy-btn { white-space: nowrap; }
.copy-btn.copied { background: #14c4b8; color: #04201e; box-shadow: none; }

.reminder-form { text-align: left; margin: 0 0 1.2rem; }
.reminder-label { display: block; font-size: 0.82rem; color: var(--ink-soft); margin-bottom: 0.4rem; }
.reminder-row { display: flex; gap: 0.5rem; }
.reminder-row input {
  flex: 1; min-width: 0;
  font: inherit;
  color: var(--ink);
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  padding: 0.7rem 0.9rem;
}
.reminder-row input:focus { outline: 2px solid var(--ring); outline-offset: 1px; }
.reminder-error { color: var(--accent); font-size: 0.82rem; margin: 0.4rem 0 0; }
.reminder-done {
  text-align: left; font-size: 0.92rem; color: var(--ink-soft); line-height: 1.5;
  background: rgba(0, 210, 198, 0.12);
  border: 1px solid rgba(0, 210, 198, 0.3);
  border-radius: 12px;
  padding: 0.8rem 1rem; margin: 0 0 1.2rem;
}
.reminder-check { color: var(--accent-3); font-weight: 700; margin-right: 0.3rem; }
.modal-dismiss { width: 100%; }

/* ---------- Shared-design landing page (/d/{token}) ---------- */
.shared-hero { text-align: center; margin: 1.5rem 0 2rem; }
.shared-eyebrow {
  display: inline-block; font-size: 0.85rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--accent-3); margin-bottom: 0.6rem;
}
.shared-title {
  font-family: var(--font-display); font-weight: 800; font-size: clamp(1.8rem, 4vw, 2.8rem);
  margin: 0 0 0.6rem; letter-spacing: -0.02em;
}
.shared-sub { color: var(--ink-soft); max-width: 46ch; margin: 0 auto; line-height: 1.5; }
.shared-grid { max-width: 360px; margin: 0 auto; }
.shared-expiry { text-align: center; color: var(--ink-soft); font-size: 0.9rem; margin: 1.4rem 0 1rem; }
.shared-cta { text-align: center; margin: 1.8rem 0 2rem; }
.shared-missing { margin-top: 3rem; }
.shared-missing .shared-sub { margin-bottom: 0.5rem; }

/* ---------- Order page (branded "view your order") ----------
   The web twin of the confirmation email (email.py): the same "Payment received"
   pill, greeting, line rows (title + combo + Premium Tee · Size · Qty) and Total
   charged row — rendered in the site's own dark palette (the email is light only
   because mail clients are; here we stay native to the dark site shell). --accent-3
   stands in for the email's teal, --accent for its pink. The thumbnail is fluid:
   big on desktop, shrinks on mobile. */
.order-receipt { max-width: 640px; margin: 1.6rem auto 0; }
.order-status-pill {
  display: inline-block; font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--accent-3);
  background: rgba(0, 210, 198, 0.12); border: 1px solid rgba(0, 210, 198, 0.3);
  padding: 0.34rem 0.7rem; border-radius: 999px;
}
.order-greeting {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.6rem, 3.5vw, 2.3rem); letter-spacing: -0.02em;
  margin: 0.9rem 0 0.5rem;
}
.order-intro { color: var(--ink-soft); line-height: 1.5; margin: 0 0 1.6rem; max-width: 52ch; }
.order-lines {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.9rem;
}
.order-line {
  display: grid; grid-template-columns: clamp(140px, 38vw, 260px) 1fr auto; gap: 1.1rem;
  align-items: center; padding-bottom: 0.9rem; border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.order-thumb {
  width: clamp(140px, 38vw, 260px); height: clamp(140px, 38vw, 260px);
  object-fit: contain; padding: 4px; border-radius: 12px;
  background: var(--bg-soft); cursor: zoom-in;
}
.order-line-info { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.order-line-name { font-weight: 600; font-size: 1rem; overflow-wrap: anywhere; }
.order-line-combo { font-size: 0.82rem; color: var(--accent); }
.order-line-meta { font-size: 0.8rem; color: var(--ink-soft); }
.order-line-total { font-weight: 700; white-space: nowrap; font-size: 1rem; }
.order-totals { margin: 1.1rem 0 0; max-width: 320px; margin-left: auto; }
.order-total-row {
  display: flex; justify-content: space-between; gap: 1.5rem;
  font-size: 0.92rem; color: var(--ink-soft); padding: 0.2rem 0;
}
.order-total-grand {
  margin-top: 0.5rem; padding-top: 0.7rem; border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-weight: 700; font-size: 1.15rem; color: var(--ink);
}
.order-note { color: var(--ink-soft); font-size: 0.88rem; line-height: 1.55; margin: 1.4rem 0 0; }
.checkout-secondary { display: inline-block; margin-top: 0.6rem; }

/* ---------- Cart badge (nav) ---------- */
.nav-cart { display: inline-flex; align-items: center; gap: 0.35rem; }
.cart-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.25rem; height: 1.25rem; padding: 0 0.35rem; box-sizing: border-box;
  background: var(--accent); color: #fff;
  font-size: 0.72rem; font-weight: 700; border-radius: 999px; line-height: 1;
}
.cart-badge-empty { display: none; }

/* ---------- Cart drawer ---------- */
.cart-drawer {
  position: relative;
  background: var(--ink-paper-soft);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  padding: 1.6rem;
  max-width: 480px; width: 100%;
  max-height: 88vh; overflow-y: auto;
  box-shadow: var(--shadow);
}
.cart-title { font-family: var(--font-display); font-size: 1.4rem; margin: 0 0 1rem; }
.cart-empty { color: var(--ink-soft); margin: 0 0 1rem; }
.cart-error { color: var(--accent); font-size: 0.85rem; margin: 0 0 0.8rem; }
.cart-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.9rem; }
.cart-line { display: grid; grid-template-columns: 220px 1fr auto; gap: 0.8rem; align-items: start; }
.cart-thumb { width: 220px; height: 220px; object-fit: contain; padding: 4px; border-radius: 10px; background: var(--bg-soft); }
.cart-line-info { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.cart-line-name { font-weight: 600; font-size: 0.92rem; }
.cart-line-combo { font-size: 0.76rem; color: var(--accent); }
.cart-line-price { font-size: 0.76rem; color: var(--ink-soft); }
.cart-line-total { font-weight: 700; white-space: nowrap; }
.cart-line-controls { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.35rem; flex-wrap: wrap; }
.cart-line-controls form { display: inline-flex; margin: 0; padding: 0; }
.cart-qty-group { display: inline-flex; align-items: center; gap: 0.4rem; }
.cart-size {
  background: var(--bg-soft); color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 8px;
  padding: 0.3rem 0.45rem; font: inherit; font-size: 0.82rem; cursor: pointer;
}
.cart-size:focus { outline: none; border-color: var(--accent-2); box-shadow: 0 0 0 3px var(--ring); }
.qty-btn {
  width: 1.7rem; height: 1.7rem; display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-soft); color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 8px;
  font-size: 1rem; line-height: 1; cursor: pointer;
}
.qty-btn:hover { border-color: var(--accent-2); }
.qty-num { min-width: 1.4rem; text-align: center; font-weight: 600; }
.cart-remove { background: none; border: none; color: var(--ink-soft); font-size: 0.76rem; cursor: pointer; text-decoration: underline; padding: 0 0 0 0.3rem; }
.cart-remove:hover { color: var(--accent); }
.cart-totals { margin: 1.2rem 0 0.4rem; border-top: 1px solid rgba(255, 255, 255, 0.1); padding-top: 0.9rem; display: flex; flex-direction: column; gap: 0.4rem; }
.cart-row { display: flex; justify-content: space-between; color: var(--ink-soft); font-size: 0.92rem; }
.cart-row-total { color: var(--ink); font-weight: 700; font-size: 1.05rem; }
/* Destination picker (ISP-223): the cart quotes the real zone rate for a stated
   country, so the total can't jump when Stripe prices the address at checkout. */
.cart-row-shipto { display: flex; justify-content: space-between; align-items: center; gap: 0.6rem; }
.cart-row-shipto label { color: var(--ink-soft); font-size: 0.92rem; }
.cart-ship-select {
  flex: 0 1 12rem; max-width: 60%; background: var(--ink-paper-soft); color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 6px;
  padding: 0.25rem 0.4rem; font: inherit; font-size: 0.88rem; cursor: pointer;
}
.cart-ship-select:hover { border-color: var(--accent-2); }
.cart-ship-note { font-size: 0.72rem; color: var(--ink-soft); margin: 0.15rem 0 0; text-align: right; }
.cart-checkout { width: 100%; margin-top: 1rem; }
.cart-note { text-align: center; font-size: 0.74rem; color: var(--ink-soft); margin: 0.7rem 0 0; }

/* ---------- Checkout result pages ---------- */
.checkout-result { display: flex; justify-content: center; padding: 3rem 1rem; }
.checkout-card {
  background: var(--ink-paper-soft); border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px; padding: 2.4rem; max-width: 520px; width: 100%;
  text-align: center; box-shadow: var(--shadow);
}
.checkout-badge {
  display: inline-block; background: rgba(17, 198, 214, 0.12); color: var(--ink-cyan);
  border: 1px solid rgba(17, 198, 214, 0.4);
  font-family: var(--font-mono);
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  padding: 0.26rem 0.55rem; border-radius: 3px; margin-bottom: 1rem;
}
.checkout-badge-muted { background: rgba(255, 255, 255, 0.08); color: var(--ink-soft); }
.checkout-title { font-family: var(--font-display); font-size: 1.7rem; margin: 0 0 0.9rem; }
.checkout-body { color: var(--ink-soft); line-height: 1.55; margin: 0 0 1.2rem; }
.checkout-fulfilment { font-size: 0.85rem; color: var(--accent-3); margin: 0 0 1.4rem; }
.checkout-actions { display: flex; gap: 0.8rem; justify-content: center; flex-wrap: wrap; }

/* ---------- Embedded (on-site) checkout page ---------- */
.checkout-pay { max-width: 560px; margin: 0 auto; padding: 2.2rem 1rem 3rem; }
.checkout-pay-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: 1.2rem; flex-wrap: wrap;
}
.checkout-pay-title { font-family: "Fraunces", serif; font-size: 1.6rem; margin: 0; }
.checkout-pay-nav { display: flex; gap: 1rem; align-items: baseline; }
.checkout-pay-back { font-size: 0.85rem; color: var(--accent-3); text-decoration: none; }
.checkout-pay-back:hover { text-decoration: underline; }
/* Stripe mounts its own iframe here; give it a calm light surface so the form
   (which renders on white) sits cleanly inside our dark chrome. */
.checkout-embed {
  background: #fff; border-radius: 16px; padding: 1.2rem;
  min-height: 320px; box-shadow: var(--shadow);
}
.checkout-pay-loading { text-align: center; color: #555; padding: 2rem 0; margin: 0; }

/* ---------- Prep-for-print popup ---------- */
.prep-modal { max-width: 540px; }
.prep-figure { margin: 0 0 0.85rem; }
.prep-img {
  width: 100%; max-height: 52vh; object-fit: contain;
  border-radius: 12px; cursor: zoom-in; padding: 0.8rem;
  /* Float the print preview (transparent-backdrop tee) on the SAME violet "stage" as
     the gallery + carousel tees (.design-media / .proof-media) instead of a flat
     near-black panel, so a tee reads consistently across the whole site. */
  background:
    radial-gradient(66% 66% at 50% 44%, rgba(107, 60, 224, 0.30), transparent 72%),
    #201a2c;
}
.prep-spec { font-size: 0.82rem; color: var(--ink-soft); margin: 0 0 1.2rem; }
.prep-spec b { color: var(--ink); }
.prep-form { display: flex; align-items: flex-end; gap: 0.6rem; justify-content: center; }
.prep-size { display: flex; flex-direction: column; gap: 0.2rem; text-align: left; flex: 0 0 auto; }
.prep-size-label { font-size: 0.68rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-soft); }
.prep-size select {
  background: var(--bg-soft); color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 10px;
  padding: 0.6rem 0.6rem; font: inherit; cursor: pointer;
}
.prep-size select:focus { outline: none; border-color: var(--accent-2); box-shadow: 0 0 0 3px var(--ring); }
.prep-form .prep-add { flex: 1 1 auto; width: auto; }

/* ---------- Size guide (ISP-170: Printful's stated measurements) ---------- */
.size-guide { margin: 1.1rem 0 0; text-align: left; }
.size-guide-toggle {
  cursor: pointer; user-select: none; display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.8rem; font-weight: 600; color: var(--accent-2); padding: 0.25rem 0; list-style: none;
}
.size-guide-toggle::-webkit-details-marker { display: none; }
.size-guide-toggle::before {
  content: "▸"; font-size: 0.72em; transition: transform 0.15s ease;
}
.size-guide[open] .size-guide-toggle::before { transform: rotate(90deg); }
.size-guide-body { margin-top: 0.7rem; }
.size-guide-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; color: var(--ink); }
.size-guide-caption {
  caption-side: top; text-align: left; font-size: 0.68rem; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-soft); margin-bottom: 0.35rem;
}
.size-guide-table th, .size-guide-table td {
  padding: 0.42rem 0.5rem; text-align: center; border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.size-guide-table thead th {
  font-size: 0.67rem; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--ink-soft); font-weight: 600;
}
.size-guide-table tbody th[scope="row"],
.size-guide-table thead th:first-child { text-align: left; font-weight: 700; color: var(--ink); }
.size-guide-note { font-size: 0.72rem; color: var(--ink-soft); margin: 0.7rem 0 0; line-height: 1.45; }
.size-guide-note b { color: var(--ink); font-weight: 600; }
.size-guide-how {
  font-size: 0.72rem; color: var(--ink-soft); line-height: 1.45;
  margin: 0.5rem 0 0; padding-left: 1.1rem;
}
.size-guide-how li { margin: 0.15rem 0; }
.size-guide-tol { font-style: italic; opacity: 0.85; }

/* Loading overlay while the upscale runs (htmx toggles .htmx-request). */
.prep-loading {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(6, 6, 18, 0.72); backdrop-filter: blur(4px);
  display: none; align-items: center; justify-content: center;
}
.prep-loading.htmx-request { display: flex; }
.prep-loading-card {
  background: var(--ink-paper-soft); border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px; padding: 1.8rem 2.2rem; text-align: center; box-shadow: var(--shadow);
}
.prep-loading-card p { color: var(--ink-soft); margin: 0.7rem 0 0; }

/* ---------- Responsive ---------- */
@media (max-width: 620px) {
  .inputs { grid-template-columns: 1fr; }
  .field-join { display: none; }
  .actions .btn { flex: 1; }
  /* Stacked layout: the FIRST field is now the one under the corner ribbon. Shift the
     text-clearance there and let the second field use full width again. Shrink the ribbon
     so it doesn't dominate the narrower card. */
  .field--second input { padding-right: 1rem; }
  .inputs .field:first-child input { padding-right: 2.9rem; }
  .quota-ribbon { width: 150px; height: 150px; }
  .quota-ribbon-band { top: 36px; left: -40px; width: 250px; font-size: 0.7rem; }
  /* Let the price + actions stack rather than collide on narrow screens. */
  .lightbox-bar { flex-wrap: wrap; }
  .lightbox-actions { flex: 1 0 100%; }
  .lightbox-actions .lightbox-cart,
  .lightbox-actions .lightbox-share { flex: 1; text-align: center; }
  .lightbox-size select { width: 100%; }
  /* Keep Older/Newer on ONE row: drop the counter to its own line above, then let the
     two nav buttons share the row below (instead of wrapping to two rows). */
  .history-pager { gap: 0.6rem; }
  .history-counter { flex: 1 0 100%; order: -1; text-align: center; white-space: normal; font-size: 0.95rem; }
  .history-nav { flex: 1 1 0; padding: 0.7rem 0.6rem; white-space: nowrap; }
}

/* ---------- Concept A hero: the live "A × B → tee" example ----------
   One centred row — [chip A] × [chip B] → [result tee] — vertically centred, chips up
   against the tee. JS-filled (js/hero.js from /static/hero/hero_pairings.json) and starts
   [hidden] so the headline + CTA stand alone if JS/manifest are unavailable. */
/* The example line — sits directly under the headline as part of the left stack, left
   aligned with the copy; the trailing → gestures across the band at the tee (the answer). */
.hero-eq {
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;    /* boxes + → sit at the tee end of the equation */
  gap: 0.7rem;
  transition: opacity 0.38s ease;  /* FADE_MS in hero.js */
}
.hero-eq.is-changing { opacity: 0.22; }   /* soft dip, never a full blank between pairings */

/* The "[box A] × [box B] →" group. */
.hero-terms {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.7rem;
}
/* Inspiration "ink swatches" — the two spot inks. A is cyan, B is magenta: a flat ink
   panel with a solid ink edge and a small ink-chip tab on top, not frosted glass. */
.hero-box {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.35rem;
  /* Fixed width + height so the swatch is the SAME size for every pairing, however long
     the label is. The label wraps inside the box instead of stretching it, so the whole
     "[box] × [box] →" equation stays a constant width — the tee no longer slides on
     desktop, and the equation no longer tips between one and two lines on mobile. */
  width: clamp(8rem, 18vw, 14.5rem);
  min-height: 8.25rem;   /* holds a two-line label, so 1- vs 2-line boxes match height */
  padding: 1.05rem 1.35rem;
  border-radius: 4px;   /* squared-off, like a printed swatch */
  background: rgba(255, 255, 255, 0.03);
  border: 1.5px solid var(--ink-cyan);
  box-shadow: 0 10px 30px rgba(17, 198, 214, 0.18);
}
.hero-box-b {
  border-color: var(--ink-magenta);
  box-shadow: 0 10px 30px rgba(255, 46, 136, 0.18);
}
/* Ink-chip tab at the top edge — a printed colour swatch label. */
.hero-box::before {
  content: "";
  position: absolute;
  top: -1.5px;
  left: 1.1rem;
  width: 2.2rem;
  height: 5px;
  background: var(--ink-cyan);
}
.hero-box-b::before { background: var(--ink-magenta); }
.hero-box-emoji {
  font-size: clamp(1.9rem, 3vw, 2.6rem);
  line-height: 1;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4));
}
.hero-box-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1rem, 1.5vw, 1.25rem);
  line-height: 1.1;
  text-align: center;
  overflow-wrap: break-word;   /* wrap long labels within the fixed-width swatch */
}

/* The × operator — the SIGNATURE: two overlapping ink discs (cyan + magenta) whose
   overlap lightens (screen) into a spark, demonstrating "two inks combine into one". The
   literal "×" glyph is hidden; the discs carry the meaning. */
.hero-op-x {
  position: relative;
  width: 2.7rem;
  height: 2rem;
  font-size: 0;          /* hide the × text node */
  flex: 0 0 auto;
}
.hero-op-x::before,
.hero-op-x::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  transform: translateY(-50%);
}
.hero-op-x::before { left: 0; background: var(--ink-cyan); }
.hero-op-x::after { right: 0; background: var(--ink-magenta); mix-blend-mode: screen; }

/* The → arrow points at the tee (the result) in the overprint violet. */
.hero-arrow {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.6rem, 2.8vw, 2.3rem);
  line-height: 1;
  color: var(--ink-violet);
}
/* The result tee — the hero's DOMINANT beauty shot, in the right column, vertically
   centred so the example line aligns with its mid-point. Rounded + drop-shadowed so it
   reads as a floating product shot now that the hero has no panel behind it. */
.hero-tee {
  position: relative;
  align-self: center;
  margin: 0;
  text-align: center;
  transition: opacity 0.38s ease;
}
.hero-tee.is-changing { opacity: 0.22; }   /* soft dip, never a full blank between pairings */
/* Clickable result shot: clicking jumps to the gallery carousel and spins to this tee.
   Subtle lift on hover/focus signals it acts; the tee itself does the transform so the
   violet stage (::before) stays put. */
.hero-tee.is-clickable { cursor: pointer; }
.hero-tee.is-clickable .hero-tee-img { transition: transform 0.25s ease, filter 0.25s ease; }
.hero-tee.is-clickable:hover .hero-tee-img,
.hero-tee.is-clickable:focus-visible .hero-tee-img {
  transform: translateY(-4px) scale(1.015);
  filter: drop-shadow(0 30px 60px rgba(0, 0, 0, 0.6));
}
.hero-tee.is-clickable:focus-visible {
  outline: 2px solid var(--accent-2, #7c3aed);
  outline-offset: 6px;
  border-radius: 16px;
}
/* Soft focal halo behind the tee so the (near-black) garment separates from the dark
   band and reads as the hero's single focal product shot. */
.hero-tee::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 46%;
  width: 135%;
  height: 130%;
  transform: translate(-50%, -50%);
  /* ellipse closest-side fades to transparent *inside* the element box on every side, so
     there's no rectangular clip — a circle (farthest-corner) overshot the narrow sides.
     A brighter violet→magenta overprint wash (the brand "result" colour) is the stage that
     lifts the near-black garment off the dark band — no white, which read as an ugly halo. */
  background: radial-gradient(ellipse closest-side at 50% 50%,
    rgba(124, 58, 237, 0.46) 0%, rgba(236, 72, 153, 0.18) 48%, transparent 86%);
  z-index: 0;
  pointer-events: none;
}
.hero-tee-img {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 520px;
  height: auto;
  /* The tee PNG is true-transparent (no baked panel box). The brighter violet stage behind
     it (.hero-tee::before) does the figure-vs-ground separation, so the garment only needs
     its dark grounding shadow for depth — no rim, no white halo. */
  filter: drop-shadow(0 24px 50px rgba(0, 0, 0, 0.55));
}
.hero-tee-cap {
  position: relative;
  z-index: 1;
  margin-top: 0.9rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
}
.hero-tee-eyebrow {
  font-family: var(--font-mono);   /* print-shop data label */
  font-weight: 700;
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-haze);
}
.hero-tee-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--ink);
}

/* CTA — the hero's single primary action (scrolls to + focuses the generator). Solid
   overprint-violet ink (the "result" colour), squared like a printed button, with a
   letterpress inset; hover brightens, active presses down like an ink stamp. Overrides
   the legacy gradient .btn-primary. */
.hero-cta {
  grid-area: cta;
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  padding: 0.85rem 1.7rem;
  border-radius: 6px;
  background: var(--ink-violet);
  color: #fff;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.18) inset, 0 10px 24px rgba(107, 60, 224, 0.35);
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.hero-cta:hover {
  background: #7a4ae8;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.22) inset, 0 14px 30px rgba(107, 60, 224, 0.45);
}
.hero-cta:active {
  transform: translateY(2px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.12) inset, 0 4px 12px rgba(107, 60, 224, 0.4);
}

@media (max-width: 1360px) {
  /* One centred column. The stack order falls straight out of document order via named areas —
     copy (title + sub) → demo (equation + tee) → CTA — so no display:contents / order tricks are
     needed (those misfire on some WebKit builds, floating the equation above the heading).
     Breakpoint is 1360px (not a phone width): below it the two-column demo column is too narrow to
     hold the equation "[A] × [B] →" on one line — first the → arrow wraps under the swatches
     (~1320px), then the swatches themselves stack (~1030px). We flip to the single column — where
     the equation gets the full page width and stays on one line — before any of that can happen.
     The swatches are clamp(8rem,18vw,14.5rem) wide, so these wrap points track the viewport. */
  .hero-inner {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;   /* override the desktop "auto 1fr"; all three rows hug content */
    grid-template-areas:
      "copy"
      "demo"
      "cta";
    row-gap: clamp(1rem, 4vw, 1.5rem);
    text-align: center;
    justify-items: center;
  }
  .hero-copy { align-items: center; }                 /* centre title + sub */
  .hero-demo { flex-direction: column; gap: clamp(1rem, 4vw, 1.5rem); }
  .hero-cta { justify-self: center; align-self: center; }
  .hero-tee-img { max-width: 320px; }
}

/* ---------- "Shop the gallery" proof carousel ---------- */
/* The buy-now / remix rail in a band near the foot of the page (below the generator + the
   visitor's results). Quiet by design — the hero is the loud band, so this reads as a tidy
   print-shop rack of finished proofs. */
.proofs {
  /* The rail shows a WHOLE number of cards (no half-card peeking): each card is a
     fraction of the track width, so `--proof-cols` fit exactly. The gap is shared by
     the card-width calc and the flex track so they can't drift. Cols step down on
     narrower viewports (below). */
  --proof-gap: 1.1rem;
  --proof-cols: 3;
  /* A "shop the gallery" band near the foot of the page: a hairline top rule sets it
     off as its own zone, below the generator and the visitor's own results. */
  margin: clamp(2.4rem, 6vw, 4rem) 0 clamp(1rem, 3vw, 2rem);
  padding-top: clamp(2rem, 5vw, 3.2rem);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.proofs-head {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin: 0 0 1.2rem;
}
/* Same mono registration-mark eyebrow as the hero, so the two sections read as one system. */
.proofs-eyebrow {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-haze);
}
.proofs-eyebrow::before { content: "✛"; color: var(--ink-cyan); font-size: 0.9em; }
.proofs-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.5rem, 3vw, 2rem);
  letter-spacing: -0.01em;
}
.proofs-sub { margin: 0; color: var(--ink-haze); font-size: 0.98rem; max-width: 46ch; }

/* Flex row: [‹ arrow] [scrolling track] [arrow ›]. The arrows sit BESIDE the track
   (never over the artwork) and `align-items: center` keeps them on the card's vertical
   midline. */
.proofs-rail { position: relative; display: flex; align-items: center; gap: 0.6rem; }
/* The scroll container. Snap so auto-advance + swipe always settle on a card edge;
   scrollbar hidden (the arrows + swipe are the affordance). */
.proofs-track {
  flex: 1 1 auto;
  min-width: 0;   /* let the track shrink beside the arrows instead of overflowing the row */
  display: flex;
  gap: var(--proof-gap);
  list-style: none;
  margin: 0;
  padding: 0.4rem 0.2rem 0.6rem;   /* room for card hover-lift + focus ring */
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 0.2rem;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.proofs-track::-webkit-scrollbar { display: none; }
.proofs-track:focus-visible { outline: 2px solid var(--ink-cyan); outline-offset: 3px; border-radius: 8px; }

.proof-card {
  flex: 0 0 auto;
  width: calc((100% - (var(--proof-cols) - 1) * var(--proof-gap)) / var(--proof-cols));
  scroll-snap-align: start;
  background: var(--ink-paper-soft);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.18s ease, box-shadow 0.2s, border-color 0.2s;
}
.proof-card:hover { transform: translateY(-4px); border-color: rgba(107, 60, 224, 0.5); box-shadow: var(--shadow); }
/* A tee opened from the hero lands highlighted (cyan = the brand's "interactive" ink, same
   as the rail focus ring) so the eye goes straight to the one they picked. Holds until they
   engage the rail. .is-landing adds a brief attention pulse on arrival. */
.proof-card.is-target {
  border-color: var(--accent-3);
  box-shadow: 0 0 0 2px var(--accent-3), 0 12px 34px rgba(0, 210, 198, 0.32);
  transform: translateY(-4px);
}
.proof-card.is-landing { animation: proof-land 0.85s ease 2; }
@keyframes proof-land {
  0%, 100% { box-shadow: 0 0 0 2px var(--accent-3), 0 12px 34px rgba(0, 210, 198, 0.32); }
  50%      { box-shadow: 0 0 0 4px var(--accent-3), 0 14px 44px rgba(0, 210, 198, 0.62); }
}
@media (prefers-reduced-motion: reduce) { .proof-card.is-landing { animation: none; } }
/* The tee floats (transparent PNG) on the "stage": a brighter violet overprint halo (the
   "result" colour) over a lifted panel, so the near-black garment separates from the dark
   card instead of fading into it. No white — it read as an ugly halo. */
.proof-media {
  position: relative;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: 0.6rem;
  background:
    radial-gradient(66% 66% at 50% 44%, rgba(107, 60, 224, 0.30), transparent 72%),
    #201a2c;
}
.proof-tee { width: 100%; height: 100%; object-fit: contain; display: block; }

.proof-body { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.9rem 0.9rem 1rem; }
/* The pairing as a print spec: ink-tinted A and B terms (cyan / magenta) — the same
   visual grammar as the hero's equation, so the card teaches "what to type". */
.proof-pairing {
  margin: 0;
  display: flex;
  align-items: center;
  align-content: flex-start;
  flex-wrap: wrap;
  /* Row gap (1st value) = the vertical space between the two prompt lines (term A / term B);
     kept tight so the pairing reads as one stacked unit. Column gap (2nd) spaces A from the ×. */
  gap: 0.1rem 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  line-height: 1.35;
  /* Reserve two lines so short pairings (one line) and long ones (two) keep the same
     card height — otherwise the title + buttons below bounce as the carousel advances.
     The trailing term matches the row gap above. */
  min-height: calc(0.76rem * 1.35 * 2 + 0.1rem);
}
/* Each term stays on its own single line (it never wraps internally); the .proof-break
   below pushes term B onto line 2 so the caption is always exactly two lines. */
.proof-term { display: inline-flex; align-items: center; gap: 0.3rem; font-weight: 700; white-space: nowrap; }
.proof-term-a { color: var(--ink-cyan); }
.proof-term-b { color: var(--ink-magenta); }
.proof-emoji { font-size: 0.95em; }
.proof-x { color: var(--ink-haze); font-weight: 700; }
/* Zero-size full-width flex item → forces the items after it onto the next line. */
.proof-break { flex-basis: 100%; height: 0; margin: 0; }
.proof-name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;   /* the product title leads the card — out-rank the price value */
  font-size: 1.22rem;
  line-height: 1.15;
  /* Reserve (and cap at) two lines so a long title like "Microverse Constellations"
     wraps without shoving the price + buttons down on that card. */
  min-height: calc(1.22rem * 1.15 * 2);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.proof-price { display: flex; align-items: baseline; gap: 0.5rem; margin: 0.1rem 0 0.3rem; }
.proof-price-value { font-family: var(--font-display); font-weight: 800; font-size: 1.3rem; color: var(--ink); }
.proof-price-note { font-size: 0.74rem; color: var(--ink-haze); }
.proof-actions { display: flex; gap: 0.55rem; margin-top: 0.2rem; }
.proof-buy { flex: 1 1 auto; display: flex; }
/* Buy = the overprint violet (the finished result / primary action). */
.btn-buy {
  width: 100%;
  background: var(--ink-violet);
  color: #fff;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.16) inset, 0 8px 20px rgba(107, 60, 224, 0.3);
}
.btn-buy:hover { background: #7a4ae8; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 12px 26px rgba(107, 60, 224, 0.42); }
.btn-remix {
  flex: 0 0 auto;
  background: transparent;
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.2);
  padding: 0.85rem 0.9rem;
  white-space: nowrap;
}
.btn-remix:hover { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.32); }

/* Scroll arrows — ink-outlined discs at the rail edges, over the artwork. JS reveals them
   only when the rail overflows; touch devices swipe instead (hidden below). */
.proofs-arrow {
  flex: 0 0 auto;   /* a flex sibling of the track (see .proofs-rail), centred on its midline */
  z-index: 2;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  color: var(--ink);
  background: rgba(20, 17, 31, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 50%;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.38);
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.12s, box-shadow 0.15s;
}
.proofs-arrow svg { display: block; }
.proofs-arrow:hover {
  background: var(--ink-violet);
  border-color: var(--ink-violet);
  color: #fff;
  box-shadow: 0 8px 22px rgba(107, 60, 224, 0.45);
}
.proofs-arrow:active { transform: scale(0.92); }
.proofs-arrow:focus-visible { outline: 2px solid var(--ink-cyan); outline-offset: 2px; }

/* Fewer columns as the rail narrows, so cards never get cramped (3 is desktop-roomy). */
@media (max-width: 880px)  { .proofs { --proof-cols: 2; } }
@media (max-width: 760px) {
  /* Touch: swipe is the affordance, so hide the arrows even if JS un-hid them, and let
     a card-and-a-bit show so the rail reads as swipeable. */
  .proofs-arrow { display: none !important; }
  .proofs-rail { gap: 0; }
  .proofs { --proof-cols: 1.15; }
}

/* The two generator inputs briefly flash when a carousel "Remix" prefills them, so the
   handoff is visible — paired with the polite #remix-status announcement (carousel.js). */
.inputs input.is-prefilled {
  animation: prefill-flash 1.6s ease-out;
}
@keyframes prefill-flash {
  0%   { box-shadow: 0 0 0 3px rgba(17, 198, 214, 0.55); border-color: var(--ink-cyan); }
  100% { box-shadow: 0 0 0 3px rgba(17, 198, 214, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .inputs input.is-prefilled { animation: none; }
}

/* Visually-hidden but screen-reader-available (for the polite Remix status line). */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}
