body {
  font-family:
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    Roboto,
    Oxygen,
    Ubuntu,
    Cantarell,
    'Open Sans',
    'Helvetica Neue',
    sans-serif;
  margin: 0;
  padding: 0;
}

h1,
h2,
p {
  margin: 0;
  padding: 0;
}

h1 {
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1.4;
  margin-bottom: 2rem;
  margin-top: 2rem;
}

h2 {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1.4;
  margin-bottom: 1rem;
}

h4 {
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
}

h5 {
  margin-bottom: 1rem;
  margin-top: 0.5rem;
}

p {
  font-size: 1rem;
  line-height: 1.4;
}

a {
  color: #000;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
  cursor: pointer;
}

hr {
  border-bottom: none;
  border-top: 1px solid #000;
  margin: 0;
}

pre {
  white-space: pre-wrap;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

body > main {
  margin: 0 1rem 1rem 1rem;
}

section {
  padding: 1rem 0;
  @media (min-width: 768px) {
    padding: 2rem 0;
  }
}

fieldset {
  display: flex;
  flex-direction: column;
  margin-bottom: 0.5rem;
  padding: 1rem;
}

form {
  max-width: 100%;
  @media (min-width: 768px) {
    max-width: 400px;
  }
}

input {
  border-radius: 4px;
  border: 1px solid #000;
  font-size: 1rem;
  margin-bottom: 0.5rem;
  margin-top: 0.25rem;
  padding: 0.5rem;
}

legend {
  font-weight: 600;
  margin-bottom: 0.5rem;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

li {
  margin-bottom: 0.5rem;
}

dl {
  margin: 0.5rem 0;
}

code {
  background: #ddd;
  border-radius: 4px;
  font-family: monospace;
  padding: 0.25rem;
}
/**
 * OLPINER design tokens — locked visual system.
 *
 * Palette — matches the approved prototype exactly (see
 * docs/brand-assets.md and DESIGN_INTEGRATION_PLAN.md):
 *   --void        #050505   primary environment — black, not off-white.
 *                            OLPINER is a black-first brand; this is the
 *                            page background everywhere by default.
 *   --void-2      #0b0b0a   secondary surface (cards, dialogs), a hair
 *                            lighter than --void so elevated panels read
 *                            as elevated without ever going toward gray.
 *   --paper       #F1EEE6   primary text/foreground on --void. Also used
 *                            as the rare "light surface" (e.g. buttons
 *                            that invert on hover).
 *   --paper-dim   #8f8c85   secondary/muted text on --void.
 *   --pink        #E600BE   the ONE permanent OLPINER accent, derived
 *                            from Ahinoam's signature. Locked — this is
 *                            not a per-drop artwork color. A Drop's own
 *                            `accent_color` field may still override
 *                            --olp-accent for artwork-specific chrome
 *                            (see --olp-accent below), but the brand
 *                            default is always this pink, never gray.
 *   Gold never appears here — gold is exclusively inside Drop 001's
 *   artwork itself, never in UI chrome. See docs/brand-assets.md.
 *
 * Type — four self-hosted families, each with one job (see
 * public/fonts/ and app/styles/fonts.css for the actual @font-face
 * declarations — nothing here is loaded from Google Fonts at runtime):
 *   Archivo (900)        — display headlines, uppercase, high-impact.
 *   Fraunces (italic)     — the "human voice": by-lines, credits,
 *                            captions, the Archive's editorial h1.
 *   Space Grotesk         — body copy, nav, UI controls, the wordmark.
 *   Space Mono            — the "catalog" register: eyebrows, edition
 *                            numbers, countdown digits, button labels.
 */

:root {
  /* ---- layout ----
     Real, measured value (header padding + tallest child) — see
     HOMEPAGE_100VH_VERIFICATION.md for how this number was obtained.
     Used by .drop-home's min-height so the homepage hero fills exactly
     the space beneath the header without hardcoding 88px in multiple
     places. */
  --header-height: 88px;

  /* ---- color: the locked palette ---- */
  --void: #050505;
  --void-2: #0b0b0a;
  --paper: #f1eee6;
  --paper-dim: #8f8c85;
  --pink: #e600be;

  /* ---- semantic aliases used throughout the app ----
     Kept as a separate layer from the raw palette above so components
     can keep referencing --olp-* names; only the *values* changed to
     match the approved system (previously these pointed at a light
     "paper" background by mistake — see DESIGN_INTEGRATION_PLAN.md §1). */
  --olp-ink: var(--void);
  --olp-paper: var(--paper);
  --olp-charcoal: var(--void-2);
  --olp-line: rgba(241, 238, 230, 0.14);
  --olp-line-dark: rgba(241, 238, 230, 0.08);

  /* The permanent brand accent. A Drop can still override --olp-accent
     inline (see app/routes/_index.tsx) for artwork-specific chrome on
     its own hero — but the default, and what every other page uses, is
     always this pink. --olp-crown is kept as an alias for any code that
     still references the old name. */
  --olp-accent: var(--pink);
  --olp-crown: var(--pink);

  --olp-bg: var(--void);
  --olp-fg: var(--paper);
  --olp-fg-muted: var(--paper-dim);
  --olp-border: var(--olp-line);
  --olp-surface: var(--void-2);
  --olp-danger: #ff5a5a;

  /* ---- type ---- */
  --olp-font-display: 'Archivo', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --olp-font-serif: 'Fraunces', Georgia, serif;
  --olp-font-body: 'Space Grotesk', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  /* Courier has the same 0.6em character advance as Space Mono. Keeping
     that advance while the font loads prevents dimension labels rewrapping. */
  --olp-font-mono: 'Space Mono', 'Courier New', monospace;

  --olp-size-display-1: clamp(2.75rem, 3vw + 2rem, 6.5rem);
  --olp-size-display-2: clamp(1.9rem, 2vw + 1.3rem, 3.4rem);
  --olp-size-title: clamp(1.25rem, 1vw + 1rem, 1.75rem);
  --olp-size-body: 1rem;
  --olp-size-small: 0.8125rem;
  --olp-size-label: 0.84375rem;

  /* Shared readability-pass tokens — a restrained global increase, not
     a wholesale interface rescale. Existing --olp-size-* tokens above
     are kept as-is (other code still depends on them); these are the
     named scale requested for new/updated rules going forward, sized
     to the same ranges applied throughout this pass: body copy
     14–16px, nav/category labels ~12–13px, buttons ~13–14px. */
  --text-xs: 0.6875rem; /* 11px — footer/policy link floor */
  --text-sm: 0.8125rem; /* 13px — metadata, credits, small labels */
  --text-body: 0.9375rem; /* 15px — supporting/body copy */
  --text-button: 0.875rem; /* 14px — buttons, form submit */
  --text-nav: 0.84375rem; /* 13.5px — nav links, category labels */
  --text-display: var(--olp-size-display-1); /* large headings unchanged */

  --olp-tracking-tight: -0.02em;
  --olp-tracking-label: 0.14em;

  --olp-leading-tight: 0.96;
  --olp-leading-body: 1.55;

  /* ---- space (8px base, one deliberate outlier for hero breathing room) ---- */
  --olp-space-1: 0.5rem;
  --olp-space-2: 1rem;
  --olp-space-3: 1.5rem;
  --olp-space-4: 2rem;
  --olp-space-5: 3rem;
  --olp-space-6: 5rem;
  --olp-space-7: 8rem;

  --olp-container: 1440px;
  --olp-gutter: clamp(1.25rem, 4vw, 3rem);

  /* ---- motion ---- */
  --olp-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --olp-duration-fast: 160ms;
  --olp-duration: 320ms;
  --olp-duration-slow: 620ms;

  --olp-radius: 2px;
  --olp-focus-ring: 0 0 0 2px var(--olp-bg), 0 0 0 4px var(--pink);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --olp-duration-fast: 0ms;
    --olp-duration: 0ms;
    --olp-duration-slow: 0ms;
  }
}

/* No light theme: OLPINER's black environment is the brand, not a mode.
   The former [data-theme='dark'] opt-in variant is intentionally removed —
   there is nothing to opt into, this is the only theme. */
/**
 * Self-hosted fonts — see DESIGN_INTEGRATION_PLAN.md §6.5 (Typography).
 *
 * Inter in particular CANNOT be imported at runtime even if we wanted
 * to: the preview server sends Content-Security-Policy with
 * default-src 'self' and font-src 'self' data:, so a Google Fonts
 * stylesheet and font file are both blocked and the heading would
 * silently fall back to Arial. The approved design's @import is a
 * reference-sheet convenience, not something that can ship here.
 *
 * Deliberately NOT loaded from Google Fonts at runtime: these are the
 * exact static WOFF2 files from each family's Fontsource package (same
 * OFL-licensed Google Fonts sources, distributed for self-hosting),
 * copied into public/fonts/ once at build time. See
 * public/fonts/LICENSE-OFL.txt for the license text.
 *
 * Only the weights/styles actually used anywhere in the app are
 * included — nothing extra is shipped to visitors:
 *   Archivo        700, 900        (display headlines)
 *   Inter          700, 900        (approved Guerrilla Poker wordmark)
 *   Fraunces       400 italic/normal, 300 italic   (credit lines, captions)
 *   Space Grotesk  400, 500        (body, nav, UI)
 *   Space Mono     400, 700        (eyebrows, catalog data, buttons)
 */

@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo-900.woff2') format('woff2');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Fraunces';
  src: url('/fonts/fraunces-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Fraunces';
  src: url('/fonts/fraunces-400-italic.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Fraunces';
  src: url('/fonts/fraunces-300-italic.woff2') format('woff2');
  font-weight: 300;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Space Grotesk';
  src: url('/fonts/space-grotesk-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Space Grotesk';
  src: url('/fonts/space-grotesk-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Space Mono';
  src: url('/fonts/space-mono-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Space Mono';
  src: url('/fonts/space-mono-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Inter — the approved weight for GUERRILLA / POKER in the collection
   wordmark. The design calls for weight 900 with tight negative
   tracking; see the .gp-hero-word rules in app.css. */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-900.woff2') format('woff2');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}
/**
 * OLPINER — global styles.
 * A gallery-wall canvas, large type, almost no chrome. The signature
 * element of this system is the Drop hero: full-bleed artwork with a single
 * line of catalog-style metadata (edition / release / status) set in mono,
 * everything else stays out of its way. See /docs/design-system.md.
 */

* {
  box-sizing: border-box;
}

html {
  background: var(--olp-bg);
  color-scheme: dark;
}

body {
  background: var(--olp-bg);
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-size: var(--olp-size-body);
  line-height: var(--olp-leading-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* header / main / footer are direct flex children, exactly like every
     prototype page's <body> — this is what lets a full-height hero (the
     Drop room) fill the space between a sticky header and the footer,
     while shorter pages (Archive, Original Art, Prints) still push the
     footer to the bottom instead of leaving it floating mid-page. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

body > main {
  /* reset.css (loaded before this file) sets margin: 0 1rem 1rem 1rem on
     this exact selector — a real, previously-undetected bug that's put a
     16px margin around every page's content since Step 2. Every full-bleed
     room/grid in this build assumes edge-to-edge main, so this needs an
     explicit override, not just new properties layered on top. */
  margin: 0;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* reset.css also sets a generic `section { padding: 2rem 0 }` at desktop
   widths — silently eating 64px of vertical space (32px top + 32px
   bottom) from every bare, unclassed <section> wrapper in this app (the
   homepage room and the apparel Piece hero). Every section in this
   design system defines its own specific padding via a class where
   padding is actually wanted (.section, .print-piece-hero, etc.) — none
   of them rely on this generic default, so it's overridden globally
   rather than patched selector-by-selector. Found while verifying the
   homepage's exact-100vh requirement — see DESIGN_INTEGRATION_PLAN.md. */
section {
  padding: 0;
}

/* The homepage's outer state wrapper — needs to be a flex container
   itself so .room's flex:1 (which only has an effect on a flex item)
   actually propagates the "fill everything between header and footer"
   behavior down to the room. Without this, the section sizes to its own
   content and the room never actually fills available space — the exact
   bug behind the homepage not being a true one-screen experience. */
.drop-home {
  display: flex;
  flex-direction: column;
  /* On mobile this simply fills remaining space in the flex column
     (body: header/main/footer), and the page scrolls naturally past it
     to reach the footer — see the desktop-only exact-fit rule below for
     why desktop is different. */
  flex: 1;
}

/* Desktop homepage only: header + hero + the compact email/footer rail
   must equal exactly 100svh, no scrollbar in either direction. Scoped
   two ways so this can't leak anywhere else: the min-width media query
   (never touches mobile, which scrolls naturally per its own rule
   above) and :has(.drop-home), which only matches when the homepage's
   specific hero markup is actually present in the body — not a global
   overflow:hidden, just this one page at this one width range. */
@media (min-width: 861px) {
  /* :not(:has(.gp-hero)) — the approved homepage is deliberately taller
     than one screen: a full hero, then a separate shirt section, then
     the More row. This rule was written for the old single-screen
     homepage and still matched, because the approved markup keeps the
     .drop-home wrapper — so the shirt section existed in the document
     (scrollHeight 1886 against a 900px screen) but could not be
     reached. */
  body:has(.drop-home):not(:has(.gp-hero)) {
    height: 100svh;
    overflow: hidden;
  }
}

img,
picture {
  border-radius: 0;
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

.mono {
  font-family: var(--olp-font-mono);
}

::selection {
  background: var(--pink);
  color: var(--olp-bg);
}

:focus-visible {
  outline: none;
  box-shadow: var(--olp-focus-ring);
  border-radius: var(--olp-radius);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

button.reset {
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  font: inherit;
  padding: 0;
}

.eyebrow {
  font-family: var(--olp-font-mono);
  font-size: var(--olp-size-label);
  letter-spacing: var(--olp-tracking-label);
  text-transform: uppercase;
  color: var(--pink);
}

.eyebrow span {
  color: var(--olp-fg-muted);
  margin-left: 10px;
}

.olp-button {
  align-items: center;
  background: var(--olp-fg);
  border: 1px solid var(--olp-fg);
  border-radius: var(--olp-radius);
  color: var(--olp-bg);
  cursor: pointer;
  display: inline-flex;
  font-family: var(--olp-font-mono);
  font-size: var(--olp-size-small);
  gap: var(--olp-space-1);
  justify-content: center;
  letter-spacing: var(--olp-tracking-label);
  padding: 0.95rem 1.5rem;
  text-transform: uppercase;
  transition: background var(--olp-duration-fast) var(--olp-ease), color var(--olp-duration-fast) var(--olp-ease), opacity var(--olp-duration-fast) var(--olp-ease);
  width: 100%;
}

.olp-button:hover:not(:disabled) {
  background: var(--pink);
  color: var(--olp-bg);
  border-color: var(--pink);
}

.olp-button:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.olp-button--ghost {
  background: transparent;
  color: var(--olp-fg);
}

.olp-button--ghost:hover:not(:disabled) {
  background: var(--olp-fg);
  color: var(--olp-bg);
}

.olp-input {
  background: var(--olp-surface);
  border: 1px solid var(--olp-border);
  border-radius: var(--olp-radius);
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-size: var(--olp-size-body);
  padding: 0.9rem 1rem;
  width: 100%;
}

.olp-field-error {
  color: var(--olp-danger);
  font-size: var(--olp-size-small);
  margin-top: var(--olp-space-1);
}

/* Label-above-input row used by the contact form. Referenced but never
   defined before this pass — labels and inputs were rendering flush
   against each other with no spacing or label styling. */
.drop-access-form__row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.drop-access-form__row label {
  font-family: var(--olp-font-mono);
  font-size: 13.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
}

/*
* --------------------------------------------------
* layout shell
* --------------------------------------------------
*/
.olp-shell {
  margin-inline: auto;
  max-width: var(--olp-container);
  padding-inline: var(--olp-gutter);
}

/*
* --------------------------------------------------
* header — ported directly from the approved prototype
* (identical across all six prototype pages)
* --------------------------------------------------
*/
.header {
  align-items: center;
  background: var(--olp-bg);
  border-bottom: 1px solid var(--olp-line-dark);
  display: flex;
  justify-content: space-between;
  padding: 22px var(--olp-gutter);
  position: sticky;
  top: 0;
  z-index: 40;
  flex-shrink: 0;
}

.brand {
  display: flex;
  flex-direction: column;
}

.wordmark {
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: 16px;
  letter-spacing: 0.16em;
  color: var(--olp-fg);
  text-decoration: none;
}

.wordmark-dot {
  color: var(--pink);
  letter-spacing: 0;
}

.by-line {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 300;
  font-size: 11.5px;
  color: var(--olp-fg-muted);
  margin-top: 3px;
  letter-spacing: 0.02em;
}

/* At the narrowest tested widths (320–360px), OLPINER + byline +
   "Menu" + "Cart" (all with visible text, per the navigation
   correction) is genuinely tight. Dropping the byline — not the Menu/
   Cart text, which must stay visible per that same correction — is
   enough on its own to guarantee no overlap; tightening the two
   buttons' spacing slightly adds extra margin on top of that. */
@media (max-width: 374px) {
  .by-line {
    display: none;
  }

  .menu-toggle {
    padding: 8px 8px 8px 0;
    margin-left: 14px;
    gap: 4px;
  }

  .cart-toggle {
    padding: 8px;
    margin-left: 14px;
    gap: 4px;
  }
}

.navlinks {
  display: flex;
  list-style: none;
}

.navlinks li {
  margin-left: 36px;
}

.navlinks li:first-child {
  margin-left: 0;
}

.navlinks a {
  color: var(--olp-fg-muted);
  text-decoration: none;
  font-size: var(--text-nav);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  position: relative;
  padding-bottom: 3px;
  transition: color 0.35s var(--olp-ease);
}

.navlinks a.active {
  color: var(--pink);
}

.navlinks a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--pink);
  transition: width 0.4s var(--olp-ease);
}

.navlinks a:hover {
  color: var(--olp-fg);
}

.navlinks a:hover::after {
  width: 100%;
}

@media (max-width: 860px) {
  header > nav:first-of-type > .navlinks:not(.navlinks--mobile) {
    display: none;
  }
}

/* Real, previously-undiscovered bug found while verifying the mobile
   menu drawer: this rule used to be split across two blocks, the first
   unconditionally setting display:none with nothing anywhere ever
   setting it back — so the nav links inside the mobile drawer have
   never actually been visible, in production or here. */
.navlinks--mobile {
  display: flex;
  flex-direction: column;
  gap: var(--olp-space-3);
}

.navutil {
  display: flex;
  align-items: center;
}

/* Desktop-only text link, matching .navlinks' typography exactly so it
   reads as part of the same nav row rather than a bolted-on extra. */
/* Now an icon (see Header.tsx) matching WhatsApp's treatment exactly —
   same size/weight/color/hover/spacing, via the shared .iconbtn base.
   This overrides nothing itself; .navutil-instagram is kept as a class
   only so the desktop-only media-query hide rule below still has
   something specific to target. */
.navutil-instagram svg {
  width: 18px;
  height: 18px;
  /* Without this, a plain SVG (no HTML width/height attribute) can get
     flex-shrunk to 0 width inside .iconbtn's tight box — this exact bug
     already broke the WhatsApp icon once (see .navutil-whatsapp svg
     below); fixing it here too before it has the chance to recur. */
  flex-shrink: 0;
}

.iconbtn {
  color: var(--olp-fg-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 9px;
  margin-left: 20px;
  border-radius: 50%;
  transition: color 0.3s var(--olp-ease), background 0.3s var(--olp-ease);
  position: relative;
  background: none;
  border: none;
  cursor: pointer;
}

.iconbtn:first-child {
  margin-left: 0;
}

.iconbtn svg {
  width: 17px;
  height: 17px;
  /* Without this, a plain SVG (no HTML width/height attribute, only
     viewBox) can get flex-shrunk to 0 width inside .iconbtn's tight
     box — this is exactly what silently broke the WhatsApp icon.
     Fixing it here too so it can't happen again for any future icon
     that only relies on this generic rule. */
  flex-shrink: 0;
}

.iconbtn:hover {
  color: var(--olp-fg);
  background: rgba(241, 238, 230, 0.06);
}

.iconbtn .dot {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--pink);
  border: 1.5px solid var(--olp-bg);
}

/* Cart — icon plus a visible "Cart" label, required at every width (not
   an icon-only affordance) per the navigation correction pass. Overrides
   the plain circular .iconbtn shape since it now carries text too. */
.cart-toggle {
  width: auto;
  height: auto;
  padding: 8px 10px;
  border-radius: 20px;
  gap: 6px;
}

.cart-toggle svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

/* The actual root cause of the "empty 18×18 link" bug: the generic
   .iconbtn svg rule below has no flex-shrink:0, so inside the tight
   .iconbtn flex box, a plain SVG with no HTML width/height attribute
   (only viewBox) was being flex-shrunk to 0 width — this is why Cart
   and Menu (which each already have their own dedicated rule with
   flex-shrink:0) rendered fine while WhatsApp, relying only on the
   generic rule, silently collapsed to nothing. Fixed at both layers:
   explicit width/height attributes on the SVG itself in Header.tsx,
   and this dedicated rule matching the working icons' pattern. */
.navutil-whatsapp svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.cart-label {
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.cart-toggle .dot {
  top: 2px;
  right: 2px;
}

/* Menu — visible "Menu" text next to the hamburger glyph, mobile only.
   Text-only hamburgers tested badly ("no visible way to reach
   navigation" was the actual bug report this fixes), so the word is
   never hidden behind just an icon. */
.menu-toggle {
  display: none;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  color: var(--olp-fg-muted);
  cursor: pointer;
  padding: 8px 10px;
  margin-left: 20px;
  border-radius: 20px;
  transition: color 0.3s var(--olp-ease), background 0.3s var(--olp-ease);
}

.menu-toggle:first-child {
  margin-left: 0;
}

.menu-toggle:hover {
  color: var(--olp-fg);
  background: rgba(241, 238, 230, 0.06);
}

.menu-toggle svg {
  width: 16px;
  height: 16px;
}

.menu-toggle span {
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

@media (max-width: 860px) {
  .menu-toggle {
    display: inline-flex;
  }

  .navutil-instagram {
    display: none;
  }

  /* Same reasoning as Instagram above — the persistent header icon is
     desktop-only; mobile's WhatsApp access lives in the menu drawer as
     visible "WHATSAPP ↗" text instead (see PageLayout.tsx). */
  .navutil-whatsapp {
    display: none;
  }
}

.header-menu-mobile {
  display: flex;
  flex-direction: column;
  gap: var(--olp-space-3);
  padding: var(--olp-space-4) var(--olp-gutter);
}

/* Instagram link inside the mobile menu drawer, sitting below the main
   nav list with clear separation — its own row, not crowded into the
   nav list itself. */
.mobile-menu-instagram,
.mobile-menu-whatsapp {
  display: block;
  color: var(--olp-fg-muted);
  text-decoration: none;
  font-size: 13.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: color 0.3s var(--olp-ease);
}

.mobile-menu-instagram {
  margin-top: var(--olp-space-4);
  padding-top: var(--olp-space-4);
  border-top: 1px solid var(--olp-line-dark);
}

.mobile-menu-whatsapp {
  margin-top: 14px;
}

.mobile-menu-instagram:hover,
.mobile-menu-whatsapp:hover {
  color: var(--pink);
}

/*
* --------------------------------------------------
* INSTAGRAM FOLLOW — replaces the old email-signup section everywhere
* (homepage rail, Limited Editions, Original Art, Prints) now that
* OLPINER isn't running an email list. One complete sentence as a
* single link — not a separate pink heading above an unrelated
* "@handle" link, which read as two disconnected pieces. The whole
* sentence shares one font/size/weight/letter-spacing and stays white
* at rest; pink is reserved for hover/focus only.
* --------------------------------------------------
*/
.instagram-follow {
  background: var(--olp-bg);
  /* Compact rail target (~64-80px total including padding/border) —
     was clamp(40px,7vw,72px) top+bottom alone, closer to 130-180px
     total once the link and its own line-height were added. */
  padding: clamp(18px, 3vw, 26px) 6vw;
  border-top: 1px solid var(--olp-line-dark);
}

.instagram-follow-inner {
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
}

.instagram-follow-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: var(--text-button);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--olp-fg);
  text-decoration: none;
  border-bottom: 1px solid var(--olp-line);
  padding-bottom: 3px;
  transition: color 0.25s var(--olp-ease), border-color 0.25s var(--olp-ease);
}

.instagram-follow-cta:hover {
  color: var(--pink);
  border-color: var(--pink);
}

.instagram-follow-cta:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 4px;
}

/* Compact desktop-homepage rail variant — shares the row with the
   footer essentials (see PageLayout.tsx's HomeCompactRail), same
   ~64-80px height budget Section 3 asks for. */
.instagram-follow--compact {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  /* .instagram-follow's own padding (clamp(18px,3vw,26px) 6vw) was
     still applying here too — both classes sit on the same <section>,
     and this variant never reset it. That alone was ~55-70px of the
     rail's ~105px measured height; zeroing it here is what actually
     gets this row down to the compact ~64-80px target, not just the
     smaller font-size already set below. */
  padding: 0;
}

.instagram-follow--compact .instagram-follow-cta {
  font-size: 12.5px;
  white-space: nowrap;
}

/* Inline variant - Limited Editions, Original Art, Prints (and the
   homepage's rarer Locked/Archived states) merge this directly into
   their own composition rather than showing it as a visually separate
   section below. */
.instagram-follow--inline {
  max-width: 460px;
}

@media (max-width: 640px) {
  .instagram-follow {
    padding: 20px 24px 28px;
  }
}

/*
* --------------------------------------------------
* DESKTOP HOMEPAGE COMPACT RAIL — Instagram follow CTA + footer merged
* into one row so header + hero + rail together equal exactly 100svh
* (see .drop-home's desktop-only exact-fit rule). See PageLayout.tsx
* for why this exists as a separate sibling to the normal
* InstagramFollow/Footer rather than a
* variant of Footer itself.
* --------------------------------------------------
*/
.mobile-home-rail {
  display: block;
}

.desktop-home-rail {
  display: none;
}

@media (min-width: 861px) {
  .mobile-home-rail {
    display: none;
  }

  .desktop-home-rail {
    display: block;
    flex-shrink: 0;
  }
}

.compact-rail {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(16px, 2.5vw, 40px);
  /* 20px top/bottom lands the row's total height (padding + content)
     in the requested ~64-80px range — measured directly after fixing
     the padding-duplication bug above, not assumed from the numbers
     alone. */
  padding: 20px 6vw;
  border-top: 1px solid var(--olp-line-dark);
  background: var(--olp-bg);
}


.compact-rail-footer {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
}

.compact-rail-brand {
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: 13.5px;
  letter-spacing: 0.1em;
  color: var(--olp-fg);
  text-decoration: none;
  white-space: nowrap;
}

.compact-rail-footer .iconbtn {
  margin-left: 0;
  width: 16px;
  height: 16px;
}

.compact-rail-copy {
  font-size: 10.5px;
  color: var(--olp-fg-muted);
  opacity: 0.6;
  white-space: nowrap;
}

/* Ultrawide: cap the rail's own inner width so its contents don't
   stretch into an awkwardly sparse single line — same "balanced space,
   not stretched content" principle as the hero media region. */
@media (min-width: 1900px) {
  .compact-rail {
    max-width: 1800px;
    margin: 0 auto;
  }
}

/* Footer links share one flow; copyright has its own centered row. */
footer {flex-shrink:0;padding:18px var(--olp-gutter) 16px;background:var(--olp-bg);border-top:1px solid var(--olp-line-dark)}
.footer-row {display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:12px clamp(12px,1vw,20px)}
.footer-row .footer-group {display:contents}
.footer-brand {font-family:var(--olp-font-body);font-weight:500;color:var(--olp-fg)}
.footer-row a {flex-shrink:0;white-space:nowrap;font-size:11px;letter-spacing:.07em;text-transform:uppercase;text-decoration:none;line-height:1.6}
.footer-group a {color:var(--olp-fg-muted);transition:color .3s}
.footer-group a:hover,.footer-group a.active {color:var(--pink)}
.footer-instagram,.footer-whatsapp {display:inline-flex;align-items:center}
footer .copy2 {margin:20px 0 0;width:100%;text-align:center;font-size:var(--text-xs);color:rgba(241,238,230,.4);letter-spacing:.06em}
@media(max-width:720px){footer{padding:24px 18px 20px}.footer-row{gap:14px 18px}.footer-row a{font-size:12px;padding-block:5px}}

/*
* --------------------------------------------------
* aside / cart drawer
* --------------------------------------------------
*/
/* Scoped to .overlay's own <aside> rather than a bare `aside[class]`
   attribute selector — the real Aside.tsx component never sets a
   className on this element at all, so `[class]` (which requires the
   attribute to be present) never matched it. This was a real, previously
   undiscovered bug: every drawer (cart/search/mobile menu) has never
   actually had its fixed positioning or slide-in transform applied —
   found while verifying the mobile menu drawer for the navigation
   correction pass, but it affects all three drawers equally, in
   production too, not just this preview. */
.overlay aside {
  background: var(--olp-bg);
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
  height: 100vh;
  position: fixed;
  right: 0;
  top: 0;
  transform: translateX(100%);
  transition: transform var(--olp-duration) var(--olp-ease);
  width: min(420px, 100vw);
  z-index: 60;
}

.overlay {
  background: rgba(11, 11, 12, 0.5);
  bottom: 0;
  left: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  right: 0;
  top: 0;
  transition: opacity var(--olp-duration) var(--olp-ease);
  z-index: 55;
}

.overlay .close-outside {
  background: transparent;
  border: 0;
  cursor: default;
  height: 100%;
  position: absolute;
  width: calc(100% - 420px);
}

.overlay.expanded {
  opacity: 1;
  pointer-events: auto;
}

.overlay.expanded aside {
  transform: translateX(0);
}

.overlay .close {
  background: transparent;
  border: none;
  color: var(--olp-fg);
  cursor: pointer;
  font-family: var(--olp-font-mono);
  font-size: var(--olp-size-small);
  letter-spacing: var(--olp-tracking-label);
  padding: var(--olp-space-3) var(--olp-gutter);
  position: absolute;
  right: 0;
  text-transform: uppercase;
  top: 0;
}

aside header {
  align-items: center;
  border-bottom: 1px solid var(--olp-border);
  display: flex;
  height: auto;
  justify-content: space-between;
  padding: var(--olp-space-3) var(--olp-gutter);
  position: static;
}

aside header h3 {
  font-family: var(--olp-font-mono);
  font-size: var(--olp-size-small);
  letter-spacing: var(--olp-tracking-label);
  text-transform: uppercase;
}

aside main {
  overflow-y: auto;
  padding: var(--olp-space-3) var(--olp-gutter);
}

/* Keep checkout in the viewport; only the item list grows and scrolls. */
aside[data-aside='cart'] {
  height: 100dvh;
  display: flex;
  flex-direction: column;
}
aside[data-aside='cart'] > header { flex-shrink: 0; }
aside[data-aside='cart'] > main {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
}
aside[data-aside='cart'] .cart-main,
aside[data-aside='cart'] .cart-details { height: 100%; min-height: 0; }
aside[data-aside='cart'] .cart-details {
  display: flex;
  flex-direction: column;
}
aside[data-aside='cart'] .cart-line-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
aside[data-aside='cart'] .cart-summary-aside {
  flex-shrink: 0;
  max-height: 60%;
  overflow-y: auto;
  margin-top: 12px;
  padding-top: 16px;
}
aside[data-aside='cart'] .cart-subtotal { margin-bottom: 12px; }

/*
* --------------------------------------------------
* cart — extremely restrained by design: image, title, selected
* options, quantity (except one-of-one Original Art), price, remove,
* subtotal, checkout. No recommendations, no trust badges, no
* urgency messaging.
* --------------------------------------------------
*/
.cart-empty {
  padding: var(--olp-space-4) 0;
}

.cart-empty p {
  color: var(--olp-fg-muted);
  margin-bottom: var(--olp-space-2);
}

.cart-line {
  border-bottom: 1px solid var(--olp-line-dark);
  padding: var(--olp-space-3) 0;
}

.cart-line-inner {
  display: flex;
  gap: var(--olp-space-2);
}

.cart-line-image {
  background: var(--olp-surface);
  flex-shrink: 0;
  height: 84px;
  object-fit: cover;
  width: 84px;
}

/* Prints and Original Art are never cropped, even in the cart — same
   principle as their room/grid/Piece-page treatment. Apparel keeps
   object-fit: cover since a garment photo has no "whole artwork" to
   preserve the way a print or painting does. */
.cart-line-image--contain {
  object-fit: contain;
  padding: 6px;
}

.cart-line-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.cart-line-title {
  color: var(--olp-fg);
  font-size: var(--olp-size-body);
  text-decoration: none;
}

.cart-line-title:hover {
  color: var(--pink);
}

.cart-line-options {
  color: var(--olp-fg-muted);
  font-size: 14px;
  list-style: none;
  margin-top: 4px;
}

.cart-line-price {
  font-size: 13.5px;
  margin-top: 8px;
}

.cart-line-quantity {
  align-items: center;
  display: flex;
  gap: var(--olp-space-2);
  margin-top: 10px;
}

.cart-line-stepper {
  align-items: center;
  border: 1px solid var(--olp-line);
  display: flex;
}

.cart-line-stepper-btn {
  background: none;
  border: none;
  color: var(--olp-fg);
  cursor: pointer;
  font-family: var(--olp-font-mono);
  padding: 4px 10px;
}

.cart-line-stepper-btn:hover:not(:disabled) {
  color: var(--pink);
}

.cart-line-stepper-btn:disabled {
  color: var(--olp-fg-muted);
  cursor: not-allowed;
}

.cart-line-qty-value {
  border-left: 1px solid var(--olp-line);
  border-right: 1px solid var(--olp-line);
  font-size: 14px;
  min-width: 26px;
  padding: 4px 0;
  text-align: center;
}

.cart-line-remove {
  background: none;
  border: none;
  color: var(--olp-fg-muted);
  cursor: pointer;
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-decoration: underline;
  text-transform: uppercase;
  text-underline-offset: 2px;
}

.cart-line-remove:hover:not(:disabled) {
  color: var(--pink);
}

.cart-summary-page,
.cart-summary-aside {
  border-top: 1px solid var(--olp-line);
  margin-top: var(--olp-space-3);
  padding-top: var(--olp-space-3);
}

.cart-subtotal {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--olp-space-3);
}

.cart-subtotal dt {
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
}

.cart-subtotal dd {
  font-size: 18px;
  color: var(--olp-fg);
}

.cart-checkout-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 48px;
  margin-top: var(--olp-space-2);
  text-decoration: none;
  border-radius: 0;
  font-family: var(--olp-font-body);
  letter-spacing: 0.02em;
}

.cart-checkout-btn--disabled {
  flex-direction: column;
  gap: 3px;
  height: auto;
  min-height: 48px;
  padding: 10px;
  border: 1px solid var(--olp-line);
  color: var(--olp-fg-muted);
  cursor: not-allowed;
}

.cart-checkout-btn--disabled span:first-child {
  font-size: 13px;
  font-weight: 500;
}

.cart-checkout-reason {
  font-family: var(--olp-font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  opacity: 0.7;
}

/* Discount/gift-card disclosure sits inside the cart summary — same
   pattern as the Piece pages' Details accordion, just scoped smaller. */
.cart-summary-page .disclosure-btn,
.cart-summary-aside .disclosure-btn {
  font-size: 14px;
  padding: 12px 0;
}

.cart-summary-page .disclosure-body,
.cart-summary-aside .disclosure-body {
  padding-bottom: 16px;
}

.cart-summary-page input[type='text'],
.cart-summary-aside input[type='text'] {
  background: rgba(241, 238, 230, 0.04);
  border: 1px solid rgba(241, 238, 230, 0.22);
  border-radius: 0;
  appearance: none;
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-size: 14px;
  padding: 8px 12px;
  outline: none;
}

.cart-summary-page input[type='text']:focus,
.cart-summary-aside input[type='text']:focus {
  border-color: var(--pink);
}

.cart-summary-page button[type='submit'],
.cart-summary-aside button[type='submit'] {
  background: none;
  border: 1px solid var(--olp-line);
  color: var(--olp-fg);
  cursor: pointer;
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  padding: 8px 12px;
  text-transform: uppercase;
}

.cart-summary-page button[type='submit']:hover,
.cart-summary-aside button[type='submit']:hover {
  border-color: var(--pink);
  color: var(--pink);
}

/*
* --------------------------------------------------
* product options / gallery
* --------------------------------------------------
*/
.product-options {
  border-bottom: 1px solid var(--olp-border);
  margin-bottom: var(--olp-space-3);
  padding-bottom: var(--olp-space-3);
}

.product-options-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--olp-space-1);
  margin-top: var(--olp-space-2);
}

.product-options-item {
  background: var(--olp-surface);
  border: 1px solid var(--olp-border);
  border-radius: var(--olp-radius);
  cursor: pointer;
  font-family: var(--olp-font-mono);
  font-size: var(--olp-size-small);
  letter-spacing: 0.04em;
  padding: 0.65rem 1.1rem;
  text-transform: uppercase;
  transition: background var(--olp-duration-fast) var(--olp-ease), color var(--olp-duration-fast) var(--olp-ease);
}

.product-options-item[data-selected='true'] {
  background: var(--olp-fg);
  color: var(--olp-bg);
}

.product-options-item[data-available='false'] {
  color: var(--olp-fg-muted);
  opacity: 0.45;
  position: relative;
  text-decoration: line-through;
}

.olp-product-layout {
  display: grid;
  gap: var(--olp-space-5);
  grid-template-columns: 1fr;
}

@media (min-width: 56em) {
  .olp-product-layout {
    grid-template-columns: 1.1fr 1fr;
    align-items: start;
  }
}

/*
* --------------------------------------------------
* DROP — the homepage state machine
* Ported directly from the approved prototype's .room/.stage/.copy system
* (identical structure across olpiner-drop-room.html, olpiner-drop-live.html,
* and reused for sold-out/archived, which the prototype doesn't cover but
* which share the same room shell per DESIGN_INTEGRATION_PLAN.md §8).
* --------------------------------------------------
*/
.room {
  position: relative;
  flex: 1;
  overflow: hidden;
  display: flex;
  /* Was flex-end — pinned .copy to the room's bottom edge, which read
     as "attached to the email/footer rail" rather than balanced with
     the shirt beside it. .room is a row-direction flex container
     (default), so align-items controls the vertical (cross) axis only
     — .copy stays exactly as left-aligned as before; only its vertical
     position changes. */
  align-items: center;
  min-height: 0;
  /* No width cap here anymore — .room (and .hero-video-stage's black
     background inside it) must stay full-bleed on ultrawide monitors.
     The bounded region that keeps the *shirt itself* from drifting
     toward the far-right edge is .hero-media-region below, one layer
     further in, so the background can be 100vw while the shirt's own
     positioning still has a sensible max width. */
}

.stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.garment-wrap {
  position: absolute;
  top: -8%;
  right: -6%;
  width: 78%;
  height: 128%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.garment-wrap.dragging {
  cursor: grabbing;
}

.garment-wrap.idle-drift {
  animation: olp-drift 7s ease-in-out infinite;
}

@keyframes olp-drift {
  0%,
  100% {
    transform: translateY(0) rotate(0deg);
  }
  50% {
    transform: translateY(-6px) rotate(0.3deg);
  }
}

.garment-wrap img {
  position: absolute;
  width: 100%;
  height: auto;
  max-width: 1000px;
  filter: drop-shadow(0 40px 60px rgba(0, 0, 0, 0.6)) brightness(0.94);
  transition: opacity 0.6s var(--olp-ease);
  pointer-events: none;
}

.garment-wrap img.frame {
  opacity: 0;
}

.garment-wrap img.frame.active {
  opacity: 1;
}

.stage::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 78% 45%, transparent 0%, rgba(0, 0, 0, 0.25) 62%, rgba(0, 0, 0, 0.55) 100%);
  pointer-events: none;
}

.stage::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, rgba(3, 3, 3, 0.94) 0%, rgba(3, 3, 3, 0.72) 26%, rgba(3, 3, 3, 0.18) 52%, transparent 66%);
  pointer-events: none;
}

.flip-cue {
  position: absolute;
  z-index: 5;
  bottom: 8%;
  right: 8%;
  background: rgba(5, 5, 5, 0.5);
  border: 1px solid rgba(241, 238, 230, 0.28);
  color: var(--olp-fg);
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 9px 16px;
  border-radius: 20px;
  cursor: pointer;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.4s var(--olp-ease), transform 0.4s var(--olp-ease), border-color 0.3s;
  backdrop-filter: blur(6px);
}

.room:hover .flip-cue {
  opacity: 1;
  transform: translateY(0);
}

/*
* --------------------------------------------------
* HERO VIDEO — homepage only. GarmentStage's .stage (absolute, inset:0,
* overflow:hidden) is reused as-is via the shared class; this only adds
* what's specific to a <video> filling that same box.
* --------------------------------------------------
*/
.hero-video-stage {
  /* Just the full-bleed black background layer now — the flex/
     positioning role that used to live here moved to .hero-media-region
     below it, so this can stay uncapped (100% of .room, itself
     uncapped) while positioning is bounded one layer further in. */
  overflow: hidden;
  /* Sampled directly from the video file's own corner pixels — it
     renders against pure #000000, not the site's --void (#050505).
     Matching that exactly (not the site's usual token) is what makes
     the video's own rectangular edge genuinely disappear instead of
     showing as a mismatched panel. This is deliberately specific to
     this one stage, not a site-wide value. */
  background: #000;
}

.hero-media-region {
  /* No width cap anymore — this used to be capped at 1800px, which on
     a genuine 34" ultrawide (3440px+) left the whole composition
     looking like an isolated block with visible black side columns
     instead of using the screen. Full width now; the shirt's position
     is controlled entirely by --hero-media-translate-x below instead
     of by capping this container. */
  position: relative;
  width: 100%;
  height: 100%;
  /* --hero-media-x/-y are also defined here (matching .hero-video's own
     values exactly, at every breakpoint below) so a sibling element
     with no real media yet (.hero-placeholder-stage, currently used
     for SHARK) can share the identical position via inheritance from
     this common parent. CSS custom properties don't cross between
     siblings — defining them only on .hero-video meant the placeholder
     had no way to know where "the same spot the shirt would sit" is,
     and defaulted to the region's own center instead, drifting into
     the text/selector column. Only x/y are duplicated here, not
     scale/max-size — the placeholder sizes to its own content, it
     doesn't need the video's size variables at all. */
  --hero-media-x: 70%;
  --hero-media-y: 50%;
}

@media (min-width: 1900px) {
  .hero-media-region {
    --hero-media-x: 70%;
  }
}

@media (max-width: 960px) {
  .hero-media-region {
    --hero-media-x: 50%;
  }
}

@media (max-width: 860px) {
  .hero-media-region {
    --hero-media-x: 50%;
    --hero-media-y: 50%;
  }
}

/* The dark gradients on .stage::before/::after exist for the Piece
   page's real product photography (GarmentStage), where they keep the
   copy text legible against a busy, lighter image. The hero video is
   already on pure black, so the heavy version isn't needed — but the
   text (.copy) is genuinely layered on top of the video/room at every
   viewport, including mobile, and completely removing the gradient
   made text unreadable where the enlarged garment sits behind it. This
   is the "reduce" half of "remove or reduce" — much lighter than
   .stage's default, present only near where the copy panel actually
   sits. */
.hero-video-stage::before {
  display: none;
}

/* Removed entirely, not just reduced — tested at several opacity levels
   (0.55, then 0.32) against real screenshots, and even the lighter
   version still made the garment hard to distinguish against the black
   background (dark garment + dark overlay compounds visually more than
   the raw opacity numbers suggest). The copy text already has its own
   strong contrast (pink/white, bold weight) against black without this
   gradient's help — confirmed legible with it fully disabled. */
.hero-video-stage::after {
  display: none;
}

.hero-video {
  /* Sized to its own contained dimensions (not width/height:100% with
     letterboxing) so the position transform below acts on the
     garment's actual visible box, not a loose parent-sized box with
     empty margin baked in. */
  height: var(--hero-media-max-size);
  width: auto;
  /* New render is a square 2880×2880 source (transcoded to 1600×1600),
     not the old portrait 734×1304 — object-fit:contain against the
     matching 1/1 box shows the complete, undistorted frame. */
  aspect-ratio: 1 / 1;
  object-fit: contain;
  display: block;
  /* Absolute + left:X% + translate(-50%, ...) positions the shirt's own
     CENTER at exactly X% of the full-width .hero-media-region — not X%
     of transform-origin. This formula holds the same percentage
     position at any viewport width, transform-origin is always
     literally "center", and scale() is purely a size multiplier. */
  position: absolute;
  top: var(--hero-media-y);
  left: var(--hero-media-x);
  transform-origin: center;
  --hero-media-scale: 1;
  --hero-media-x: 70%;
  --hero-media-y: 50%;
  /* The garment fills ~93% of its own video frame (measured directly
     against the source render, not assumed) — targeting ~82% of the
     room's actual available height here lands the *visible garment*
     at the requested ~70–78%, not the box itself. --header-height and
     the ~75px compact rail are real, measured values (see .drop-home's
     exact-fit rule and the compact rail's own CSS), not guesses. */
  --hero-media-max-size: calc((100svh - var(--header-height) - 75px) * 0.82);
  transform: translate(-50%, -50%) scale(var(--hero-media-scale));
  transition: transform 0.3s var(--olp-ease), left 0.3s var(--olp-ease);
}

/* Short laptop screens (1366×768, 1280×720-class — limited height, not
   necessarily limited width) — the height-driven formula above would
   otherwise size the garment against a very short available hero
   height and read as cramped; a slightly larger multiplier compensates
   since these screens have the width to support it even without much
   height. */
@media (min-height: 700px) and (max-height: 820px) {
  .hero-video {
    --hero-media-max-size: calc((100svh - var(--header-height) - 75px) * 0.86);
  }
}

@media (min-width: 1900px) {
  .hero-video {
    /* Ultrawide: the height-based formula already scales independently
       of width (no fixed max-width to hit a ceiling on), so this only
       needs its own --hero-media-x — 70% of a 3440px viewport is a much
       larger absolute offset than the same percentage at 1440px, kept
       explicit here rather than assumed to just carry over correctly. */
    --hero-media-x: 70%;
  }
}

@media (max-width: 960px) {
  .hero-video {
    --hero-media-max-size: calc((100svh - var(--header-height) - 75px) * 0.7);
    --hero-media-scale: 1.05;
    --hero-media-x: 50%;
  }
}

@media (max-width: 860px) {
  .hero-video {
    /* The rotating shirt has its own dedicated block in normal
       document flow (.hero-block-media, sized by --hero-media-height-
       mobile below) — not a cramped leftover space behind the copy
       like the previous absolutely-positioned layout. So this sizes
       against that block's own real height, filling nearly all of it.
       Named to match the requested --hero-media-*-mobile scheme;
       these are what .hero-video's shared properties actually resolve
       to at this width. Matches .hero-block-media's own height value
       exactly (see the mobile reflow rules above) — the two must stay
       in sync since the video sizes itself against this block. */
    --hero-media-scale-mobile: 1;
    --hero-media-x-mobile: 50%;
    --hero-media-y-mobile: 50%;
    --hero-media-height-mobile: clamp(260px, 34svh, 340px);

    --hero-media-scale: var(--hero-media-scale-mobile);
    --hero-media-x: var(--hero-media-x-mobile);
    --hero-media-y: var(--hero-media-y-mobile);
    /* Slightly under the block's own height so the garment (which
       nearly fills its own frame — measured directly against the
       source video) reads as ~80–90% of the available width, matching
       the requested target, with a touch of breathing room rather than
       touching the block's own edges. */
    --hero-media-max-size: calc(var(--hero-media-height-mobile) * 0.92);
    /* height-based sizing with a 1:1 aspect-ratio derives width from
       height — on a narrow, tall phone the height target alone could
       compute a width wider than the viewport. This caps the actual
       rendered box to whichever of height/width is more restrictive,
       so the ~80–90%-of-width target is genuinely honored, not just
       assumed to follow from the height. */
    max-height: var(--hero-media-max-size);
    max-width: 86vw;
    height: auto;
    width: auto;
  }

  @media (max-height: 820px) {
    .hero-video {
      --hero-media-height-mobile: clamp(230px, 30svh, 300px);
    }
  }
}

.flip-cue:hover {
  border-color: var(--pink);
  color: var(--pink);
}

.flip-cue .ico {
  display: inline-block;
  margin-right: 6px;
}

/* ---------- COPY (the text overlay on the room) ---------- */
.copy {
  position: relative;
  z-index: 3;
  /* Was 0 top / up to 56px bottom — that asymmetry made sense when
     .room bottom-anchored this block (breathing room above the rail);
     now that .room vertically centers it, matching that gap on both
     sides keeps it genuinely centered rather than sitting slightly low. */
  padding: clamp(24px, 6vh, 56px) 6vw;
  max-width: 640px;
}

/*
* --------------------------------------------------
* PIECE LAYOUT — product page's gallery + info, side by side
* --------------------------------------------------
* Replaces GarmentStage's layered "text over image" approach for this
* page specifically — the correction that introduced this explicitly
* asked for product info to sit beside the media, never on top of it
* (the old approach was reported as making the media area read as
* "almost completely empty" at some sizes). .copy is reused as-is on
* the homepage (still layered there, unchanged) — this resets just the
* positioning that only made sense in that overlay context.
*/
.piece-layout {
  display: grid;
  /* The media column is a MEASURED width, not a fraction of the page.
     A fractional column grew with the viewport, which is how the
     garment ended up dominating at ~604px wide on a 1440 screen. A
     fixed 400-440px column keeps the shirt the size of an object on a
     page rather than a backdrop, and keeps the title, price and the
     colour/size controls beside it in proportion.

     The whole grid is then centred and capped, so the two columns stay
     together in the middle of wide screens instead of drifting apart. */
  /* Sized so the VISIBLE GARMENT — not its image canvas — lands around
     470-500px on a wide desktop. The derivatives are now trimmed to the
     garment (see scripts/lib/png.mjs trimTransparent), so frame width
     and shirt width are finally the same measurement; the previous
     432px frame was only showing a ~320px shirt because a third of the
     canvas was empty. */
  grid-template-columns: clamp(420px, 38vw, 540px) minmax(0, 1fr);
  max-width: 1280px;
  margin-inline: auto;
  /* Explicit rows/areas — with three direct children now (piece-heading,
     EditionGallery, copy) instead of two, default grid auto-placement
     would put the third item back in column 1 on a new row, splitting
     the gallery's column. Named areas keep the gallery spanning both
     rows in column 1 while heading+copy stack in column 2, exactly the
     visual result the previous two-child grid had. */
  grid-template-areas: 'gallery heading' 'gallery copy';
  grid-template-rows: auto 1fr;
  /* Generous separation between the media and the information — the
     restraint this page is after comes from the space, not from
     decoration. */
  column-gap: clamp(48px, 6vw, 96px);
  row-gap: clamp(20px, 2.5vw, 32px);
  padding: clamp(28px, 4vw, 56px) 6vw clamp(48px, 6vw, 80px);
  align-items: start;
}

.piece-layout > .edition-gallery {
  grid-area: gallery;
}

.piece-heading {
  grid-area: heading;
}

.piece-layout .copy {
  grid-area: copy;
  position: static;
  z-index: auto;
  padding: 0;
  max-width: none;
}

@media (max-width: 960px) {
  .piece-layout {
    grid-template-columns: 1fr;
    /* Mobile order: product heading, then media (gallery — internally
       reordered to carousel-then-thumbs, see the gallery rules below),
       then price/size/button/collaboration (copy) — the reverse of
       desktop's stacking, achieved the same way as the homepage hero's
       mobile reflow: named areas swap for a plain single-column order
       sequence, not a duplicated markup structure. */
    grid-template-areas: 'heading' 'gallery' 'copy';
    grid-template-rows: auto;
    padding: 0 0 40px;
    /* Spacing between the three blocks is set per block below, because
       the two gaps want different sizes: a single 28px gap applied to
       both left 88px between the title and the collar once the title's
       own 32px bottom margin and the approved stills' built-in
       transparent top band (62 of 1024px, ~28px here) were added. */
    gap: 0;
  }

  /* Title to shirt: the heading introduces the garment directly beneath
     it. With the title's bottom margin removed this leaves ~44px from
     the title to the collar at 390 — 16px here plus the still's own
     transparent band, which stays, so every garment keeps the same
     breathing room above its collar. */
  .piece-heading {
    padding: 0 20px;
    margin-bottom: 16px;
  }

  .piece-heading h1 {
    margin-bottom: 0;
  }

  /* Thumbnails to price keeps the previous 28px. */
  .piece-layout > .edition-gallery {
    margin-bottom: 28px;
  }

  /* The same inset for the price, colour and size controls. The
     single-column layout drops its own padding so the gallery can run
     edge to edge, and without this the controls under it did too —
     sitting flush against the screen edge beneath a padded heading. */
  .piece-layout .copy {
    padding-inline: 20px;
  }
}

/*
* --------------------------------------------------
* EDITION GALLERY — reusable across any edition (see edition.ts)
* --------------------------------------------------
*/
.edition-gallery {
  position: sticky;
  top: calc(var(--header-height) + 24px);
}

@media (max-width: 960px) {
  .edition-gallery {
    position: static;
  }
}

.edition-gallery-stage {
  width: 100%;
  /* PORTRAIT, because the approved stills are. All 21 share one
     768x1024 canvas with the garment ~730x900 inside it, and the old
     5:4 landscape frame was written for trimmed landscape photographs:
     height-limited, it drew the shirt at 296x365 in a 540x432 box and
     left a third of the column empty on either side.

     9:10 draws the same stills 1.40x larger at ANY column width — the
     ratio of (10/9 - 3%) to (4/5 - 3%) content heights — so the increase
     is a property of the shape, not of one viewport. object-fit: contain
     below still guarantees collar, sleeves and hem are never cut.

     The cap keeps the thumbnails on the first screen, which matters
     more here than usual: the gallery is sticky, so a gallery taller
     than the viewport would hide its own thumbnails until the whole
     information column had scrolled past. The subtraction is the header,
     the layout's top padding and the 16px + 60px thumbnail row plus a
     breath. Not reached at 1440x900 or 1280x800; a 768px-high laptop
     trades a little of the increase for reachable thumbnails. */
  aspect-ratio: 9 / 10;
  max-height: max(380px, calc(100svh - var(--header-height) - clamp(28px, 4vw, 56px) - 96px));
  background: transparent;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The garment is inset within its frame, which is what actually makes
   the presentation smaller: the frame keeps its layout size, the shirt
   sits at ~72% of it. Combined with the narrower column this is roughly
   a 25-30% reduction from the previous presentation, and it guarantees
   the collar, both sleeves and the hem stay clear of the frame edge. */
.edition-gallery-stage-img,
.edition-gallery-stage .edition-video,
.edition-gallery-stage video {
  width: 100%;
  height: 100%;
  /* contain, always — never cover. A cover fit crops a garment, and the
     brief for this page is that the whole garment stays visible. */
  object-fit: contain;
  object-position: center;
  /* The derivatives now carry only a few pixels of their own border, so
     this is a hairline inset to keep a feathered sleeve off the frame
     edge — not a second round of shrinking. */
  padding: 1.5%;
  box-sizing: border-box;
}

.edition-gallery-mobile-slide img {
  object-fit: contain;
  padding: 6%;
  box-sizing: border-box;
}

.edition-gallery-stage-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.edition-gallery-thumbs {
  display: flex;
  flex-wrap: wrap;
  /* Centred on the same axis as the garment, which object-fit centres
     within the full-width stage. Left-aligned, the strip started under
     the empty margin beside the shirt rather than under the shirt. */
  justify-content: center;
  gap: 8px;
  margin-top: 16px;
}

.edition-gallery-thumb {
  /* Compact and uniform: six of these sit on one row under a 400-440px
     column without wrapping, and every one is the same square whatever
     the image inside it. */
  width: 60px;
  height: 60px;
  flex-shrink: 0;
  border: 1px solid var(--olp-line);
  background: #000;
  padding: 0;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity 0.25s var(--olp-ease), border-color 0.25s var(--olp-ease);
  position: relative;
  overflow: hidden;
}

.edition-gallery-thumb:hover,
.edition-gallery-thumb:focus-visible {
  opacity: 0.85;
}

.edition-gallery-thumb[data-selected='true'] {
  opacity: 1;
  border-color: var(--pink);
}

.edition-gallery-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.edition-gallery-thumb-video {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}

.edition-gallery-thumb-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 20px;
  height: 20px;
}

/* Mobile carousel — hidden on desktop, where the stage + thumb strip
   above handle everything. */
.edition-gallery-mobile-track {
  display: none;
}

@media (max-width: 960px) {
  /* Real bug found via rendered browser testing (not visible from CSS
     alone): DOM order is stage, thumbs, carousel — with the stage
     hidden below, that put the thumb row visually ABOVE the carousel,
     the reverse of the requested "media, then thumbnails" order. flex
     + order fixes the visual order without changing the DOM (the
     thumb strip's aria roving-tabindex logic in EditionGallery.tsx
     doesn't care about visual position). */
  .edition-gallery {
    display: flex;
    flex-direction: column;
  }

  .edition-gallery-stage {
    display: none;
  }

  .edition-gallery-mobile-track {
    order: 1;
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }

  .edition-gallery-mobile-slide {
    flex: 0 0 100%;
    scroll-snap-align: center;
    aspect-ratio: 4 / 5;
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .edition-gallery-mobile-slide img {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }

  .edition-gallery-mobile-slide .edition-video-wrap,
  .edition-gallery-mobile-slide .edition-video {
    width: 100%;
    height: 100%;
  }

  .edition-gallery-mobile-slide .edition-video {
    object-fit: contain;
  }

  /* Real, larger thumbnails at mobile too (76–92px) — not the tiny
     position dots this used to convert to. The swipeable carousel
     above still handles swipe gestures; these stay genuinely tappable
     preview images underneath it, sized for a comfortable touch target
     rather than shrunk to a decorative indicator. */
  .edition-gallery-thumbs {
    order: 2;
    justify-content: center;
    margin-top: 12px;
  }

  .edition-gallery-thumb {
    width: 84px;
    height: 84px;
  }
}

/* .copy h1 and .piece-heading h1 — the product page's title moved out
   of .copy into its own .piece-heading sibling (so mobile can reorder
   it ahead of the gallery; see products.$handle.tsx), so this selector
   needs to match both now or the title silently falls back to the
   browser default size again — exactly the same class of bug already
   found and fixed for .home-title earlier in this pass. */
.copy h1,
.piece-heading h1 {
  font-family: var(--olp-font-display);
  font-weight: 900;
  text-transform: uppercase;
  font-size: clamp(34px, min(7vw, 9vh), 84px);
  line-height: 0.94;
  letter-spacing: -0.01em;
  color: var(--olp-fg);
}

/* .home-title (the collection-hero's title, see HomeApparelSelector.tsx)
   stopped matching this rule entirely when that component moved off
   the shared .copy wrapper onto its own .hero-block-eyebrowtitle — with
   no font-size rule of its own since, it was silently falling back to
   the browser/reset default h1 size (measured directly: 25.6px, not
   the intended large display treatment "9 FISH / 100" needs to read as
   the principal art object's title). Same treatment as .copy h1, with
   the requested larger minimum. */
.home-title {
  font-family: var(--olp-font-display);
  font-weight: 900;
  text-transform: uppercase;
  font-size: clamp(40px, min(7vw, 9vh), 84px);
  line-height: 0.94;
  letter-spacing: -0.01em;
  color: var(--olp-fg);
}

/* Desktop: the full title ("9 FISH / 100") must stay on one line — at
   the standard .copy max-width (640px) it was wide enough at large
   clamped sizes to wrap "/ 100" onto a second line. Widening .copy here
   (desktop only; mobile keeps its own wrapping behavior, unchanged) and
   forcing nowrap on the title specifically fixes it without touching
   font-size, since the room has ample width to spare at this size — the
   video is centered/letterboxed within its own stage, not edge-to-edge,
   so a wider copy column doesn't crowd it. */
@media (min-width: 861px) {
  .copy {
    max-width: 820px;
  }

  .copy h1,
  .piece-heading h1 {
    white-space: nowrap;
  }

  .home-title {
    white-space: nowrap;
  }
}

.credit {
  margin-top: clamp(10px, 2.2vh, 18px);
  padding-top: clamp(8px, 1.8vh, 16px);
  border-top: 1px solid var(--olp-line);
}

.credit-line {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(13.5px, 1.3vw, 15px);
  color: var(--olp-fg-muted);
  line-height: 1.5;
}

.credit-line b {
  color: var(--olp-fg);
  font-weight: 400;
  font-style: normal;
}

.credit-by {
  font-family: var(--olp-font-mono);
  font-size: var(--text-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  margin-top: 8px;
}

.credit-by b {
  color: var(--pink);
  font-weight: 400;
}

.desc {
  font-size: var(--text-body);
  line-height: 1.6;
  color: var(--olp-fg-muted);
  margin-top: clamp(8px, 1.8vh, 16px);
  max-width: 420px;
}

/* ---------- countdown ---------- */
.countdown {
  display: flex;
  gap: 30px;
  margin-top: clamp(16px, 3.5vh, 34px);
}

.countdown .unit {
  text-align: left;
}

.countdown .num {
  font-family: var(--olp-font-mono);
  font-weight: 700;
  font-size: clamp(24px, 3vw, 34px);
  color: var(--olp-fg);
  line-height: 1;
  display: inline-block;
}

.countdown .lbl {
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--olp-fg-muted);
  text-transform: uppercase;
  margin-top: 6px;
  display: block;
}

/* ---------- notify / access-code form ----------
   Shared shell for both — the prototype only shows a notify form on the
   locked room, but the access-code form is the same interaction pattern
   (single text input + adjacent button), so it reuses this exactly rather
   than inventing a second visual style. */
.notify {
  display: flex;
  flex-wrap: wrap;
  margin-top: clamp(14px, 3vh, 32px);
  max-width: 420px;
}

.notify input {
  flex: 1;
  background: rgba(241, 238, 230, 0.04);
  border: 1px solid rgba(241, 238, 230, 0.22);
  border-right: none;
  border-radius: 0;
  appearance: none;
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-size: 14px;
  padding: 0 16px;
  height: 46px;
  outline: none;
  transition: border-color 0.3s;
}

.notify input::placeholder {
  color: var(--olp-fg-muted);
}

.notify input:focus {
  border-color: var(--pink);
}

.notify button {
  background: var(--olp-fg);
  color: var(--olp-bg);
  border: none;
  height: 46px;
  padding: 0 22px;
  font-family: var(--olp-font-mono);
  font-size: 13.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.3s var(--olp-ease), color 0.3s var(--olp-ease);
  flex-shrink: 0;
}

.notify button:hover:not(:disabled) {
  background: var(--pink);
  color: var(--olp-bg);
}

.notify button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ---------- acquire / certificate (Live state) ---------- */
.acquire-btn {
  margin-top: clamp(14px, 3vh, 32px);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 420px;
  height: 50px;
  background: var(--olp-fg);
  color: var(--olp-bg);
  border: none;
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: var(--text-button);
  letter-spacing: 0.06em;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.35s var(--olp-ease), color 0.35s var(--olp-ease);
}

.acquire-btn:hover {
  background: var(--pink);
  color: var(--olp-bg);
}

/* Coming-soon button (Gorilla) — same footprint as .acquire-btn so
   switching entries doesn't jump the layout, but visually and
   functionally inert (disabled, no hover treatment) rather than
   looking like a live, clickable acquisition button. */
.acquire-btn--inactive,
.acquire-btn--inactive:hover {
  background: transparent;
  color: var(--olp-fg-muted);
  border: 1px solid var(--olp-line);
  cursor: default;
}

/*
* --------------------------------------------------
* HOME APPAREL SELECTOR / COLLECTION HERO — .room--collection is a
* four-block structure (eyebrow+title, selector, media, desc+cta) that
* genuinely reflows between desktop and mobile, not just resizes. See
* HomeApparelSelector.tsx's top-of-file comment for why: mobile needs
* the media positioned BETWEEN the selector and the description, which
* isn't achievable by CSS-repositioning one nested text block.
* --------------------------------------------------
*/
.room--collection {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* .room's base rule sets align-items:center (for the single-product
     hero's centering) — without an explicit override here that bleeds
     through onto this flex-column layout too, shrinking every
     .hero-block-* to its own content width (auto) instead of
     stretching to fill the available column, which was silently
     capping the selector and text blocks far narrower than intended. */
  align-items: stretch;
  height: 100%;
  /* The 6vw left offset lives here once, not repeated as padding on
     each max-width-capped block below — 6vw on an element that's ALSO
     max-width:640px scales with viewport width regardless of that cap
     (206px per side at 3440px, verified directly), silently eating
     most of a fixed-width box on ultrawide. One offset here, computed
     against the actual full-bleed room, stays correct at any width. */
  padding-left: 6vw;
  padding-right: 6vw;
}

.hero-block-eyebrowtitle,
.hero-block-name,
.hero-block-selector,
.hero-block-cta,
.hero-block-desc {
  position: relative;
  z-index: 1;
  max-width: 640px;
}

/* Desktop: the full-bleed black stage, spanning the entire room exactly
   as the single-product hero's .stage always has — absolutely
   positioned relative to .room--collection (position:relative above),
   deliberately escaping the 640px/padded text blocks' width so the
   background stays edge-to-edge regardless of how narrow the copy
   column is. */
.hero-block-media {
  position: absolute;
  /* Inset from the left past the copy column (max-width:640px plus the
     room's 6vw padding) so the garment and the heading can never
     overlap, at any width. Below that combined width the media simply
     has no room on this axis and the mobile layout takes over. */
  inset: 0 0 0 clamp(320px, calc(6vw + 640px), 62%);
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Vertical breathing room so the collar and hem are never clipped by
     the room's own edges. */
  padding: clamp(24px, 5vh, 64px) clamp(16px, 3vw, 48px);
  pointer-events: none;
}

/* The stage itself is capped rather than filling the inset area, so the
   garment reads as an object placed in the room instead of a full-bleed
   background. Tuned by eye against the real photographs at 1280, 1366
   and 1440 — the shirt sits clear of the text at all three. */
.collection-media-stage {
  position: relative;
  width: 100%;
  height: 100%;
  max-width: min(520px, 40vw);
  max-height: min(560px, 68vh);
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: center;
}

.collection-media-stage video,
.collection-media-stage .edition-video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* EditionVideo wraps its <video> in .edition-video-wrap, and that wrap
   had no height of its own — so the rule above resolved height:100%
   against an auto-height parent, fell back to the video's intrinsic
   aspect, and the stage's max-height cap never applied. object-fit:
   contain cannot crop what was never constrained.

   It went unnoticed while the only rotation was 9 FISH at 1600x1600:
   square, so at 520px wide it rendered 520px tall and happened to fit
   the 560px cap. A 768x1024 rotation renders 693px tall at the same
   width and overruns it, putting the garment's hem below the fold at
   1366x768 and 1280x720.

   Giving the wrap the stage's box makes contain mean what it says: the
   whole garment, whatever the footage's aspect ratio. The caps above
   are unchanged. */
.collection-media-stage .edition-video-wrap {
  width: 100%;
  height: 100%;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* contain, always: the whole garment — collar, both sleeves, hem — has
   to stay visible. These derivatives have a transparent background, so
   there is no box edge to hide and nothing to crop against. */
.collection-media-garment {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}

.hero-block-eyebrowtitle .home-title {
  margin-top: 6px;
}

/* ---------- selector ---------- */

.apparel-selector-label {
  font-size: 13.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  margin-bottom: 10px;
}

.apparel-selector {
  display: flex;
  width: 100%;
  gap: 10px;
}

/* Two EQUAL-WIDTH panels (flex:1 each), not compact auto-sized tabs —
   deliberately large and unmistakable rather than looking like
   pagination. Both shirt names are always visible without interacting;
   the FEATURED/COMING SOON tag makes the status legible at a glance
   too, not just on hover/focus. */
.apparel-selector-tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 3px;
  min-height: 64px;
  padding: 12px 18px;
  background: transparent;
  border: 1.5px solid var(--olp-line);
  cursor: pointer;
  color: var(--olp-fg-muted);
  text-align: left;
  transition: color 0.25s var(--olp-ease), border-color 0.25s var(--olp-ease), background 0.25s var(--olp-ease);
}

.apparel-selector-tab:hover {
  color: var(--olp-fg);
  border-color: var(--olp-fg-muted);
}

.apparel-selector-tab:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

/* Selected: border + text color + a restrained fill together — not
   relying on any single one of these alone, so the active tab reads as
   unmistakably active even if a visitor's display has weak contrast. */
.apparel-selector-tab[data-selected='true'] {
  color: var(--pink);
  border-color: var(--pink);
  background: rgba(230, 0, 190, 0.1);
}

.apparel-selector-num {
  font-size: 12px;
  letter-spacing: 0.04em;
  opacity: 0.7;
}

.apparel-selector-title {
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: 16.5px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  white-space: nowrap;
}

.apparel-selector-tag {
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.65;
}

/*
* --------------------------------------------------
* COLLECTION HERO — the six-design Guerrilla Poker collection teaser.
* See HomeCollectionHero.tsx. Reuses .room--collection/.hero-block-*
* for layout (same max-width/padding/mobile-order system the old
* two-shirt selector used) — only the content inside each block and
* the selector strip below are new.
* --------------------------------------------------
*/

/* .home-title's desktop nowrap rule is for short titles like the old
   "9 FISH / 100" state — "The Guerrilla Poker Collection" is longer
   and meant to wrap across two lines per the layout sketch, so this
   explicitly allows that rather than forcing a single line (which
   would either overflow or shrink the clamp() down to an
   illegibly small size to fit). */
.collection-title {
  white-space: normal !important;
  max-width: 620px;
}

.collection-subline {
  margin-top: clamp(10px, 2vh, 18px);
  padding-left: 20px;
  position: relative;
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pink);
}

/* The short horizontal rule the layout sketch shows to the left of
   this line — a quiet accent, not a full-width divider. */
.collection-subline::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 1px;
  background: var(--pink);
}

.collection-selector-label {
  font-size: 12.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  margin-bottom: 12px;
}

.collection-selector-count {
  color: var(--pink);
}

.collection-selector {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* The homepage hero's single per-design product link, sitting under the
   thumbnail strip. Deliberately the existing .quiet-link treatment
   rather than a new visual element — this pass adds working commerce
   links, not new hero styling. */
.collection-selector-link {
  display: inline-block;
  margin-top: 16px;
}

/* Compact artwork thumbnails — deliberately small (unlike the earlier
   two-shirt selector's large named panels), since there are six of
   these instead of two and the sketch shows them as a tight row of
   small squares, not full-width panels. Selected state uses a border
   (not just an image swap or opacity shift) so it doesn't rely on
   color alone. */
.collection-selector-thumb {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  padding: 4px;
  background: transparent;
  border: 1.5px solid var(--olp-line);
  cursor: pointer;
  transition: border-color 0.25s var(--olp-ease), opacity 0.25s var(--olp-ease);
  opacity: 0.75;
}

.collection-selector-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.collection-selector-thumb:hover {
  opacity: 1;
  border-color: var(--olp-fg-muted);
}

.collection-selector-thumb:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

.collection-selector-thumb[data-selected='true'] {
  opacity: 1;
  border-color: var(--pink);
  background: rgba(230, 0, 190, 0.08);
}

@media (max-width: 860px) {
  .collection-selector-thumb {
    width: 52px;
    height: 52px;
  }

  /* A compact horizontal strip that scrolls within itself if six
     thumbnails don't comfortably fit the width, rather than wrapping
     to a second row (which would push the CTA further down and risk
     it falling below the fold on shorter phones). */
  .collection-selector {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: 4px;
    -webkit-overflow-scrolling: touch;
  }
}

/* Homepage-specific typography increases (Section 1) — targeted
   overrides on the .home-* classes rather than bumping the shared
   .desc/.credit-by/.acquire-btn tokens globally, since those are also
   used on the product page and other hero states where this
   correction doesn't apply. */
.home-desc {
  font-size: 16.5px;
}

.home-credit .credit-by {
  font-size: 13.5px;
}

.home-cta {
  font-size: 15px;
}

.apparel-selector-tab[data-selected='true'] .apparel-selector-tag {
  opacity: 0.85;
}

/* ---------- mobile: genuinely reflowed, not just rescaled ---------- */

@media (max-width: 860px) {
  .room--collection {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding: clamp(16px, 3vh, 24px) 0 0;
    height: auto;
    /* No uniform gap here — each block sets its own margin-bottom
       below, so the selector→media gap can be tuned independently
       (24–36px, explicitly requested) from the other transitions
       rather than all five blocks sharing one spacing value. */
  }

  .hero-block-eyebrowtitle {
    order: 1;
    margin-bottom: clamp(12px, 2vh, 18px);
  }

  .hero-block-selector {
    order: 2;
    /* The specific gap this correction asked for — was sharing the
       container's uniform ~18–28px gap with every other transition,
       which read as the shirt being attached to the selector rather
       than its own distinct block. */
    margin-bottom: clamp(24px, 4vh, 36px);
  }

  /* The rotating shirt's own dedicated block — normal document flow,
     not an absolutely-positioned background behind the copy. Sits
     between the selector and the CTA, matching the requested mobile
     reading order (CTA now comes right after the media, with the
     description/credit moved below it — see .hero-block-cta/-desc). */
  .hero-block-media {
    order: 3;
    position: static;
    inset: auto;
    z-index: auto;
    width: 100%;
    height: var(--hero-media-height-mobile, clamp(260px, 34svh, 340px));
    margin-bottom: clamp(16px, 2.5vh, 22px);
  }

  /* Mobile drops the desktop caps entirely: the block already has a
     measured height, and capping the stage inside it as well would
     shrink the garment twice. `contain` keeps the whole shirt visible
     within whatever height the tier above chose. */
  .collection-media-stage {
    max-width: none;
    max-height: none;
    padding: 0;
  }

  .hero-block-media {
    padding: 0 clamp(12px, 4vw, 24px);
  }

  /* SHOP THE COLLECTION comes right after the media on mobile — before
     the description/credit, the reverse of desktop's order — so it's
     the last thing in the "must be above the fold" sequence (header,
     eyebrow, title, both selector panels, most/all of the shirt, then
     this button) rather than requiring a scroll past descriptive text
     first. Still normal document flow, not fixed/sticky/overlaid. */
  .hero-block-cta {
    order: 4;
    margin-bottom: clamp(20px, 3vh, 28px);
  }

  .hero-block-desc {
    order: 5;
  }

  .hero-block-eyebrowtitle,
  .hero-block-name,
  .hero-block-selector,
  .hero-block-cta,
  .hero-block-desc {
    max-width: none;
    /* The name block shares the hero inset. Without it the design
       name sat flush against the left edge and its first character
       was clipped. */
    padding: 0 20px;
  }

  .hero-block-cta .acquire-btn {
    width: 100%;
    min-height: 48px;
  }

  .apparel-selector-tab {
    min-height: 56px;
  }

  /* Shorter mobile screens: reduce the non-essential gaps first
     (per Section 7) rather than shrinking the shirt itself. 820px
     (not the original 700px) — measured directly: 360×800 and 375×812
     both had the CTA falling below the fold by 35-40px with only the
     700px tier active, since their height sits just above that
     threshold but still isn't tall enough for the base spacing. */
  @media (max-height: 820px) {
    .room--collection {
      padding-top: 10px;
    }

    .hero-block-eyebrowtitle {
      margin-bottom: 8px;
    }

    .hero-block-selector {
      margin-bottom: 24px;
    }

    .hero-block-media {
      height: var(--hero-media-height-mobile, clamp(230px, 30svh, 300px));
      margin-bottom: 12px;
    }

    .hero-block-cta {
      margin-bottom: 16px;
    }
  }
}

@media (max-width: 380px) {
  /* Narrowest phones only — two equal panels with the full title text
     ("9 FISH / 100") can get cramped below ~380px; stacking keeps both
     comfortably legible rather than shrinking text to fit side by side. */
  .apparel-selector {
    flex-direction: column;
  }
}


/* The Gorilla placeholder's media stage — deliberately no image at
   all, just the same black stage background with quiet centered text,
   so it reads as clearly intentional rather than a broken image. */
.hero-placeholder-stage {
  /* Was width:100%;height:100%;display:flex;align-items:center;
     justify-content:center — centering within the FULL media region
     (which spans the whole room on desktop), not at the same ~70%
     position the video itself sits at. That drifted the placeholder
     left into the text/selector column instead of matching where the
     real shirt would be. Absolute + the same --hero-media-x/-y
     percentage system .hero-video uses positions it identically. */
  position: absolute;
  top: var(--hero-media-y, 50%);
  left: var(--hero-media-x, 70%);
  transform: translate(-50%, -50%);
}

.hero-placeholder-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--olp-line);
  padding: clamp(28px, 4vw, 44px) clamp(32px, 5vw, 56px);
  white-space: nowrap;
}

