/* Paralllelll Universe — site layout & page styles */

body{margin:0;background-color:var(--paper);background-image:var(--canvas-texture)}
[hidden]{display:none!important}
.container{max-width:var(--container);margin:0 auto;padding-left:34px;padding-right:34px}
.eyebrow{font-family:var(--font-arcade);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2);margin:0}
.h-xl{font-family:var(--font-lettering);font-weight:800;letter-spacing:.01em;line-height:1.02;margin:0}
.body-text{font-family:var(--font-body);font-size:16px;line-height:1.55;color:var(--ink);margin:0}
.caps{font-family:var(--font-lettering);font-weight:800;text-transform:uppercase;letter-spacing:.1em}
/* Patugo Demo (the lettering font) has no digit glyphs, so any number
   mixed into lettering-styled text (a product title with a year, say)
   would otherwise fall back to the body font one character at a time —
   this makes that an intentional, whole-number style switch instead
   (see numSpan() in app.js). */
.num{font-family:var(--font-body)}
img.ph-fallback{object-fit:cover}
img.ph-fallback[data-broken]{display:flex;align-items:center;justify-content:center;background:var(--wash-lilac);color:var(--ink-2);font-family:var(--font-body);font-size:13px;text-align:center;padding:8px}

/* ---------- Header ---------- */
.site-header{position:sticky;top:0;z-index:80;background:var(--paper)}
.site-header .bar{display:flex;align-items:center;justify-content:space-between;gap:32px;padding:16px 34px;border-bottom:var(--stroke-bold) solid var(--ink);background:var(--paper)}
.brand{display:flex;align-items:center;gap:14px;text-decoration:none;color:var(--ink)}
/* Sized to the brand-name text block's own height (2 lines * font-size *
   line-height) rather than a fixed square — the logo asset's true shape is
   a wide mark (~1.56:1), so pinning both width and height to the same value
   let its default "meet" scaling letterbox it inside a taller-than-needed
   box, leaving visible padding above/below and making it read shorter than
   the two-line text beside it. width:auto lets the (now tightly-cropped)
   SVG's real aspect ratio drive the width instead. */
.brand img{width:auto;height:39px}
.brand .brand-name{font-family:var(--font-lettering);font-weight:800;font-size:19px;line-height:1.02;letter-spacing:.055em;text-transform:uppercase;color:var(--ink)}
.nav-links{display:flex;align-items:center;gap:26px;font-family:var(--font-lettering);font-weight:800;font-size:15px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink)}
/* margin-top nudges the text+underline block down so its centerline
   matches the header's icon buttons — .nav-links centers each <a> as a
   whole (text + gap + underline bar), which sits the text itself above
   true-center since there's nothing below the icon buttons to match that
   trailing underline space. Measured empirically against the actual
   rendered icon-button center, not guessed. */
.nav-links a{display:flex;flex-direction:column;align-items:center;gap:4px;margin-top:7px;color:var(--ink);text-decoration:none}
.nav-links a .underline{display:block;width:100%;height:3px;background:transparent}
.nav-links a.is-active .underline{background:var(--red)}
.header-actions{display:flex;align-items:center;gap:12px}
.header-actions .basket-wrap{position:relative}
.basket-count{position:absolute;top:-7px;right:-7px;min-width:21px;height:21px;padding:0 4px;display:grid;place-items:center;background:var(--red);color:var(--paper-2);border:2px solid var(--ink);border-radius:999px;font-family:var(--font-body);font-weight:800;font-size:11px}

/* ---------- Landing door ---------- */
/* Full styling lives in landing.css (the interactive wall + scroll-driven
   ANN logo morph) — this file just establishes the containing block the
   fixed-position wall measures itself against. */
#view-landing{position:relative}

/* ---------- Hero ---------- */
/* Three stacked layers under the copy, back to front: a photo (texture/
   interest), the wall's own plaid pattern (fine graphic texture, multiplied
   so it reads as grain rather than a competing pattern), then the red
   itself — still the dominant color, just translucent enough to let both
   show through faintly rather than a flat fill. */
/* Tall enough that the "About us" band only just peeks in at the very
   bottom of the first viewport — a deliberate nudge to scroll, not the
   whole next section on offer for free. 190px is the sticky header's own
   height (~120px) plus a ~70px sliver of the band below. */
