/* nyArt storefront styles - served as one cached file (was inline in
   public_base.html until 2026-10-09). Linked via asset_url(), whose ?v=
   fingerprint changes whenever this file does. */
    @font-face {
      font-family: "Figtree"; font-style: normal; font-weight: 300 900; font-display: swap;
      src: url("/static/fonts/Figtree-Variable.ttf") format("truetype");
    }
    @font-face {
      font-family: "Caprasimo"; font-style: normal; font-weight: 400; font-display: swap;
      src: url("/static/fonts/Caprasimo-Regular.ttf") format("truetype");
    }
    /* ── Theme palette - "Organic": warm cream ground, terracotta accent,
       sage second accent. Tokens keep their original names so every
       inline var(--*) usage across templates repaints automatically. ── */
    :root {
      --bg: #f5ead8;
      --surface: #ebddc5;
      --surface-muted: #f4ecd8;
      --surface-dim: #dcd3c4;
      --placeholder: #eee7db;
      --text: #201e1d;
      --text-secondary: #474238;
      --text-muted: #645c50;
      --text-faint: #675f52;
      --border: #dcd3c4;
      --accent: #c67139;
      --accent-2: #7a8a5e;
      --accent-link: #8c491a;
      --accent-hover-border: #d67f48;
      --accent-soft: #fff2eb;
      --accent-selected: #ffe1d0;
      --accent-700: #8c491a;
      --accent-800: #643312;
      --btn-bg: #8c491a;
      --btn-bg-hover: #643312;
      --btn-fg: #fdf6ec;
      --price-green: #8c491a;
      --nav-bg: var(--bg);
      --nav-text: var(--text);
      --nav-muted: #645c50;
      --shadow: rgba(46, 43, 37, .14);
      --shadow-strong: rgba(46, 43, 37, .18);
      --shadow-sm: 0 1px 2px rgba(46, 43, 37, .14);
      --shadow-md: 0 3px 10px rgba(46, 43, 37, .16);
      --shadow-lg: 0 12px 32px rgba(46, 43, 37, .22);
      --paper: #fdfbf6;
      --paper-edge: rgba(32, 30, 29, .06);
      --stage-shadow: rgba(46, 43, 37, .2);
      --font-heading: "Caprasimo", system-ui, sans-serif;
      --font-body: "Figtree", system-ui, sans-serif;
      --radius-sm: 8px;
      --radius-md: 16px;
      --radius-lg: 28px;
    }
    html[data-theme="dark"] {
      --bg: #221d19;
      --surface: #2c2521;
      --surface-muted: #282219;
      --surface-dim: #3a3226;
      --placeholder: #3a3226;
      --text: #f3ead9;
      --text-secondary: #ddcfb8;
      --text-muted: #b3a58c;
      --text-faint: #a09378;
      --border: #3f3527;
      --accent: #e08a4f;
      --accent-2: #97ab77;
      --accent-link: #f6a06b;
      --accent-hover-border: #f6a06b;
      --accent-soft: rgba(198, 113, 57, .16);
      --accent-selected: rgba(198, 113, 57, .26);
      --price-green: #f6a06b;
      --btn-bg: #e08a4f;
      --btn-bg-hover: #f0a06c;
      --btn-fg: #221d19;
      --nav-bg: var(--bg);
      --nav-text: var(--text);
      --nav-muted: #b3a58c;
      --shadow: rgba(0, 0, 0, .35);
      --shadow-strong: rgba(0, 0, 0, .5);
      --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
      --shadow-md: 0 4px 14px rgba(0, 0, 0, .45);
      --shadow-lg: 0 14px 36px rgba(0, 0, 0, .55);
      --paper: #efe4cd;
      --paper-edge: rgba(255, 255, 255, .12);
      --stage-shadow: rgba(0, 0, 0, .55);
    }

    *, *::before, *::after { box-sizing: border-box; }
    body { margin: 0; font-family: var(--font-body);
           background: var(--bg); color: var(--text); line-height: 1.6; }
    h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); font-weight: 400; }

    /* ── Navigation - sits on the page ground with a hairline divider,
       not a dark bar; brand set in the display face. ── */
    .site-nav {
      background: var(--nav-bg);
      border-bottom: 1px solid var(--border);
      padding: .75rem 2rem;
      display: flex;
      align-items: center;
      gap: 1.5rem;
    }
    .site-nav-logo {
      color: var(--nav-text);
      font-family: var(--font-heading);
      font-size: 1.35rem;
      text-decoration: none;
    }
    /* "ny" set in the body face at normal weight (Caprasimo is a single-weight
       display font - no bold to lighten), "Art" stays in the display face;
       --accent/--accent-2 already carry the exact per-theme colours wanted
       here, so no new tokens. */
    /* Wordmark: the palette's darker terracotta + green in light theme (5.7:1
       and 6:1 on the header - the lighter accents were ~3:1, owner
       2026-09-25); dark theme keeps its lighter accents (6.3:1 / 6.7:1). */
    .logo-ny { font-family: var(--font-body); font-weight: 400; color: var(--accent-700); }
    .logo-art { color: #4f5d38; }
    html[data-theme="dark"] .logo-ny { color: var(--accent); }
    html[data-theme="dark"] .logo-art { color: var(--accent-2); }
    .site-nav-cities { display: flex; gap: 1.25rem; align-items: center; min-width: 0; }
    .site-nav-cities a:not(.nav-icon-btn) {
      color: var(--accent-link);
      text-decoration: none;
      font-size: .9rem;
      white-space: nowrap;
      padding: .35rem 0;
      border-bottom: 2px solid transparent;
    }
    .site-nav-cities a:not(.nav-icon-btn):hover { color: var(--accent-800); }
    html[data-theme="dark"] .site-nav-cities a:not(.nav-icon-btn):hover { color: var(--accent); }
    .site-nav-cities a[aria-current="page"] { border-bottom-color: currentColor; }
    .site-nav-actions { display: flex; gap: .5rem; align-items: center; margin-left: auto; }
    /* Icon-only controls - 44×44 touch targets, themed hover/focus. */
    .nav-cart { position: relative; }
    .cart-count:not(:empty) {
      position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px;
      padding: 0 5px; border-radius: 999px; background: var(--btn-bg, var(--accent));
      color: var(--btn-fg); font-size: .7rem; font-weight: 700; line-height: 18px;
      text-align: center;
    }
    .nav-icon-btn {
      width: 44px; height: 44px;
      display: inline-flex; align-items: center; justify-content: center;
      border: 1px solid var(--border); border-radius: 999px;
      background: transparent; color: var(--nav-muted);
      cursor: pointer; text-decoration: none; padding: 0;
    }
    .nav-icon-btn:hover { color: var(--accent-link); border-color: var(--accent-hover-border); }
    .nav-search {
      display: inline-flex; align-items: center; gap: .45rem;
      height: 44px; padding: 0 1rem; box-sizing: border-box;
      border: 1px solid var(--border); border-radius: 999px; color: var(--nav-muted);
    }
    .nav-search:focus-within { border-color: var(--accent-hover-border); color: var(--accent-link); }
    .nav-search svg { flex: none; }
    .nav-search input {
      border: 0; outline: 0; background: transparent; color: var(--text);
      font: inherit; font-size: .9rem; width: 13rem; min-width: 0; padding: 0;
    }
    .nav-search input::placeholder { color: var(--text-muted); }
    .nav-search input::-webkit-search-cancel-button { cursor: pointer; }
    .theme-icon-sun { display: none; }
    html[data-theme="dark"] .theme-icon-sun { display: inline; }
    html[data-theme="dark"] .theme-icon-moon { display: none; }
    /* Phones: ONE row - ☰, logo, then search / favorites / cart / theme;
       the cities and the account live in the ☰ menu (2026-09-24 redesign). */
    @media (max-width: 600px) {
      .site-nav { padding: .45rem .75rem; gap: .35rem; position: relative; }
      .site-nav-logo { font-size: 1.25rem; margin-right: auto; }
      .site-nav-actions { gap: .3rem; margin-left: 0; }
      .site-nav-cities { display: none; }
      .site-nav-actions a[href="/login"], .site-nav-actions a[href="/account/"] { display: none; }
      .nav-icon-btn { width: 40px; height: 40px; }
      .site-nav .nav-menu { display: block; }
      .nav-menu > summary { list-style: none; }
      .nav-menu > summary::-webkit-details-marker { display: none; }
      .nav-menu[open] > summary { background: var(--surface); }
      .nav-menu-panel {
        position: absolute; left: 0; right: 0; top: 100%; z-index: 60;
        display: grid; padding: .5rem 1rem 1rem; background: var(--surface);
        border-bottom: 1px solid var(--border); box-shadow: 0 16px 24px -12px rgba(0,0,0,.3);
      }
      .nav-menu-panel a { padding: .7rem .25rem; color: var(--text); text-decoration: none; font-size: 1rem; border-bottom: 1px solid var(--border); }
      .nav-menu-label { margin: .7rem .25rem .1rem; font-size: .7rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); }
    }
    .nav-search-sm { display: none; }
    .nav-menu { display: none; }
    @media (max-width: 600px) { .nav-search-sm { display: inline-flex; } }
    /* Very narrow phones: drop the theme toggle before anything else. */
    @media (max-width: 360px) { #theme-toggle { display: none; } }

    /* Themed keyboard focus everywhere (never the browser default); hidden
       radios can't show their own ring, so the label/swatch draws it. */
    :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    label:has(input[type="radio"]:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
    .swatch-option:has(input[type="radio"]:focus-visible) { outline: none; }
    .swatch-option:has(input[type="radio"]:focus-visible) .swatch-circle { outline: 2px solid var(--accent); outline-offset: 3px; }

    /* ── Layout ── */
    .container { max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; }
    main { min-height: calc(100vh - 56px - 72px); }

    /* ── Footer ── */
    footer {
      background: var(--nav-bg);
      color: var(--nav-muted);
      border-top: 1px solid var(--border);
      text-align: center;
      padding: 1.5rem;
      font-size: .8rem;
      margin-top: 4rem;
    }
    footer p { margin: 0; }

    /* ── Home page: hero, trust strip, shop-by-city ── */
    .home-hero {
      display: block;
      position: relative;
      width: 100%;
      aspect-ratio: 16/9;
      max-height: 720px;
      border-radius: var(--radius-lg);
      overflow: hidden;
      margin-top: 2rem;
      background: var(--surface-dim);
      text-decoration: none;
    }
    .home-hero-image { width: 100%; height: 100%; object-fit: cover; display: block; }
    .home-hero-scrim {
      position: absolute;
      inset: 0;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding: 1.75rem;
      background: linear-gradient(to top, rgba(32, 30, 29, .65), transparent 55%);
    }
    .home-hero-eyebrow { color: #fff; font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; margin: 0 0 .35rem; opacity: .92; }
    .home-hero-title { color: #fff; margin: 0 0 .25rem; font-size: 2.4rem; }
    .home-hero-by { color: #fff; margin: 0 0 .6rem; font-size: .9rem; opacity: .95; }
    .home-hero-price { color: #fff; margin: 0; font-size: .95rem; display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; }
    .home-hero-cta {
      background: var(--btn-bg); color: var(--btn-fg); border-radius: 999px;
      padding: .6rem 1.3rem; font-size: .88rem; font-weight: 600;
    }
    /* Phones: type under the painting, never on it (contrast + legibility). */
    @media (max-width: 600px) {
      .home-hero { aspect-ratio: auto; max-height: none; overflow: visible; background: none; border-radius: 0; margin-top: 1rem; }
      .home-hero-image { height: auto; aspect-ratio: 16/9; border-radius: var(--radius-lg); }
      .home-hero-scrim { position: static; background: none; padding: .9rem 0 0; }
      .home-hero-eyebrow, .home-hero-price, .home-hero-by { color: var(--text-muted); }
      .home-hero-title { color: var(--text); font-size: 1.6rem; }
    }

    .trust-strip {
      display: grid;
      grid-template-columns: repeat(4, auto);
      justify-content: space-between;
      gap: 1rem 1.5rem;
      padding: 1.75rem 0;
      margin-bottom: 1rem;
      border-bottom: 1px solid var(--border);
    }
    .trust-item {
      display: flex;
      align-items: center;
      gap: .5rem;
      font-size: .82rem;
      color: var(--text-secondary);
    }
    @media (max-width: 600px) {
      .trust-strip { grid-template-columns: 1fr 1fr; gap: .75rem 1rem; padding: 1.25rem 0; }
      .trust-item { font-size: .8rem; }
    }
    .trust-item svg { flex-shrink: 0; color: var(--accent); }

    .home-section-heading { font-size: 1.3rem; margin: 0 0 1rem; }

    .city-door-row {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
      gap: 1.25rem;
      margin-bottom: 2.5rem;
    }
    .city-door-row--single { grid-template-columns: minmax(0, 520px); }
    .city-door {
      display: block;
      position: relative;
      border-radius: var(--radius-lg);
      overflow: hidden;
      text-decoration: none;
      background: var(--surface-dim);
      aspect-ratio: 16/9;
    }
    .city-door-image {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .city-door-scrim {
      position: absolute;
      inset: 0;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding: 1rem;
      background: linear-gradient(to top, rgba(32, 30, 29, .55), transparent 60%);
    }
    .city-door-name { color: #fff; margin: 0 0 .2rem; font-size: 1.1rem; }
    .city-door-count { color: #fff; font-size: .78rem; opacity: .9; }
    /* No city photo yet: a plain label surface, not a copy of the artwork. */
    .city-door--plain { background: var(--surface); box-shadow: inset 0 0 0 1px var(--border); }
    .city-door--plain .city-door-scrim { background: none; justify-content: center; padding: 1.25rem 1.5rem; }
    .city-door--plain .city-door-name { color: var(--text); font-size: 1.7rem; }
    .city-door--plain .city-door-count { color: var(--text-muted); opacity: 1; font-size: .85rem; }

    /* ── City collection page ── */
    .city-header {
      display: flex;
      justify-content: space-between;
      align-items: flex-end;
      flex-wrap: wrap;
      gap: 1rem;
      padding: 2.5rem 0 1.5rem;
    }
    .city-eyebrow {
      font-size: .75rem;
      text-transform: uppercase;
      letter-spacing: .08em;
      color: var(--text-faint);
      margin: 0 0 .3rem;
    }
    .city-header h1 { margin: 0; }
    .seg { display: flex; gap: .4rem; flex-wrap: wrap; }
    .seg-opt {
      position: relative;
      display: inline-flex;
      align-items: center;
      gap: .35rem;
      padding: .4rem .9rem;
      border-radius: 999px;
      border: 1.5px solid var(--border);
      font-size: .82rem;
      color: var(--text-secondary);
      text-decoration: none;
      cursor: pointer;
    }
    .seg-opt:hover { border-color: var(--accent-hover-border); }
    .seg-opt-selected, .seg-opt-selected:hover {
      border-color: var(--accent);
      background: var(--accent-selected);
      color: var(--text);
    }

    /* ── Gallery grid ── */
    .gallery-header { padding: 2.5rem 0 .5rem; }
    .gallery-header h1 { font-size: 1.6rem; font-weight: 700; margin: 0 0 .4rem; }
    .gallery-header p { font-size: .95rem; color: var(--text-muted); margin: 0; }

    .gallery-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
      gap: 2rem 1.5rem;
      padding: 2rem 0 3rem;
    }
    /* One work = one large card, never a 220px tile in a field of cream. */
    .gallery-grid--single { grid-template-columns: minmax(0, 760px); }

    /* No card box (owner, 2026-09-23): the wall tile and plain text below,
       straight on the page - all the room goes to the art. */
    .art-card { display: block; color: inherit; }
    /* "More from {city}" under the product page */
    .related { background: var(--bg); padding: 2.5rem 0 3rem; border-top: 1px solid var(--border); }
    .related-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.4rem; }
    .related-head h2 { font-family: var(--font-heading); font-weight: 400; font-size: 1.5rem; margin: 0; color: var(--text); }
    .related-all { color: var(--accent-link); font-weight: 600; font-size: .92rem; text-decoration: none; }
    .related-all:hover { text-decoration: underline; }
    .related-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    @media (max-width: 900px) { .related-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

    /* Floating prints (owner, 2026-09-24): no tile, no wall colour, no paper
       border - each work hangs straight on the page with only its shadow.
       The picture area is an invisible, same-size box (--card-tile) and the
       work is as large as fits in it, whole (never cropped) and CENTRED both
       ways: a square box makes a 4:3 landscape and a 3:4 portrait the same
       size, a square work the largest, a panorama full-width.
       The size is computed, not left to the browser's flex/max-size
       interplay (which placed real 900px portraits off-centre): with the
       work's own shape as --ar (inline, from its recorded pixels) the width
       is min(box width, box height × ar) in container units. A work whose
       shape isn't known yet falls back to max-width/max-height. */
    .gallery-grid { --card-tile: 1 / 1; }
    /* One work alone on a city page: a wide box, not a 760px-tall square. */
    .gallery-grid--single { --card-tile: 4 / 3; }
    .art-card-image-wrap {
      position: relative;
      aspect-ratio: var(--card-tile, 1 / 1);
      container-type: size;
      display: flex; align-items: center; justify-content: center;
    }
    .art-card-image {
      display: block; flex: none;
      width: auto; height: auto; max-width: 100%; max-height: 100%;
      object-fit: contain;
      /* softly rounded corners (owner, 2026-09-24) - the shadow follows them */
      border-radius: var(--radius-sm);
      /* a hairline keeps a very pale work from melting into the page */
      box-shadow: 0 0 0 1px rgba(0, 0, 0, .06), 0 3px 6px rgba(0, 0, 0, .16), 0 12px 28px rgba(0, 0, 0, .22);
      transition: transform 180ms ease, box-shadow 180ms ease;
    }
    .art-card-image[style*="--ar"] {
      width: min(100cqw, 100cqh * var(--ar));
      max-width: none; max-height: none;
    }
    .art-card-link:hover .art-card-image {
      transform: translateY(-3px);
      box-shadow: 0 0 0 1px rgba(0, 0, 0, .06), 0 5px 10px rgba(0, 0, 0, .16), 0 18px 36px rgba(0, 0, 0, .26);
    }
    /* "View on a wall" hover cue - a small pill at the box's centre, which
       every centred print covers whatever its shape. */
    .art-card-hover {
      position: absolute;
      left: 50%; top: 50%;
      transform: translate(-50%, -50%);
      padding: .3rem .75rem;
      border-radius: 999px;
      white-space: nowrap;
      font-family: var(--font-heading);
      font-size: .75rem;
      color: #fff;
      background: rgba(32, 30, 29, .62);
      opacity: 0;
      transition: opacity 150ms ease;
      pointer-events: none;
    }
    .art-card-link:hover .art-card-hover { opacity: 1; }
    @media (prefers-reduced-motion: reduce) {
      .art-card-image, .art-card-link:hover .art-card-image { transition: none; transform: none; }
    }
    /* The shared starting price, said once above a grid whose cards all share it. */
    .grid-price-note { margin: 1rem 0 -.75rem; font-size: .9rem; color: var(--text-muted); }
    .art-card-placeholder {
      width: 100%;
      aspect-ratio: var(--card-tile, 1 / 1);
      border-radius: var(--radius-md);
      background: var(--placeholder);
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--text-faint);
      font-size: .8rem;
    }
    /* The heart leads the title (owner, 2026-09-24), a little space after it. */
    .art-card-body { padding: .9rem .1rem 0; display: flex; align-items: flex-start; justify-content: flex-start; gap: .4rem; }
    .art-card-text { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
    .art-card-title-link { text-decoration: none; color: inherit; }
    .art-card-title-link:hover .art-card-title { text-decoration: underline; text-underline-offset: 3px; }
    /* the artist's name in its own colour (a deeper sage that reads, AA) */
    .art-card-artist { color: #4f5d38; text-decoration: none; font-weight: 600; }
    html[data-theme="dark"] .art-card-artist { color: #b6c99a; }
    .art-card-city { color: inherit; text-decoration: none; }
    .art-card-artist:hover, .art-card-city:hover { text-decoration: underline; text-underline-offset: 2px; }
    .art-card-title { font-family: var(--font-heading); font-weight: 400; font-size: 1rem; margin: 0; color: var(--text); }
    .art-card-meta { font-size: .78rem; color: var(--text-muted); margin: 0; }
    .art-card-price { font-size: .85rem; font-weight: 600; color: var(--price-green); margin: .4rem 0 0; }
    .art-card-price-na { font-size: .82rem; color: var(--text-faint); margin: 0; }

    .gallery-empty {
      text-align: center;
      padding: 6rem 1rem;
      color: var(--text-faint);
    }
    .gallery-empty h2 { font-size: 1.25rem; margin: 0 0 .5rem; color: var(--text-muted); }
    .gallery-empty p { margin: 0; font-size: .9rem; }

    /* ── Product detail ── */
    .container--wide { max-width: 1280px; }
    /* The painted ground can be any of ten colors, so this row sits on a
       small theme-colored chip instead of on the paint - ONE chip, not two:
       breadcrumb left, Share right, inside the same pill. */
    .crumbs-share-row {
      display: flex; align-items: center; justify-content: space-between;
      flex-wrap: wrap; gap: .75rem; margin: 0 0 1.25rem;
      padding: .5rem 1.05rem; border-radius: 999px;
      background: var(--surface); border: 1px solid var(--border);
    }
    .crumbs {
      display: inline-flex; align-items: center; margin: 0;
      font-size: .8rem; color: var(--text-muted);
    }
    .crumbs a { color: var(--accent-link); text-decoration: none; }
    .crumbs a:hover { text-decoration: underline; }
    .product-layout {
      display: grid;
      grid-template-columns: minmax(0, 1.3fr) minmax(320px, .9fr);
      grid-template-areas: "media card" "notes card";
      grid-template-rows: auto 1fr;
      gap: 1.5rem 1.75rem;
      /* no top padding: the breadcrumb row's own 1.25rem margin is the gap,
         equal to the 1.25rem above it; the ground's own padding is the
         bottom (owner, 2026-09-24) */
      padding: 0;
      align-items: start;
    }
    .product-media { grid-area: media; }
    .product-notes { grid-area: notes; }
    /* Share: the right half of .crumbs-share-row's one shared chip - no
       surface/border of its own now, that's the row's job. */
    .share-island { margin: 0; display: inline-flex; align-items: center; }
    /* Phones: the row wraps, so Share goes back to a full-width block. */
    @media (max-width: 600px) { .share-island { display: flex; width: 100%; } }
    @media (max-width: 1023px) {
      .product-layout {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: "media" "card" "notes";
        grid-template-rows: none;
        gap: 1.25rem; padding-bottom: 6.5rem;
      }
    }

    /* ── The painted ground. The selected wall color paints everything between
       header and footer; header and footer never change. Everything readable
       on it (breadcrumb, wall island, buy card, notes, reviews) sits on a
       theme surface, so text contrast never depends on the paint. ── */
    .pdp-ground {
      /* No paint picked yet: the wall is the theme's own background. */
      --wall-hex: var(--bg);
      /* The wall colour paints only the artwork's stage now, never the whole
         page (owner, 2026-09-26) - the page keeps the theme background. */
      --wall-shown: var(--bg);
      background: var(--bg);
      /* padding-top, never a child's margin-top, for the gap below the
         header: a block child's top margin collapses straight through an
         ancestor with no border/padding of its own, landing ABOVE this
         element instead of inside it - the paint never got a chance to
         show behind that space at all, revealing the plain page background
         (--bg) instead. Nearly invisible in light theme, where --bg and the
         default wall paint are similar tones; glaring in dark theme, where
         they're nothing alike. padding always paints with the background
         and can never collapse away, so this is the actual fix, not the
         .crumbs-share-row margin tweaks that came before it. */
      padding-top: 1.25rem;
      padding-bottom: 2rem;
      /* the footer follows the content here: a full-screen minimum left a tall
         empty band of wall paint on big monitors (owner, 2026-09-24) */
      min-height: 0;
    }
    .pdp-ground + footer { margin-top: 0; }
    /* Dark theme leaves the paint alone: Warm ivory stays Warm ivory. Only the
       chrome (header, footer, islands, buy card) follows the theme. */
    /* Same ids/hexes as WALL_COLORS in app/routers/gallery.py. A custom
       colour (the picker) is set inline on .pdp-ground by artwork.html. */
    body:has(#wall-bright-white:checked) .pdp-ground { --wall-hex: #f3f2ed; }
    body:has(#wall-warm-white:checked) .pdp-ground { --wall-hex: #ece5d6; }
    body:has(#wall-greige:checked) .pdp-ground { --wall-hex: #cfc7b9; }
    body:has(#wall-light-gray:checked) .pdp-ground { --wall-hex: #c9c9c4; }
    body:has(#wall-khaki:checked) .pdp-ground { --wall-hex: #bba786; }
    body:has(#wall-sage:checked) .pdp-ground { --wall-hex: #a2ac94; }
    body:has(#wall-navy:checked) .pdp-ground { --wall-hex: #3f4a5a; }
    body:has(#wall-charcoal:checked) .pdp-ground { --wall-hex: #3a3836; }

    /* The buy card: furniture on the wall, always on the theme surface. It
       rides the painted wall on desktop (its grid area spans the media and
       notes rows, so there is room to travel and nothing below it to cover). */
    .buy-card {
      grid-area: card;
      position: sticky; top: 1rem; align-self: start;
      background: var(--surface);
      border-radius: var(--radius-lg);
      padding: 1.4rem;
      display: flex; flex-direction: column; gap: 1.1rem;
    }
    /* Description + Share: their own surface island, after the card in the
       document and never inside it. */
    .notes-island {
      background: var(--surface);
      border-radius: var(--radius-lg);
      padding: 1.25rem 1.4rem;
      display: flex; flex-direction: column; gap: 1rem;
    }
    /* Phones stack in document order (media, card, notes) with the pinned bar
       keeping Add to Cart in reach - no sticky card there. */
    @media (max-width: 1023px) { .buy-card { position: static; } }
    .tag {
      display: inline-block; margin-top: .55rem;
      padding: .2rem .75rem; border-radius: 999px;
      font-size: .72rem; font-weight: 600; letter-spacing: .02em;
    }
    .tag-accent { background: var(--accent-soft); color: var(--accent-link); }
    .product-meta { font-size: .82rem; color: var(--text-muted); margin: .1rem 0 0; }
    .product-artist-link { color: var(--accent-link); text-decoration: none; }
    .product-artist-link:hover { text-decoration: underline; }
    /* ── Artist page ── */
    .artist-crumbs { display: inline-block; margin: 1.5rem 0 1rem; }
    .artist-header { display: flex; gap: 1.5rem; align-items: flex-start; margin-bottom: 2rem; }
    .artist-photo { width: 140px; height: 140px; object-fit: cover; border-radius: 50%; flex: none; }
    .artist-bio { max-width: 62ch; color: var(--text-secondary); line-height: 1.6; }
    .artist-works-heading { font-size: 1.2rem; margin: 0 0 1rem; }
    @media (max-width: 600px) { .artist-header { flex-direction: column; } .artist-photo { width: 110px; height: 110px; } }

    /* ── Style: one row of narrow, tall tiles (owner, 2026-10-07, after
       Amazon's colour tiles): the picture on top, a short name under it; all
       five fit one row on desktop and phones (sideways scroll only if not) ── */
    .option-compare, .option-about-more, .special-order a { color: var(--accent-link); font-size: .82rem; text-decoration: underline; text-underline-offset: 3px; }
    .option-tiles {
      display: flex; gap: .4rem; overflow-x: auto; scroll-snap-type: x mandatory;
      padding: .15rem .15rem .4rem; margin: 0 -.15rem; scrollbar-width: thin;
    }
    .option-body { min-width: 0; }
    #finish-frame-step { margin-top: .2rem; }
    .option-tile {
      position: relative; flex: 1 0 4.1rem; max-width: 5.6rem; scroll-snap-align: start;
      display: flex; flex-direction: column; align-items: stretch; gap: .3rem;
      padding: .35rem .25rem .35rem; border-radius: var(--radius-sm); border: 1.5px solid var(--border);
      background: var(--bg); cursor: pointer; transition: border-color .12s, background .12s;
    }
    .option-tile:hover { border-color: var(--accent-hover-border); }
    .option-tile input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
    .option-tile:has(input:checked) { border-color: var(--accent); background: var(--accent-selected); box-shadow: 0 0 0 1px var(--accent); }
    .option-tile:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
    .option-name { flex: 1; min-height: 2.3em; display: flex; align-items: center; justify-content: center;
      font-size: .72rem; font-weight: 600; line-height: 1.15; text-align: center; color: var(--accent-link); }
    /* the picture: THIS artwork drawn as the style */
    .option-thumb { height: 3.4rem; display: grid; place-items: center; }
    /* the thumbnail shrink-wraps the picture, which fits a box by BOTH
       sides - a portrait work never pokes out of its card (owner, 2026-09-26) */
    .ot { position: relative; display: inline-block; line-height: 0; }
    .ot img { display: block; width: auto; height: auto; max-width: 56px; max-height: 46px; }
    .ot--mat img, .ot--fcanvas img { max-width: 44px; max-height: 34px; }
    .ot--print { box-shadow: 0 1px 2px rgba(0,0,0,.18), 0 3px 8px rgba(0,0,0,.16); }
    .ot--mat, .ot--fcanvas { background: var(--frame, #1d1b19); padding: 3px; box-shadow: 0 2px 5px rgba(0,0,0,.28); }
    .ot--mat .ot-in { display: block; background: #f7f5ef; padding: 5px; }
    .ot--fcanvas .ot-in { display: block; background: #0f0c0a; padding: 2px; }
    .ot--acrylic { box-shadow: 0 2px 8px rgba(0,0,0,.3); outline: 1px solid rgba(255,255,255,.8); outline-offset: -1px; }
    .ot--acrylic::after { content: ""; position: absolute; inset: 0; background: linear-gradient(118deg, rgba(255,255,255,.45), rgba(255,255,255,0) 45%); }
    .ot--stretched { box-shadow: 1px 1px 0 #8d887f, 2px 2px 0 #7d786f, 2px 4px 8px rgba(0,0,0,.22); }
    /* "About <style>": folded, just above the sizes */
    .style-about { margin: .9rem 0 0; padding: .5rem .8rem; border-radius: var(--radius-sm); background: var(--surface-dim, rgba(0,0,0,.05)); }
    .style-about > summary { cursor: pointer; list-style: none; display: flex; gap: .3rem;
      font-size: .85rem; font-weight: 600; color: var(--text-secondary); }
    .style-about > summary::-webkit-details-marker { display: none; }
    .style-about > summary::after { content: "+"; font-weight: 700; margin-left: auto; }
    .style-about[open] > summary::after { content: "\2212"; }
    /* the size's fit, a changed-size note, and the "you're buying" line */
    .size-note { margin: .45rem 0 0; padding: .35rem .6rem; border-radius: var(--radius-sm); font-size: .8rem;
      background: var(--accent-soft); color: var(--text); }
    .buy-summary { margin: .15rem 0 .5rem; font-size: .9rem; line-height: 1.45; color: var(--text); }
    .buy-summary-main { font-family: var(--font-heading); font-size: 1rem; }
    .option-about { margin: .55rem 0 0; font-size: .82rem; line-height: 1.5; color: var(--text-secondary); }
    .special-order { margin: .35rem 0 0; font-size: .8rem; color: var(--text-muted); }

    /* ── Family control - three purchase paths, one selected ── */
    .family-seg { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: .45rem; }
    /* Finish buttons: all the same width, one row (owner, 2026-09-25). */
    #finish-seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
    /* The name sits at the same height on every button (just above centre,
       whether or not a second line follows); the second line hugs it. */
    .family-opt {
      position: relative; display: flex; flex-direction: column; gap: 0;
      align-items: center; justify-content: flex-start; text-align: center;
      min-height: 58px; padding: .7rem .8rem .55rem;
      border: 1.5px solid var(--border); border-radius: var(--radius-md);
      background: var(--bg); cursor: pointer;
      transition: border-color .12s, background .12s;
    }
    .family-opt:hover { border-color: var(--accent-hover-border); }
    .family-opt input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
    .family-opt:has(input:checked) { border-color: var(--accent); background: var(--accent-selected); }
    .family-opt-name { font-family: var(--font-heading); font-size: .9rem; color: var(--text); }
    .family-opt-from { font-size: .72rem; line-height: 1.3; color: var(--text-muted); }
    /* above the full-size hidden radio, so it is its own link */
    .family-more { position: relative; z-index: 2; color: var(--accent-link); text-decoration: underline; text-underline-offset: 2px; }
    #variant-selector { display: flex; flex-direction: column; gap: 1.1rem; }

    /* ── The stage. A fixed 4:3 box (capped at 640px tall) so switching
       between portrait and landscape sizes never reflows the page; the print
       is sized inside it from container units. ── */
    .image-stage, .product-media, .buy-card { min-width: 0; }
    .main-image-frame {
      position: relative;
      width: 100%;
      aspect-ratio: 16/10;
      max-height: 640px;
      container-type: size;
      overflow: hidden;
      display: flex; align-items: center; justify-content: center;
    }
    /* The object on the wall: [frame] > [mat / paper liner] > artwork.
       On the wall it keeps ONE visual size (finish + wall color are the
       subject); the art box is sized from the artwork's own ratio. */
    .wall-object {
      position: relative; z-index: 1;
      display: block; text-decoration: none; cursor: zoom-in;
      border-radius: 2px;
      box-shadow: 0 1px 2px rgba(0,0,0,.18), 0 8px 22px rgba(0,0,0,.22);
    }
    .wall-mat { display: flex; position: relative; }
    /* the wall sample: the stage box around the artwork (the only painted area) */
    .image-stage .main-image-frame { background: var(--wall-hex); border-radius: var(--radius-lg); }
    /* --pad is the border the finish adds on each side (paper margin, frame,
       mat), so the whole object - not just the art - fits the stage with room
       left for the contact shadow. */
    .wall-stage { --pad: 0cqw; --ex: 0; --ey: 0; }
    /* --ex/--ey: borders that are a SHARE of the picture's own width (a
       frame's or a stretcher's real inches over the printed width - see
       _wall_geometry_style()), summed across the object horizontally and
       vertically. The picture's width is solved so picture + borders fit the
       same budget: w·(1+ex) ≤ 92cqw and w/ar + w·ey ≤ 74cqh. With both 0 this
       is the old sizing exactly. --art-w lives on .wall-object so every layer
       inside it (frame padding, reveal, wrap depth) can be a share of it. */
    .wall-object {
      --art-w: min(calc((92cqw - 2 * var(--pad)) / (1 + var(--ex))),
                   calc((74cqh - 2 * var(--pad)) / (var(--ar-h, 9) / var(--ar-w, 16) + var(--ey))));
    }
    .wall-art {
      display: block; position: relative;
      /* 74cqh (not 88): the cast shadow below a stretched/framed canvas
         reaches up to ~28-38px past the object's own box (see the shadow
         rules below) - on a short stage (mobile's 380px cap) 88cqh's
         ~6%-per-side margin wasn't reliably enough room and the stage's
         own overflow:hidden cut the shadow off before it faded. A first
         pass at 80cqh measured out to more margin than the shadow's own
         reach on paper, but still read as tight in practice (a Gaussian
         blur's visible tail runs past its nominal radius) - 74cqh is the
         safety margin that actually held up. */
      width: var(--art-w);
      aspect-ratio: var(--ar-w, 16) / var(--ar-h, 9);
    }
    .wall-art img { display: block; width: 100%; height: 100%; object-fit: cover; }

    /* Paper, unframed: printed edge to edge - the picture itself is the object,
       no paper margin drawn around it (that is what ships). */
    .wall-stage[data-family="print_on_paper"][data-frame="none"] { --pad: 0cqw; }
    /* Acrylic panel (2026-09-24): the print behind 10 mm of clear acrylic on a
       floating subframe - a glossy sheen across the face, a bright polished
       edge, and a deeper contact shadow (it stands ~15 mm off the wall).
       Shadow reach kept inside the stage margin (see the 74cqh note above). */
    .wall-stage[data-family="print_on_acrylic"] .wall-object {
      border-radius: 3px;
      box-shadow: 0 1px 3px rgba(0,0,0,.2), 0 10px 20px rgba(0,0,0,.26);
    }
    .wall-stage[data-family="print_on_acrylic"] .wall-art::after {
      content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: 3px;
      background: linear-gradient(128deg, rgba(255,255,255,.26) 0%, rgba(255,255,255,.08) 26%,
                  rgba(255,255,255,0) 44%, rgba(255,255,255,0) 72%, rgba(255,255,255,.10) 100%);
      box-shadow: inset 0 0 0 1px rgba(255,255,255,.55), inset 0 0 0 3px rgba(255,255,255,.10);
    }
    /* Shape not stored yet: hold the print back for the instant it takes the page
       to read the image's own shape (a <noscript> rule reveals it without JS). */
    .wall-stage[data-ar="pending"] .wall-object { visibility: hidden; }

    /* ── Frame molding: a real material, not a flat swatch - layered gradient +
       wood-grain + fractal-noise "tooth", with a raised-bevel inset shadow so
       it reads as a profile catching light, not a painted box. Six real
       vendor colours (the Frame catalog's real colour value), picked
       by data-frame-color; unknown/missing falls back to a plain dark frame.
       Width (padding) is --wall-frame-pad: the frame's TRUE size is fixed in
       inches, so it visually thickens on a small print and thins on a large
       one (see _wall_geometry_style() - never the print's own box, which
       stays one size). ── */
    .wall-stage {
      --wm-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
      --wm-grain:
        repeating-linear-gradient(91deg, rgba(55,32,10,.20) 0 1px, rgba(255,255,255,.05) 1px 3px, transparent 3px 7px),
        repeating-linear-gradient(89deg, rgba(40,22,6,.12) 0 2px, transparent 2px 9px);
      --wm-fill: linear-gradient(135deg, #2d2d2d 0%, #131313 50%, #272727 100%); /* fallback: plain black */
    }
    .wall-stage[data-frame-color="black"]   { --wm-fill: linear-gradient(135deg, #2d2d2d 0%, #131313 50%, #272727 100%); }
    .wall-stage[data-frame-color="white"]   { --wm-fill: linear-gradient(135deg, #f6f3ec 0%, #e0dbd0 50%, #efe9df 100%); }
    .wall-stage[data-frame-color="gold"]    { --wm-fill: linear-gradient(135deg, #5e4719 0%, #b9983f 20%, #f3e0a6 45%, #d4ad55 53%, #9c7a2b 72%, #574014 100%); }
    .wall-stage[data-frame-color="silver"]  { --wm-fill: linear-gradient(135deg, #5c5c61 0%, #a7a7ac 22%, #eeeef1 46%, #b6b6bb 54%, #87878c 72%, #525257 100%); }
    .wall-stage[data-frame-color="natural"] { --wm-fill: var(--wm-grain), linear-gradient(118deg, #b88a52 0%, #e3c08a 34%, #cda472 54%, #b58a52 78%, #d2ab74 100%); }
    .wall-stage[data-frame-color="brown"]   { --wm-fill: var(--wm-grain), linear-gradient(118deg, #4a3120 0%, #7a5532 35%, #5f4128 56%, #48311f 86%); }
    .wall-stage[data-frame="on"] { --pad: var(--wall-frame-pad, 1.5cqw); }
    .wall-stage[data-frame="on"] .wall-object {
      background: var(--wm-noise), var(--wm-fill);
      background-blend-mode: overlay;
      padding: var(--wall-frame-pad, 1.5cqw);
      /* raised profile: lit top-left, shadowed bottom-right, deep cast shadow */
      box-shadow:
        inset 0 2px 1px rgba(255,255,255,.32), inset 2px 0 1px rgba(255,255,255,.16),
        inset 0 -2px 2px rgba(0,0,0,.5), inset -2px 0 2px rgba(0,0,0,.4),
        inset 0 0 0 1px rgba(0,0,0,.25), 0 12px 26px rgba(0,0,0,.35);
    }
    .wall-stage[data-family="print_on_paper"][data-frame="on"][data-mat="none"] { --pad: 2.4cqw; }
    .wall-stage[data-family="print_on_paper"][data-frame="on"][data-mat="none"] .wall-mat { background: var(--paper); padding: .9cqw; }
    /* Framed with mat (paper, owner 2026-09-25): true sizes, all shares of the
       mat WINDOW's width (the picture) - the Classic frame face (--r-frame),
       the mat on each axis (--r-mat-x/-y, 2 in), and the 2.4 mm
       bevel (--r-bevel), set per size by artwork.html. Light comes from the
       top-left, like every shadow on the stage: the frame's lip shades the
       mat's top and left edge, and the bevel's top/left faces sit in shade
       while its bottom/right faces catch the light. */
    .wall-stage[data-family="print_on_paper"][data-frame="on"][data-mat="on"] {
      --pad: 0cqw;
      --ex: calc(2 * (var(--r-frame, .066) + var(--r-mat-x, .167)));
      --ey: calc(2 * (var(--r-frame, .066) + var(--r-mat-y, .167)));
    }
    .wall-stage[data-family="print_on_paper"][data-frame="on"][data-mat="on"] .wall-object {
      padding: calc(var(--art-w) * var(--r-frame, .066));
    }
    .wall-stage[data-family="print_on_paper"][data-frame="on"][data-mat="on"] .wall-mat {
      background: var(--mat-hex, #f7f5ef);
      padding: calc(var(--art-w) * var(--r-mat-y, .167)) calc(var(--art-w) * var(--r-mat-x, .167));
      box-shadow: inset calc(var(--art-w) * .006) calc(var(--art-w) * .008) calc(var(--art-w) * .02) rgba(0,0,0,.2);
    }
    .wall-stage[data-family="print_on_paper"][data-frame="on"][data-mat="on"] .wall-art::before {
      content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
      border-style: solid;
      border-width: max(2px, calc(var(--art-w) * var(--r-bevel, .008)));
      /* shaded top/left faces vs the bright lit bottom/right faces - on a
         white mat the white core only reads through this contrast */
      border-color: #bdb7aa #ffffff #ffffff #c8c2b5;  /* top, right, bottom, left */
      box-shadow:
        /* the crease where the cut meets the mat's face */
        0 0 0 .5px rgba(0,0,0,.14),
        /* the mat stands a little proud of the print: its shadow, top-left */
        inset calc(var(--art-w) * .004) calc(var(--art-w) * .005) calc(var(--art-w) * .012) rgba(0,0,0,.32);
    }

    /* ── Canvas ── */
    /* Unframed, stretched: no flat box - a shallow 3D wrap. The front face is
       the artwork; two more strips (right, bottom) show the same picture,
       mirrored, standing in for the canvas wrapping the stretcher bars
       (a mirrored wrap). Depth is --wall-depth, the stretcher's real
       width - same "true size" scaling as the frame. Never shown for a
       paper leaf, a floater-framed canvas (the frame covers the wrap), or
       the rolled/"print" finish (shipped flat, no depth to show). */
    .wall-stage[data-family="print_on_canvas"][data-frame="none"] .wall-object {
      /* filter:drop-shadow, not box-shadow - box-shadow casts from the full
         RECTANGULAR border box regardless of a descendant's clip-path, so it
         leaked straight back through .wall-mat's newly-clipped corners
         (same visible artifact as the flat-fill attempt, just softer).
         drop-shadow follows the actual rendered (clipped) alpha shape
         instead - the same technique oldpainters uses, for this exact
         reason. Two layers chained to keep the original two-shadow look. */
      box-shadow: none;
      /* A small, un-offset ambient layer first (a little visible lift on
         every side, including the top - the two directional layers below
         put almost nothing there by design), then the two directional
         layers for the real cast shadow. */
      filter:
        drop-shadow(0 0 6px rgba(0,0,0,.14))
        drop-shadow(0 2px 4px rgba(0,0,0,.2))
        drop-shadow(4px 8px 20px rgba(0,0,0,.28));
    }
    .wall-side { display: none; position: absolute; overflow: hidden; pointer-events: none; }
    /* skewY/skewX leave the strip's SHARED edge (touching the artwork) flush
       the whole way down, but its own outer top/bottom edge recedes away from
       that anchor, leaving a small triangular wedge of the strip's own
       rectangular padding-box uncovered - a dark fill there (tried first)
       read as a broken patch, not a shadow. The correct fix: clip .wall-mat
       to the true six-point silhouette (front face + the two skewed side
       faces) so there is nothing left in that wedge to paint at all - the
       wall just shows through, cleanly, exactly like the object's genuinely
       unwrapped top/left edges already do. Points, clockwise from the front
       face's own top-left: top-right, the right strip's far top corner, its
       far bottom corner, the bottom strip's far corner, the front face's
       bottom-left. */
    /* The wrap's visible depth is the 38 mm bar, seen at an angle, as a share
       of the picture's width (--r-depth): it grows on a small print and thins
       on a large one - the same law as the frame. It sticks out right and
       bottom only, so it adds one depth to each budget. */
    .wall-stage[data-family="print_on_canvas"][data-frame="none"]:not([data-finish="print"]) {
      --ex: var(--r-depth, .03); --ey: var(--r-depth, .03);
    }
    .wall-stage[data-family="print_on_canvas"][data-frame="none"]:not([data-finish="print"]) .wall-object {
      --wall-depth: calc(var(--art-w) * var(--r-depth, .03));
    }
    .wall-stage[data-family="print_on_canvas"][data-frame="none"]:not([data-finish="print"]) .wall-mat {
      padding: 0 var(--wall-depth, 0) var(--wall-depth, 0) 0;
      clip-path: polygon(
        0 0, calc(100% - var(--wall-depth, 0px)) 0,
        100% var(--wall-depth, 0px), 100% 100%,
        var(--wall-depth, 0px) 100%, 0 calc(100% - var(--wall-depth, 0px))
      );
    }
    .wall-stage[data-family="print_on_canvas"][data-frame="none"]:not([data-finish="print"]) .wall-side {
      display: block;
    }
    /* Each side is two layers, kept deliberately separate: the outer span is
       SKEWED into an oblique face (a fixed 45° - the geometry's horizontal
       and vertical reach are equal); its own ::before is MIRRORED and carries
       the image. Combining both transforms on one element would skew the
       mirror itself, distorting the reflected edge. */
    .wall-side-r {
      left: 100%; top: 0; width: var(--wall-depth, 0); height: 100%;
      transform-origin: left top; transform: skewY(45deg);
    }
    .wall-side-r::before {
      content: ""; position: absolute; inset: 0;
      background-image: var(--wall-src); background-repeat: no-repeat;
      background-size: auto 100%; background-position: right top;
      transform: scaleX(-1);
    }
    .wall-side-r::after {
      content: ""; position: absolute; inset: 0;
      background:
        linear-gradient(90deg, rgba(255,255,255,.22) 0, rgba(255,255,255,.22) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0,0,0,.16), rgba(0,0,0,.36));
    }
    /* bottom side: the artwork's bottom edge, mirrored, hanging off the bottom */
    .wall-side-b {
      top: 100%; left: 0; width: 100%; height: var(--wall-depth, 0);
      transform-origin: left top; transform: skewX(45deg);
    }
    .wall-side-b::before {
      content: ""; position: absolute; inset: 0;
      background-image: var(--wall-src); background-repeat: no-repeat;
      background-size: 100% auto; background-position: left bottom;
      transform: scaleY(-1);
    }
    .wall-side-b::after {
      content: ""; position: absolute; inset: 0;
      background:
        linear-gradient(180deg, rgba(255,255,255,.2) 0, rgba(255,255,255,.2) 1px, transparent 1px),
        linear-gradient(180deg, rgba(0,0,0,.3), rgba(0,0,0,.52));
    }
    /* Floater frame: a dark reveal gap (not the wall colour) separates the
       molding from the canvas, which sits recessed below the frame's lip - 
       the wrapped edges are hidden under the frame, so no depth strips here. */
    /* Moulding face and reveal gap are real sizes too - shares of the
       picture's width (--r-frame, --r-reveal), on both sides of each axis. */
    .wall-stage[data-family="print_on_canvas"][data-frame="on"] {
      --pad: 0cqw;
      --ex: calc(2 * (var(--r-frame, .05) + var(--r-reveal, .015)));
      --ey: calc(2 * (var(--r-frame, .05) + var(--r-reveal, .015)));
    }
    .wall-stage[data-family="print_on_canvas"][data-frame="on"] .wall-object {
      padding: calc(var(--art-w) * var(--r-frame, .05));
    }
    .wall-stage[data-family="print_on_canvas"][data-frame="on"] .wall-mat {
      /* A soft top-lit gradient (not a flat fill) plus a thin inner rim
         light, so this reads as a recessed gap catching light - the same
         visual language as the frame molding's own bevel - rather than an
         unexplained flat black stripe. */
      background: radial-gradient(120% 160% at 50% -10%, #221c16 0%, #100d0a 65%);
      padding: calc(var(--art-w) * var(--r-reveal, .015));
      box-shadow: inset 0 2px 5px rgba(0,0,0,.7), inset 0 1px 0 rgba(255,255,255,.08);
    }
    /* Matted & framed: mat and frame are always present. */
    .wall-stage[data-family="matted"] .wall-object { background: var(--frame-hex, #26221f); padding: 1.5cqw; }
    .wall-stage[data-family="matted"] { --pad: 7cqw; }
    .wall-stage[data-family="matted"] .wall-mat { background: var(--mat-hex, #f4ecd8); padding: 5.5cqw; }
    /* No picture: the notice sits on a surface chip, never bare on the paint. */
    .main-image-empty {
      color: var(--text-muted); font-size: .9rem;
      background: var(--surface); border-radius: 999px; padding: .4rem 1rem;
    }

    /* ── Wall-color island: the ten dots live on a theme surface under the
       artwork, never floating on the paint. ── */
    /* Painted air between the sheet and the controls. The stage already leaves
       ~6% of its height under the sheet; this margin tops it up to roughly the
       wall the sheet has at its side and above it (never more), so the print
       reads as hung on a wall, not parked on the controls. Phones get a
       proportionally smaller top-up (their side wall is narrower too). */
    .wall-island {
      margin-top: 2.5rem;
      background: var(--surface);
      border-radius: var(--radius-lg);
      padding: .8rem 1.1rem .85rem;
    }
    .wall-island-label { margin: 0 0 .4rem; }
    .wall-island-hint { display: none; }
    /* One line, no scrollbar: the eight paints + the picker share the row. */
    .wall-dots { display: flex; gap: .2rem; padding-bottom: .25rem; }
    .wall-dot { position: relative; flex: 1 1 0; min-width: 0; text-align: center; cursor: pointer; }
    .wall-dot input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
    .wall-dot-swatch {
      display: block; width: 26px; height: 26px; margin: 6px auto 0;
      border-radius: 50%; border: 1px solid var(--border);
    }
    /* Dark island: Charcoal and Olive would vanish into it, so every dot carries
       a light hairline (~5:1 against the surface). */
    html[data-theme="dark"] .wall-dot-swatch { border: 1.5px solid rgba(243, 234, 217, .6); }
    .wall-dot:hover .wall-dot-swatch { border-color: var(--accent-hover-border); }
    .wall-dot:has(input:checked) .wall-dot-swatch { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
    .wall-dot-label { display: block; font-size: .68rem; color: var(--text-muted); margin-top: .3rem; line-height: 1.15; }
    @media (max-width: 480px) { .wall-dot-label { display: none; } }
    /* The picker: a colour wheel until used, then the chosen colour, ringed. */
    .wall-dot--custom .wall-dot-swatch {
      background: conic-gradient(#e0453a, #e8c33a, #5fbf4a, #3ab8c9, #4a5fd6, #c94ab8, #e0453a);
    }
    .wall-dot--custom.is-active .wall-dot-swatch {
      background: var(--custom-hex);
      box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
    }

    /* ── Preview switch + "In a room" (offered only once room photos exist) ── */
    .image-stage-tabs { margin-top: .9rem; }
    /* On the theme surface like every other block (never bare on the paint). */
    .image-stage-tabs .seg-opt { background: var(--surface); }
    /* the two view buttons: equal width, bold (owner, 2026-09-26) */
    .image-stage-tabs { display: inline-grid; grid-template-columns: 1fr 1fr; }
    .image-stage-tabs .seg-opt { justify-content: center; font-weight: 700; }
    /* the picture's control row; a layout-free wrapper except on desktop */
    .stage-controls { display: contents; }
    .wm-note { display: none; }
    /* "Made to order" and "Original medium" stand out in the panel */
    .made-to-order { font-weight: 700; color: #4f5d38; }
    html[data-theme="dark"] .made-to-order { color: #b6c99a; }
    .product-original strong { font-weight: 700; color: var(--text); }
    .seg-opt input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
    .seg-opt:has(input[type="radio"]:checked) {
      border-color: var(--accent);
      background: var(--accent-selected);
      color: var(--text);
    }
    .room-plates { display: none; }
    body:has(#view-room:checked) .room-plates { display: block; }
    .room-plate-row { display: flex; gap: .6rem; justify-content: space-evenly; overflow-x: auto; padding-bottom: .25rem; }
    @media (max-width: 600px) { .room-plate-row { justify-content: flex-start; } }
    .room-plate-pick { position: relative; flex: none; width: 96px; text-align: center; cursor: pointer; font-size: .68rem; color: var(--text-muted); }
    .room-plate-pick input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
    /* The thumbnail's wall is see-through too: it shows the current paint. */
    .room-plate-pick img { display: block; width: 96px; height: 64px; object-fit: cover; border-radius: 8px; background: var(--wall-hex); box-shadow: inset 0 0 0 1.5px var(--border); }
    .room-plate-pick:has(input:checked) img { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
    .room-note { font-size: .7rem; color: var(--text-muted); margin: .45rem 0 0; }
    /* The room photo is placed by its own 3:2 shape - full stage width,
       vertically centred (the 16:10 stage trims ~3% top and bottom) - so a
       spot measured on the photo lands in the same place on any screen:
       photo x → x·100cqw, photo y → 50cqh − 33.333cqw + y·66.667cqw. */
    .room-enlarge { display: none; }
    body:has(#view-room:checked) .main-image-frame .room-enlarge {
      display: block; position: absolute; inset: 0; z-index: 0; cursor: zoom-in;
    }
    .room-plate { display: none; position: absolute; left: 0; top: 50%; width: 100%; height: auto; transform: translateY(-50%); z-index: 0; pointer-events: none; }
    /* The wall is a see-through layer in the photo: the stage's background
       is the paint picked in the wall-colour dots. Room rules are page-wide
       ("room view is on"), so the enlarged stage in the lightbox follows. */
    /* The paint sits behind the photo itself, so it stops at the photo's
       edges: in room view nothing outside the room is painted (owner,
       2026-10-05) - the wall view still paints the whole stage/column. */
    body:has(#view-room:checked) .main-image-frame { background: transparent; border-radius: var(--radius-lg); }
    .room-plate { background: var(--wall-hex); border-radius: var(--radius-md); }
    body:has(#view-room:checked) .pdp-ground { --wall-shown: var(--bg); }
    /* The print, at its true width (printed inches over the inches of wall
       the photo shows; frame and canvas sides are shares of it), is CENTRED
       at eye level (--room-center, 57in above the floor) and lifted only
       when a big one's bottom would pass the furniture line (--room-hang);
       never above the ceiling/trim line (--room-top), where the width is
       also capped. --obj-h is the whole object's height: picture + its
       vertical borders. */
    body:has(#view-room:checked) .wall-object {
      position: absolute;
      left: calc(var(--room-x, .5) * 100cqw);
      transform: translateX(-50%);
      --art-w: min(calc(var(--art-w-in, 16) / var(--room-wall-in, 160) * 100cqw),
                   calc((var(--room-hang, .5) - var(--room-top, .05)) * 66.667cqw * var(--ar-w, 4) / var(--ar-h, 5)));
      --obj-h: calc(var(--art-w) * (var(--ar-h, 5) / var(--ar-w, 4) + var(--ey)) + 2 * var(--pad));
      top: calc(50cqh - 33.333cqw + clamp(var(--room-top, .05) * 66.667cqw, var(--room-center, .35) * 66.667cqw - var(--obj-h) / 2, var(--room-hang, .5) * 66.667cqw - var(--obj-h)));
    }
    /* Shadow in a room (owner, 2026-09-23: too broad, too wide): the print is
       small there, so the wall view's fixed-pixel shadow spread far past what
       it really casts. Here it's a share of the print's own width - a print
       hung close to the wall casts a short, tight shadow - so it stays true
       at every size. The frame keeps its moulding bevel (the inset layers). */
    body:has(#view-room:checked) .wall-object {
      box-shadow: 0 calc(var(--art-w) * .004) calc(var(--art-w) * .01) rgba(0,0,0,.2),
                  0 calc(var(--art-w) * .012) calc(var(--art-w) * .03) rgba(0,0,0,.16);
    }
    body:has(#view-room:checked) .wall-stage[data-frame="on"] .wall-object {
      box-shadow:
        inset 0 2px 1px rgba(255,255,255,.32), inset 2px 0 1px rgba(255,255,255,.16),
        inset 0 -2px 2px rgba(0,0,0,.5), inset -2px 0 2px rgba(0,0,0,.4),
        inset 0 0 0 1px rgba(0,0,0,.25),
        0 calc(var(--art-w) * .006) calc(var(--art-w) * .012) rgba(0,0,0,.22),
        0 calc(var(--art-w) * .015) calc(var(--art-w) * .035) rgba(0,0,0,.18);
    }
    body:has(#view-room:checked) .wall-stage[data-family="print_on_canvas"][data-frame="none"] .wall-object {
      box-shadow: none;
      filter:
        drop-shadow(0 0 calc(var(--art-w) * .005) rgba(0,0,0,.14))
        drop-shadow(calc(var(--art-w) * .006) calc(var(--art-w) * .012) calc(var(--art-w) * .028) rgba(0,0,0,.26));
    }
    /* Enlarged in a room: the clean picture, never the watermarked zoom. */
    .wall-art .room-image { display: none; }
    body:has(#view-room:checked) .lightbox-stage .wall-art .main-image:not(.room-image) { display: none; }
    body:has(#view-room:checked) .lightbox-stage .wall-art .room-image { display: block; }
    body:has(#view-room:checked) .lightbox-stage .wall-side::before { background-image: var(--wall-src-room, var(--wall-src)); }
    /* Enlarged in room view: the whole room at its own 3:2 shape, as big as
       the viewport allows (the photo then fills the stage exactly). */
    body:has(#view-room:checked) .lightbox-content .main-image-frame {
      aspect-ratio: 3 / 2;
      width: min(100%, calc(92vh * 1.5));
      margin: 0 auto;
    }
    @media (max-width: 1023px) { .main-image-frame { max-height: 380px; flex-shrink: 0; } .wall-island { margin-top: 1.5rem; } }

    /* ── Search page ── */
    .search-heading { margin: 2rem 0 1rem; }
    .search-form { display: flex; gap: .5rem; max-width: 640px; margin-bottom: 1.5rem; }
    .search-form input[type="search"] {
      flex: 1; min-width: 0; padding: .7rem 1rem; border-radius: 999px; border: 1.5px solid var(--border);
      background: var(--surface); color: var(--text); font: 1rem var(--font-body);
    }
    .search-form input[type="search"]:focus-visible { border-color: var(--accent); }
    .search-form .search-submit { margin-top: 0; width: auto; flex: 0 0 auto; padding: .7rem 1.4rem; }
    .search-count { color: var(--text-muted); font-size: .9rem; margin: 0 0 1rem; }

    /* ── "Ask us" - the chat assistant (partials/chat_widget.html) ── */
    /* The bubble docks to the END OF THE CONTENT, not the window: it follows
       you while you scroll, then stops above the footer - never on or under
       it (owner, 2026-09-26, all layouts). A zero-height sticky dock at the
       end of <main>; the bubble hangs from it. */
    /* An invisible layer covering <main> (so it never changes the page's own
       layout); inside it a zero-height anchor sticks to the window's bottom
       edge but can't leave the layer - so the bubble follows you down the
       page and rests at the bottom of the page area, above the footer, even
       on short pages. */
    body > main { position: relative; }
    .chat-dock { position: absolute; inset: 0; z-index: 45; pointer-events: none; }
    .chat-anchor { position: sticky; top: calc(100svh - .1px); height: 0; }
    .chat-widget { position: absolute; right: 1rem; bottom: .75rem; pointer-events: auto; }
    .chat-toggle {
      list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .45rem;
      background: var(--btn-bg); color: var(--btn-fg); border-radius: 999px;
      padding: .6rem 1rem; font: 600 .88rem var(--font-body); box-shadow: var(--shadow-md);
    }
    .chat-toggle::-webkit-details-marker { display: none; }
    .chat-widget[open] .chat-toggle { position: absolute; right: 0; bottom: 0; }
    .chat-widget[open] .chat-toggle span { display: none; }
    .chat-panel {
      position: absolute; right: 0; bottom: 3.4rem; width: min(360px, calc(100vw - 2rem));
      height: min(520px, calc(100vh - 7rem)); display: flex; flex-direction: column;
      background: var(--surface); color: var(--text); border: 1px solid var(--border);
      border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;
    }
    .chat-head { padding: .75rem 1rem; border-bottom: 1px solid var(--border); display: flex; flex-direction: column; gap: .15rem; }
    .chat-head span { font-size: .72rem; color: var(--text-muted); }
    .chat-head a { color: var(--accent-link); }
    /* column-reverse keeps the newest message in view with no script */
    .chat-scroll { flex: 1; overflow-y: auto; display: flex; flex-direction: column-reverse; padding: .75rem; }
    .chat-log { display: flex; flex-direction: column; gap: .5rem; }
    .chat-msg { max-width: 85%; padding: .5rem .75rem; border-radius: 14px; font-size: .88rem; line-height: 1.45; overflow-wrap: anywhere; }
    .chat-msg--bot { align-self: flex-start; background: var(--surface-dim); border-bottom-left-radius: 4px; }
    .chat-msg--user { align-self: flex-end; background: var(--accent-selected); border-bottom-right-radius: 4px; }
    .chat-msg a { color: var(--accent-link); }
    .chat-typing { font-size: .78rem; color: var(--text-muted); padding: 0 1rem .35rem; }
    .chat-typing.htmx-indicator { display: none; }
    .chat-typing.htmx-request { display: block; }
    .chat-form { display: flex; gap: .4rem; padding: .6rem; border-top: 1px solid var(--border); }
    .chat-form input[type="text"] {
      flex: 1; min-width: 0; padding: .55rem .75rem; border-radius: 999px; border: 1px solid var(--border);
      background: var(--bg); color: var(--text); font: .88rem var(--font-body);
    }
    .chat-send { width: 2.3rem; border: 0; border-radius: 50%; background: var(--btn-bg); color: var(--btn-fg); font-size: 1rem; cursor: pointer; }
    .chat-send:disabled, .chat-form input:disabled { opacity: .6; }
    /* Phones: sit above the sticky Add-to-Cart bar on product pages. */
    @media (max-width: 1023px) {
      .chat-anchor { top: calc(100svh - 4.1rem); }
      .chat-widget { right: .75rem; }
      body:not(:has(.buy-bar)) .chat-anchor { top: calc(100svh - .1px); }
      .chat-toggle span { display: none; }
    }

    /* ── Mobile buy bar: live price + Add to Cart ── */
    .buy-bar { display: none; }
    @media (max-width: 1023px) {
      .buy-bar {
        display: flex; align-items: center; gap: .75rem;
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
        background: var(--surface); border-top: 1px solid var(--border);
        padding: .7rem 1rem calc(.7rem + env(safe-area-inset-bottom, 0px));
      }
      .buy-bar-price { font-family: var(--font-heading); font-size: 1.2rem; color: var(--text); }
      .buy-bar { flex-wrap: wrap; row-gap: .35rem; }
      .buy-bar-summary { flex: 1 0 100%; font-size: .84rem; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
      .buy-bar-summary:empty { display: none; }
      /* all five style tiles fit across a phone (sideways scroll only below ~340px) */
      .option-tiles { gap: .3rem; }
      .option-tile { flex-basis: 3.3rem; padding: .3rem .15rem; }
      .option-name { font-size: .66rem; }
      .ot img { max-width: 46px; max-height: 40px; }
      .ot--mat img, .ot--fcanvas img { max-width: 34px; max-height: 28px; }
      .buy-bar .btn-add-cart { margin-top: 0; flex: 1; }
      .buy-bar-feedback { position: absolute; left: 1rem; right: 1rem; bottom: 100%; margin-bottom: .4rem; font-size: .8rem; color: var(--text); }
      .buy-bar-feedback:not(:empty) { background: var(--surface); border: 1px solid var(--border); border-radius: 999px; padding: .35rem .9rem; text-align: center; }
    }

    /* ── Lightbox (CSS-only, opens via :target) ── */
    .lightbox {
      display: none;
      position: fixed;
      inset: 0;
      z-index: 100;
      align-items: center;
      justify-content: center;
    }
    .lightbox:target { display: flex; }
    .lightbox-backdrop {
      position: absolute;
      inset: 0;
      background: rgba(17, 24, 39, .85);
      cursor: zoom-out;
    }
    .lightbox-content {
      position: relative;
      width: min(92vw, 1300px);
      max-height: 92vh;
    }
    /* The lightbox stage: the same wall-stage markup as the small stage
       (product_lightbox.html), just bigger - form-fit to the artwork's own
       shape (not the small stage's fixed 16:10 letterbox box) since it no
       longer shares the row with the buy card. --ar-w/--ar-h are the same
       vars the small stage sets; falls back to today's 16:10 when unknown. */
    .lightbox-content .main-image-frame {
      width: 100%;
      max-height: 92vh;
      aspect-ratio: var(--ar-w, 16) / var(--ar-h, 10);
    }
    /* Close control lives on the veil (top-right of the viewport), never over the art. */
    .lightbox-close {
      position: fixed;
      top: 1rem;
      right: 1rem;
      width: 44px; height: 44px;
      display: inline-flex; align-items: center; justify-content: center;
      border-radius: 999px;
      background: rgba(255, 255, 255, .12);
      color: #fff;
      font-size: 1.8rem;
      line-height: 1;
      text-decoration: none;
      z-index: 2;
    }
    .lightbox-close:hover { background: rgba(255, 255, 255, .24); }
    /* Home filters: all visible on desktop; on phones one "Filter & sort"
       toggle opens them (2026-09-24 redesign). */
    .filter-fields { display: contents; }
    .filters-toggle, .filters-toggle-label { display: none; }
    @media (max-width: 600px) {
      .filters-toggle-label {
        display: inline-flex; align-items: center; gap: .4rem; cursor: pointer;
        padding: .5rem 1rem; border: 1px solid var(--border); border-radius: 999px;
        background: var(--surface); font-weight: 600; font-size: .9rem; color: var(--text);
      }
      .filters-toggle-label::after { content: "▾"; font-size: .8rem; }
      .filters-toggle:checked + .filters-toggle-label::after { content: "▴"; }
      .filter-fields { display: none; }
      .filters-toggle:checked ~ .filter-fields { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; width: 100%; }
      .filter-fields select { width: 100%; min-width: 0; }
      /* two artworks per row */
      .gallery-grid:not(.gallery-grid--single) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.4rem .9rem; }
      .gallery-grid:not(.gallery-grid--single) .art-card-title { font-size: .9rem; }
      .gallery-grid:not(.gallery-grid--single) .art-card-meta { font-size: .72rem; }
      .gallery-grid:not(.gallery-grid--single) .fav-btn-card { width: 30px; height: 30px; font-size: 1rem; }
    }
    /* Sits ON the painting, centred at its top edge (owner, 2026-09-24). */
    .lightbox-note {
      position: absolute; left: 50%; top: 14px; transform: translateX(-50%); z-index: 3;
      max-width: calc(100% - 24px); padding: .5rem 1rem; border-radius: 999px;
      background: #f5ead8; color: #201e1d; font-size: .8rem; font-weight: 600; line-height: 1.35;
      white-space: nowrap; text-align: center; box-shadow: 0 4px 16px rgba(0, 0, 0, .35); pointer-events: none;
    }
    /* room view enlarges the clean picture - no watermark to explain */
    body:has(#view-room:checked) .lightbox-note { display: none; }
    /* framed with mat: the note sits on the mat's top band, off the picture
       (owner, 2026-09-25) - centred in the band's height */
    .wall-stage[data-mat="on"] .lightbox-note {
      top: calc(-.5 * var(--art-w) * var(--r-mat-y, .167) - max(2px, calc(var(--art-w) * var(--r-bevel, .008))));
      transform: translate(-50%, -50%);
    }

    .product-title {
      font-size: 1.65rem;
      font-weight: 400;
      margin: 0 0 .2rem;
      line-height: 1.15;
      color: var(--text);
    }
    .product-artist { font-size: .95rem; color: var(--text-muted); margin: 0 0 1rem; }
    .product-description {
      font-size: .88rem;
      color: var(--text-secondary);
      line-height: 1.65;
      margin: .45rem 0 0;
    }
    /* "About this artwork" in the buy panel - closed by default */
    .product-about { margin-top: .55rem; }
    .product-about > summary {
      cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: .35rem;
      font-size: .82rem; font-weight: 600; color: var(--accent-link);
    }
    .product-about > summary::-webkit-details-marker { display: none; }
    .product-about > summary::after { content: "+"; font-weight: 700; }
    .product-about[open] > summary::after { content: "\2212"; }
    .product-divider {
      border: none;
      border-top: 1px solid var(--border);
      margin: 1.25rem 0;
    }
    .product-section-label {
      font-size: .75rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: .07em;
      color: var(--text-muted);
      margin: 0 0 .6rem;
    }

    /* ── Size list (row 2) - hidden radios, full-width rows so the price
       lines up flush right, matching the size step of a plain checkout list ── */
    .size-pill-row { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .1rem; }
    .size-pill {
      position: relative; display: flex; align-items: baseline; gap: .45rem;
      padding: .45rem .7rem; border: 1.5px solid var(--chip-line); border-radius: var(--radius-sm);
      background: var(--surface); cursor: pointer; user-select: none; transition: border-color .12s, background .12s;
    }
    .size-pill:hover { border-color: var(--accent-hover-border); }
    .size-pill:has(input[type="radio"]:checked) { border: 2px solid var(--accent); background: var(--accent-selected);
      padding: calc(.45rem - .5px) calc(.7rem - .5px); }
    .size-pill:has(input[type="radio"]:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
    .size-pill input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
    .size-pill-dim { font-size: .88rem; font-weight: 600; color: var(--size-ink); white-space: nowrap; }
    .size-pill-price { font-size: .9rem; font-weight: 700; color: var(--accent-link); white-space: nowrap; }
    /* the two theme accents: sage for the size, terracotta for the price */
    .size-pill-row { --size-ink: #4f5d38; --chip-line: #a8998a; }
    html[data-theme="dark"] .size-pill-row { --size-ink: #b6c99a; --chip-line: #7d7064; }
    /* inches or centimetres, from the switch above the sizes */
    .u-cm { display: none; }
    #variant-selector:has(#unit-cm:checked) .u-in { display: none; }
    #variant-selector:has(#unit-cm:checked) .u-cm { display: inline; }
    .size-head { display: flex; align-items: center; justify-content: space-between; margin: 1.1rem 0 .8rem; }
    .size-head .product-section-label { margin: 0; }
    .label-value { margin-left: .3rem; font-weight: 700; color: var(--accent-link); text-transform: none; letter-spacing: 0; }
    .unit-switch { display: inline-flex; padding: 2px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); }
    .unit-switch label { position: relative; }
    .unit-switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
    .unit-switch span { display: block; padding: .15rem .65rem; border-radius: 999px; font-size: .78rem; font-weight: 700; color: var(--text-secondary); }
    .unit-switch label:has(input:checked) span { background: var(--btn-bg); color: var(--btn-fg); }
    .unit-switch label:has(input:focus-visible) span { outline: 2px solid var(--accent); outline-offset: 2px; }
    /* display:flex above beats the browser's own [hidden] rule - a size the
       style isn't made in must really disappear */
    .size-pill[hidden] { display: none; }
    #variant-selector form .product-section-label:not(:first-child) { margin-top: 1.1rem; }
    #variant-selector form > .product-section-label:first-of-type { margin-top: .2rem; }

    /* ── Frame / Mat swatches - same hidden-radio pattern as size pills ── */
    .swatch-row { display: flex; flex-wrap: wrap; gap: .65rem; margin-bottom: 1rem; }
    .swatch-option {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: .3rem;
      cursor: pointer;
      user-select: none;
      width: 64px;
      text-align: center;
    }
    .swatch-option input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
    .swatch-circle {
      width: 34px;
      height: 34px;
      border-radius: 50%;
      border: 1.5px solid var(--border);
      transition: box-shadow .12s, border-color .12s;
    }
    .swatch-none {
      background: repeating-linear-gradient(45deg, var(--surface-dim), var(--surface-dim) 4px, var(--surface) 4px, var(--surface) 8px);
    }
    .swatch-option:hover .swatch-circle { border-color: var(--accent-hover-border); }
    html[data-theme="dark"] .swatch-circle:not(.swatch-none) { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22); }
    .swatch-option:has(input[type="radio"]:checked) .swatch-circle {
      border-color: var(--accent);
      box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
    }
    .swatch-label { font-size: .7rem; color: var(--text-muted); line-height: 1.2; }

    /* ── Price display (HTMX target) ── */
    .price-display {
      margin-top: 1.1rem;
      padding: .9rem 0 0;
      border-top: 1px solid var(--border);
      min-height: 64px;
    }
    .price-display-hint { font-size: .875rem; color: var(--text-faint); margin: 0; }
    .price-display-amount { font-family: var(--font-heading); font-size: 2rem; color: var(--text); margin: 0; }
    .product-reproduction { margin: .5rem 0 0; font-size: .8rem; line-height: 1.5; color: var(--text-muted); }
    /* "reproduction" stands out in a deeper sage that still reads (AA) on the card */
    .reproduction-word { font-weight: 600; color: #4f5d38; }
    html[data-theme="dark"] .reproduction-word { color: #b6c99a; }
    .payment-note { margin: 0 0 .6rem; font-size: .84rem; line-height: 1.5; color: var(--text-secondary); }
    .price-display-delivery { margin: .3rem 0 0; font-size: .88rem; font-weight: 600; color: var(--text); }
    .price-display-label { font-size: .86rem; color: var(--text-secondary); margin: .2rem 0 0; }

    /* ── Add to Cart ── */
    .btn-add-cart {
      margin-top: 1rem;
      width: 100%;
      padding: .75rem 1rem;
      background: var(--btn-bg);
      color: var(--btn-fg);
      font-family: var(--font-heading);
      font-size: .95rem;
      border: none;
      border-radius: 999px;
      cursor: pointer;
      transition: background .12s;
    }
    .btn-add-cart:hover { background: var(--btn-bg-hover); }
    /* One primary action (Add to Cart) plus one secondary path - never a
       second buy button. */
    .btn-ghost-block {
      display: block;
      width: 100%;
      margin: 1.25rem 0 0;  /* the same space above it as below it */
      padding: .5rem;
      text-align: center;
      font-size: .85rem;
      color: var(--accent-link);
      text-decoration: none;
    }
    .btn-ghost-block:hover { text-decoration: underline; }
    /* "Added to your cart" panel (PDP): top-right card on desktop, a sheet
       above the buy bar on phones. The in-card line stays for screen readers. */
    .cart-added { position: fixed; z-index: 60; top: 84px; right: 24px; width: min(360px, calc(100vw - 32px));
                  background: var(--surface); color: var(--text); border: 1px solid var(--border);
                  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 1rem 1.1rem 1.1rem;
                  animation: cart-added-in .18s ease-out; }
    .cart-added:empty { display: none; }
    @keyframes cart-added-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
    @media (prefers-reduced-motion: reduce) { .cart-added { animation: none; } }
    .cart-added-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .7rem; }
    .cart-added-title { margin: 0; font-weight: 700; color: var(--price-green); }
    .cart-added-close { background: none; border: 0; font-size: 1.5rem; line-height: 1; color: var(--text-muted);
                        cursor: pointer; width: 44px; height: 44px; margin: -10px -12px -10px 0; }
    .cart-added-item { display: flex; gap: .8rem; align-items: center; }
    .cart-added-text { min-width: 0; }
    .cart-added-text p { margin: 0; }
    .cart-added-name { font-weight: 700; }
    .cart-added-detail { font-size: .85rem; color: var(--text-muted); }
    .cart-added-price { font-weight: 700; margin-top: .15rem !important; }
    .cart-added-total { display: flex; justify-content: space-between; margin: .9rem 0 .8rem; padding-top: .7rem;
                        border-top: 1px solid var(--border); font-size: .92rem; }
    .cart-added-checkout { display: block; text-align: center; background: var(--btn-bg); color: var(--btn-fg);
                           border-radius: 999px; padding: .75rem 1rem; font-weight: 700; text-decoration: none; }
    .cart-added-more { display: flex; justify-content: space-between; gap: 1rem; margin-top: .6rem; }
    .cart-added-more a { color: var(--accent-link); font-weight: 600; font-size: .92rem; padding: .45rem 0; }
    .cart-added-note { margin: .4rem 0 0; font-size: .78rem; color: var(--text-muted); text-align: center; }
    .cart-feedback { margin-top: .6rem; font-size: .85rem; }
    .cart-feedback:empty { display: none; }  /* no reserved blank line under the buttons */
    .cart-feedback p { margin: 0; }
    .cart-feedback-ok { color: var(--price-green); font-weight: 600; }
    .cart-feedback-error { color: #dc2626; font-weight: 600; }
    html[data-theme="dark"] .cart-feedback-error { color: #f87171; }

    /* ── Utility ── */
    .back-link { font-size: .85rem; color: var(--accent-link); text-decoration: none; display: inline-block; padding-top: 1.5rem; }
    .back-link:hover { text-decoration: underline; }
    .htmx-indicator { opacity: 0; transition: opacity 200ms ease; }
    .htmx-request .htmx-indicator { opacity: 1; }

    /* ── Filter bar (gallery search & facets) ── */
    .filter-bar {
      display: flex;
      flex-wrap: wrap;
      gap: .6rem;
      align-items: center;
      padding: .25rem 0 .75rem;
    }
    .filter-bar select {
      padding: .45rem .8rem;
      border: 1px solid var(--border);
      border-radius: 999px;
      background: var(--surface);
      color: var(--text);
      font-family: inherit;
      font-size: .85rem;
    }
    .filter-clear { font-size: .82rem; color: var(--accent-link); text-decoration: none; }
    .filter-clear:hover { text-decoration: underline; }
    .filter-apply {
      padding: .45rem .9rem; border: 1px solid var(--border); border-radius: 999px;
      background: var(--surface); color: var(--text); font-family: inherit; font-size: .85rem;
    }
    .gallery-result-count { font-size: .82rem; color: var(--text-muted); margin: .25rem 0 0; }

    /* ── Cards now host a heart overlay; the anchor moved inside ── */
    .art-card { position: relative; }
    .art-card-link { display: block; text-decoration: none; color: inherit; }

    /* ── Favorite hearts ── */
    .fav-btn {
      border: none;
      cursor: pointer;
      font-family: inherit;
      line-height: 1;
      color: var(--text-faint);
      background: var(--surface);
      transition: color .12s, transform .12s;
    }
    .fav-btn:hover { color: #dc2626; transform: scale(1.12); }
    .fav-btn-on, .fav-btn-on:hover { color: #dc2626; }
    .fav-btn-card {
      flex: none;
      width: 34px;
      height: 34px;
      margin: -.4rem 0 0 -.45rem;
      border-radius: 50%;
      font-size: 1.15rem;
      background: transparent;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .fav-btn-detail {
      font-size: 1.5rem;
      background: transparent;
      padding: .15rem .3rem;
      flex-shrink: 0;
    }
    .product-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem; }

    /* ── Rating + reviews ── */
    .review-stars { color: #f59e0b; letter-spacing: .08em; }
    .product-rating { font-size: .88rem; color: var(--text-muted); margin: .25rem 0 1rem; }
    .reviews-section { max-width: 720px; padding: 1.5rem 0 3rem; }
    .pdp-ground .reviews-section {
      max-width: none; margin-top: 1.5rem; padding: 1.5rem 1.6rem;
      background: var(--surface); border-radius: var(--radius-lg);
    }
    .reviews-heading { font-size: 1.15rem; margin: 0 0 .5rem; }
    .reviews-summary { font-size: .92rem; color: var(--text-secondary); margin: 0 0 1rem; }
    .reviews-gate-note, .reviews-empty { font-size: .85rem; color: var(--text-faint); margin: .5rem 0 1rem; }
    .review-list { list-style: none; margin: 1rem 0 0; padding: 0; }
    .review-item { border-top: 1px solid var(--border); padding: 1rem 0; }
    .review-item-head { margin: 0 0 .15rem; font-size: .92rem; display: flex; gap: .6rem; align-items: baseline; }
    .review-item-meta { font-size: .78rem; color: var(--text-faint); margin: 0 0 .5rem; }
    .review-verified { color: var(--price-green); font-weight: 600; margin-left: .4rem; }
    .review-item-body { font-size: .9rem; color: var(--text-secondary); margin: 0; line-height: 1.65; }
    .review-form { display: flex; flex-direction: column; gap: .6rem; margin: .75rem 0 1.25rem; }
    .review-form-heading { font-size: .85rem; font-weight: 700; margin: 0; }
    .review-rating-row { display: flex; flex-direction: row-reverse; justify-content: flex-end; gap: .35rem; }
    .review-rating-pick input { position: absolute; opacity: 0; pointer-events: none; }
    .review-rating-pick span {
      display: inline-block; padding: .3rem .6rem; border: 1.5px solid var(--border);
      border-radius: 999px; font-size: .82rem; cursor: pointer; user-select: none;
    }
    .review-rating-pick input:checked + span { border-color: var(--accent); background: var(--accent-selected); }
    .review-form input[type="text"], .review-form textarea {
      padding: .5rem .8rem; border: 1px solid var(--border); border-radius: var(--radius-md);
      background: var(--surface); color: var(--text); font-family: inherit; font-size: .88rem;
    }
    .btn-review-submit {
      align-self: flex-start; padding: .5rem 1.1rem; background: var(--btn-bg); color: var(--btn-fg);
      border: none; border-radius: 999px; font-family: var(--font-heading); font-size: .86rem; cursor: pointer;
    }
    .btn-review-submit:hover { background: var(--btn-bg-hover); }

    /* ── Cart & checkout ── */
    .cart-wrap, .checkout-wrap { max-width: 880px; margin: 0 auto; }
    .cart-line {
      display: flex; gap: 1rem; align-items: flex-start;
      padding: 1rem 0; border-bottom: 1px solid var(--border);
    }
    .cart-line-unavailable { opacity: .65; }
    .cart-line-thumb {
      width: 72px; height: 90px; object-fit: cover; border-radius: var(--radius-sm);
      background: var(--placeholder); display: block;
    }
    /* The print as ordered, small (partials/print_thumb.html). The object
       fits a square box at its own shape; finishes are drawn around it. */
    .print-thumb { width: var(--thumb); height: var(--thumb); flex: none; display: flex;
                   align-items: center; justify-content: center; container-type: size; }
    .print-thumb-object { display: block; position: relative; box-shadow: 0 2px 6px rgba(0,0,0,.22);
                          background: var(--placeholder); max-width: 80cqw; max-height: 80cqh; }
    .print-thumb-object[style*="--ar"] { width: min(80cqw, 80cqh * var(--ar)); aspect-ratio: var(--ar); max-height: none; }
    .print-thumb-object img { display: block; width: 100%; height: 100%; object-fit: cover; }
    .print-thumb-object:not([style*="--ar"]) img { max-width: 80cqw; max-height: 80cqh; object-fit: contain; }
    .print-thumb-empty { width: 70%; height: 80%; border-radius: var(--radius-sm); background: var(--placeholder); }
    /* stretched canvas: the wrapped sides, seen from the lower right */
    .print-thumb[data-family="canvas"][data-finish="stretched"] .print-thumb-object {
      box-shadow: 1px 1px 0 rgba(0,0,0,.30), 2px 2px 0 rgba(0,0,0,.27), 3px 3px 0 rgba(0,0,0,.24),
                  4px 4px 0 rgba(0,0,0,.21), 5px 7px 9px rgba(0,0,0,.28); }
    /* framed: the chosen frame colour; paper adds its white mat */
    .print-thumb[data-finish="framed"] .print-thumb-object {
      border: 5px solid var(--frame, #1d1b19); padding: 1.5px; background: #1a1714;
      box-shadow: inset 0 0 0 1px rgba(255,255,255,.12), 0 3px 8px rgba(0,0,0,.3); }
    .print-thumb[data-family="paper"][data-finish="framed"] .print-thumb-object { padding: 7px; background: #f7f4ee; }
    /* acrylic: a glossy face and a bright polished edge */
    .print-thumb[data-family="acrylic"] .print-thumb-object { outline: 1.5px solid rgba(255,255,255,.7); outline-offset: -1.5px;
      box-shadow: 0 4px 10px rgba(0,0,0,.3); }
    .print-thumb[data-family="acrylic"] .print-thumb-object::after { content: ""; position: absolute; inset: 0;
      background: linear-gradient(125deg, rgba(255,255,255,.35), rgba(255,255,255,0) 40%); pointer-events: none; }
    .checkout-items .order-item-row { display: flex; align-items: center; gap: .8rem; }
    .order-item-row .order-item-text { flex: 1; min-width: 0; }
    .cart-line-thumb-empty { width: 72px; height: 90px; border-radius: var(--radius-sm); background: var(--placeholder); }
    .cart-line-info { flex: 1; min-width: 0; }
    .cart-line-title { margin: 0 0 .1rem; font-weight: 600; font-size: .95rem; }
    .cart-line-title a { color: var(--text); text-decoration: none; }
    .cart-line-artist { margin: 0 0 .25rem; font-size: .78rem; color: var(--text-faint); }
    .cart-line-variant { margin: 0; font-size: .82rem; color: var(--text-muted); }
    .cart-line-warn { margin: .35rem 0 0; font-size: .8rem; color: #dc2626; font-weight: 600; }
    html[data-theme="dark"] .cart-line-warn { color: #f87171; }
    .cart-line-qty { display: flex; align-items: center; gap: .5rem; padding-top: .25rem; }
    .qty-btn {
      width: 28px; height: 28px; border: 1px solid var(--border); border-radius: 999px;
      background: var(--surface); color: var(--text); font-size: 1rem; cursor: pointer; line-height: 1;
    }
    .qty-btn:hover { border-color: var(--accent-hover-border); }
    .qty-value { min-width: 1.4em; text-align: center; font-size: .92rem; font-weight: 600; }
    .cart-line-price { text-align: right; min-width: 90px; }
    .cart-line-total { margin: 0; font-weight: 700; font-size: .95rem; }
    .cart-line-unit { margin: .1rem 0 0; font-size: .75rem; color: var(--text-faint); }
    .cart-line-remove {
      margin-top: .4rem; background: none; border: none; color: var(--text-faint);
      font-size: .78rem; cursor: pointer; text-decoration: underline; font-family: inherit;
    }
    .cart-line-remove:hover { color: var(--accent-link); }
    .cart-summary { padding: 1.25rem 0 3rem; max-width: 380px; margin-left: auto; }
    .cart-summary-row { display: flex; justify-content: space-between; font-size: .92rem; margin: .35rem 0; }
    .cart-summary-note { font-size: .78rem; color: var(--text-faint); margin: .5rem 0 1rem; }
    .btn-checkout {
      display: block; text-align: center; padding: .75rem 1rem; background: var(--btn-bg);
      color: var(--btn-fg); border: none; border-radius: 999px; font-family: var(--font-heading); font-size: .95rem;
      cursor: pointer; text-decoration: none;
    }
    .btn-checkout:hover { background: var(--btn-bg-hover); }
    .checkout-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; padding-bottom: 4rem; }
    @media (max-width: 1023px) { .checkout-layout { grid-template-columns: 1fr; } }
    .checkout-step-title { font-size: 1rem; margin: 0 0 1rem; }
    .checkout-form { grid-template-columns: 1fr 1fr; display: grid; gap: .75rem; }
    .checkout-form label { display: flex; flex-direction: column; gap: .25rem; font-size: .75rem; font-weight: 600; color: var(--text-muted); }
    .checkout-form input { padding: .5rem .8rem; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); font-family: inherit; font-size: .88rem; }
    .checkout-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
    .checkout-form .btn-save, .checkout-email-note { grid-column: 1 / -1; }
    .checkout-form .btn-save {
      padding: .6rem 1.2rem; border: none; border-radius: 999px; background: var(--btn-bg);
      color: var(--btn-fg); font-family: var(--font-heading); font-size: .9rem; cursor: pointer; width: fit-content;
    }
    .checkout-form .btn-save:hover { background: var(--accent-link); }
    .checkout-email-note { font-size: .85rem; color: var(--text-muted); margin: 0; }
    .checkout-saved-label { display: block; font-size: .78rem; font-weight: 600; color: var(--text-muted); margin-bottom: 1rem; }
    .checkout-saved-label select { display: block; width: 100%; margin-top: .3rem; padding: .5rem .8rem; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); font-family: inherit; font-size: .85rem; }
    .checkout-summary-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1.5rem; }
    .checkout-summary-hint { font-size: .85rem; color: var(--text-faint); }
    .checkout-items { list-style: none; margin: 0 0 1rem; padding: 0 0 .75rem; border-bottom: 1px solid var(--border); }
    .checkout-items li { display: flex; justify-content: space-between; gap: 1rem; font-size: .86rem; margin: .3rem 0; }
    .checkout-total { font-weight: 700; font-size: 1rem; border-top: 1px solid var(--border); padding-top: .6rem; }
    .checkout-error { color: #dc2626; font-size: .88rem; font-weight: 600; }
    html[data-theme="dark"] .checkout-error { color: #f87171; }
    .ship-speeds { border: 0; padding: 0; margin: 1rem 0 .5rem; display: grid; gap: .5rem; }
    .ship-speeds legend { font-weight: 700; font-size: .92rem; margin-bottom: .5rem; color: var(--text); }
    .ship-speed { display: flex; align-items: flex-start; gap: .7rem; padding: .75rem .9rem; border: 1px solid var(--border);
                  border-radius: var(--radius-md); cursor: pointer; background: var(--bg); }
    .ship-speed:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
    .ship-speed input { margin-top: .2rem; accent-color: var(--accent); }
    .ship-speed-main { flex: 1; display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
    .ship-speed-name { font-weight: 700; color: var(--text); }
    .ship-speed-blurb { font-size: .82rem; color: var(--text-muted); }
    .ship-speed-date { font-size: .85rem; color: var(--text); }
    .ship-speed-price { font-weight: 700; white-space: nowrap; color: var(--text); }
    .ship-speed-note { font-size: .8rem; color: var(--text-muted); margin: .4rem 0 .9rem; line-height: 1.45; }
    .ship-speed-note strong { color: var(--text); }
    .ship-total { display: none !important; }
    form:has(#ship-Standard:checked) .ship-total[data-speed="Standard"] { display: flex !important; }
    form:has(#ship-Express:checked) .ship-total[data-speed="Express"] { display: flex !important; }
    form:has(#ship-Overnight:checked) .ship-total[data-speed="Overnight"] { display: flex !important; }
    .checkout-stripe-note { font-size: .75rem; color: var(--text-faint); text-align: center; margin: .75rem 0 0; }
    /* "Please wait" lines under the two checkout buttons: shown while the
       request runs (htmx puts .htmx-request on the form) and, for Pay, kept
       up while the browser leaves for Stripe (.is-leaving, set by a script
       in checkout.html) - no double submit, no "is it stuck?". */
    .checkout-wait { display: none; margin: .6rem 0 0; font-size: .85rem; line-height: 1.5; color: var(--text-muted); }
    form.htmx-request .checkout-wait, form.is-leaving .checkout-wait { display: block; }
    form.is-leaving button[type="submit"] { opacity: .6; pointer-events: none; }

    /* ── Share links ── */
    .share-row { display: flex; gap: .55rem; flex-wrap: wrap; align-items: center; }
    .share-label { font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--accent-2); margin-right: .2rem; }
    /* Plain links, no per-link pill - same pattern as .crumbs a: one pill
       (the shared row container), plain text inside it. A pill-per-link
       made .share-island's own content taller than .crumbs's plain text
       line, so no amount of matching outer padding could make the two
       blocks the same height; this is what actually fixes that. */
    .share-link { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%;
                  color: var(--accent-link); text-decoration: none; transition: background 120ms ease, color 120ms ease; }
    .share-link:hover { background: var(--accent-2); color: var(--bg); }
    button.share-link { border: 0; background: transparent; cursor: pointer; padding: 0; }
    button.share-link[hidden] { display: none; }
    .share-copied { font-size: .75rem; color: var(--accent-2); font-weight: 600; }
    .share-copied:empty { display: none; }
    /* ── Phone product page (2026-09-24 redesign; ≤1023px since 2026-09-26 - no in-between layout; last in the sheet so it wins) ──────────────
       Picture first and big; it stays pinned at the top while the options
       scroll underneath, so every finish/frame/wall change is seen live; the
       buy bar (price + Add to Cart) is always on screen; nothing between the
       header and the picture but one line of breadcrumb. Desktop untouched. */
    .native-share { display: none; }
    @media (max-width: 1023px) {
      .pdp-ground { padding-top: .35rem; padding-bottom: 0; }
      .crumbs-share-row { background: none; border: 0; padding: 0 .1rem; margin: 0 0 .35rem; border-radius: 0; }
      .crumbs { font-size: .78rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
      /* the share icons give way to the native share button when it exists */
      .has-webshare .share-island { display: none; }
      .share-island { overflow-x: auto; flex-wrap: nowrap; }
      .native-share:not([hidden]) {
        display: inline-flex; align-items: center; justify-content: center; flex: none;
        width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--border);
        background: transparent; color: var(--text); cursor: pointer;
      }
      .product-title-row { gap: .35rem; }
      /* the picture block pins to the top while you choose */
      .product-media {
        position: sticky; top: 0; z-index: 6;
        background: var(--wall-shown, var(--bg));
        margin-inline: -1rem; padding: .25rem 1rem .4rem;
        box-shadow: 0 10px 14px -12px rgba(0,0,0,.35);
      }
      .image-stage { position: relative; }
      .main-image-frame {
        aspect-ratio: auto; max-height: none;
        height: min(30svh, calc(100vw * var(--ar-h, 10) / var(--ar-w, 16) + 28px));
      }
      .wall-object {
        --art-w: min(calc((96cqw - 2 * var(--pad)) / (1 + var(--ex))),
                     calc((84cqh - 2 * var(--pad)) / (var(--ar-h, 9) / var(--ar-w, 16) + var(--ey))));
      }
      body:has(#view-room:checked) .main-image-frame { height: auto; aspect-ratio: 3 / 2; }
      /* "On the wall / In a room" rides on the picture */
      .image-stage-tabs { position: absolute; top: .35rem; left: .35rem; margin: 0; z-index: 4; gap: .25rem; }
      .image-stage-tabs .seg-opt { padding: .25rem .6rem; font-size: .72rem; min-height: 0; }
      /* wall colours: one slim row of dots under the picture */
      .wall-island { margin-top: .2rem; padding: .15rem .5rem; background: transparent; }
      .wall-dot-label, .room-note { display: none; }
      /* phones have no names under the dots, so the title says what they do */
      .wall-island-label { display: none; }
      .wall-island-hint { display: none; }
      .wall-dots { flex-wrap: nowrap; overflow-x: auto; justify-content: flex-start; gap: .55rem; scrollbar-width: none; }
      .wall-dot-swatch { width: 20px; height: 20px; }
      /* frame colours: one row of six, like desktop */
      .swatch-row { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .3rem; }
      .swatch-option { width: auto; min-width: 0; }
      .swatch-circle { width: 30px; height: 30px; }
      /* the card: identity, then compact choices; the bar carries price + button */
      .buy-card { padding: 1rem; gap: .8rem; }
      .product-title { font-size: 1.35rem; }
      .price-display-amount, .buy-card .btn-add-cart { display: none; }
      .price-display { padding-top: .2rem; }
      /* sizes: the same two-column grid as desktop - no sideways scrolling */
      .size-pill-cm { display: none; }
      .family-seg, #finish-seg { gap: .35rem; }
      .family-opt { min-height: 50px; padding: .55rem .5rem .4rem; }
      #variant-selector { gap: .85rem; }
      .product-layout { gap: .9rem; padding-bottom: 1.25rem; }
      body:has(#buy-bar) { padding-bottom: 6rem; }
    }

    .btn-add-cart .btn-price { display: none; }
    /* Hover to inspect: the round lens (artwork.html) - desktop mouse only. */
    .wall-loupe {
      position: fixed; z-index: 50; width: 240px; height: 240px; border-radius: 50%;
      overflow: hidden; pointer-events: none; background: var(--wall-hex, var(--bg));
      box-shadow: 0 0 0 3px var(--surface), 0 10px 30px rgba(0,0,0,.35);
    }
    /* the stage copy inside the lens: same pixel size as the real stage, then
       scaled + moved by artwork.html (transform-origin top-left) */
    .wall-loupe .wall-loupe-stage {
      position: absolute; left: 0; top: 0; margin: 0; max-height: none; aspect-ratio: auto;
      transform-origin: 0 0;
    }
    .wall-loupe[hidden] { display: none; }
    /* ── Desktop product page: the artwork first (owner, 2026-09-26, after
       comparing with Old Painters). ≥1024px only; phones keep their layout.
       The page uses the whole window width; the stage is the full visible
       height beside a narrow sticky buy panel; the view switch and the wall
       paints float on the stage (still on the theme surface, so contrast
       never depends on the paint); sizes are a chip grid, not seven rows. ── */
    @media (min-width: 1024px) {
      .pdp-ground {
        padding-top: .75rem;
        /* the stage height, and the one wall margin used on all four sides
           of the hung object (owner, 2026-09-26: equal margins) */
        --stage-h: clamp(520px, calc(100svh - 150px), 1150px);
        --stage-m: max(3.5rem, calc(var(--stage-h) * .08));
        /* the stage's width: the window less the left gutter and the panel
           (the panel runs to the window's right edge) */
        /* the buy panel: roomier on smaller desktop screens, where its cards
           and size chips were tight (owner, 2026-10-07) */
        --panel-w: clamp(440px, 32vw, 480px);
        --stage-w: calc(100vw - var(--panel-w));
      }
      /* Two columns under the header (owner, 2026-09-26, after Old Painters):
         breadcrumb, stage and reviews on the left; the buy panel is the whole
         right column, flush to the window edge, from the header down to the
         footer. The page scrolls - never the panel - and the stage stays in
         view (sticky) while the options are read. */
      .pdp-ground { padding-top: 0; }
      .pdp-ground > .container--wide {
        max-width: none; padding: 0;
        display: grid; grid-template-columns: minmax(0, 1fr) var(--panel-w);
        /* The breadcrumb + Share line runs the full width under the header,
           and the panel hangs straight off it - one piece, no gap
           (owner, 2026-09-26). */
        grid-template-areas: "crumbs crumbs" "media card" "reviews card";
        grid-template-rows: auto auto 1fr;
      }
      .pdp-ground .product-layout { display: contents; }
      /* The whole left side is the wall (owner, 2026-09-26): painted in the
         chosen wall colour from under the breadcrumb line to the footer and
         from the window edge to the panel, so the page background never
         shows around the preview - in wall view and in room view. */
      .pdp-ground > .container--wide::before {
        content: ""; grid-column: 1; grid-row: 2 / -1; background: var(--wall-hex);
      }
      /* ...but in room view only the room photo carries the paint. */
      body:has(#view-room:checked) .pdp-ground > .container--wide::before { background: var(--bg); }
      .pdp-ground .crumbs-share-row {
        grid-area: crumbs; margin: 0; align-self: stretch;
        padding: .6rem 1.5rem;
        background: var(--surface); border: 0; border-radius: 0;
      }
      .pdp-ground .product-media { grid-area: media; position: sticky; top: 0; align-self: start; padding-top: 0; }
      .pdp-ground .image-stage .main-image-frame,
      body:has(#view-room:checked) .pdp-ground .image-stage .main-image-frame { border-radius: 0; }
      .pdp-ground .reviews-section { margin: 1.5rem; }
      .pdp-ground .reviews-section { grid-area: reviews; align-self: start; }
      .crumbs-share-row { padding: .3rem .9rem; margin-bottom: .75rem; }
      /* Panel against the right edge; the stage fills the rest and the hung
         object is centred in it (owner, 2026-09-26, option B): any extra
         width becomes wall on BOTH sides of the picture, never a strip
         beyond the panel. Top/bottom keep the one wall margin; the sides
         are that margin at least. No column gap - the stage's own margin is
         the space before the panel. */
      .product-layout {
        grid-template-columns: minmax(0, 1fr) var(--panel-w);
        /* no gaps: below the picture there is just its wall margin, then the
           footer - the same space as above it (owner, 2026-09-26) */
        gap: 0;
      }
      .pdp-ground { padding-bottom: 0; }
      .image-stage { position: relative; }
      /* Only as tall as the hung object needs at this width (never a fixed
         window-tall box): a landscape work in a narrower window gets a
         shorter stage instead of big empty wall above and below it
         (owner, 2026-09-26). Capped at the window height. */
      .image-stage .main-image-frame {
        aspect-ratio: auto; max-height: none;
        height: min(var(--stage-h),
          calc((var(--stage-w) - 2 * var(--stage-m) - 2 * var(--pad)) / (1 + var(--ex))
               * (var(--ar-h, 9) / var(--ar-w, 16) + var(--ey)) + 2 * var(--pad) + 2.9rem + 2 * var(--stage-m)));
      }
      /* Room view (owner, 2026-09-26): nothing floats over the room photo -
         the view switch + wall colour are a row ABOVE it and the room
         thumbnails a row BELOW it. The photo keeps its own 3:2 (so the
         measured hanging spots stay true), sized so all three rows fit the
         stage height, and centred in the column. */
      /* Equal air all the way down (owner, 2026-09-26): breadcrumb line ->
         controls -> room photo -> thumbnails -> footer. The room view fills
         the whole left column (down to the footer) as one column, and
         space-evenly shares what's left into four equal gaps. */
      body:has(#view-room:checked) .pdp-ground .container--wide:not(:has(#reviews-section)) .product-media {
        grid-row: 2 / -1; align-self: stretch;
        /* the column's own size, for the room photo below (cqw / cqh) */
        container-type: size;
      }
      /* --room-gap is the least air in each of the four places; padding
         (top, bottom) + gap (between) guarantee it, and space-evenly adds
         any spare height to all four equally - so they stay equal and never
         reach zero, however wide or narrow the window (owner, 2026-09-26). */
      body:has(#view-room:checked) .image-stage {
        --room-gap: clamp(1.25rem, 3vh, 2.5rem);
        display: flex; flex-direction: column; flex-wrap: nowrap; align-items: center;
        justify-content: space-evenly; gap: var(--room-gap); padding: var(--room-gap) 0;
        height: 100%; box-sizing: border-box;
      }
      /* the photo leaves room for the controls, the thumbnails and the four
         gaps inside one window height */
      body:has(#view-room:checked) .image-stage .main-image-frame {
        order: 2; flex: none; margin: 0;
        width: min(100% - 3rem, calc((100svh - 95px - 3.2rem - 2.2rem - 6rem - 4 * var(--room-gap)) * 1.5));
        height: auto; aspect-ratio: 3 / 2;
      }
      /* When the room view has the whole column, the photo grows to what the
         column allows (its width, or its height less the controls, the
         thumbnails and the four least gaps) - so a tall panel beside it
         gives a bigger room, not huge gaps. */
      body:has(#view-room:checked) .pdp-ground .container--wide:not(:has(#reviews-section)) .image-stage .main-image-frame {
        width: min(100cqw - 3rem, calc((100cqh - 2.4rem - 5.8rem - 4 * var(--room-gap)) * 1.5));
      }
      body:has(#view-room:checked) .stage-controls {
        position: static; order: 1; flex: none; width: 100%; align-content: normal;
      }
      body:has(#view-room:checked) .image-stage-tabs,
      body:has(#view-room:checked) .image-stage .wall-island { box-shadow: none; }
      body:has(#view-room:checked) .image-stage .wall-island { border: 1px solid var(--border); }
      body:has(#view-room:checked) .image-stage .room-plates {
        order: 3; flex: none; width: 100%; position: static; max-width: none;
        background: transparent; box-shadow: none; padding: 0;
      }
      body:has(#view-room:checked) .image-stage .room-plate-row { justify-content: center; }
      /* the object's budget on the stage: the stage less the margin on each
         side (and the chip band on top) - so the margin is the same all round */
      .image-stage .wall-object, .wall-loupe-stage .wall-object {
        --art-w: min(calc((100cqw - 2 * var(--stage-m) - 2 * var(--pad)) / (1 + var(--ex))),
                     calc((100cqh - 2.9rem - 2 * var(--stage-m) - 2 * var(--pad)) / (var(--ar-h, 9) / var(--ar-w, 16) + var(--ey))));
      }
      /* the whole picture budget: art up to 92% wide / 76% tall of the stage
         (the band below it holds the floating wall-colour chip) */
      .wall-object {
        --art-w: min(calc((92cqw - 2 * var(--pad)) / (1 + var(--ex))),
                     calc((min(76cqh, 100cqh - 10.4rem) - 2 * var(--pad)) / (var(--ar-h, 9) / var(--ar-w, 16) + var(--ey))));
      }
      /* The chips take the stage's top band, so the picture is centred in the
         space BELOW them - equal wall above and below (owner, 2026-09-26: it
         had ~44px above vs ~90px below). A top margin the size of the band
         does it: flex centring splits it evenly. "On the wall" only - room
         view places the print by the room's own measurements. The lens's
         copy of the stage gets the same rule so it stays aligned. */
      body:not(:has(#view-room:checked)) .image-stage .main-image-frame .wall-object,
      body:not(:has(#view-room:checked)) .wall-loupe-stage .wall-object { margin-top: 2.9rem; }
      /* The picture's controls: ONE centred row across the top of the stage -
         view switch, wall colour, watermark note (owner, 2026-09-26). The
         wrapper covers the stage (clicks pass through it to the picture) and
         wraps: if the note doesn't fit beside the others on a narrower
         window, it drops to its own line, and space-between sends that line
         to the stage's bottom margin instead of over the picture. */
      .stage-controls {
        display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
        align-content: space-between; gap: .55rem .6rem;
        position: absolute; inset: .9rem; z-index: 4; pointer-events: none;
      }
      .stage-controls > * { pointer-events: auto; flex: none; }
      .image-stage-tabs { position: static; margin: 0; }
      .image-stage-tabs .seg-opt { padding: .35rem 1.15rem; font-size: .82rem; min-height: 0; min-width: 7.2rem; }
      .wm-note {
        display: inline-flex; align-items: center; gap: .4rem; margin: 0;
        padding: .4rem .85rem; border-radius: 999px;
        /* the theme green with white type - reassuring, never a warning */
        background: #4f5d38; color: #fff; border: 0;
        font-size: .78rem; font-weight: 700; box-shadow: 0 4px 14px rgba(0,0,0,.15);
      }
      .wm-note svg { color: currentColor; flex: none; }
      body:has(#view-room:checked) .wm-note { display: none; }
      /* the wall paints: one chip on the top row, right of the view switch
         (owner, 2026-09-26: above the artwork, not under it) */
      .wall-island {
        position: static; margin: 0;
        display: flex; align-items: center; gap: .55rem;
        max-width: calc(100% - 1.8rem);
        padding: .35rem .75rem .35rem 1rem; border-radius: 999px;
        box-shadow: 0 4px 14px rgba(0,0,0,.12);
      }
      .wall-island .wall-island-label { margin: 0 .4rem 0 0; font-size: .66rem; white-space: nowrap; line-height: 1; display: flex; align-items: center; }
      .wall-dots { gap: .35rem; padding-bottom: 0; }
      .wall-dot { flex: 0 0 auto; }
      .wall-dot-swatch { width: 22px; height: 22px; margin: 0; }
      .wall-dot-label { display: none; }
      @media (max-width: 1199px) { .wall-dots { gap: .25rem; } .wall-dot-swatch { width: 20px; height: 20px; } .wall-island { gap: .4rem; } }
      .room-note { display: none; }
      /* the buy panel: the full right column - no card edges, a hairline on
         its left, content at full length (no inner scroll) */
      .buy-card {
        grid-area: card; align-self: stretch; position: relative; top: auto;
        padding: 1.5rem 1.5rem 1.25rem; gap: .85rem;
        border-radius: 0; border-left: 1px solid var(--border);
      }
      /* Rounded inside corners where the panel meets the breadcrumb line
         (top) and the footer (bottom) (owner, 2026-09-26): a small square
         just left of the panel, filled with the panel colour except for a
         quarter circle - so the panel curves into the line instead of
         meeting it at a hard right angle. The hairline follows the curve. */
      .buy-card::before, .buy-card::after {
        content: ""; position: absolute; left: calc(-1 * var(--join-r)); width: var(--join-r); height: var(--join-r);
        pointer-events: none;
      }
      .buy-card { --join-r: 1.25rem; }
      .buy-card::before {
        top: 0;
        background: radial-gradient(circle at 0 100%, transparent calc(var(--join-r) - 1px),
                    var(--border) calc(var(--join-r) - 1px), var(--border) var(--join-r), var(--surface) var(--join-r));
      }
      .buy-card::after {
        bottom: 0;
        background: radial-gradient(circle at 0 0, transparent calc(var(--join-r) - 1px),
                    var(--border) calc(var(--join-r) - 1px), var(--border) var(--join-r), var(--surface) var(--join-r));
      }
      .product-title { font-size: 1.45rem; }
      .option-tiles { overflow-x: visible; flex-wrap: wrap; }
      /* one line per product-type / finish button: name, then "More info" */
      .family-opt { min-height: 0; padding: .5rem .45rem .4rem; }
      .family-opt-name { font-size: .82rem; white-space: nowrap; }
      .family-count { display: none; }
      .product-reproduction { font-size: .8rem; margin-top: .35rem; }
      .product-section-label { margin-bottom: .35rem; }
      #variant-selector { gap: .75rem; }
      .price-display-amount { font-size: 1.75rem; }
      .crumbs-share-row { margin-bottom: .6rem; }
      /* Taller than the window? The PAGE scrolls, and Add to Cart (with the
         live price) stays pinned to the window's bottom inside the panel. */
      .buy-card .btn-add-cart { position: sticky; bottom: .75rem; z-index: 3;
        /* a solid band of panel colour around it, then a short fade above,
           so text scrolling under it never shows half-cut */
        box-shadow: 0 0 0 .55rem var(--surface), 0 -1.6rem 1rem .1rem var(--surface); }
      .swatch-row { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .3rem; margin-bottom: .4rem; }
      .swatch-option { width: auto; min-width: 0; }
      .swatch-circle { width: 30px; height: 30px; }
      .btn-add-cart .btn-price { display: inline; }
      /* the chat button moves off the pinned Add to Cart, onto the stage's
         bottom-right corner, left of the buy panel */
      /* stage + panel are centred now, so the chat button takes the window's
         bottom-left corner (wall margin), well clear of the panel */
      body:has(.buy-card) .chat-widget { right: auto; left: 1rem; }
      body:has(.buy-card) .chat-widget[open] .chat-toggle { right: auto; left: 0; }
      body:has(.buy-card) .chat-panel { right: auto; left: 0; }
    }

    /* Shared primary form button (order lookup, support, checkout, account). */
    .btn-save {
      display: inline-flex; align-items: center; justify-content: center; min-height: 44px;
      padding: .6rem 1.4rem; border: 0; border-radius: 999px; cursor: pointer;
      background: var(--btn-bg); color: var(--btn-fg); font: 600 .95rem var(--font-body);
    }
    .btn-save:hover { background: var(--accent-link); }
    /* Phones: 16px form text - iPhones zoom the whole page into any field set
       smaller (2026-09-24 redesign, phase 3). */
    @media (max-width: 1023px) {
      input:not([type="checkbox"]):not([type="radio"]):not([type="color"]), select, textarea { font-size: 16px !important; }
    }
    /* Phones (owner, 2026-09-24): the enlarged view shows the clean picture
       (the same one the page shows) - no watermark, so no note either; the
       watermarked zoom stays desktop-only. The wrap sides follow suit. */
    @media (max-width: 1023px) {
      .lightbox-stage .wall-art .main-image:not(.room-image) { display: none; }
      .lightbox-stage .wall-art .room-image { display: block; }
      .lightbox-stage .wall-side::before { background-image: var(--wall-src-room, var(--wall-src)); }
      .lightbox-note { display: none; }
      .family-count { display: none; }
    }