.hero-placeholder-title {
  font-family: var(--olp-font-display);
  font-weight: 900;
  text-transform: uppercase;
  font-size: clamp(22px, 2.6vw, 32px);
  letter-spacing: 0.02em;
  color: var(--olp-fg-muted);
}

.hero-placeholder-subtitle {
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  opacity: 0.75;
}

/* Same footprint as .acquire-btn (so nothing jumps when a release sells
   out) but visually inert — a quiet, bordered notice, not a disabled
   button pretending to still be clickable. */
.sold-out-archived {
  margin-top: clamp(14px, 3vh, 32px);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 420px;
  height: 50px;
  border: 1px solid var(--olp-line);
  background: transparent;
}

.sold-out-archived-label {
  font-family: var(--olp-font-mono);
  font-size: 14px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
}

.cert-toggle {
  margin-top: clamp(12px, 2.5vh, 22px);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--olp-font-mono);
  font-size: 13.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  transition: color 0.3s;
}

.cert-toggle:hover {
  color: var(--pink);
}

.cert-toggle .chev {
  transition: transform 0.35s var(--olp-ease);
  font-size: 13px;
}

.cert-toggle[aria-expanded='true'] .chev {
  transform: rotate(180deg);
}

.cert-panel {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--olp-line);
  max-width: 420px;
  display: flex;
  align-items: flex-start;
  gap: 18px;
}