.hero{position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center;min-height:calc(100svh - 190px);border-bottom:var(--stroke-bold) solid var(--ink)}
.hero-bg-photo{position:absolute;inset:0;background-image:url(../assets/dino-wedding-hero-bg.jpg);background-size:cover;background-position:50% 6%;opacity:.6}
.hero-bg-texture{position:absolute;inset:0;background-image:url(../assets/background.png);background-size:340px auto;background-repeat:repeat;mix-blend-mode:multiply;opacity:.5}
.hero-bg-tint{position:absolute;inset:0;background:var(--red);opacity:.76}
.hero-copy{position:relative;padding:56px 24px;display:flex;flex-direction:column;align-items:center;gap:34px;text-align:center;max-width:720px}
.hero-copy h1{font-size:clamp(34px,4.4vw,66px);color:var(--paper-2)}
/* Each line is its own block so it can be pushed independently — driven by
   scrollHeroFly() in app.js, which sets --fly on this element as the hero
   scrolls past. Left/right alternate per data-dir so the three lines peel
   apart in opposite directions instead of leaving together. .hero's own
   overflow:hidden (above) clips them once they've flown past its edge. */
.hero-fly-line{display:block;transform:translateX(calc(var(--fly, 0) * var(--fly-dist)));opacity:calc(1 - var(--fly, 0) * 1.15)}
.hero-fly-line[data-dir="left"]{--fly-dist:-70vw}
.hero-fly-line[data-dir="right"]{--fly-dist:70vw}

/* Shared look for any loose sticker (not the sticker-label text pill —
   these are the illustrated stickers dropped in from /stickers). A plain
   border/box-shadow only ever draws a rectangle, but these are irregular,
   transparent-background artwork, so the "white outline + black drop
   shadow" from the button/sticker-label look is faked with a ring of
   white drop-shadows tracing the artwork's own silhouette, plus one more
   offset in var(--ink) matching --shadow-sticker exactly. */