.cert-panel[hidden] {
  display: none;
}

.cert-mark {
  width: 56px;
  height: auto;
  opacity: 0.92;
  flex-shrink: 0;
  margin-top: 2px;
}

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

.cert-fact {
  display: flex;
  gap: 10px;
  align-items: baseline;
}

.cert-fact dt {
  font-family: var(--olp-font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  width: 84px;
  flex-shrink: 0;
}

.cert-fact dd {
  margin: 0;
  font-size: 14px;
  color: var(--olp-fg);
}

/* ---------- quiet-link — used by Sold Out / Archived "View the Archive"
   (this pattern is lifted from the prototype's Original Art / Prints
   empty-room states, which use the exact same restrained link style for
   the same purpose: a low-emphasis way out of a non-buyable room) ---------- */
.quiet-link {
  font-family: var(--olp-font-mono);
  font-size: 13.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color 0.3s, border-color 0.3s;
}

.quiet-link:hover {
  color: var(--pink);
  border-color: var(--pink);
}

@media (prefers-reduced-motion: reduce) {
  .garment-wrap {
    transition: none;
  }
}

@media (max-width: 860px) {
  .garment-wrap {
    right: -18%;
    width: 96%;
    top: -4%;
    height: 70%;
  }
  .stage::after {
    background: linear-gradient(0deg, rgba(3, 3, 3, 0.95) 42%, rgba(3, 3, 3, 0.55) 66%, transparent 100%);
  }
  .copy {
    padding: 0 22px 32px;
    max-width: none;
  }
  .countdown {
    gap: 20px;
  }
  .notify {
    flex-direction: column;
    gap: 10px;
  }
  .notify input {
    border-right: 1px solid rgba(241, 238, 230, 0.22);
    height: 44px;
  }
  .notify button {
    height: 44px;
  }
}

/*
* --------------------------------------------------
* PIECE PAGE — back-link, acquire-form wrapper, and the
* below-the-fold sections (Collaboration essay, Provenance record,
* Details accordion). Ported directly from olpiner-piece.html.
* --------------------------------------------------
*/
.back-link {
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  text-decoration: none;
  transition: color 0.3s;
}

.back-link:hover {
  color: var(--pink);
}

/* Restyles the shared ProductForm/AddToCartButton to match .acquire-btn
   without touching those components — they're used elsewhere (cart
   context) with their own default styling. */
.acquire-form {
  margin-top: 24px;
  max-width: 420px;
}

.acquire-form .product-options {
  border-bottom: none;
  margin-bottom: 0;
  padding-bottom: 0;
}

.acquire-form .product-options h5 {
  font-family: var(--olp-font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  font-weight: 400;
  margin-bottom: 10px;
}

.acquire-form .product-options-grid {
  margin-top: 0;
}

.acquire-form .product-options-item {
  background: transparent;
  border-color: rgba(241, 238, 230, 0.22);
}

.acquire-form .product-options-item[data-selected='true'] {
  background: var(--olp-fg);
  color: var(--olp-bg);
  border-color: var(--olp-fg);
}

.acquire-form .product-options-item[data-available='false'] {
  opacity: 0.35;
}

.acquire-form .olp-button {
  margin-top: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 50px;
  background: var(--olp-fg);
  color: var(--olp-bg);
  border: none;
  border-radius: 0;
  font-family: var(--olp-font-body);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: none;
  padding: 0;
  transition: background 0.35s var(--olp-ease), color 0.35s var(--olp-ease);
}

.acquire-form .olp-button:hover:not(:disabled) {
  background: var(--pink);
  color: var(--olp-bg);
}

.section {
  padding: 88px 6vw;
  max-width: 900px;
  border-top: 1px solid var(--olp-line-dark);
}

.section-label {
  font-family: var(--olp-font-mono);
  font-size: 13.5px;
  letter-spacing: 0.18em;
  color: var(--pink);
  text-transform: uppercase;
  margin-bottom: 28px;
}

.essay p {
  font-family: var(--olp-font-serif);
  font-weight: 300;
  font-size: clamp(16px, 1.9vw, 19px);
  line-height: 1.75;
  color: var(--olp-fg);
  margin-bottom: 20px;
  max-width: 560px;
}

.essay p:last-of-type {
  margin-bottom: 0;
}

.essay-link {
  display: inline-block;
  margin-top: 30px;
  font-family: var(--olp-font-mono);
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 3px;
  transition: color 0.3s, border-color 0.3s;
}

/* Edition badge — sits quietly under the price, not shouting. One of
   exactly two places this shows (the other is the provenance panel's
   Edition row) — see products.$handle.tsx / _index.tsx. */
.edition-tag {
  margin-top: 8px;
  font-size: 13.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
}

/* Artist's Note — reuses .essay's typography exactly (same section as
   The Collaboration), this just adds the optional artwork-pair grid for
   when real photography exists. Renders nothing extra today. */
.artist-note-artworks {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--olp-space-3);
  margin-top: var(--olp-space-4);
  max-width: 640px;
}

.artist-note-artworks img {
  width: 100%;
  height: auto;
  display: block;
  background: var(--olp-surface);
}

@media (max-width: 640px) {
  .artist-note-artworks {
    grid-template-columns: 1fr;
  }
}

.essay-link:hover {
  color: var(--pink);
  border-color: var(--pink);
}

/* Provenance — museum record */
.record {
  width: 100%;
  border-collapse: collapse;
}

.record tr {
  border-bottom: 1px solid rgba(241, 238, 230, 0.08);
}

.record tr:last-child {
  border-bottom: none;
}

.record td {
  padding: 13px 0;
  font-size: 13px;
}

.record td:first-child {
  font-family: var(--olp-font-mono);
  font-size: 13.5px;
  letter-spacing: 0.1em;
  color: var(--olp-fg-muted);
  text-transform: uppercase;
  width: 40%;
  vertical-align: top;
}

.record td:last-child {
  color: var(--olp-fg);
  text-align: right;
}

/* Disclosure accordion — Materials, Spec, Print & production, Size,
   Shipping. No Packaging item — see DESIGN_INTEGRATION_PLAN.md. */
.disclosure-group {
  border-top: 1px solid rgba(241, 238, 230, 0.08);
}

.disclosure {
  border-bottom: 1px solid rgba(241, 238, 230, 0.08);
}

.disclosure-btn {
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 0;
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-size: 13.5px;
  transition: color 0.3s;
}

.disclosure-btn:hover {
  color: var(--pink);
}

.disclosure-btn .chev {
  font-family: var(--olp-font-mono);
  font-size: 13px;
  color: var(--olp-fg-muted);
  transition: transform 0.35s var(--olp-ease);
}

.disclosure-btn[aria-expanded='true'] .chev {
  transform: rotate(180deg);
}

.disclosure-body {
  padding: 0 0 24px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--olp-fg-muted);
  max-width: 480px;
  white-space: pre-line;
}

.disclosure-body[hidden] {
  display: none;
}

@media (max-width: 860px) {
  .section {
    padding: 56px 22px;
  }
}

/*
* --------------------------------------------------
* ARCHIVE — ported directly from olpiner-archive.html
* --------------------------------------------------
*/
.archive-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 64px 6vw 40px;
}

.head {
  max-width: 640px;
  margin-bottom: 56px;
}

.head h1 {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(30px, 4vw, 46px);
  line-height: 1.15;
  color: var(--olp-fg);
}

.head p {
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--olp-fg-muted);
  margin-top: 18px;
  max-width: 460px;
}

/* Limited Editions' current state — no products, no grid, just an
   honest "not yet" notice ahead of the email signup below it. Kept
   deliberately restrained (no border/box), matching the same quiet
   status-word treatment used elsewhere (.eyebrow, .num) rather than
   inventing a new "empty state" visual pattern. */
/* Limited Editions' own composition — the h1 here is the status word
   itself ("Coming Soon"), not the category name (that's the eyebrow
   above it), and the signup form directly below (EmailSignup's inline
   mode) needs a bit less top gap than .head's usual 56px so the whole
   thing reads as one connected block rather than two stacked ones. */
.le-head {
  margin-bottom: 32px;
}

.grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  background: rgba(241, 238, 230, 0.08);
  flex: 1;
}

.piece {
  background: var(--olp-bg);
  flex: 1 1 320px;
  min-width: 280px;
  padding: 44px 36px;
  display: flex;
  flex-direction: column;
  transition: background 0.5s var(--olp-ease);
}

.piece:hover {
  background: var(--olp-surface);
}

.piece .thumb-wrap {
  position: relative;
  width: 100%;
  /* Was 220px — sized originally for a static thumbnail, but that made
     the spinning video (substantially taller/more detailed content)
     read as "extremely small inside a narrow stage." Card structure is
     untouched (.piece still flex-basis 320px+ in the grid) — just
     letting the media itself use nearly the full card width instead of
     a fixed narrow column within it. */
  max-width: 360px;
  margin: 0 auto 28px;
}

.piece .stamp {
  position: absolute;
  bottom: -6px;
  right: -14px;
  width: 34px;
  height: auto;
  filter: grayscale(100%) contrast(0.9) brightness(1.3);
  opacity: 0.5;
  transform: rotate(-9deg);
  pointer-events: none;
  transition: opacity 0.4s var(--olp-ease);
}

.piece:hover .stamp {
  opacity: 0.75;
}

.piece .thumb {
  width: 100%;
  display: block;
  filter: grayscale(10%);
}

/* Gorilla's card thumbnail — same restrained black stage as the
   homepage placeholder (HomeApparelSelector.tsx), not a stock image or
   invented artwork. aspect-ratio keeps the card's proportions
   consistent with the video/image thumbs beside it in the grid. */
.thumb-placeholder {
  width: 100%;
  /* Matches the 9 FISH card's now-square video thumb (1600×1600 source,
     was portrait 734×1304) — kept consistent so the two Apparel cards
     read as the same shape in the grid, not oddly mismatched. */
  aspect-ratio: 1 / 1;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.thumb-placeholder-label {
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
  border: 1px solid var(--olp-line-dark);
  padding: 8px 14px;
}

/* Catalogue card video — same treatment as the thumb image (grayscale
   touch, same box), plus the shared .edition-video-wrap/-video rules
   below for the player itself. */
.piece .thumb-video {
  width: 100%;
  filter: grayscale(10%);
}

/* Wraps the whole card (thumb, status, title) in one link when a
   product handle exists — makes the video/thumb itself clickable, not
   just the "View the piece" text underneath. */
.piece-card-link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.piece-card-link:hover .thumb,
.piece-card-link:hover .thumb-video {
  filter: grayscale(0%);
}

/*
* --------------------------------------------------
* EDITION VIDEO — reusable player (catalogue card + product gallery)
* --------------------------------------------------
*/
.edition-video-wrap {
  position: relative;
  width: 100%;
}

.edition-video {
  width: 100%;
  height: auto;
  display: block;
  object-fit: contain;
  background: #000;
}

.edition-video-playpause {
  position: absolute;
  bottom: 14px;
  right: 14px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 5, 5, 0.6);
  border: 1px solid rgba(241, 238, 230, 0.28);
  color: var(--olp-fg);
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: background 0.3s var(--olp-ease), border-color 0.3s var(--olp-ease);
}

.edition-video-playpause:hover,
.edition-video-playpause:focus-visible {
  background: var(--pink);
  border-color: var(--pink);
}

.piece .num {
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.14em;
  color: var(--pink);
  text-transform: uppercase;
}

.piece .num-suffix {
  color: var(--olp-fg-muted);
  letter-spacing: 0.1em;
}

/* Restrained, not a badge — same weight/size as the other status
   words, just in the accent color so the current release is easy to
   spot at a glance in a grid mixing live and past drops. */
.piece .num-suffix--live {
  color: var(--pink);
}

.piece h2 {
  font-family: var(--olp-font-serif);
  font-weight: 400;
  font-size: 20px;
  margin-top: 10px;
  color: var(--olp-fg);
}

.piece .meta {
  font-size: 13px;
  color: var(--olp-fg-muted);
  margin-top: 6px;
  letter-spacing: 0.03em;
}

.piece .link {
  display: inline-block;
  margin-top: 22px;
  font-size: 14px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg);
  text-decoration: none;
  border-bottom: 1px solid var(--pink);
  padding-bottom: 3px;
  transition: opacity 0.3s;
  align-self: flex-start;
}

.piece .link:hover {
  opacity: 0.6;
}

.piece.placeholder {
  align-items: center;
  justify-content: center;
  text-align: center;
}

.piece.placeholder .num {
  color: var(--olp-fg-muted);
}

.piece.placeholder h2 {
  color: var(--olp-fg-muted);
  font-style: italic;
}

@media (max-width: 720px) {
  .archive-main {
    padding: 48px 22px 30px;
  }
}



/*
* --------------------------------------------------
* PRINTS — empty room, ported directly from olpiner-prints.html
* --------------------------------------------------
*/
.prints-empty-room {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
  text-align: center;
}

.prints-empty-room .tag {
  font-size: 13.5px;
  letter-spacing: 0.18em;
  color: var(--pink);
  text-transform: uppercase;
  margin-bottom: 56px;
}

.shelf-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 40px;
}

.sleeves {
  display: flex;
  align-items: flex-end;
}