.sticker-outlined{
  filter:
    drop-shadow(2px 0 0 #fff) drop-shadow(-2px 0 0 #fff)
    drop-shadow(0 2px 0 #fff) drop-shadow(0 -2px 0 #fff)
    drop-shadow(1.5px 1.5px 0 #fff) drop-shadow(-1.5px 1.5px 0 #fff)
    drop-shadow(1.5px -1.5px 0 #fff) drop-shadow(-1.5px -1.5px 0 #fff)
    drop-shadow(3px 3px 0 var(--ink));
}

/* Sits just above the art-frame's own top-right corner, like a sticker
   slapped onto the frame — .art-frame is already position:relative (see
   "Generic section band" below), so this just needs a plain absolute
   position, no JS-measured anchor. Selector has to out-specificity ".band
   .art-frame img" below (which matches ANY <img> in the frame, sticker
   included) and explicitly reset everything it sets — without this the
   sticker inherited the photo's full-width sizing, forced aspect-ratio and
   border/shadow instead of its own outline treatment. Reveal (scale +
   opacity) is scroll-driven, the same --reveal-custom-property pattern as
   .hero-seam-sticker above — see initAboutArtStickerReveal() in app.js.
   The rotate() is a fixed resting tilt, not part of that reveal — only the
   scale half of the transform reads --reveal. */
.band .art-frame .about-art-sticker{position:absolute;top:-26px;right:-14px;width:110px;aspect-ratio:auto;object-fit:unset;border:none;border-radius:0;box-shadow:none;transform:rotate(-8deg) scale(calc(.7 + var(--reveal, 0) * .3));opacity:var(--reveal, 0);pointer-events:none;z-index:1}
@media (max-width:720px){
  .band .art-frame .about-art-sticker{width:80px;top:-18px;right:-10px}
}

/* Straddles the hero/band seam: a zero-height anchor sitting exactly on
   the boundary in normal flow (so no hero height needs measuring in JS),
   with the actual image absolutely centered on that point via
   translateY(-50%). Being position:relative itself, per CSS stacking
   rules it paints above both the hero and the (non-positioned) band
   backgrounds regardless of DOM order, so no z-index tug-of-war either
   side of the seam. Reveal (opacity/scale) is scroll-driven — see
   initSeamStickerReveal() in app.js — via the same --reveal-custom-
   property pattern the hero fly-text uses. */
.hero-seam-sticker{position:relative;height:0}
.hero-seam-sticker img{position:absolute;top:0;left:10%;width:170px;transform:translateY(-50%) scale(calc(.7 + var(--reveal, 0) * .3));opacity:var(--reveal, 0)}

/* True parallax, not a one-time reveal: --about-parallax is a live value
   (0-1) tracking scroll progress through this band specifically — see
   initAboutParallax() in app.js, which sets it on #about-parallax (the
   shared column wrapper) so both groups below read the same progress via
   inheritance — same split-the-concept-across-elements pattern as .hero's
   --fly. Header/subheader fly in from the left, the body copy from the
   right: opposite signs on the same custom property via a data-dir switch,
   just like the hero fly-lines (only entering rather than exiting). */
.about-fly{transform:translateX(calc((1 - var(--about-parallax, 0)) * var(--about-fly-dist)));opacity:calc(.4 + var(--about-parallax, 0) * .6)}
.about-fly[data-dir="left"]{--about-fly-dist:-120px}
.about-fly[data-dir="right"]{--about-fly-dist:120px}

/* Reusable reveal for any image that should wipe in AND back out as it
   passes through view: a hard-edged mask sweeping across in visible
   steps() jumps instead of a smooth gradient, closer to an old console's
   blocky loading-in than a dissolve — fits this brand's arcade/pixel-grid
   motifs (the plaid wall, the arcade cabinet art) better than a plain fade
   would. A transition (not a keyframe animation) on mask-size specifically
   so leaving view reverses the same steps() wipe for free, rather than
   needing a separate "un-reveal" keyframe — see initPixelWipeReveal() in
   app.js, which toggles .is-revealed continuously off live intersection
   rather than firing once and disconnecting. */
.pixel-wipe{-webkit-mask-image:linear-gradient(to right, #000 100%, transparent 0);mask-image:linear-gradient(to right, #000 100%, transparent 0);-webkit-mask-size:0% 100%;mask-size:0% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;transition:-webkit-mask-size 900ms steps(8, end), mask-size 900ms steps(8, end)}
.pixel-wipe.is-revealed{-webkit-mask-size:100% 100%;mask-size:100% 100%}

/* ---------- Generic section band ---------- */
.band{border-bottom:var(--stroke-bold) solid var(--ink);padding:60px 52px}
.band-2col{display:grid;grid-template-columns:1fr 1fr;gap:52px;align-items:center}
.band.wash-mint{background-color:var(--wash-mint);background-image:var(--canvas-texture)}
.band.wash-sky{background-color:var(--wash-sky);background-image:var(--canvas-texture)}
.band.paper{background-color:var(--paper);background-image:var(--canvas-texture)}
.art-frame{position:relative}
.band .art-frame img{width:100%;aspect-ratio:4/3;object-fit:cover;border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-lg);box-shadow:var(--shadow-panel-lg)}

/* New & Now band — tiles reuse .artwork-tile (components.css) as <a> links */
.drops-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}

/* ---------- Footer ---------- */
.site-footer{background:var(--ink);padding:38px 52px;display:flex;align-items:flex-start;justify-content:space-between;gap:40px;flex-wrap:wrap}
.site-footer .foot-brand{display:flex;flex-direction:column;gap:8px}
.site-footer .foot-brand .caps{color:var(--paper-2);font-size:16px}
.site-footer .foot-brand span.small{color:var(--paper-3);font-size:13px;font-family:var(--font-body)}
.site-footer .foot-cols{display:flex;gap:44px;flex-wrap:wrap}
.foot-col{display:flex;flex-direction:column;gap:7px}
.foot-col .eyebrow{color:var(--ink-3)}
.foot-col span{color:var(--paper-3);font-size:13px;font-family:var(--font-body)}
.foot-col a{color:var(--paper-3);font-size:13px;font-family:var(--font-body);text-decoration:none}
.foot-col a:hover{color:var(--pink-light)}
.foot-door{display:grid;place-items:center;width:46px;height:46px;border-radius:999px;background:var(--mint);border:3px solid var(--paper-3)}

/* ---------- Policy pages ---------- */
.policy-page{max-width:var(--container-narrow);margin:0 auto;padding:56px 32px 80px;display:flex;flex-direction:column;align-items:flex-start;gap:18px}
.policy-page h1{margin:0}
.policy-body{display:flex;flex-direction:column;gap:14px}
.policy-body ul{margin:0;padding-left:22px;display:flex;flex-direction:column;gap:8px}

/* ---------- Shop ---------- */
.shop-head{padding:42px 52px 30px;display:flex;align-items:flex-end;justify-content:space-between;gap:30px;flex-wrap:wrap;border-bottom:var(--stroke-bold) solid var(--ink);background-color:var(--paper);background-image:var(--canvas-texture)}
.shop-filters{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.shop-grid-wrap{background-color:var(--paper);background-image:var(--canvas-texture);padding:36px 52px 56px}
.shop-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:26px}
/* The "hero item" spotlight — same .product-card markup as every other
   card (so it's clickable through to the same product page, no
   special-cased rendering path), just given the bigger 2x2 grid slot and a
   squarer image/larger title to fill it. Everything else (background,
   border, shadow, overflow) already comes from .product-card itself. */
/* Spans both columns but stays a single row tall — a wide banner, not a
   2x2 square — so it can't tower over the regular cards beside it. The
   image's own aspect-ratio below is what actually controls its height. */
.shop-feature{grid-column:span 2}
.shop-feature .pc-media img{aspect-ratio:2/1}
.shop-feature .pc-title{font-size:28px}
@media (max-width:900px){
  .shop-grid{grid-template-columns:repeat(2,1fr)}
}
/* Below 720px every card (feature included) collapses to a single
   horizontal row — image on the left, name/price on the right — instead
   of a cramped 2-up grid of vertical cards, which was fiddly to tap
   through to a product on a phone. */
@media (max-width:720px){
  .shop-grid{grid-template-columns:1fr}
  .shop-feature{grid-column:auto}
  .product-card{flex-direction:row}
  .product-card .pc-media{flex:0 0 120px;border-bottom:none;border-right:var(--stroke-bold) solid var(--ink)}
  .product-card .pc-media img{width:120px;height:100%;aspect-ratio:1/1}
  .product-card .pc-body{flex:1;justify-content:center;padding:14px 16px}
  .product-card .pc-price-row{margin-top:6px;padding-top:0}
  .product-card .pc-soldout{top:8px;right:-34px;width:130px;font-size:10px;padding:5px 0}
}

/* ---------- Basket ---------- */
.basket-layout{padding:44px 52px 56px;display:grid;grid-template-columns:1.55fr .95fr;gap:38px;align-items:start}
.basket-title-row{display:flex;align-items:baseline;gap:14px;margin-bottom:22px}
.basket-title-row h1{font-size:40px}
.basket-list{background:var(--paper-2);border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-lg);box-shadow:var(--shadow-panel);overflow:hidden}
.basket-row{display:grid;grid-template-columns:132px 1fr auto auto;gap:22px;align-items:center;padding:20px;border-bottom:var(--stroke-bold) solid var(--ink)}
.basket-row:last-child{border-bottom:none}
.basket-row img{width:132px;aspect-ratio:4/5;object-fit:cover;border:var(--stroke-bold) solid var(--ink);border-radius:12px}
.basket-row .br-info{display:flex;flex-direction:column;gap:6px}
.basket-row .br-info h3{font-size:21px}
.basket-row .br-info .meta{font-size:14px;color:var(--ink-2)}
.basket-row .br-info .edition{font-size:13px;color:var(--ink-3)}
.basket-row .br-side{display:flex;flex-direction:column;align-items:flex-end;gap:12px}
.basket-empty{display:flex;flex-direction:column;align-items:flex-start;gap:14px}
/* Explicit grid-column so the empty-state art always lands in the same cell
   .basket-summary would occupy — only one of the two is ever unhidden at a
   time, so this just swaps what fills that slot rather than adding a 3rd
   grid item that would wrap to its own row. */
.basket-summary,.basket-empty-art{grid-column:2}
.basket-summary{background:var(--wash-pink);border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-lg);box-shadow:var(--shadow-panel);padding:28px;display:flex;flex-direction:column;gap:18px}
.summary-line{display:flex;justify-content:space-between}
.summary-line .val{font-family:var(--font-lettering);font-weight:800}
.summary-rule{height:3px;background:var(--ink)}
.summary-total{display:flex;justify-content:space-between;align-items:center}
.basket-empty-art img{width:100%;aspect-ratio:1/1;object-fit:cover;border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-xl);box-shadow:var(--shadow-panel-lg)}
@media (max-width:900px){
  .basket-layout{grid-template-columns:1fr}
  .basket-summary,.basket-empty-art{grid-column:auto}
  /* The stepper (a 3rd item alongside img/info/side) had no named area here,
     so grid auto-placement dropped it into its own extra row, flush-left
     under the image with nothing beside it — the actual cause of the
     lopsided mobile spacing. Giving it a "qty" cell next to "side" puts it
     on the same row as the price + remove bubble, so all three line up. */
  /* First column is pinned to the image's own 84px everywhere else, but the
     qty-stepper (padding + two buttons + the count) genuinely needs more
     than that — at 84px it overflowed into the price/remove column and the
     two ended up overlapping. minmax(84px, max-content) keeps the image
     column's normal width but lets it grow to whatever the stepper actually
     needs instead of clipping/overlapping. */
  .basket-row{grid-template-columns:minmax(84px, max-content) 1fr;grid-template-areas:"img info" "qty side";row-gap:14px;column-gap:20px;padding:16px}
  .basket-row img{grid-area:img;width:84px}
  .basket-row .br-info{grid-area:info}
  .basket-row .qty-stepper{grid-area:qty;justify-self:start;padding:3px;gap:6px}
  .basket-row .qty-stepper button{width:28px;height:28px}
  .basket-row .br-side{grid-area:side;align-items:center;flex-direction:row;justify-content:space-between}
}

/* ---------- Checkout ---------- */
.checkout-form{display:flex;flex-direction:column;gap:16px}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.field-row.field-row-3{grid-template-columns:1fr 1fr 1fr}
#checkout-empty{padding:44px 52px 56px}
.checkout-summary-list{display:flex;flex-direction:column;gap:10px}
.cs-row{display:flex;justify-content:space-between;gap:12px;font-size:14px}
.cs-row .cs-name{color:var(--ink)}
.cs-row .cs-qty{color:var(--ink-3)}
@media (max-width:900px){
  #checkout-empty{padding:44px 20px 56px}
  .field-row,.field-row.field-row-3{grid-template-columns:1fr}
}

/* ---------- Login drawer ---------- */
/* This and .mobile-menu below hide off-screen via a fixed pixel `right`
   value, not a percentage or transform — both of those were tried and both
   caused phantom horizontal scroll on the whole page. transform:translateX
   on a position:fixed element makes some browsers count its pre-transform
   box toward the document's scrollable width even though nothing is
   visibly out of place. right:-100% is just as bad in a different way: the
   percentage resolves against a containing-block width that itself grows
   with the resulting overflow, so it settles into a stable but still very
   overflowed state instead of correcting itself. A plain large pixel
   offset has no such feedback loop and was confirmed clean. */
.login-scrim{position:fixed;inset:0;background:rgba(32,30,26,.5);backdrop-filter:blur(3px);z-index:100;opacity:0;pointer-events:none;transition:opacity var(--dur) var(--ease-out)}
.login-scrim.is-open{opacity:1;pointer-events:auto}
.login-drawer{position:fixed;top:0;right:-2000px;bottom:0;width:430px;max-width:92vw;background:var(--paper-2);border-left:4px solid var(--ink);padding:30px;display:flex;flex-direction:column;gap:20px;overflow-y:auto;z-index:101;transition:right var(--dur-slow) var(--ease-bounce)}
.login-drawer.is-open{right:0}
/* .ld-head/.ld-perks/.perk/.ld-foot are standalone (not scoped to
   .login-drawer) since the login page's centered lightbox reuses them too,
   without being a slide-in drawer itself — only the search drawer still
   uses the fixed off-canvas .login-drawer shell above. */
.ld-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
.ld-head h2{font-size:30px}
.ld-perks{background:var(--wash-mint);border:var(--stroke-bold) solid var(--ink);border-radius:12px;padding:18px;display:flex;flex-direction:column;gap:10px}
.perk{display:flex;gap:10px;align-items:flex-start;font-size:14px}
.ld-foot{font-size:13px;color:var(--ink-3);margin-top:auto}
.login-row{display:flex;align-items:center;justify-content:space-between;gap:12px}
/* .tabs's own flex-wrap is what a wider tab group relies on, but "Sign in"/
   "Create account" don't fit on one line inside the drawer at mobile
   widths — wrapping left the second tab poking out past the pill's
   border-radius (sized for the original one-line height). Forcing both
   buttons to share the row instead of wrapping fixes it. */
.login-tabs{flex-wrap:nowrap}
.login-tabs button{flex:1;text-align:center}

/* ---------- Login page ---------- */
/* Same three-layer technique as .hero (photo, texture, flat tint, stacked
   by DOM order alone) — just a lighter mint tint instead of the hero's
   dominant red, since here the photo should still read clearly through it. */
.login-page{position:relative;overflow:hidden;min-height:calc(100svh - 190px);display:flex;align-items:center;justify-content:center;padding:56px 24px;border-bottom:var(--stroke-bold) solid var(--ink)}
.login-bg-photo{position:absolute;inset:0;background-image:url(../assets/onam-pirates.jpg);background-size:cover;background-position:50% 30%}
.login-bg-texture{position:absolute;inset:0;background-image:var(--canvas-texture)}
.login-bg-tint{position:absolute;inset:0;background:var(--mint);opacity:.3}
.login-lightbox{position:relative;width:100%;max-width:460px;background:var(--paper-2);border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-xl);box-shadow:var(--shadow-panel-lg);padding:30px;display:flex;flex-direction:column;gap:20px}
@media (max-width:900px){
  .login-page{padding:32px 16px;min-height:auto}
  .login-lightbox{padding:24px}
}