.sleeve {
  margin-left: 26px;
  border: 1px solid rgba(241, 238, 230, 0.16);
  position: relative;
  flex-shrink: 0;
  background: linear-gradient(180deg, rgba(241, 238, 230, 0.02), transparent 70%);
  transition: transform 0.5s var(--olp-ease), border-color 0.5s var(--olp-ease);
}

.sleeve:first-child {
  margin-left: 0;
}

.sleeve::before {
  content: '';
  position: absolute;
  top: 14px;
  left: 7px;
  right: 7px;
  height: 1px;
  background: rgba(241, 238, 230, 0.1);
  transition: background 0.5s var(--olp-ease);
}

.sleeve.s1 {
  width: 46px;
  height: 150px;
}
.sleeve.s2 {
  width: 58px;
  height: 190px;
}
.sleeve.s3 {
  width: 46px;
  height: 130px;
}
.sleeve.s4 {
  width: 58px;
  height: 172px;
}

.sleeve:hover {
  transform: translateY(-6px);
  border-color: rgba(241, 238, 230, 0.32);
}

.sleeve:hover::before {
  background: rgba(241, 238, 230, 0.22);
}

.ledge {
  width: 340px;
  height: 1px;
  background: rgba(241, 238, 230, 0.16);
  margin-top: 0;
}

.plaque {
  margin-top: 16px;
  text-align: center;
}

.plaque .num {
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--olp-fg-muted);
  opacity: 0.5;
}

.prints-empty-room .caption {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-size: 15px;
  color: var(--olp-fg-muted);
  margin-bottom: 8px;
}

@media (max-width: 640px) {
  .sleeve {
    margin-left: 16px;
  }
  .sleeve.s1 {
    width: 34px;
    height: 110px;
  }
  .sleeve.s2 {
    width: 42px;
    height: 140px;
  }
  .sleeve.s3 {
    width: 34px;
    height: 96px;
  }
  .sleeve.s4 {
    width: 42px;
    height: 128px;
  }
  .ledge {
    width: 260px;
  }
}

/*
* --------------------------------------------------
* PRINTS — populated room (no prototype reference for this state;
* designed to match the Archive grid's restrained catalog language)
* --------------------------------------------------
*/
.print-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  background: rgba(241, 238, 230, 0.08);
  flex: 1;
}

.print-tile {
  background: var(--olp-bg);
  flex: 1 1 280px;
  min-width: 240px;
  padding: 36px 30px;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  transition: background 0.5s var(--olp-ease);
}

.print-tile:hover {
  background: var(--olp-surface);
}

.print-tile-thumb-wrap {
  width: 100%;
  aspect-ratio: 4 / 5;
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--olp-surface);
}

.print-tile-thumb {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  transition: opacity 0.4s var(--olp-ease);
}

.print-tile:hover .print-tile-thumb {
  opacity: 0.85;
}

.print-tile h2 {
  font-family: var(--olp-font-serif);
  font-weight: 400;
  font-size: 18px;
  color: var(--olp-fg);
}

.print-tile .meta {
  font-size: 14px;
  color: var(--olp-fg-muted);
  margin-top: 5px;
  letter-spacing: 0.03em;
}

.print-tile-price {
  font-size: 13.5px;
  color: var(--olp-fg);
  margin-top: 14px;
}

.print-tile-price .num-suffix {
  color: var(--olp-fg-muted);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/*
* --------------------------------------------------
* PRINT PIECE PAGE — hero layout + artwork viewer
* --------------------------------------------------
*/
.print-piece-hero {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  min-height: calc(100vh - 140px);
}

.artwork-viewer {
  flex: 1 1 58%;
  min-width: 320px;
  display: flex;
  flex-direction: column;
  padding: 40px 6vw;
}

.artwork-primary {
  position: relative;
  display: block;
  width: 100%;
  border: none;
  background: var(--olp-surface);
  padding: 0;
  cursor: zoom-in;
  overflow: hidden;
}

.artwork-primary img {
  width: 100%;
  height: auto;
  display: block;
  max-height: 78vh;
  object-fit: contain;
}

.artwork-zoom-hint {
  position: absolute;
  bottom: 14px;
  right: 14px;
  background: rgba(5, 5, 5, 0.55);
  color: var(--olp-fg);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 7px 12px;
  border-radius: 20px;
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.3s;
}

.artwork-primary:hover .artwork-zoom-hint {
  opacity: 1;
}

.artwork-thumbs {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

.artwork-thumb {
  width: 56px;
  height: 56px;
  padding: 0;
  border: 1px solid transparent;
  background: none;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.3s, border-color 0.3s;
}

.artwork-thumb.active {
  opacity: 1;
  border-color: var(--pink);
}

.artwork-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.artwork-empty {
  flex: 1 1 58%;
  min-width: 320px;
  aspect-ratio: 4 / 5;
  background: var(--olp-line);
}

.artwork-dialog {
  background: var(--olp-bg);
  border: none;
  height: 100vh;
  max-height: 100vh;
  max-width: 100vw;
  padding: 0;
  width: 100vw;
}

.artwork-dialog-close {
  color: var(--olp-fg);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 20px;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1;
  background: none;
  border: none;
  cursor: pointer;
}

.artwork-dialog-img {
  height: 100%;
  width: 100%;
  margin: 0 auto;
  object-fit: contain;
  display: block;
}

.print-piece-copy {
  flex: 1 1 38%;
  min-width: 320px;
  padding: 40px 6vw 40px 0;
  align-self: center;
  position: static;
}

@media (max-width: 960px) {
  .print-piece-hero {
    flex-direction: column;
    min-height: 0;
  }
  .artwork-viewer,
  .print-piece-copy {
    padding: 28px 22px;
    flex: none;
    min-width: 0;
  }
}

/* ==========================================================================
   ORIGINAL ART — collection and artwork pages
   ==========================================================================
   The photographs are shown whole: nothing here crops, covers or clips an
   image. The keyline, padding and rounded corners belong to the frame
   AROUND the photograph, which keeps square corners of its own, so all
   four corners of every photograph stay visible. The frame is a website
   treatment, not a physical frame.
   ========================================================================== */

.oa-frame {
  display: block;
  padding: var(--oa-frame-pad, 8px);
  border: 1px solid rgba(241, 238, 230, 0.2);
  border-radius: 5px;
  background: rgba(241, 238, 230, 0.025);
  transition: border-color 240ms var(--olp-ease);
}

.oa-frame img {
  display: block;
  border-radius: 0;
}

.oa-ref {
  letter-spacing: 0.08em;
}

.oa-price {
  margin: 0;
  color: var(--olp-fg);
  font-variant-numeric: tabular-nums;
}

/* The enquiry link. Pink, mono and quiet on a card; a full cream button on
   the artwork page. Always a link to WhatsApp, never a cart control. */
.oa-enquire {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--pink);
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color 160ms ease, background 160ms ease;
}

.oa-enquire--compact {
  min-height: 36px;
  padding-block: 8px;
}

.oa-enquire--compact:hover {
  color: var(--olp-fg);
}

.oa-enquire:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

.oa-enquire-icon {
  flex: 0 0 auto;
}

/* ---------- Collection ---------- */

.oa-count {font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--olp-fg-muted)}

/* Shared Prints / Original Art collection. Source order is row-major. */
/* Shared centimetre scale across rows. Card minima protect labels, not image size. */
/* Ordered two-dimensional float packing: a following block can settle beside
   a taller predecessor. Floats never move above the preceding block's top,
   so DOM/tab order and a top-to-bottom browsing sequence remain aligned.
   CSS reserves image + text geometry immediately; no JS measurement/relayout. */
.ac-grid {container-type:inline-size;display:flow-root;--gallery-gap-x:28px;--gallery-gap-y:40px}
.ac-card {float:left;width:max(220px,var(--art-width));max-width:calc(100% - var(--gallery-gap-x));margin:0 var(--gallery-gap-x) var(--gallery-gap-y) 0;display:flex;flex-direction:column;color:var(--olp-fg)}
.ac-card-link {display:block;color:inherit;text-decoration:none;min-width:0}
.ac-card-link:focus-visible {outline:2px solid var(--pink);outline-offset:6px}
.ac-image {width:var(--art-width);height:var(--art-height);max-width:100%;padding:0;background:transparent;display:flex;align-items:center;justify-content:center;margin:0 auto 24px;box-sizing:border-box}
.ac-image img {display:block;width:100%;height:100%;min-width:0;min-height:0;object-fit:contain}
.ac-dimensions.eyebrow {display:block;font-size:17px;line-height:1.5;letter-spacing:.025em;text-transform:none;color:var(--pink)}
.ac-card .ac-title {font-family:var(--olp-font-sans);font-style:normal;font-size:24px;line-height:1.25;margin:8px 0;overflow-wrap:anywhere}
.ac-card-foot {display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;min-height:44px}
.ac-card .ac-price {font-size:16px;line-height:1.6;margin:8px 0}
.ac-card .oa-enquire--compact {min-height:44px}
@media(max-width:1023px){.ac-grid{--gallery-gap-x:24px;--gallery-gap-y:36px}.ac-card{width:max(170px,var(--art-width))}}
@media(max-width:599px){.ac-grid{--gallery-gap-x:16px;--gallery-gap-y:32px}.ac-card{width:max(132px,var(--art-width))}.ac-card .ac-title{font-size:20px}.ac-dimensions.eyebrow{font-size:14px}.ac-image{margin-bottom:20px}.ac-card .ac-price{font-size:15px}}

/* ---------- Artwork page ---------- */

.oa-detail {
  --oa-frame-pad: clamp(8px, 0.9vw, 12px);
  --oa-frame-chrome: calc(2 * var(--oa-frame-pad) + 2px);
  /* The tallest the photograph may be: most of the first screen. */
  --oa-stage-h: clamp(420px, calc(100svh - var(--header-height) - 110px), 900px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 380px);
  grid-template-areas: 'gallery head' 'gallery info';
  grid-template-rows: auto 1fr;
  align-items: start;
  column-gap: clamp(40px, 5vw, 96px);
  row-gap: 28px;
  /* An explicit width: the page sits in a flex column, where an auto width
     shrinks to content — and the gallery's photograph is sized from its
     container, so it contributes no content width. Without this the
     gallery column collapsed to 0px and the artwork vanished. */
  width: 100%;
  box-sizing: border-box;
  /* Close enough that the photograph and its information read as one
     composition rather than two ends of a wide room. */
  max-width: 1200px;
  margin-inline: auto;
  padding: clamp(24px, 3vw, 48px) 6vw clamp(64px, 7vw, 112px);
}

.oa-detail-head {
  grid-area: head;
  padding-top: 6px;
}

.oa-detail-gallery {
  grid-area: gallery;
  min-width: 0;
}

.oa-detail-info {
  grid-area: info;
}

.oa-detail-title {
  margin: 22px 0 0;
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(34px, 3.4vw, 52px);
  line-height: 1.08;
  color: var(--olp-fg);
}

.oa-detail-ref {
  margin: 12px 0 0;
  font-size: 12.5px;
  letter-spacing: 0.12em;
  color: var(--olp-fg-muted);
}

.oa-facts {
  display: grid;
  gap: 18px;
  margin: 0;
  padding-top: 22px;
  border-top: 1px solid var(--olp-line);
}

.oa-fact dt {
  font-family: var(--olp-font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
}

.oa-fact dd {
  margin: 6px 0 0;
  font-size: 16px;
  color: var(--olp-fg);
}

.oa-price--detail {
  font-size: 22px;
}

.oa-detail-enquiry {
  margin-top: 32px;
}

.oa-detail-enquiry-ref {
  margin: 0 0 12px;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--olp-fg-muted);
}

.oa-detail-enquiry-ref .oa-ref {
  color: var(--olp-fg);
}

.oa-enquire--prominent {
  justify-content: center;
  gap: 10px;
  width: 100%;
  min-height: 56px;
  padding: 16px 24px;
  background: var(--olp-fg);
  color: var(--olp-bg);
  font-family: var(--olp-font-body);
  font-size: 15px;
  letter-spacing: 0;
  text-transform: none;
}

.oa-enquire--prominent:hover {
  background: var(--pink);
  color: var(--olp-fg);
}

/* ---------- Artwork gallery ---------- */

.oa-stage {
  position: relative;
}

/* One horizontal scroll-snap track holds every photograph. With a single
   photograph it does not scroll at all. */
.oa-track {
  display: flex;
  align-items: center;
  overflow: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  border-radius: 6px;
}

.oa-track::-webkit-scrollbar {
  display: none;
}

.oa-gallery:not([data-count='1']) .oa-track {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.oa-track:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 4px;
}

.oa-slide {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  scroll-snap-align: center;
  container-type: inline-size;
}

.oa-zoom {
  display: block;
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: inherit;
  cursor: zoom-in;
}

.oa-zoom:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 4px;
}

.oa-zoom:hover .oa-frame {
  border-color: rgba(241, 238, 230, 0.45);
}

/* The box is computed from the photograph's own ratio (--oa-ar) and the
   space available — as wide as the column allows without exceeding the
   stage height — so it is correct before the file arrives. */
.oa-stage-img {
  aspect-ratio: var(--oa-ar);
  width: min(calc(100cqw - var(--oa-frame-chrome)), calc((var(--oa-stage-h) - var(--oa-frame-chrome)) * var(--oa-ar)));
  height: auto;
}

/* Previous / next — only rendered when there is more than one photograph. */
.oa-step {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(241, 238, 230, 0.3);
  border-radius: 50%;
  background: rgba(5, 5, 5, 0.62);
  color: var(--olp-fg);
  cursor: pointer;
  transform: translateY(-50%);
  transition: border-color 160ms ease, color 160ms ease;
}

.oa-step--prev {
  left: 10px;
}

.oa-step--next {
  right: 10px;
}

.oa-step:hover {
  border-color: var(--pink);
  color: var(--pink);
}

.oa-step:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

.oa-gallery-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
}

.oa-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex: 1 1 280px;
  min-width: 0;
}

.oa-thumb {
  flex: 0 0 64px;
  width: 64px;
  height: 64px;
  padding: 4px;
  border: 1px solid var(--olp-line);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 160ms ease, border-color 160ms ease;
}

.oa-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.oa-thumb:hover {
  opacity: 0.85;
}

.oa-thumb[aria-current='true'] {
  opacity: 1;
  border-color: var(--pink);
}

.oa-thumb:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 2px;
}

.oa-counter {
  flex: 0 0 auto;
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--olp-fg-muted);
}

/* Keep ordinary desktop/tablet sizing. On wide screens the old 1200px cap
   combined with viewport-sized padding consumed the gallery column itself. */
@media (min-width: 1600px) {
  .oa-detail {
    max-width: min(92vw, 2400px);
    padding-inline: clamp(48px, 4vw, 112px);
    grid-template-columns: minmax(0, 1fr) minmax(380px, 480px);
    column-gap: clamp(64px, 4vw, 128px);
    --oa-stage-h: clamp(650px, calc(100svh - var(--header-height) - 220px), 1100px);
  }
  .oa-detail-title { font-size: clamp(52px, 2.5vw, 68px); }
}

/* ---------- Enlarged view ---------- */

.oa-lightbox {
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgba(5, 5, 5, 0.97);
  color: var(--olp-fg);
}

.oa-lightbox[open] {
  display: flex;
  flex-direction: column;
}

.oa-lightbox::backdrop {
  background: rgba(5, 5, 5, 0.9);
}

.oa-lightbox-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px clamp(16px, 3vw, 32px);
}

.oa-lightbox-caption {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--olp-fg-muted);
}

.oa-lightbox-close {
  min-height: 44px;
  padding: 10px 18px;
  border: 1px solid rgba(241, 238, 230, 0.35);
  background: transparent;
  color: var(--olp-fg);
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

.oa-lightbox-close:hover {
  border-color: var(--pink);
  color: var(--pink);
}

.oa-lightbox-close:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

/* The whole photograph, fitted to the screen — never cropped. */
.oa-lightbox-body {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 clamp(12px, 5vw, 80px) clamp(16px, 3vw, 32px);
}

.oa-lightbox-img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.oa-step--lightbox {
  position: fixed;
}

/* ---------- Artwork page: phones and tablets ---------- */

/* Title, then the large gallery, then price and enquiry. */
@media (max-width: 900px) {
  .oa-detail {
    --oa-stage-h: min(72svh, 640px);
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'head' 'gallery' 'info';
    grid-template-rows: auto;
    row-gap: 20px;
    padding: 16px 20px 56px;
  }

  .oa-detail-head {
    padding-top: 0;
  }

  .oa-detail-title {
    margin-top: 14px;
    font-size: clamp(30px, 8vw, 40px);
  }

  .oa-detail-ref {
    margin-top: 8px;
  }

  .oa-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    padding-top: 18px;
  }

  .oa-price--detail {
    font-size: 19px;
  }

  .oa-detail-enquiry {
    margin-top: 22px;
  }
}

/*
* --------------------------------------------------
* STUDIO — no prototype reference (not in the approved five files).
* A loose mosaic, not a biography page. Deliberately flex-wrap, NOT CSS
* columns — columns lay out column-by-column (column-major order), which
* visually scrambles a curated sort_order sequence; flex-wrap preserves
* strict reading order (left→right, top→bottom) so manual curation is
* actually predictable. See DESIGN_INTEGRATION_PLAN.md Step 9 correction
* #5. Images keep their natural proportions (no cropping), same principle
* as Prints/Original Art. A fragment can be image-only, text-only, or
* both — text-only fragments render as a quiet typographic block, not a
* broken empty image slot.
* --------------------------------------------------
*/
.studio-mosaic {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 1px;
  background: rgba(241, 238, 230, 0.08);
}

.studio-fragment {
  background: var(--olp-bg);
  flex: 1 1 260px;
  min-width: 220px;
  max-width: 100%;
  position: relative;
}

.studio-fragment img {
  width: 100%;
  height: auto;
  display: block;
}

.studio-fragment figcaption {
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.studio-fragment-kind {
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--pink);
}

.studio-fragment-body {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-size: 14px;
  line-height: 1.6;
  color: var(--olp-fg);
}

.studio-fragment-caption {
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--olp-fg-muted);
}

.studio-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 60px 24px;
}

.studio-empty h1 {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 4vw, 42px);
  margin-top: 12px;
}

@media (max-width: 640px) {
  .studio-fragment {
    flex-basis: 100%;
  }
}

/*
* --------------------------------------------------
* ERROR / 404 — matches the quiet-empty-state language used by
* Prints/Original Art/Studio's empty states. Wrapped in the real
* PageLayout (see root.tsx) so it isn't a bare, unbranded page.
* --------------------------------------------------
*/
.route-error {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 80px 24px;
}

.route-error h1 {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(30px, 4vw, 46px);
  margin-top: 12px;
  color: var(--olp-fg);
}

.route-error p {
  color: var(--olp-fg-muted);
  font-size: 13.5px;
  margin-top: 16px;
  max-width: 40ch;
}

.route-error-links {
  display: flex;
  gap: 28px;
  margin-top: 32px;
}

/*
* --------------------------------------------------
* SEARCH — predictive search drawer
* --------------------------------------------------
*/
.predictive-search {
  display: flex;
  flex-direction: column;
  gap: var(--olp-space-3);
}

.search-input-row {
  display: flex;
  gap: 0;
}

.search-input-row input {
  flex: 1;
  background: rgba(241, 238, 230, 0.04);
  border: 1px solid rgba(241, 238, 230, 0.22);
  border-right: none;
  border-radius: 0;
  appearance: none;
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-size: 13px;
  padding: 0 14px;
  height: 42px;
  outline: none;
}

.search-input-row input:focus {
  border-color: var(--pink);
}

.search-input-row button {
  background: var(--olp-fg);
  color: var(--olp-bg);
  border: none;
  height: 42px;
  padding: 0 18px;
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}

.search-input-row button:hover {
  background: var(--pink);
}

.predictive-search-status {
  font-size: 14px;
  color: var(--olp-fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.predictive-search-result {
  margin-top: var(--olp-space-2);
}

.predictive-search-result h5 {
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--pink);
  margin-bottom: 8px;
}

.predictive-search-result-item {
  list-style: none;
  border-bottom: 1px solid var(--olp-line-dark);
  padding: 10px 0;
}

.predictive-search-result-item a {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--olp-fg);
  text-decoration: none;
  font-size: 13px;
}

.predictive-search-result-item a:hover {
  color: var(--pink);
}

.predictive-search-result-item img {
  width: 40px;
  height: 40px;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--olp-surface);
}

.predictive-search p {
  color: var(--olp-fg-muted);
  font-size: 13px;
}

.search-result {
  margin-top: var(--olp-space-4);
}

.search-result h2 {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-size: 18px;
  margin-bottom: var(--olp-space-2);
}

.search-results-item {
  border-bottom: 1px solid var(--olp-line-dark);
  padding: 12px 0;
}

.search-results-item a {
  color: var(--olp-fg);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 12px;
}

.search-results-item a:hover {
  color: var(--pink);
}

.search-results-item img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  background: var(--olp-surface);
  flex-shrink: 0;
}

/*
* --------------------------------------------------
* ACCESSIBILITY — skip link (keyboard users otherwise have to tab
* through the full nav on every single page before reaching content)
* --------------------------------------------------
*/
.skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 100;
  background: var(--pink);
  color: var(--olp-bg);
  font-family: var(--olp-font-mono);
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 12px 18px;
  text-decoration: none;
  transition: top 0.2s var(--olp-ease);
}

.skip-link:focus {
  top: 12px;
}

/*
* --------------------------------------------------
* PRODUCT PRICE — ProductPrice.tsx's own wrapper classes had NO rules
* anywhere until this pass, meaning every price on every Piece page and
* in the cart was rendering as plain inherited body text instead of the
* prominent mono treatment shown in every preview. Real bug, fixed here.
* --------------------------------------------------
*/
.product-price {
  font-family: var(--olp-font-mono);
  font-size: 18px;
  color: var(--olp-fg);
  /* "USD" and the amount are two text nodes inside one inline span —
     at narrow container widths the natural space between them was
     acting as a normal line-wrap point, splitting "USD" and "$120.00"
     onto two separate lines. This is one price, never two lines. */
  white-space: nowrap;
}

.product-price-on-sale {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.product-price-on-sale s {
  color: var(--olp-fg-muted);
  font-size: 14px;
  text-decoration-color: var(--pink);
}

/* Inside a cart line, the price reads smaller than the Piece-page hero
   price — .cart-line-price already sets its own font-size, this just
   keeps ProductPrice's internal layout (the on-sale flex row) working
   at that smaller scale too. */
.cart-line-price .product-price {
  font-size: inherit;
}

.cart-discount {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--olp-fg-muted);
}

.cart-discount code {
  font-family: var(--olp-font-mono);
  color: var(--olp-fg);
}

.cart-line-children {
  list-style: none;
  margin-top: 8px;
  padding-left: 20px;
  border-left: 1px solid var(--olp-line-dark);
}

/*
* --------------------------------------------------
* POLICIES — linked from the global footer on every page, so this
* needs a real (if minimal) pass rather than staying stock markup.
* --------------------------------------------------
*/
.policy h1 {
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 4vw, 40px);
  margin: 20px 0 32px;
}

.policy-body {
  max-width: 680px;
  color: var(--olp-fg-muted);
  font-size: 14px;
  line-height: 1.75;
}

.policy-body h2,
.policy-body h3 {
  color: var(--olp-fg);
  font-family: var(--olp-font-body);
  font-weight: 500;
  margin: 28px 0 12px;
}

.policy-body p {
  margin-bottom: 16px;
}

.policy-body a {
  color: var(--pink);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.policy-body ul,
.policy-body ol {
  margin: 0 0 16px 20px;
}

.policies-list {
  list-style: none;
}

.policies-list li {
  border-bottom: 1px solid var(--olp-line-dark);
  padding: 14px 0;
}

/* Merchant-authored product description from Shopify's descriptionHtml
   (see app/routes/products.$handle.tsx). Scoped so merchant formatting
   inherits the site's typography instead of browser defaults — the
   sanitizer permits these tags, so each needs a defined appearance. */
.product-description-rich {
  font-size: var(--text-body);
  line-height: 1.6;
  color: var(--olp-fg-muted);
}

.product-description-rich p {
  margin: 0 0 12px;
}

.product-description-rich p:last-child {
  margin-bottom: 0;
}

.product-description-rich ul,
.product-description-rich ol {
  margin: 0 0 12px;
  padding-left: 20px;
}

.product-description-rich li {
  margin-bottom: 6px;
}

.product-description-rich strong,
.product-description-rich b {
  color: var(--olp-fg);
  font-weight: 500;
}

.product-description-rich a {
  color: var(--olp-fg);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.product-description-rich a:hover {
  color: var(--pink);
}

.product-description-rich h1,
.product-description-rich h2,
.product-description-rich h3,
.product-description-rich h4,
.product-description-rich h5,
.product-description-rich h6 {
  font-family: var(--olp-font-body);
  font-size: var(--text-body);
  font-weight: 500;
  color: var(--olp-fg);
  margin: 16px 0 8px;
  letter-spacing: 0.02em;
}

/* ---------- Honest status notes ----------
   Two small, low-emphasis notices that must read as information rather
   than as an error state or a marketing message:
   .form-note  — shown on a product page when the variant list this page
                 was built from could be incomplete.
   .cart-unavailable-notice
               — shown when a cart line's product no longer resolves, so
                 a hidden line is never mistaken for a silent swap. */
.form-note,
.cart-unavailable-notice {
  font-family: var(--olp-font-mono);
  font-size: 12.5px;
  line-height: 1.6;
  letter-spacing: 0.04em;
  color: var(--olp-fg-muted);
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--olp-line);
}


/* ---------- Product gallery: refinement pass ----------
   The thumbnails carry transparent garment derivatives, so they need no
   frame fill of their own; the selected state is carried by the pink
   rule and full opacity rather than by a box. */
.edition-gallery-thumb {
  background: transparent;
}

.edition-gallery-thumb img {
  object-fit: contain;
  padding: 4px;
  box-sizing: border-box;
}

.edition-gallery-thumb[data-selected='true'] {
  opacity: 1;
  border-color: var(--pink);
}

.edition-gallery-thumb:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 2px;
}

/* The information column: the heading sits at the top of the media, not
   floating above it, so the two columns read as one composition. */
.piece-layout .piece-heading {
  grid-area: heading;
  padding-top: 0;
}

@media (max-width: 900px) {
  .piece-layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'heading' 'gallery' 'copy';
    max-width: 560px;
    column-gap: 0;
  }

  /* Mobile: the media must not push the colour/size controls off the
     first screen. Capping the stage by viewport height — not by width —
     is what keeps "choose a size" reachable without scrolling past an
     oversized gallery on a tall, narrow phone. */
  .piece-layout .edition-gallery-stage {
    aspect-ratio: auto;
    height: min(46svh, 360px);
  }

  .edition-gallery-thumbs {
    justify-content: center;
    padding-inline: 16px;
  }

  /* Touch targets stay comfortably above the 44px minimum even though
     the thumbnails themselves are small. */
  .edition-gallery-thumb {
    width: 52px;
    height: 52px;
    min-width: 44px;
    min-height: 44px;
  }
}


/* ---------- The Guerrilla Poker collection grid ----------
   The six drawings have very different intrinsic proportions — the owl
   is tall, the crow is wide. The grid used to be a flex-wrap row of
   cards whose media height simply followed whatever the artwork was, so
   in the first desktop row the titles and "View the piece" links ended
   up nearly 200px apart vertically.

   The structure below fixes that at the level it actually breaks: a
   real grid with equal rows, a fixed-proportion media frame per card,
   and the artwork contained inside that frame rather than defining it.
   Nothing is cropped, stretched or truncated — the frame is square and
   the drawing sits inside it at its own aspect ratio. */
.gp-collection-grid {
  display: grid;
  /* Exactly three columns, so six cards fill two complete rows. The
     previous auto-fit grid produced four-plus-two at desktop widths,
     and the empty half-row exposed the grid container's own background
     as a grey rectangle. Three columns has no empty cell to expose. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  align-items: stretch;
}

@media (max-width: 1100px) and (min-width: 701px) {
  .gp-collection-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.gp-collection-grid .piece {
  /* Cards stretch to their row, and the internal order below is what
     puts the number, title, credit and link at the same height in every
     card of that row. */
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}

.gp-collection-grid .piece-card-link {
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* One media frame shape for every card. The artwork is centred inside
   it with contain, so a tall drawing and a wide drawing occupy the same
   footprint without either being distorted. */
.gp-collection-grid .thumb-wrap {
  width: 100%;
  max-width: 320px;
  aspect-ratio: 1 / 1;
  /* Without this a flex container still takes its content's height as a
     floor, defeating the aspect-ratio above. */
  min-height: 0;
  overflow: hidden;
  margin: 0 auto 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The artwork is CONSTRAINED by the frame, never sized to fill it.
   Stretching it to 100%/100% let a tall drawing override the frame's
   aspect-ratio and grow the box — which is what left the owl's frame
   69px taller than its neighbours and pushed its title down with it.
   max-width/max-height with auto sizing means the frame decides the
   footprint and the drawing simply sits inside it at its own ratio. */
.gp-collection-grid .thumb {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  object-position: center;
}

/* Reserved space for a title that wraps to two lines, so a one-line
   title in the same row does not pull everything under it upwards. */
.gp-collection-grid .piece h2 {
  min-height: 2.3em;
  display: flex;
  align-items: flex-start;
  margin-bottom: 6px;
}

.gp-collection-grid .piece .num {
  margin-bottom: 8px;
}

/* The credit takes the remaining slack, which pins the link to the
   bottom of every card — and because the rows are equal height, the
   links line up along one baseline across the row. */
.gp-collection-grid .piece .meta {
  margin-bottom: 20px;
}

.gp-collection-grid .piece > .link {
  margin-top: auto;
  align-self: flex-start;
}




/* ---------- Limited Editions ----------
   One real edition, presented with the same restraint as the collection
   cards and reusing their frame rules rather than inventing a second
   card style. Capped so a single entry reads as one object on a shelf,
   not as a stretched full-width banner. */
.le-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 340px));
  gap: 1px;
  justify-content: start;
}

.le-piece .thumb-wrap {
  width: 100%;
  max-width: 300px;
  aspect-ratio: 1 / 1;
  min-height: 0;
  overflow: hidden;
  margin: 0 auto 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.le-piece .thumb {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.le-year {
  margin-top: 4px;
  opacity: 0.75;
}


/* ---------- Opening viewport / footer separation ----------
   The homepage's opening room fills the viewport; the footer should
   begin CLEARLY after it, not peek in at the fold. This reserves
   64-96px of additional space below the room so the two stay
   intentionally separate, which is the relationship the approved
   design has between the opening statement and the site chrome.

   Scoped with :has() to the homepage room specifically — every other
   page wants its footer to follow its content normally. */
main:has(.room--collection) {
  padding-bottom: 0;
}

/* body is a 100vh flex column, so padding alone changes nothing — the
   room simply shrinks to absorb it and the page still ends exactly at
   the fold. Giving the room a viewport-relative MINIMUM height is what
   makes the document taller than the viewport; the rail's margin below
   it is then the visible separation.

   The 10rem allows for the header above and the rail itself, so the
   room still fills the opening screen rather than overshooting it. */
/* The homepage's bottom rail is a direct child of <body>, not of
   <main>, and <body> is a 100vh flex column — so main simply shrinks to
   absorb any padding, margin or spacer added inside it and the rail
   stays pinned to the bottom of the first screen. (Sizing .drop-home
   instead just made its content overflow THROUGH the rail.)

   Giving the ROOM a viewport-relative minimum height is what actually
   makes the document taller than the screen. The 5.5rem allowance is
   the header above it; the spacer below then provides the visible
   separation, so the rail lands roughly 65-80px past the fold at every
   size checked (1280, 1366, 1440, tablet). */
main:has(.room--collection) .drop-home {
  display: flex;
  flex-direction: column;
}

/* main must not shrink, or the rail is simply laid out after main's
   (shrunken) BOX while the content overflows through it — which is
   exactly what happened: the document grew but the rail never moved. */
main:has(.room--collection) {
  /* flex-basis:auto, not the skeleton's 0%. With basis 0% main's box is
     only the leftover space, so its content overflowed INVISIBLY and
     the rail stayed pinned at the fold no matter how tall the content
     grew — the document height increased while the rail did not move. */
  flex: 1 0 auto;
}

/* Desktop only. The mobile layout below 860px reflows into a normal
   document flow where the room is auto-height and the rail follows
   naturally — forcing a viewport-height room there would just add dead
   space under the CTA. */
@media (min-width: 861px) {
  main:has(.room--collection) .room--collection {
    flex: 1 0 auto;
    /* The room is one viewport tall less 1rem, and the header above it
       is ~5.7rem — so the rail below lands roughly 75px past the fold,
       inside the requested 64-96px, and stays there at every desktop
       height because the whole expression is viewport-relative.
       Measured at 1280x720, 1366x768, 1440x900 and 768x1024. */
    min-height: calc(100svh - 1rem);
  }
}



/* ---------- Product gallery on mobile ----------
   EditionGallery renders a desktop stage AND a separate swipeable
   carousel; below 900px the stage is hidden and the carousel is what is
   actually on screen. Capping the stage therefore did nothing — the
   carousel kept its 4/5 aspect ratio and pushed the colour and size
   controls a full screen down.

   Capping the carousel by VIEWPORT HEIGHT is what keeps "choose a
   colour and size" reachable on the first screen of a tall phone,
   instead of behind a scroll through an oversized image. */
/* SUPERSEDED FOR THE STILLS: the shirt now fills the phone's width.
   The 330px height cap drew a 224px-wide garment on a 390px screen —
   57% of the width — which is what "too small" looked like on mobile.
   The colour and size controls now sit below a full-width shirt; the
   request was explicitly for the garment to lead.

   The numbers are matched rather than tuned: 5% inline padding leaves
   an image 90% of the slide wide, a 3:4 still that wide is 1.2x the
   slide width tall, and a 5:6 slide is exactly 1.2x — so the image
   fills the slide top to bottom with no dead band, and the garment
   (95% of its canvas width) lands ~28px from each edge of a 390 phone.
   Applied through 960px, where the stage hands over to this carousel,
   so there is no band of widths left on the old rules. */
@media (max-width: 960px) {
  .piece-layout .edition-gallery-mobile-slide {
    aspect-ratio: 5 / 6;
    height: auto;
    background: transparent;
  }

  .piece-layout .edition-gallery-mobile-slide img,
  .piece-layout .edition-gallery-mobile-slide .edition-video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 0 5%;
    box-sizing: border-box;
  }

  .piece-layout .edition-gallery-mobile-track {
    background: transparent;
  }
}


/* ---------- Product page: copy sections ---------- */
.product-description-copy {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--olp-fg);
  margin: 0;
  max-width: 52ch;
}

.product-care-list {
  list-style: none;
  padding: 0;
  margin: 14px 0 0;
  font-family: var(--olp-font-mono);
  font-size: 12.5px;
  line-height: 1.9;
  color: var(--olp-fg-muted);
}

.product-care-list li::before {
  content: '— ';
  opacity: 0.6;
}

/* The merchant's own measurement table. Scrolls inside its own box so a
   wide table can never make the PAGE scroll sideways. */
.product-size-guide {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.product-size-guide table {
  border-collapse: collapse;
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  min-width: 420px;
}

.product-size-guide td,
.product-size-guide th {
  border: 1px solid var(--olp-line-dark);
  padding: 5px 8px;
  text-align: left;
  white-space: nowrap;
  color: var(--olp-fg-muted);
}

/* The heading should introduce the shirt, not compete with it. */
.piece-heading h1 {
  font-size: clamp(28px, 3.4vw, 52px);
  line-height: 1.04;
}


/* ---------- Collection: mobile carousel ----------
   A real horizontal scroll-snap strip, not a JS slideshow. Swiping is
   native, the previous/next buttons just scroll it, and nothing moves on
   its own. Only this element scrolls sideways — the document never does,
   which is why the track is the scroller and not the page. */
.gp-collection {
  position: relative;
}

.gp-carousel-controls {
  display: none;
}

@media (max-width: 700px) {
  .gp-collection-grid {
    display: grid;
    grid-auto-flow: column;
    /* One generous card plus a glimpse of the next, so it reads as a
       strip that continues rather than a single static card. */
    grid-auto-columns: 82%;
    grid-template-columns: none;
    gap: 12px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;
    /* The 1px hairline background belongs to the desktop grid; on a
       scrolling strip it just draws lines between cards. */
    background: transparent;
    scrollbar-width: none;
  }

  .gp-collection-grid::-webkit-scrollbar {
    display: none;
  }

  .gp-collection-grid .piece {
    scroll-snap-align: center;
    border: 1px solid var(--olp-line-dark);
    padding: 28px 22px;
  }

  .gp-collection-grid .thumb-wrap {
    max-width: none;
  }

  .gp-carousel-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
    margin-top: 18px;
  }

  .gp-carousel-btn {
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid var(--olp-line);
    color: var(--olp-fg-muted);
    font-size: 16px;
    cursor: pointer;
    transition: color 0.25s var(--olp-ease), border-color 0.25s var(--olp-ease);
  }

  .gp-carousel-btn:hover,
  .gp-carousel-btn:focus-visible {
    color: var(--pink);
    border-color: var(--pink);
  }

  .gp-carousel-btn[disabled] {
    opacity: 0.3;
    cursor: default;
  }

  .gp-carousel-position {
    font-size: 12px;
    letter-spacing: 0.12em;
    color: var(--olp-fg-muted);
    min-width: 64px;
    text-align: center;
  }
}


/* ---------- Homepage hero: desktop keeps the name in the selector ---------- */
.hero-block-name {
  display: none;
}

/* ==========================================================================
   MOBILE HOMEPAGE HERO
   ==========================================================================
   The opening screen is: the complete shirt, a small collaboration
   credit, the selected design's name, a compact selector with its
   position count, and SHOP THE COLLECTION — in that order, inside one
   viewport, at 390x844 and 430x932, with the header included in the
   budget.

   How the budget is met: the media is the only elastic element. It takes
   whatever is left after the header and the five fixed rows, via a
   min()/max() expression in svh — so it shrinks on a short phone rather
   than pushing the CTA off-screen, and it never has to be cropped,
   clipped or scaled below a legible size. Text sizes are untouched.

   What it is NOT doing: no overflow:hidden on the page, no fixed
   viewport height that traps content, no tiny type. At enlarged text
   settings or on an unusually short screen the content simply exceeds
   the viewport and the page scrolls normally — see the short-screen
   block at the end.
   ========================================================================== */
@media (max-width: 860px) {
  .room--collection {
    /* The five hero rows plus the media, in the requested order. */
    padding-top: clamp(8px, 1.4vh, 14px);
  }

  /* 1. The shirt, first. */
  .hero-block-media {
    order: 1;
    /* Elastic: one viewport, minus the header and the five fixed rows
       below, clamped so it can never collapse to nothing or grow past
       the point where the CTA would be pushed under the fold. */
    height: clamp(190px, calc(100svh - 400px), 420px);
    margin-bottom: clamp(10px, 1.6vh, 18px);
    padding: 0 clamp(12px, 4vw, 24px);
  }

  /* 2. The collaboration credit — small, one line. */
  .hero-block-eyebrowtitle {
    order: 2;
    margin-bottom: clamp(6px, 1vh, 10px);
  }

  /* The display heading and subline are desktop-only: three lines of
     48px type is most of a phone screen, and the CTA directly below
     already names the collection. */
  .room--collection .home-title,
  .room--collection .collection-subline {
    display: none;
  }

  /* 3. The selected design's name. */
  .hero-block-name {
    display: block;
    order: 3;
    margin-bottom: clamp(10px, 1.6vh, 16px);
  }

  .collection-active-name {
    font-family: var(--olp-font-display);
    font-weight: 700;
    font-size: clamp(19px, 5.4vw, 25px);
    letter-spacing: 0.02em;
    line-height: 1.1;
    color: var(--olp-fg);
  }

  /* 4. The compact selector, with its position count. */
  .hero-block-selector {
    order: 4;
    margin-bottom: clamp(12px, 2vh, 20px);
  }

  /* The name is already its own row above; showing it again here is the
     repetition this pass is removing. The count stays. */
  .collection-selector-label-name {
    display: none;
  }

  .collection-selector-label {
    margin-bottom: 8px;
    font-size: 11.5px;
  }

  .collection-selector {
    gap: 6px;
  }

  .collection-selector-thumb {
    width: 46px;
    height: 46px;
  }

  /* The secondary "View the piece" link is removed from the mobile hero:
     the whole opening screen is a route into the collection already, and
     it was competing with the CTA for the same job. */
  .collection-selector-link {
    display: none;
  }

  /* 5. The call to action, fully visible. */
  .hero-block-cta {
    order: 5;
    margin-bottom: 0;
  }

  /* Below the fold: the fuller artwork credit, retained rather than
     deleted — just not inside the opening-screen budget. */
  .hero-block-desc {
    order: 6;
    margin-top: clamp(28px, 5vh, 44px);
  }
}

/* Short screens and enlarged text: give the media a smaller floor and
   let the page scroll rather than clipping anything. Accessibility wins
   over the one-screen goal — the requirement is that it fits at the two
   named sizes at default text size, not that content is ever hidden. */
@media (max-width: 860px) and (max-height: 700px) {
  .hero-block-media {
    height: clamp(150px, calc(100svh - 360px), 300px);
  }

  .collection-active-name {
    font-size: clamp(17px, 5vw, 21px);
  }
}

/* ---------- Product-gallery video transparency ----------
   Still needed: the PRODUCT page's gallery plays video. The homepage
   hero no longer contains a <video> at all — see the ghost-mannequin
   section below — so the .collection-media-stage selectors that used to
   be listed here have gone with it. */
.edition-video,
.edition-gallery-stage .edition-video,
.edition-gallery-stage video {
  background: transparent;
}

.edition-video-wrap,
.edition-gallery-stage {
  background: transparent;
}

/* ==========================================================================
   HOMEPAGE HERO — APPROVED GHOST-MANNEQUIN STILLS (PORTRAIT)
   ==========================================================================
   The approved stills are portrait 3:4 (768x1024). The tier this replaces
   was written for landscape 840x675 Shopify photographs and drove the box
   from its WIDTH, which for a portrait garment overflows vertically. Here
   the height leads and the width follows the aspect ratio.

   Composition: on desktop the title is back in the left text column with
   the copy and CTA beneath it, and the shirt is on the right with its
   front/back control below it. On mobile the title is ordered above the
   shirt. There is only ever ONE title element — it moves between the two
   positions, it is never duplicated.
   ========================================================================== */

/* Part of the left column on desktop, alongside the eyebrow and copy. */
.hero-block-title,
.hero-block-subline {
  position: relative;
  z-index: 1;
  max-width: 640px;
}

.hero-block-title .home-title {
  margin-top: 6px;
}

/* Shirt, then control. */
.hero-block-media {
  height: auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 2vh, 20px);
  pointer-events: none;
}

/* The block stays transparent to the pointer so it can never sit in
   front of the copy column; its own controls must still be clickable. */
.hero-block-media > * {
  pointer-events: auto;
}

.collection-media-stage {
  position: relative;
  display: block;
  /* The stills' own ratio, so the box has no dead space and its size
     cannot depend on which face is showing. */
  aspect-ratio: 3 / 4;
  /* Height-led: a portrait garment runs out of vertical room long before
     it runs out of column width. */
  height: min(62svh, 600px);
  width: auto;
  max-width: 100%;
  max-height: none;
  margin-inline: auto;
  padding: 0;
  overflow: visible;
}

/* Moves the whole image together — no rotation, no scale, no lighting
   change. ~4px over 5.5s. */
.garment-float {
  position: absolute;
  inset: 0;
  animation: olp-garment-float 5.5s ease-in-out infinite;
  will-change: transform;
}

/* Paused rather than removed, so returning to view resumes smoothly.
   Set by an IntersectionObserver in the component and mirrored in
   enhance.js for the preview. Default is running, so a visitor with no
   JS still gets the intended presentation. */
.collection-media-stage[data-offscreen] .garment-float {
  animation-play-state: paused;
}

@keyframes olp-garment-float {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-4px);
  }
}

.garment-view {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* contain, always: collar, shoulders, both sleeves and hem stay
     visible. Never cropped to fill the box. */
  object-fit: contain;
  object-position: center;
  display: block;
  opacity: 0;
  transition: opacity 200ms ease;
}

.collection-media-stage[data-view='back'] .garment-view--back,
.collection-media-stage[data-view='front'] .garment-view--front {
  opacity: 1;
}

.garment-view-toggle {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 2px;
  color: var(--olp-fg);
  font-family: var(--olp-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* 44px is the touch-target floor, not a decorative minimum. */
  min-height: 44px;
  padding: 0 22px;
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}

.garment-view-toggle:hover {
  border-color: var(--pink);
  color: var(--pink);
}

.garment-view-toggle:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .garment-float {
    animation: none;
  }
  .garment-view {
    transition: none;
  }
}

/* ---------- Tablet ---------- */
@media (max-width: 900px) {
  .hero-block-media {
    height: auto;
    padding: 0 clamp(10px, 3.5vw, 20px);
  }

  .collection-media-stage {
    height: min(48svh, 460px);
  }
}

/* ---------- Mobile ---------- */
@media (max-width: 860px) {
  /* The title leads, above the shirt. This overrides the earlier tier
     that hid the display heading on mobile entirely: it is now the only
     heading on the page, so it has to be visible here. */
  .room--collection .home-title {
    display: block;
  }

  .hero-block-title {
    order: 0;
    max-width: none;
    padding: 0 20px;
    margin-bottom: clamp(8px, 1.4vh, 14px);
  }

  .room--collection .hero-block-title .home-title {
    font-size: clamp(20px, 5.6vw, 27px);
    line-height: 1.1;
    white-space: normal;
  }

  /* The subline stays desktop-only: on a phone the shirt plus the few
     lines needed to choose one are the whole opening screen. */
  .hero-block-subline {
    display: none;
  }

  .hero-block-media {
    order: 1;
    height: auto;
    margin-bottom: clamp(12px, 2vh, 20px);
  }

  /* Elastic, so the complete garment stays visible and the CTA stays
     reachable on a short phone. The gap below leaves room for the float
     without the shirt ever touching the header. */
  .collection-media-stage {
    height: clamp(280px, calc(100svh - 430px), 470px);
  }
}