/* ---------- Account page ---------- */
#account-orders{display:flex;flex-direction:column;gap:14px}
.order-card{border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-lg);padding:18px;display:flex;flex-direction:column;gap:10px}
.order-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
.order-status{font-family:var(--font-lettering);font-weight:var(--weight-lettering-heavy);font-size:var(--text-sm);text-transform:uppercase;letter-spacing:var(--tracking-caps);background:var(--wash-mint);border:var(--stroke) solid var(--ink);border-radius:var(--radius-pill);padding:4px 12px}

/* ---------- Search ---------- */
.search-results{display:flex;flex-direction:column;gap:10px;overflow-y:auto}
.search-result{display:flex;align-items:center;gap:14px;padding:8px;border:var(--stroke) solid var(--ink);border-radius:12px;color:inherit;text-decoration:none;transition:transform var(--dur-fast) var(--ease-bounce),box-shadow var(--dur-fast) var(--ease-out)}
.search-result:hover{transform:var(--hover-lift);box-shadow:var(--shadow-sticker)}
.search-result img{width:56px;height:56px;aspect-ratio:1/1;object-fit:cover;border:var(--stroke) solid var(--ink);border-radius:8px;flex:none}
.search-result .sr-info{display:flex;flex-direction:column;gap:2px;min-width:0}
.search-result .sr-title{font-family:var(--font-lettering);font-weight:800;font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.search-result .sr-price{font-size:13px;color:var(--ink-2)}
.search-result .sr-price s{opacity:.7;text-decoration-thickness:1.5px;margin-right:2px}
.search-empty{font-size:14px;color:var(--ink-3)}

/* ---------- Work / lightbox ---------- */
.work-head{padding:40px 52px 0;display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:26px;flex-wrap:wrap}
.work-head h1{font-size:42px}
.work-grid-wrap{background-color:var(--paper);background-image:var(--canvas-texture);padding:0 52px 52px}
.work-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
@media (max-width:900px){.work-grid{grid-template-columns:repeat(2,1fr)}}
/* grid-template-columns is explicit (not the implicit auto-track default)
   so the track sizes to the scrim's own content-box width. Left implicit,
   an auto track sizes to the item's max-content contribution instead —
   which for .lightbox-card means its raw width:800px, since a percentage
   max-width can't resolve during that same intrinsic-sizing pass. That
   blows the track out to 800px on any viewport narrower than that, and
   .lightbox-card's max-width:100% ends up 100% of the blown-out track,
   not the viewport — so it never actually constrains anything on mobile. */
.lightbox-scrim{position:fixed;inset:0;background:rgba(32,30,26,.5);backdrop-filter:blur(3px);display:grid;grid-template-columns:minmax(0,1fr);place-items:center;padding:44px;z-index:110;opacity:0;pointer-events:none;transition:opacity var(--dur) var(--ease-out)}
.lightbox-scrim.is-open{opacity:1;pointer-events:auto}
.lightbox-card{width:800px;max-width:100%;background:var(--paper-2);border:4px solid var(--ink);border-radius:var(--radius-lg);box-shadow:var(--shadow-panel-lg);overflow:hidden;position:relative}
.lightbox-card img{width:100%;aspect-ratio:16/10;object-fit:cover;border-bottom:var(--stroke-bold) solid var(--ink)}
.lightbox-info{padding:22px 26px;display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}
.lightbox-info h3{font-size:26px}
.lightbox-info .meta{font-size:14px;color:var(--ink-2)}
.lightbox-actions{display:flex;align-items:center;gap:12px}
.lightbox-close{position:absolute;top:14px;right:14px}

/* ---------- Info popup (Contact Us / Shipping) ---------- */
/* Same centered-scrim mechanics as .lightbox-scrim above, just a smaller
   card sized for a couple sentences of text instead of an image. Needs the
   explicit grid-template-columns for the same reason .lightbox-scrim does
   (an implicit auto-track blows out to the item's own sizing), but unlike
   .lightbox-card, a plain width:480px;max-width:100% wasn't enough here —
   confirmed live that a *fixed* width (not just max-width) on the grid
   item still contributes its full 480px as an automatic minimum, growing
   the 1fr track past the viewport on mobile regardless of max-width. Width
   as a percentage instead of a fixed length (matching .login-lightbox's
   own width:100%;max-width:Npx pattern) doesn't have that minimum. */
.info-scrim{position:fixed;inset:0;background:rgba(32,30,26,.5);backdrop-filter:blur(3px);display:grid;grid-template-columns:minmax(0,1fr);place-items:center;padding:24px;z-index:110;opacity:0;pointer-events:none;transition:opacity var(--dur) var(--ease-out)}
.info-scrim.is-open{opacity:1;pointer-events:auto}
.info-popup{width:100%;max-width:480px;background:var(--paper-2);border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-xl);box-shadow:var(--shadow-panel-lg);padding:30px;position:relative;display:flex;flex-direction:column;gap:14px}
.info-popup.tone-mint{background-color:var(--wash-mint);background-image:var(--canvas-texture)}
.info-popup-close{position:absolute;top:14px;right:14px}