@media (max-width: 860px) and (max-height: 740px) {
  .collection-media-stage {
    height: clamp(210px, calc(100svh - 360px), 300px);
  }

  .hero-block-media {
    gap: 8px;
    margin-bottom: 10px;
  }
}

/* ---------- MOBILE FOLD BUDGET ----------
   Portrait stills are much taller than the landscape photographs this
   layout was last tuned for, which pushed SHOP THE COLLECTION below the
   fold at 390x844 and 430x932.

   Spacing is reclaimed first and the shirt reduced only by what is left
   over, because the brief asks for a prominent shirt. The stage keeps
   its 3:4 ratio and object-fit: contain throughout, so this shows the
   whole garment slightly smaller — never a cropped one. */
@media (max-width: 860px) {
  .hero-block-media {
    gap: 10px;
    margin-bottom: clamp(8px, 1.2vh, 12px);
  }

  .hero-block-title {
    margin-bottom: clamp(6px, 1vh, 10px);
  }

  .hero-block-eyebrowtitle {
    margin-bottom: clamp(4px, 0.8vh, 8px);
  }

  .hero-block-name {
    margin-bottom: clamp(6px, 1vh, 10px);
  }

  .hero-block-selector {
    margin-bottom: clamp(10px, 1.6vh, 16px);
  }

  /* What the rows above could not give back. 508px is the measured cost
     of the header, title, control, credit, name, selector and CTA
     together after the tightening above — 500 left the CTA exactly one
     pixel below the fold at 430x932, which is either true or it isn't. */
  .collection-media-stage {
    height: clamp(250px, calc(100svh - 508px), 440px);
  }
}

/* ==========================================================================
   APPAREL COLLECTION — OPEN GALLERY
   ==========================================================================
   Replaces the six boxed square cards. The shirts sit directly on the page
   background: no card background, no border, no rounded container, no drop
   shadow, and no square crop.

   Deliberately NEW class names (.gp-gallery / .gp-piece-*) rather than a
   rewrite of .gp-collection-grid / .piece. Those rules are still used by
   other catalogue pages, so leaving them untouched and simply not using
   them here is safer than editing shared styles — including the mobile
   scroll-snap carousel, which this presentation does not want: the spec
   is two columns stacking to one, not a swipeable strip.
   ========================================================================== */

.gp-gallery {
  display: grid;
  /* Two generous columns on desktop. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(40px, 6vw, 96px) clamp(32px, 5vw, 80px);
  align-items: start;
  /* Measured at 1440 without this: each media box came out 590x787, so a
     single shirt filled the viewport and the name, price and link sat
     below an 787px image. Capped, the pair still reads large while all
     six stay browseable. */
  max-width: 1040px;
  margin-inline: auto;
  /* No hairline gap background, no container fill — the page shows through. */
  background: transparent;
}

.gp-piece {
  display: flex;
  flex-direction: column;
  min-width: 0;
  color: inherit;
  text-decoration: none;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

/* The image area carries the stills' own portrait ratio, so every shirt
   occupies an identical footprint and none is cropped to fit a square. */
.gp-piece-media {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  margin-bottom: clamp(18px, 2.2vw, 28px);
}

.gp-piece-img {
  width: 100%;
  height: 100%;
  /* contain: the whole garment, never cropped to fill the box. */
  object-fit: contain;
  /* Centred, not bottom-anchored: these stills are already normalised
     and centred on their own canvas, so anchoring to the bottom only
     added dead space above every shirt. */
  object-position: center;
  display: block;
  /* The previous cards desaturated their thumbnails. These are approved
     photographs of the real product — shown as they are. */
  filter: none;
  transition: transform 600ms var(--olp-ease, ease);
}

.gp-piece:hover .gp-piece-img {
  transform: translateY(-4px);
}

@media (prefers-reduced-motion: reduce) {
  .gp-piece-img,
  .gp-piece:hover .gp-piece-img {
    transition: none;
    transform: none;
  }
}

.gp-piece-name {
  font-family: var(--olp-font-display);
  font-weight: 700;
  font-size: clamp(17px, 1.5vw, 22px);
  letter-spacing: 0.02em;
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--olp-fg);
  margin: 0 0 8px;
}

.gp-piece-price {
  font-family: var(--olp-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--olp-fg-muted);
  margin-bottom: 12px;
}

.gp-piece-link {
  font-family: var(--olp-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--olp-fg);
  border-bottom: 1px solid rgba(255, 255, 255, 0.35);
  padding-bottom: 3px;
  align-self: flex-start;
  transition: color 160ms ease, border-color 160ms ease;
}

.gp-piece:hover .gp-piece-link,
.gp-piece:focus-visible .gp-piece-link {
  color: var(--pink);
  border-color: var(--pink);
}

.gp-piece:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 6px;
}

/* ---------- One column on narrow screens ---------- */
@media (max-width: 700px) {
  .gp-gallery {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(44px, 11vw, 72px);
  }

  .gp-piece-name {
    font-size: clamp(18px, 5.2vw, 22px);
  }
}

/* ==========================================================================
   HOMEPAGE — APPROVED GUERRILLA POKER HERO + SHIRT CAROUSEL
   ==========================================================================
   Replaces the shirt-in-the-hero composition. The hero is now the official
   Guerrilla symbol and the collection wordmark; the six shirts live in
   their own section after a separator.

   COMPOSITION CORRECTION (annotated screenshot): the logo is NOT parked at
   the far side of a wide grid. The lockup shrink-wraps its own contents and
   is centred as ONE object, with the logo tucked close beside the lower
   POKER / Collection portion of the title. That is why this uses an
   inline-flex lockup aligned to flex-end rather than the reference's
   1.7fr/1fr grid with a 7% gap.
   ========================================================================== */

.gp-hero {
  /* ONE TYPE SCALE FOR THE WHOLE COMPOSITION.
     --gp-word is the GUERRILLA/POKER size and every other measurement
     in the lockup — the italic lines, the spacing between them and the
     logo — is a fixed multiple of it. That is what makes the logo's
     placement arithmetic rather than emergent: it is positioned
     against the letterforms without ever consulting its own intrinsic
     dimensions.

     The vh term is not decoration. Four stacked lines at 11vw do not
     share a short-but-wide window with the copy, the CTA and the
     scroll cue, and min() lets one rule serve a 1440x900 laptop and a
     1024x500 landscape phone without a breakpoint for each.

     11vw is ~158px at 1440 against the previous version's 121px — the
     ~30% increase the composition was asked to grow by. */
  --gp-word: clamp(40px, min(11vw, 18vh), 176px);
  --gp-word-lh: 0.97;
  --gp-the: calc(var(--gp-word) * 0.39);
  --gp-the-gap: calc(var(--gp-word) * 0.04);
  --gp-collection: calc(var(--gp-word) * 0.655);
  --gp-collection-lh: 1.12;
  --gp-collection-gap: calc(var(--gp-word) * 0.05);

  /* The band the logo occupies: the POKER line box, the Collection
     line box and the margin between them. 0.99 so a sub-pixel
     rounding difference can never make the image the thing that
     decides how tall those rows are. */
  --gp-logo-band: calc(
    (var(--gp-word) * var(--gp-word-lh)) + var(--gp-collection-gap) +
      (var(--gp-collection) * var(--gp-collection-lh))
  );
  --gp-logo-h: calc(var(--gp-logo-band) * 0.99);
  /* 1011 / 1310 — the supplied file's own ratio, as a number, so the
     rendered box preserves it exactly at every size. */
  --gp-logo-ratio: 0.771756;

  /* The hero occupies the first screen below the real header. 88px is
     --header-height from tokens.css, so this follows the actual header
     rather than a guessed constant. A MINIMUM, never a fixed height, so
     text zoom and short landscape screens can grow it instead of
     clipping. */
  min-height: calc(100svh - var(--header-height));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(16px, 2.6vh, 34px);
  padding: clamp(22px, 3.4vh, 52px) 6vw clamp(26px, 4vh, 60px);
  text-align: center;
}

.gp-hero-kicker {
  margin: 0;
  color: var(--pink);
  font-family: var(--olp-font-mono);
  font-size: clamp(11px, 1.05vw, 15px);
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

/* THE COMBINED COMPOSITION — one object, centred as one object.

   A grid, not a flex row: a row can only ever place the logo AFTER the
   whole title block, which is what parked it past the final "A". Here
   GUERRILLA spans both columns and therefore sets the composition's
   width; POKER and Collection occupy column 1; the logo takes column 2
   across those two rows and is pushed to the grid's right edge — which
   IS GUERRILLA's right edge. Its footprint lands under the word's last
   letters with its right edge on the final "A" by construction, at any
   type size, before or after the webfont loads.

   inline-grid so the box shrink-wraps its contents and the pair
   centres together instead of sitting at opposite ends of the room. */
.gp-hero-lockup {
  display: inline-grid;
  grid-template-columns: auto auto;
  align-items: start;
  column-gap: clamp(8px, 1vw, 26px);
  max-width: 100%;
}

/* The h1 IS the grid, so its four children can be placed directly. */
.gp-hero-title {
  display: contents;
}

/* EXPLICIT ROWS, not auto-placement. The logo is last in the DOM, so
   with an auto row the placement cursor had already passed POKER and
   put it in rows 4-5 — inventing a fifth row that added 151px of empty
   space under the lockup and dropped the scroll cue below the fold.
   Naming all four rows removes the cursor from the question. */
.gp-hero-the {
  grid-area: 1 / 1 / 2 / -1;
}

.gp-hero-word--guerrilla {
  grid-area: 2 / 1 / 3 / -1;
}

.gp-hero-word--poker {
  grid-area: 3 / 1 / 4 / 2;
}

.gp-hero-collection {
  grid-area: 4 / 1 / 5 / 2;
}

.gp-hero-the {
  display: block;
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-size: var(--gp-the);
  line-height: 1;
  letter-spacing: -0.01em;
  margin: 0 0 var(--gp-the-gap);
  text-align: left;
}

/* GUERRILLA / POKER. Inter 900 with the approved tight negative tracking.
   Inter is self-hosted (see fonts.css): the preview server's CSP blocks
   Google Fonts outright, so an @import would silently fall back to Arial. */
.gp-hero-word {
  display: block;
  font-family: 'Inter', 'Archivo', Arial, sans-serif;
  font-weight: 900;
  font-size: var(--gp-word);
  line-height: var(--gp-word-lh);
  letter-spacing: -0.058em;
  color: var(--olp-fg);
  text-align: left;
}

.gp-hero-collection {
  display: block;
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--gp-collection);
  line-height: var(--gp-collection-lh);
  letter-spacing: -0.035em;
  margin: var(--gp-collection-gap) 0 0;
  text-align: left;
}

/* Tucked under the ILLA of GUERRILLA, in the space beside POKER and
   Collection, sitting on the Collection line.

   WIDTH plus aspect-ratio — never height:100% with align-self:stretch.
   That pairing asked the grid how tall the row was while the row was
   still asking the image how tall it was; the image's intrinsic
   1011x1310 won the tie and inflated the lockup to 1390px. Here both
   axes are computed from --gp-word, so the picture is told its size
   and gets no vote in the row's. */
.gp-hero-logo {
  display: block;
  /* Rows 3 and 4 — beside POKER and Collection, under GUERRILLA. */
  grid-area: 3 / 2 / 5 / 3;
  align-self: end;
  justify-self: end;
  width: calc(var(--gp-logo-h) * var(--gp-logo-ratio));
  height: auto;
  aspect-ratio: 1011 / 1310;
  max-width: 100%;
  margin: 0;
}

.gp-hero-intro {
  margin: 0;
  max-width: 640px;
  font-size: clamp(14px, 1.45vw, 19px);
  line-height: 1.7;
  color: var(--olp-fg-muted);
}

/* Two sentences, one line each, until there is room for both on one —
   at which point the hero reclaims a whole line of the first screen,
   which is the difference between the scroll cue landing above the
   fold on a 900px-high laptop and below it. */
.gp-hero-intro > span {
  display: block;
}

@media (min-width: 1100px) {
  .gp-hero-intro {
    max-width: 860px;
  }

  .gp-hero-intro > span {
    display: inline;
  }
}

.gp-hero-cta {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(28px, 4vw, 60px);
  min-width: 280px;
  min-height: 60px;
  padding: 18px 28px;
  background: var(--olp-fg);
  color: var(--olp-bg);
  font-family: var(--olp-font-body);
  font-size: clamp(14px, 1.35vw, 17px);
  font-weight: 400;
  letter-spacing: 0.015em;
  text-decoration: none;
  border: 1px solid var(--olp-fg);
  transition: background 160ms ease, color 160ms ease;
}

.gp-hero-cta:hover {
  background: #fff;
  color: var(--olp-bg);
}

.gp-hero-cta > span {
  font-size: 1.4em;
  line-height: 1;
}

.gp-hero-cta:focus-visible {
  outline: 2px solid var(--olp-fg);
  outline-offset: 4px;
}

/* ---------- The shirts: their own section, after a fine separator ---------- */

.gp-shirts {
  border-top: 1px solid var(--olp-line-dark, #302b2d);
  padding: clamp(44px, 7vh, 82px) 6vw clamp(38px, 6vh, 70px);
}

.gp-shirts-inner {
  max-width: 1440px;
  margin: 0 auto;
}

.gp-shirts-kicker {
  margin: 0 0 20px;
  color: var(--pink);
  font-family: var(--olp-font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.gp-shirts-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 20px;
}

.gp-shirts-top h2 {
  margin: 0;
  font-family: var(--olp-font-display);
  font-weight: 500;
  font-size: clamp(24px, 2.9vw, 42px);
  line-height: 1.2;
  letter-spacing: -0.025em;
  color: var(--olp-fg);
}

.gp-carousel-controls {
  display: flex;
  align-items: center;
  gap: 14px;
}

.gp-arrow {
  width: 46px;
  height: 46px;
  min-width: 46px;
  border: 1px solid #514b50;
  background: transparent;
  color: var(--olp-fg);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}

.gp-arrow:not(:disabled):hover {
  border-color: var(--pink);
  color: var(--pink);
}

.gp-arrow:disabled {
  opacity: 0.3;
  cursor: default;
}

.gp-arrow:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

.gp-count {
  min-width: 104px;
  text-align: center;
  font-family: var(--olp-font-mono);
  font-size: 12px;
  color: var(--olp-fg-muted);
}

/* Three across on desktop, capped so ultrawide does not turn them into
   oversized crops. touch-action keeps vertical page scrolling natural
   while horizontal swipes drive the carousel. */
.gp-shirt-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3.2vw, 56px);
  margin-top: clamp(26px, 4vh, 42px);
  touch-action: pan-y;
  /* Measured at 2560 without this: the visible shirt came out 443x590,
     bigger than at 1440, because the 1440 cap was on the wrapper and the
     grid inside it stretched freely. Capping the grid itself is what
     actually holds shirt size on an ultrawide display. */
  max-width: 1440px;
  margin-inline: auto;
}

.gp-shirt {
  margin: 0;
  min-width: 0;
}

.gp-shirt-link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.gp-shirt-link:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 6px;
}

.gp-shirt img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 4;
  /* contain: the complete garment — neckline, sleeves, hem — never a crop. */
  object-fit: contain;
}

.gp-shirt figcaption {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  border-top: 1px solid #393238;
  margin-top: 15px;
  padding-top: 16px;
  font-size: clamp(12px, 1.05vw, 15px);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

.gp-shirt-arrow {
  color: var(--pink);
}

.gp-shirts-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-top: 30px;
  color: var(--olp-fg-muted);
  font-size: 12px;
}

.gp-paging {
  display: flex;
  gap: 10px;
}

.gp-page {
  width: 42px;
  height: 34px;
  min-height: 34px;
  padding: 7px 0 0;
  background: transparent;
  border: 0;
  border-top: 2px solid #514b50;
  color: var(--olp-fg-muted);
  font-family: var(--olp-font-mono);
  font-size: 12px;
  cursor: pointer;
}

.gp-page[aria-pressed='true'] {
  border-color: var(--pink);
  color: var(--olp-fg);
}

.gp-page:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

/* ---------- More from Ahinoam ---------- */

.gp-more {
  border-top: 1px solid #292723;
  margin: 0 6vw;
  padding: 34px 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}

.gp-more p {
  margin: 0;
  font-family: var(--olp-font-serif);
  font-style: italic;
  font-size: clamp(18px, 1.9vw, 23px);
  color: var(--olp-fg);
}

.gp-more-links {
  display: flex;
  flex-wrap: wrap;
  gap: 27px;
  font-size: 13px;
}

.gp-more-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--olp-fg);
  text-decoration: underline;
  text-underline-offset: 5px;
  transition: color 160ms ease;
}

.gp-more-links a:hover {
  color: var(--pink);
}

.gp-more-links a:focus-visible {
  outline: 2px solid var(--olp-fg);
  outline-offset: 4px;
}

/* ---------- Tablet ---------- */
@media (max-width: 900px) {
  .gp-shirt-grid {
    gap: 20px;
  }
}

/* ---------- Narrow mobile ---------- */
@media (max-width: 640px) {
  .gp-hero-cta {
    min-width: 0;
    width: min(100%, 320px);
    gap: 20px;
  }

  .gp-shirt-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .gp-shirt img {
    width: 100%;
    max-width: 340px;
    margin: 0 auto;
  }

  .gp-shirts-top {
    align-items: flex-start;
  }

  .gp-carousel-controls {
    width: 100%;
    justify-content: space-between;
  }

  .gp-shirts-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }

  .gp-more {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ==========================================================================
   APPROVED HERO — PAGE FIT
   ==========================================================================
   The homepage's exact-fit rules are keyed on main:has(.room--collection),
   which was the old shirt-in-the-hero markup. The approved hero does not
   carry that class, so nothing pushed the Instagram rail below the fold
   and it rendered at y=835 inside a 900px screen, overlapping a hero that
   runs to y=903.

   The requirement changed along with the class. The old homepage wanted
   header + hero + rail to total exactly one screen. This one wants the
   HERO to own the first screen and the shirt section to begin after it,
   so the rail simply follows the content.

   main must not shrink for that to work: body is a flex column, so with
   flex-basis 0% main's BOX stays at the leftover size while its content
   overflows invisibly, and anything after it stays pinned at the fold no
   matter how tall the content grows.
   ========================================================================== */
main:has(.gp-hero) {
  flex: 1 0 auto;
  padding-bottom: 0;
}

main:has(.gp-hero) .drop-home {
  display: flex;
  flex-direction: column;
}

/* ==========================================================================
   HERO — SCROLL CUE AND PRODUCT-GALLERY ZOOM
   ==========================================================================
   MEASURED AT 1440 WITH INTER LOADED, which is what the composition
   above is built from:
     GUERRILLA  811px wide at font-size 158.4  (5.12 x the type size)
     POKER      504px                          (3.18 x)
     "ILLA"     314px                          (1.98 x)
     the space under GUERRILLA and right of POKER: 307 x 278

   The logo is 1.35 x the type size wide and 1.74 x tall, so it lands
   inside that space with its right edge on the final "A" and roughly a
   0.6-em breath after POKER. Those are ratios, not pixels, so they hold
   at every width the type scale reaches.
   ========================================================================== */

/* ---------- Scroll cue ---------- */

.gp-hero-cue {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  /* Separation from the CTA on top of the hero's own flex gap, which
     already spaces the blocks — this only marks the cue as a distinct
     invitation rather than a second button. */
  margin-top: clamp(6px, 1.2vh, 18px);
  min-height: 44px;
  padding: 6px 14px;
  color: var(--pink);
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: opacity 160ms ease;
}

.gp-hero-cue:hover {
  opacity: 0.72;
}

.gp-hero-cue:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 4px;
}

/* A static chevron, below the label so the arrow is the thing nearest
   the section it points at. No looping bounce — the brief is explicit. */
.gp-hero-cue-chevron {
  display: block;
  width: 22px;
  height: 22px;
  line-height: 1;
}

/* The anchor target. scroll-margin keeps the fixed-height header from
   covering the section heading when the browser jumps to it. */
.gp-shirts {
  scroll-margin-top: calc(var(--header-height) + 12px);
}

/* Smooth only when the visitor has not asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* ---------- 4. Product-gallery zoom ---------- */

/* The button must FILL the fixed-ratio stage. The stage image sizes
   itself with height:100%, and against a button of auto height that
   resolved to the portrait still's natural height — taller than the 5:4
   stage, whose overflow:hidden then cut the garment off at the hem. */
.edition-gallery-zoom {
  display: block;
  width: 100%;
  height: 100%;
  align-self: stretch;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  position: relative;
}

.edition-gallery-zoom:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 4px;
}

.edition-gallery-zoom-hint {
  position: absolute;
  right: 12px;
  bottom: 12px;
  padding: 7px 12px;
  border-radius: 20px;
  background: rgba(5, 5, 5, 0.6);
  color: var(--olp-fg);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 240ms ease;
  pointer-events: none;
}

.edition-gallery-zoom:hover .edition-gallery-zoom-hint,
.edition-gallery-zoom:focus-visible .edition-gallery-zoom-hint {
  opacity: 1;
}

/* A native <dialog>: focus trapping, Escape-to-close and focus return
   come from the platform rather than being re-implemented. Same pattern
   already used by the Original Art viewer. */
.edition-zoom-dialog {
  width: min(96vw, 1400px);
  max-width: 96vw;
  height: min(94vh, 1400px);
  max-height: 94vh;
  padding: 0;
  border: 0;
  background: #050505;
  color: var(--olp-fg);
  overflow: auto;
  overscroll-behavior: contain;
}

.edition-zoom-dialog::backdrop {
  background: rgba(5, 5, 5, 0.92);
}

.edition-zoom-figure {
  margin: 0;
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

/* Never scaled beyond the source's own resolution — an upscaled blur
   would be claiming detail the file does not contain. It is shown at up
   to 1:1 and the dialog scrolls when that exceeds the viewport, which is
   what makes panning on a phone possible. */
.edition-zoom-img {
  display: block;
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

.edition-zoom-close {
  position: sticky;
  top: 0;
  left: 100%;
  z-index: 2;
  margin: 10px;
  min-height: 44px;
  padding: 10px 18px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.35);
  color: var(--olp-fg);
  font-family: var(--olp-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

.edition-zoom-close:hover {
  border-color: var(--pink);
  color: var(--pink);
}

.edition-zoom-close:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

/* ==========================================================================
   HERO — RESPONSIVE
   ==========================================================================
   Three cases, and only three. Everything else is already handled by
   --gp-word's min(11vw, 18vh), which shrinks the whole composition
   together whenever either axis runs short.
   ========================================================================== */

/* NARROW MOBILE — the same composition as desktop, at the phone's size.

   The title stays on the left with the logo tucked under the ILLA of
   GUERRILLA, and the whole lockup centres as one object. Only the type
   scale is re-based — on width alone, since a phone held upright has
   height to spare — and the grid, row placement and logo size all
   follow from --gp-word. The logo therefore scales with the title:
   ~67px wide at 390 and ~55px at 320.

   A fixed 70px logo was tried at 320 and rejected: it is taller than
   the POKER + Collection band there and rose 13px into "ILLA". */
@media (max-width: 640px) {
  .gp-hero {
    /* Dynamic viewport excludes browser controls; a minimum lets short
       screens and larger text grow naturally without clipping. */
    min-height: calc(100svh - var(--gp-header-height, var(--header-height)));
    min-height: calc(100dvh - var(--gp-header-height, var(--header-height)));
    padding: clamp(24px, 3.6vh, 40px) 6vw clamp(30px, 4.4vh, 52px);
    gap: clamp(16px, 2.4vh, 28px);
    --gp-word: clamp(34px, 13.3vw, 64px);
  }

  .gp-hero-kicker {
    margin-top: auto;
  }

  .gp-hero-cue {
    margin-top: auto;
  }
}

/* Explicit per-shirt view controls, outside the product link. */
.gp-shirt-faces {
  display: flex;
  justify-content: center;
  gap: 4px;
  margin-top: 12px;
}

.gp-shirt-faces button {
  min-height: 44px;
  padding: 8px 14px;
  background: transparent;
  color: var(--olp-fg-muted);
  border: 1px solid transparent;
  border-radius: 2px;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

.gp-shirt-faces button[aria-pressed='true'] {
  border-color: var(--pink);
  color: var(--olp-fg);
}

.gp-shirt-faces button:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

@media (max-width: 640px) {
  .gp-shirts .gp-paging {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 4px;
    width: 100%;
  }

  .gp-shirts .gp-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    height: auto;
    min-width: 0;
    padding: 4px 0 8px;
    border: 1px solid transparent;
    border-bottom-color: #514b50;
    gap: 4px;
  }

  .gp-shirts .gp-page img {
    width: 100%;
    max-width: 48px;
    height: auto;
    aspect-ratio: 3 / 4;
    object-fit: contain;
  }

  .gp-shirts .gp-page[aria-pressed='true'] {
    border-color: var(--pink);
    background: rgb(255 255 255 / 4%);
  }
}

/* SHORT LANDSCAPE, above the mobile breakpoint. The type has already
   shrunk via the vh term; this only tightens the rhythm between the
   blocks so the cue clears a 420px-high window. */
@media (min-width: 641px) and (max-height: 620px) {
  .gp-hero {
    gap: clamp(10px, 1.8vh, 20px);
    padding-top: clamp(16px, 2.6vh, 30px);
    padding-bottom: clamp(18px, 3vh, 34px);
  }

  .gp-hero-cue {
    margin-top: clamp(8px, 1.4vh, 16px);
  }
}

/* VERY SHORT LANDSCAPE. min-height would force a screen the content
   cannot fill sensibly; let the section be as tall as it needs and let
   the page scroll, which is what the cue is there to invite. */
@media (min-width: 641px) and (max-height: 460px) {
  .gp-hero {
    min-height: 0;
  }
}

/* Policy and customer-help additions. */
.help-page summary{cursor:pointer;color:var(--paper);padding:12px 0}
.help-section-links,.product-support-links{display:flex;gap:12px;flex-wrap:wrap}
.help-page details{border-bottom:1px solid var(--olp-line);padding:8px 0}
.help-page details p{margin-top:12px}
.help-page section{scroll-margin-top:100px}
.home-support-links{padding:16px var(--olp-gutter) 24px;border-top:1px solid var(--olp-line)}
.support-links{flex-wrap:wrap;gap:12px 20px;line-height:1.6}
.product-support-links a{color:var(--pink);text-decoration:underline}
.policy-body{overflow-wrap:anywhere}

/* Shared purchase and contact controls, all page types. */
.olp-button.primary-add,.pr-review button.primary-add{background:var(--pink,#e600be);border:1px solid var(--pink,#e600be);color:#080808;opacity:1;font-weight:700;min-height:48px;padding:14px 24px;cursor:pointer}
.olp-button.primary-add:hover:not(:disabled),.pr-review button.primary-add:hover:not(:disabled){background:#ff55d9;border-color:#ff55d9}
.olp-button.primary-add:focus-visible,.pr-review button.primary-add:focus-visible{outline:2px solid #f2efe6;outline-offset:4px}
.olp-button.primary-add:disabled,.pr-review button.primary-add:disabled{background:#555;border-color:#777;color:#fff;cursor:not-allowed}
.olp-button.primary-add[aria-busy=true],.pr-review button.primary-add[aria-busy=true]{background:#ee00bf;color:#080808;cursor:progress}
.whatsapp-icon{display:inline-block;flex:none;vertical-align:middle;overflow:visible}
footer .whatsapp-icon{width:15px;height:15px;margin-right:5px}
.oa-enquire .whatsapp-icon{width:16px;height:16px}
.cart-sync-status{font-size:13px;line-height:1.5;margin:12px 0;color:var(--olp-fg,#f2efe6)}
.preparation-progress{height:3px;background:#3b1832;overflow:hidden;margin:16px 0 24px;width:100%}
.preparation-progress[hidden]{display:none}
.preparation-progress span{display:block;width:35%;height:100%;background:#ee00bf;animation:prepare-slide 1.4s ease-in-out infinite}
@keyframes prepare-slide{from{transform:translateX(-110%)}to{transform:translateX(390%)}}
@media(prefers-reduced-motion:reduce){.preparation-progress span{animation:none;width:100%}}
/* Reusable apparel quick shopping; controls sit below artwork, never over it. */
.gp-piece-details{display:block;color:inherit;text-decoration:none}
.qa-trigger{display:flex;align-items:center;justify-content:space-between;gap:24px;min-height:44px;padding:10px 16px;margin-top:12px;border:1px solid #777;background:#0b0b0b;color:#f2efe6;width:100%;font:inherit;font-size:13px;cursor:pointer;opacity:0;transition:opacity .15s,border-color .15s}
.gp-shirt:hover .qa-trigger,.gp-shirt:focus-within .qa-trigger,.gp-piece:hover .qa-trigger,.gp-piece:focus-within .qa-trigger,.qa-trigger:focus{opacity:1}
.qa-trigger:hover,.qa-trigger:focus-visible{border-color:#ee00bf}.qa-trigger span{color:#ee00bf}
.gp-shirts-bottom{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:24px}
.gp-collection-cta{display:contents}
.gp-shirts-bottom>.gp-paging{justify-self:end}
.gp-collection-cta>span{justify-self:start}
.gp-collection-cta>a{display:inline-flex;align-items:center;justify-content:center;gap:36px;min-width:300px;min-height:60px;padding:18px 28px;background:#f2efe6;color:#111;text-decoration:none;font-size:17px;font-weight:400;border:1px solid #f2efe6}
.gp-collection-cta>a:hover{border-color:#ee00bf}.gp-collection-cta>a span{color:#a90088}
.qa-dialog{box-sizing:border-box;width:min(470px,calc(100vw - 32px));max-height:calc(100dvh - 40px);padding:22px;margin:auto;border:1px solid #666;background:#101010;color:#f2efe6;overflow-y:auto;overscroll-behavior:contain;box-shadow:0 18px 70px #0009}
.qa-dialog::backdrop{background:#000b}.qa-dialog button,.qa-dialog a{font:inherit}.qa-close{display:block;margin:0 0 6px auto;min-height:44px;padding:8px 12px;color:inherit;border:1px solid #666;background:transparent;cursor:pointer}
.qa-body h2{font-size:24px;line-height:1.2;margin:4px 0 16px}.qa-summary{display:flex;gap:24px;align-items:center;margin-bottom:20px}.qa-summary img{width:100px;height:125px;object-fit:contain;background:#191919}.qa-summary p{font-size:18px}
.qa-dialog fieldset{border:0;padding:0;margin:0 0 20px;min-width:0}.qa-dialog legend{font-size:13px;margin-bottom:10px}.qa-options{display:flex;flex-wrap:wrap;gap:8px}.qa-options button{min-width:44px;min-height:44px;padding:8px 12px;background:transparent;color:inherit;border:1px solid #666;display:flex;align-items:center;gap:8px;cursor:pointer}.qa-options button[aria-pressed=true]{border-color:#ee00bf;background:#292029}.qa-options button:disabled{opacity:.4;cursor:not-allowed;text-decoration:line-through}.qa-swatch{display:inline-grid;place-items:center;width:18px;height:18px;border:1px solid #aaa;border-radius:50%;font-size:10px}
.qa-status{font-size:13px;line-height:1.5;min-height:20px}.qa-add{width:100%;min-height:48px;padding:12px;background:#f2efe6;color:#111;border:1px solid #f2efe6;cursor:pointer}.qa-add:disabled{opacity:.45;cursor:not-allowed}.qa-details{display:block;width:fit-content;margin:12px auto 0;padding:12px 0;color:inherit;text-underline-offset:5px;font-size:13px}.qa-dialog :focus-visible,.gp-collection-cta a:focus-visible{outline:2px solid #ee00bf;outline-offset:3px}
@media(hover:none),(pointer:coarse){.qa-trigger{opacity:1}}
@media(max-width:640px){.qa-trigger{opacity:1}.gp-collection-cta{flex-direction:column;align-items:stretch;gap:12px;width:100%}.gp-shirts-bottom{flex-direction:column;align-items:stretch;gap:24px}.gp-shirts-bottom .gp-paging{justify-content:center;flex-wrap:wrap}.qa-dialog{width:100%;max-width:100%;max-height:90dvh;margin:auto 0 0;padding:16px 20px max(20px,env(safe-area-inset-bottom));border-radius:16px 16px 0 0}.qa-summary{margin-bottom:16px}.qa-summary img{height:100px;width:80px}}

@media(max-width:900px){.gp-shirts-bottom{display:flex;flex-direction:column;align-items:center;gap:24px}.gp-collection-cta{display:flex;flex-direction:column;align-items:center;gap:16px;width:100%;text-align:center}.gp-collection-cta>a{width:min(100%,340px);min-width:0}.gp-shirts-bottom>.gp-paging{justify-content:center;flex-wrap:wrap}}
.oa-print-crosslink{margin-top:14px;line-height:1.6}
.oa-print-link{display:inline-flex;align-items:center;min-height:44px;padding:8px 0;font-size:17px;color:var(--olp-fg);text-underline-offset:5px}
.oa-print-link:hover{color:var(--pink)}
.oa-print-link:focus-visible{outline:2px solid var(--pink);outline-offset:4px}
.oa-enquire--prominent .whatsapp-icon{width:20px;height:20px;flex:0 0 20px}
.oa-enquire--prominent span{min-width:0;line-height:1.4}

/* Image-only thumbnails, separated from the following content. */
.oa-gallery-nav {margin-top:24px;margin-bottom:32px;gap:16px;align-items:center}
.oa-thumbs {flex:0 1 auto;gap:12px;flex-wrap:nowrap}
.oa-thumb {flex:0 1 64px;min-width:44px}
.cp-artwork-gallery + p,.cp-artwork-gallery + .cp-presentation {margin-top:28px}
.cp-surface-note {max-width:42ch;line-height:1.6;margin:16px 0 24px}

.ac-grid {position:relative}
.ac-grid[data-packed='true'] > .ac-card {position:absolute;float:none;margin:0}
.ac-mobile-scale{display:none}
.ac-dimensions .ac-measurement,.ac-dimensions .ac-dimension-order{color:inherit;font:inherit;display:inline}
/* Mobile keeps two small works together; larger blocks use the full slot.
   Shared (C-gap)/150 scale is bounded to 42%–100% for usability.
   Narrow phones use single blocks with a 65% minimum instead of tiny columns. */
@media(max-width:767px){
 .archive-main.ac-collection{padding:28px 18px 32px;min-width:0}
 .ac-collection .head{margin-bottom:28px;scroll-margin-top:110px}
 .ac-collection .head h1{margin-top:0}
 .ac-collection .head .oa-count{font-size:12px;letter-spacing:.035em;line-height:1.6}
 .ac-desktop-scale{display:none}.ac-mobile-scale{display:inline}
 .ac-grid{--gallery-gap-x:16px;--gallery-gap-y:26px;width:100%}
 .ac-card{width:100%;max-width:100%}
 .ac-card[data-mobile-small=true]{width:calc((100% - 16px)/2)}
 .ac-image{width:clamp(42cqw,var(--mobile-art-width),100cqw);max-width:100%;height:auto;aspect-ratio:var(--art-ratio);margin-bottom:12px}
 .ac-dimensions.eyebrow{font-size:14px;letter-spacing:0;line-height:1.4;color:var(--pink)}
 .ac-measurement{white-space:nowrap}
 .ac-dimensions .ac-dimension-order{display:block;font-size:11px;letter-spacing:0;color:var(--olp-fg-muted)}
 .ac-card .ac-title{font-size:19px;margin:5px 0;line-height:1.25}
 .ac-card-foot{gap:0 10px;align-items:center}
 .ac-card .ac-price{font-size:14px;margin:4px 0}
 .ac-card .oa-enquire--compact{letter-spacing:.04em;font-size:11px}
}
@media(max-width:359px){
 .ac-card[data-mobile-small=true]{width:100%}
 .ac-image{width:clamp(65cqw,var(--narrow-art-width),100cqw)}
}
.oa-image-feedback{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:4px 20px;color:#ccc;font-size:12px;min-height:44px}.oa-image-feedback button{padding:8px 12px;min-height:44px;color:inherit;background:transparent;border:1px solid #666}.oa-full-detail .oa-lightbox-body{display:block;overflow:auto;touch-action:pan-x pan-y pinch-zoom}.oa-full-detail .oa-lightbox-img{max-width:none;max-height:none;width:200%;height:auto}

[data-oa-stage-retry]:not([hidden]){display:block;margin:8px auto;min-height:44px;background:transparent;color:inherit;border:1px solid #555;padding:8px 12px}

/* Approved model cutouts; shirt-only mini thumbnails retain their original assets. */
.gp-shirt-link .model-cutout{object-fit:contain;object-position:center bottom;aspect-ratio:1121/1403;padding:0;background:transparent}
.gp-piece-media .model-cutout{width:100%;height:100%;object-fit:contain;object-position:center bottom;background:transparent}
.model-image-caption{font-size:12px;line-height:1.5;color:#aaa;margin:48px 0 12px}

@media(max-width:960px){.edition-gallery[data-model-preview] .edition-gallery-stage:has(dialog[open]){display:block!important;height:0;min-height:0}.edition-gallery[data-model-preview] .edition-gallery-stage:has(dialog[open])>.edition-gallery-zoom{display:none}}

/* Mobile design chooser stays immediately above the changing model. */
@media(max-width:640px){
.gp-shirts-inner>.gp-paging{display:flex;justify-content:space-between;gap:4px;width:100%;margin:18px 0 10px}
.gp-shirts-inner>.gp-paging .gp-page{flex:1;min-width:0;padding:5px 2px}
.gp-shirts-inner>.gp-paging .gp-page img{width:100%;max-width:48px;height:58px;object-fit:contain}
.gp-shirts .gp-shirt-grid{margin-top:0}
.gp-shirts .gp-shirt-link .model-cutout{width:calc(100% + 32px);max-width:calc(100vw - 24px);margin-inline:-16px;height:min(62svh,515px);aspect-ratio:auto;object-fit:contain;object-position:center}
}

@media(max-width:640px){.gp-shirts-inner>.gp-paging{scroll-margin-top:110px}}

/* Approved mobile shopping bar: anchored below the shirt, over the pants. */
.gp-model-title{color:inherit;text-decoration:none}
@media(max-width:640px){
.gp-model-stage{position:relative}
.gp-model-info{position:absolute;z-index:2;left:0;bottom:5px;width:100%;min-height:60px;padding:8px 10px;background:rgba(0,0,0,.76);border-block:1px solid rgba(255,255,255,.25);box-sizing:border-box;display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-rows:1fr 1fr;column-gap:10px;align-items:center}
.gp-model-info .gp-model-title{display:block;color:#f5f2ea;font-size:12px;font-weight:500;letter-spacing:.025em;text-decoration:none;line-height:1.2;grid-column:1;grid-row:1}
.gp-model-info figcaption{padding:0;margin:0;border:0;font:inherit;line-height:inherit;display:block}
.gp-model-info .gp-shirt-arrow{display:none}
.gp-model-info .gp-piece-price{font-size:12px;line-height:1.2;color:#ddd;margin:0;letter-spacing:.02em;grid-column:1;grid-row:2}
.gp-model-info .qa-trigger{margin:0;width:auto;min-height:44px;padding:8px 10px;font-size:13px;background:rgba(0,0,0,.35);color:#fff;border:1px solid #777;display:flex;justify-content:space-between;gap:16px;grid-column:2;grid-row:1/3}
.gp-model-info .qa-trigger span{color:#ff00bc}
.gp-shirts .gp-shirt-faces{margin-top:14px}
}

/* Approved Guerrilla collection presentation. */
.archive-main .head.collection-review-head{max-width:none;margin-bottom:44px}
.collection-review-brand{display:flex;align-items:center;gap:20px;margin-bottom:22px}
.collection-review-title .collection-review-logo{width:.65em;height:.85em;object-fit:contain;align-self:center;flex-shrink:0}
.collection-review-brand .eyebrow{margin:0;color:#ee00bf}
.archive-main .head .collection-review-title{display:flex;align-items:baseline;flex-wrap:nowrap;gap:.22em;white-space:nowrap;font-size:clamp(20px,4.5vw,76px);line-height:1.16;letter-spacing:0;margin:0 0 24px;font-weight:400}
.collection-review-title .collection-script{font-family:var(--olp-font-serif);font-style:italic;font-weight:400;letter-spacing:-.035em}
.collection-review-title .collection-wordmark{font-family:Inter,Archivo,Arial,sans-serif;font-weight:900;font-style:normal;letter-spacing:-.058em;word-spacing:.12em}
.archive-main .head .collection-review-intro{font-family:var(--olp-font-sans,Arial,sans-serif);font-size:15px;line-height:1.65;color:#d4d1c9;max-width:780px;margin:0;letter-spacing:0}
.archive-main .gp-piece .qa-trigger{opacity:1;visibility:visible;min-height:48px;font-size:16px;margin-top:16px}
@media(max-width:640px){.collection-review-brand{gap:12px;margin-bottom:18px}.archive-main .head .collection-review-title{font-size:clamp(24px,7.4vw,46px);flex-wrap:wrap;white-space:normal;column-gap:.18em;row-gap:0;margin-bottom:18px}.archive-main .head.collection-review-head{margin-bottom:28px}.archive-main .head .collection-review-intro{font-size:14px;line-height:1.65}}

/* Compact category-specific delivery and verified shirt specifications. */
.shipping-notice {font-family:var(--font-body, sans-serif);font-size:14px;line-height:1.55;color:var(--text, #eeeae2);margin:12px 0 18px;max-width:64ch;letter-spacing:normal;}
.shirt-benefits {margin:18px 0 12px;}
.shirt-benefits p {margin:0;line-height:1.5;letter-spacing:normal;}
.shirt-benefits-headline {font-size:18px;color:var(--text, #eeeae2);}
.shirt-benefits .shirt-benefits-specs {font-size:14px;margin-top:5px;color:var(--text, #eeeae2);}
.home-shirt-benefits {text-align:center;margin:12px auto 8px;}
.home-shirt-benefits>p {font-size:16px;margin:0;}
.home-shirt-benefits .shipping-notice {margin:6px auto 12px;}
.gp-more-category {max-width:440px;}
.gp-more-category .shipping-notice {font-size:14px;line-height:1.55;margin:10px 0 0;}
@media(max-width:767px){.shirt-benefits-headline {font-size:17px;}.shirt-benefits {margin-top:14px;}.shipping-notice {margin:10px 0 16px;}.gp-more-links {flex-wrap:wrap;}.gp-more-category {width:100%;}}

.head .shipping-notice, .copy .shipping-notice, .head .shirt-benefits p, .copy .shirt-benefits p {color:#e4e0d8;}
.head .shirt-benefits-headline, .copy .shirt-benefits-headline {font-size:18px;}
.head .shirt-benefits-specs, .copy .shirt-benefits-specs, .head .shipping-notice, .copy .shipping-notice {font-size:14px;}

.gp-more-category .shipping-notice {font-family:var(--olp-font-body);font-style:normal;color:var(--olp-fg);}
.shipping-notice, .shirt-benefits {font-family:var(--olp-font-body);}

.home-shirt-benefits {text-align:left;margin:0 0 20px;color:var(--olp-fg);}.home-shirt-benefits .shipping-notice {margin:6px 0 0;}

/* Collection benefit heading precedes its supporting introduction. */
.archive-main .head .collection-benefit-heading{font-family:var(--olp-font-body);font-size:clamp(22px,2.2vw,30px);font-weight:500;line-height:1.35;letter-spacing:normal;color:var(--olp-fg);margin:0 0 12px;max-width:40ch}
.archive-main .head .collection-benefit-specs{font-family:var(--olp-font-body);font-size:14px;line-height:1.55;letter-spacing:normal;color:#e4e0d8;margin:12px 0 0}

/* Collection introduction above the brand name; homepage unchanged. */
.archive-main .head .collection-review-title{display:block;white-space:normal;font-size:clamp(28px,4.5vw,68px);margin-bottom:28px}
.collection-review-title .collection-official-line{display:block;font-size:.62em;line-height:1.15;margin-bottom:.22em}
.collection-review-title .collection-name-line{display:inline-flex;align-items:center;gap:.25em;white-space:nowrap}
.collection-review-title .collection-name-line .collection-review-logo{align-self:center;width:.65em;height:.85em}
.collection-review-title .collection-script{white-space:nowrap}
@media(max-width:640px){.archive-main .head .collection-review-title{font-size:clamp(24px,8.2vw,48px);margin-bottom:24px}.collection-review-title .collection-official-line{font-size:.76em;margin-bottom:.28em}}

/* Practical sizing guidance shared by the six shirts. */
.size-guide-help{font-size:14px;line-height:1.6;margin-bottom:18px;max-width:65ch}
.size-guide-help p{margin:0 0 12px}.size-guide-help ul{padding-left:20px;margin:0 0 14px}.size-guide-help li{margin-bottom:8px}
.product-size-guide caption{text-align:left;font-size:13px;line-height:1.5;padding-bottom:12px;color:inherit}
.product-size-guide th{text-align:left}.product-size-guide td{white-space:nowrap}
.size-guide-help,.size-guide-help p,.size-guide-help li{color:#d9d5ce}
.product-size-guide table{font-size:13px}.product-size-guide td,.product-size-guide th{color:#d9d5ce;padding:8px 10px}.product-size-guide caption{white-space:normal;text-align:left}