/* ---------- Product detail page ---------- */
/* A dedicated page (#/product/<id>) rather than a popup — a modal
   gallery+form was cramped and fiddly to scroll/tap on a phone; a full
   page gives the same gallery/options/qty/add-to-basket content room to
   breathe, and is just a normal in-flow page like every other view. */
.product-page{padding:32px 52px 56px}
.product-page-back{margin-bottom:22px}
.product-page-layout{display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:start}
.pp-gallery{position:relative;background:var(--paper-2);border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-lg);box-shadow:var(--shadow-panel);overflow:hidden}
.pp-gallery-main{position:relative}
.pp-gallery-main img{width:100%;aspect-ratio:4/5;object-fit:cover;display:block}
/* .icon-btn:hover/:active set their own `transform`, which replaces
   (doesn't add to) this translateY(-50%) vertical-centering instead of
   combining with it — without restating it here too, the button lost its
   centering offset entirely on hover/press and jumped down by roughly
   half its own height. */
.pp-arrow{position:absolute;top:50%;transform:translateY(-50%)}
.pp-arrow-prev{left:12px}
.pp-arrow-next{right:12px}
.pp-arrow:hover{transform:translateY(-50%) var(--hover-lift);color:var(--pink)}
.pp-arrow:active{transform:translateY(-50%) var(--press-sink);box-shadow:var(--shadow-pressed)}
.pp-dots{display:flex;justify-content:center;gap:8px;padding:14px;border-top:var(--stroke-bold) solid var(--ink)}
.pp-dot{width:9px;height:9px;padding:0;cursor:pointer;background:var(--paper-2);border:var(--stroke) solid var(--ink);border-radius:50%}
.pp-dot.is-active{background:var(--ink)}
.pp-body{display:flex;flex-direction:column;gap:14px;align-items:flex-start;padding-top:6px}
/* #pp-description now renders the real WooCommerce description HTML
   (headings, lists) rather than flattened plain text, so a wp-admin
   heading like "Care Instructions" reads as an actual heading here instead
   of running straight into the paragraph before it. */
#pp-description p{margin:0 0 12px}
#pp-description p:last-child{margin-bottom:0}
#pp-description h3,#pp-description h4{font-family:var(--font-lettering);font-weight:var(--weight-lettering-heavy);font-size:13px;text-transform:uppercase;letter-spacing:var(--tracking-caps);color:var(--ink-2);margin:4px 0 8px}
#pp-description ul,#pp-description ol{margin:0 0 12px;padding-left:20px}
#pp-description ul:last-child,#pp-description ol:last-child{margin-bottom:0}
#pp-description li{margin-bottom:4px}
.pp-options-list{display:flex;gap:10px;flex-wrap:wrap;margin-top:8px}
.pp-option{padding:8px 16px;cursor:pointer;background:var(--paper-2);color:var(--ink);font-family:var(--font-body);font-weight:600;font-size:var(--text-sm);border:var(--stroke) solid var(--ink);border-radius:var(--radius-pill)}
.pp-option.is-selected{background:var(--ink);color:var(--paper-2)}
.pp-option.is-sold-out{color:var(--ink-3);text-decoration:line-through}
.pp-option.is-sold-out.is-selected{background:var(--paper-3);color:var(--ink-3)}
.pp-actions{display:flex;align-items:center;gap:16px;margin-top:8px;flex-wrap:wrap}
@media (max-width:900px){
  .product-page-layout{grid-template-columns:1fr}
}
@media (max-width:720px){
  .product-page{padding:24px 20px 40px}
}

/* ---------- About ---------- */
.about-hero{background-color:var(--wash-sky);background-image:var(--canvas-texture);border-bottom:var(--stroke-bold) solid var(--ink);padding:56px 52px;display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center}
.about-hero h1{font-size:clamp(30px,4vw,48px)}
.about-hero .art-frame img{aspect-ratio:1/1;object-fit:cover;border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-xl);box-shadow:var(--shadow-panel-lg)}
.about-hero .bubble-pos{position:absolute;bottom:-18px;left:-18px}
.about-grid{background-color:var(--paper);background-image:var(--canvas-texture);padding:52px;display:grid;grid-template-columns:repeat(3,1fr);gap:28px}
.about-card{background:var(--paper-2);border:var(--stroke-bold) solid var(--ink);border-radius:var(--radius-lg);box-shadow:var(--shadow-panel);padding:24px;display:flex;flex-direction:column;gap:10px}
@media (max-width:900px){.about-hero,.about-grid{grid-template-columns:1fr}}

/* ---------- Responsive tweaks ---------- */
@media (max-width:900px){
  .hero-copy{padding:40px 24px}
  .band-2col{grid-template-columns:1fr;gap:32px}
  .band{padding:44px 24px}
  .container{padding-left:20px;padding-right:20px}
  .shop-head,.shop-grid-wrap,.basket-layout,.work-head,.work-grid-wrap{padding-left:20px;padding-right:20px}
  .site-footer{padding:32px 20px}
}

/* ---------- Mobile header / hamburger menu ---------- */
.hamburger-btn{display:none}
@media (max-width:720px){
  .site-header .bar{padding:14px 20px}
  .nav-links,[data-desktop-only]{display:none}
  .hamburger-btn{display:grid}
  .header-actions{gap:10px}
  .brand .brand-name{font-size:15px}
  .brand img{width:auto;height:31px}
}
.mobile-menu-scrim{position:fixed;inset:0;background:rgba(32,30,26,.5);backdrop-filter:blur(3px);z-index:100;opacity:0;pointer-events:none;transition:opacity var(--dur) var(--ease-out)}
.mobile-menu-scrim.is-open{opacity:1;pointer-events:auto}
.mobile-menu{position:fixed;top:0;right:-2000px;bottom:0;width:300px;max-width:85vw;background:var(--paper-2);border-left:4px solid var(--ink);padding:24px;display:flex;flex-direction:column;gap:24px;overflow-y:auto;z-index:101;transition:right var(--dur-slow) var(--ease-bounce)}
.mobile-menu.is-open{right:0}
.mm-head{display:flex;align-items:center;justify-content:space-between}
.mm-links{display:flex;flex-direction:column;gap:4px}
.mm-links a{font-family:var(--font-lettering);font-weight:800;font-size:22px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink);text-decoration:none;padding:10px 0;border-bottom:var(--stroke) solid var(--border-soft)}
.mm-actions{display:flex;flex-direction:column;gap:12px;margin-top:auto}
.mm-actions .btn{justify-content:flex-start;gap:12px}
