/* =====================================================================
   Landing page: the advent door wall, the deal of the week card, and the
   snowy scene standing in the tree line.
   ===================================================================== */

/* ---------- Hero: one product, as big as the screen allows ----------
   This replaced the usual "words on the left, picture on the right" split, which the
   owner found templated -- and they were right, it is the most common layout on the web.

   The reasoning, from how shops actually sell in 2026: focused single-product imagery
   beats a collage of products by a wide margin, and this shop's box art is its strongest
   asset. Six of the seven products are near-identical dumpling calendars, so a grid of
   lookalike thumbnails above the fold would read as noise rather than choice. One box,
   very large, with the price and the button directly under it and no paragraph anywhere.

   The counter-finding -- that hiding products behind a tall hero costs small shops sales
   -- is handled by sizing the hero to about 86% of the screen instead of all of it, so
   the top of the product grid is always peeking and there is something to scroll to. */
.hero-doors { position: relative; overflow: hidden; padding-top: clamp(0.5rem, 2vw, 1.5rem); }

.hero-stage { display: grid; justify-items: center; gap: clamp(0.5rem, 1.4vh, 1rem); }

.hero-stage__eyebrow {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.6rem;
  font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  font-size: 0.78rem; color: var(--muted);
  margin: 0;
}

/* the information sits under the product as one calm block, centred */
.hero-stage__bar { display: grid; justify-items: center; gap: 0.5rem; text-align: center; max-width: 60ch; }
.hero-stage__row { display: grid; justify-items: center; gap: 0.5rem; }
.hero-stage__name {
  margin: 0;
  font-size: clamp(1.35rem, 1.05rem + 1.1vw, 2.1rem);
  line-height: 1.12;
}
.hero-stage__variant { color: var(--muted); font-weight: 700; }
.hero-stage__variant::before { content: ' — '; }
.hero-stage__prices { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center; gap: 0.6rem; margin: 0; }
.hero-stage__now { font: 600 clamp(1.9rem, 1.4rem + 1.7vw, 2.9rem)/1 var(--font-display); color: var(--accent); }
.hero-stage__was { color: var(--muted); font-size: 1.05rem; }
.hero-stage__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.7rem; margin-top: 0.15rem; }
.hero-stage__fine { margin: 0; color: var(--muted); font-size: 0.9rem; }

/* The product, as large as the window allows.

   home.js wraps the showcase in a link, and that link is a grid item with
   justify-items: center, so it shrinks to its content. The showcase inside is sized
   with a percentage, which then resolved against a zero-width parent and collapsed the
   whole thing to nothing. So the WRAPPER carries the width and the showcase fills it.
   Both selectors are needed: without JavaScript the showcase is a direct child. */
.hero-stage > .showcase,
.hero-stage > .showcase__link { width: min(100%, 680px); }
.hero-stage > .showcase__link > .showcase { width: 100%; }

@media (min-width: 880px) {
  /* Leave the hero a little shorter than the screen so the grid below always peeks.
     86svh, not 100: the owner asked to see the whole hero without scrolling, and an
     oversized hero that hides every product is the thing the research warns against. */
  /* Exactly one screen: min and max are the same, so the hero can neither fall short
     of the fold nor run past it. The owner has asked twice for the whole thing to be
     visible without scrolling, and a fixed height is the only way to guarantee it. */
  .hero-doors {
    min-height: calc(100svh - var(--header-h) - var(--lights-h));
    max-height: calc(100svh - var(--header-h) - var(--lights-h));
    display: flex; flex-direction: column;
  }
  /* min-height: 0 lets this shrink below its content. Without it the stage refuses to
     compress, and the snowy ground gets pushed out of the hero and clipped away by
     overflow: hidden -- which is exactly what happened the first time. */
  /* The snowman and the gifts stand ABOVE the top edge of the ground -- the scene strip
     is anchored to the bottom of the ground and its figures are taller than the padding
     above them. So the stage needs real clearance underneath or the fine print lands on
     the snowman's head, which it did. */
  /* align-content: center means that if the content is TALLER than this box it spills
     equally out of both ends -- the badge went up under the string lights and the fine
     print went down towards the snowman. So the contents have to actually fit. */
  .hero-stage { flex: 1 1 auto; min-height: 0; align-content: center; padding-bottom: clamp(1rem, 4vh, 3rem); }
  /* This padding is the ONLY headroom the snowman, the crate and the candy cane have:
     .xmas-ground hides its overflow, so anything taller than (padding + tree line) gets
     its head cut off. Trimming this to buy height for the product did exactly that --
     the snowman lost 28px off the top. Keep it in step with the figure caps below. */
  .hero-doors .xmas-ground { flex: 0 0 auto; padding-top: clamp(18px, 3vh, 48px); }
  /* Everything in the snowy scene is sized from the WIDTH of the window only, so on a
     wide short laptop the figures grew tall while the space for them shrank, and their
     boxes ran up into the price and the buttons. Inside the hero they are capped by
     height as well, and they sit lower in the snow, so the whole scene stays inside its
     own band and never reaches the text. (Measured with box intersection, not by eye --
     the artwork is mostly transparent, so a real overlap can look fine and still be
     wrong at another window size.) */
  .hero-doors .treeline { height: clamp(76px, min(11vw, 12vh), 140px); }
  .hero-doors .scene-strip { bottom: clamp(4px, 1vh, 14px); }
  .hero-doors .snowman { width: clamp(64px, min(13vw, 10.5vh), 168px); }
  .hero-doors .gifts   { width: clamp(88px, min(17vw, 13.5vh), 230px); }
  .hero-doors .cane    { width: clamp(26px, min(5vw, 4.4vh), 62px); }
  .hero-doors .stall   { width: clamp(76px, min(15vw, 11.5vh), 186px); }
  /* the picture is bounded by the height of the window, not just its width */
  /* The showcase box is nearly square (1 / 0.92) but the box art is wide, about 1.64:1.
     Letterboxed inside a square-ish box the picture came out 347x212 -- 24% of the
     screen -- with 60px of dead height inside its own container, which is what made the
     page look so empty. In the hero the box takes the shape of the artwork instead, so
     the same height buys a far wider picture. */
  .hero-stage > .showcase,
  .hero-stage > .showcase__link { width: min(100%, 940px, 44vh * 1.58); }
  .hero-stage .showcase { aspect-ratio: 1 / 0.63; }
  .hero-stage .showcase__stage { width: 95%; height: 90%; top: 48%; }
  .hero-stage .showcase__mound { width: 56%; height: 15%; bottom: 2%; }
  .hero-stage .showcase__glow { width: 72%; }

  /* On a computer the name, the price and the buttons sit on ONE line under the box.
     Stacked, they ate about 110px of height and the product had to shrink to a
     thumbnail, which defeats the whole point of leading with one big picture. */
  .hero-stage__bar { max-width: none; }
  .hero-stage__row {
    display: flex; align-items: center; justify-content: center;
    flex-wrap: wrap; gap: 0.75rem 1.4rem;
  }
  /* 22ch forced the name onto three lines, which made this bar 141px tall and stole
     that height from the picture. There is plenty of width out here; let it run. */
  .hero-stage__row .hero-stage__name { text-align: left; max-width: 26ch; }
  .hero-stage__prices { gap: 0.5rem; }
}
/* Short windows. A 1280x720 laptop has the least room of any common size, and at the
   full picture size the badge ended up 3px under the garland with the fine print
   touching a figure. The picture gives way first; it is still the biggest thing here. */
@media (min-width: 880px) and (max-height: 820px) {
  .hero-stage__name { font-size: clamp(1.2rem, 1rem + 0.9vw, 1.7rem); }
  .hero-stage__now  { font-size: clamp(1.6rem, 1.25rem + 1.2vw, 2.2rem); }
  .hero-stage { gap: 0.4rem; }
  /* Headroom again: this override was starving the figures on a 720px-tall laptop and
     cutting the snowman's hat off. It has to keep pace with the figure caps, not just
     with the space the picture wants. */
  .hero-doors .xmas-ground { padding-top: clamp(16px, 3.2vh, 40px); }
  .hero-doors .snowman { width: clamp(60px, min(13vw, 9.5vh), 168px); }
  .hero-doors .stall   { width: clamp(72px, min(15vw, 10.5vh), 186px); }
  .hero-stage > .showcase,
  .hero-stage > .showcase__link { width: min(100%, 940px, 39vh * 1.58); }
}
@media (min-width: 880px) and (max-height: 700px) {
  .hero-stage__fine { font-size: 0.82rem; }
  .hero-stage > .showcase,
  .hero-stage > .showcase__link { width: min(100%, 940px, 33vh * 1.58); }
}

/* ---------- Deal of the week ---------- */
.deal { display: grid; gap: 0.9rem; justify-items: start; }
.deal__eyebrow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; font-size: 0.78rem; color: var(--muted); }
.deal__badge {
  padding: 0.3rem 0.7rem; border-radius: var(--radius-pill);
  background: var(--red); color: #fff; letter-spacing: 0.04em;
  animation: badge-pop 4.5s ease-in-out infinite;
}
@keyframes badge-pop { 0%, 88%, 100% { transform: rotate(-2deg) scale(1); } 93% { transform: rotate(2deg) scale(1.07); } }
.deal__name { font-size: clamp(1.75rem, 1.3rem + 2vw, 2.9rem); line-height: 1.05; }
.deal__variant { color: var(--muted); font-weight: 700; }
.deal__prices { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.6rem; }
.deal__now { font: 600 clamp(1.9rem, 1.4rem + 1.6vw, 2.6rem)/1 var(--font-display); color: var(--accent); }
.deal__was { color: var(--muted); font-size: 1.05rem; }
.deal__ship { color: var(--muted); font-size: 0.95rem; }
.deal__actions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 0.25rem; }
.deal__timer { display: flex; align-items: center; gap: 0.45rem; color: var(--muted); font-size: 0.92rem; }
.deal__timer::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); animation: tick 2s ease-in-out infinite; }
@keyframes tick { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(0.75); } }
.deal__sample { font-size: 0.85rem; color: var(--muted); }

/* ---------- The showcase: this week's product on display ----------
   Runs on its own, nothing to press. The product lifts, settles and gives
   a soft squish, a gloss sweeps across it, sparkles catch its edges. The
   gloss is a white copy of the same picture, cut to a moving diagonal
   band, so it only ever lights up the product and never the box around it.
   ------------------------------------------------------------------- */
.showcase { position: relative; width: min(100%, 460px); aspect-ratio: 1 / 0.92; margin-inline: auto; }

/* warm light behind the toy */
.showcase__glow {
  position: absolute; left: 50%; top: 46%; translate: -50% -50%;
  width: 88%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle, rgb(255 220 147 / 0.30) 0%, rgb(255 220 147 / 0.12) 42%, transparent 68%);
  animation: glow-breathe 5s ease-in-out infinite;
}
@keyframes glow-breathe { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.09); opacity: 1; } }

/* both pictures share one box, so the gloss always lines up with the product */
.showcase__stage {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%; z-index: 2;
  width: min(92%, 400px); height: 78%;
  transform-origin: 50% 92%;
  animation: showcase-settle 9s ease-in-out infinite;
}
.showcase__product,
.showcase__shine { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }

.showcase__product { filter: drop-shadow(0 22px 30px rgb(0 0 0 / 0.5)); }

/* lift, land, and give like something soft */
@keyframes showcase-settle {
  0%, 100% { transform: translateY(0) rotate(-0.8deg) scale(1, 1); }
  24%      { transform: translateY(-11px) rotate(0.9deg) scale(0.994, 1.012); }
  48%      { transform: translateY(0) rotate(0.2deg) scale(1.035, 0.962); }
  63%      { transform: translateY(-5px) rotate(-0.35deg) scale(0.982, 1.018); }
  82%      { transform: translateY(0) rotate(-0.7deg) scale(1.01, 0.993); }
}

/* The gloss sweep.

   The mask is STATIC and only the band inside it moves. An earlier version animated
   `mask-position` on a white copy of the product, which forced the browser to re-apply
   a mask and a blur to a 400px image on every single frame -- the kind of thing that
   makes a cheap laptop stutter. Now the wrapper is clipped to the product silhouette
   once, and the band inside is moved with `transform` alone, which the compositor does
   for free. The silhouette comes from --deal-mask, set by deal-ui.js next to the
   picture, so it follows whichever product is on offer. */
.showcase__gloss {
  position: absolute; inset: 0; z-index: 3; overflow: hidden;
  -webkit-mask-image: var(--deal-mask); mask-image: var(--deal-mask);
  -webkit-mask-size: contain;          mask-size: contain;
  -webkit-mask-repeat: no-repeat;      mask-repeat: no-repeat;
  -webkit-mask-position: center;       mask-position: center;
}
.showcase__glossband {
  position: absolute; top: -30%; bottom: -30%; left: -45%; width: 42%;
  background: linear-gradient(104deg, transparent 0%, rgb(255 255 255 / 0.5) 42%, rgb(255 255 255 / 0.5) 58%, transparent 100%);
  transform: translate3d(-120%, 0, 0);
  will-change: transform, opacity;
  opacity: 0;
  animation: showcase-shine 7.5s ease-in-out infinite;
}
@keyframes showcase-shine {
  0%, 52%   { transform: translate3d(-120%, 0, 0); opacity: 0; }
  60%       { opacity: 0.85; }
  88%       { opacity: 0.85; }
  96%, 100% { transform: translate3d(420%, 0, 0);  opacity: 0; }
}

/* snowy mound it stands on */
.showcase__mound {
  position: absolute; left: 50%; bottom: 4%; translate: -50% 0; z-index: 1;
  width: 68%; height: 13%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 40%, var(--snow) 0%, rgb(246 241 230 / 0.82) 52%, transparent 74%);
}

/* light catching the edges, each star on its own beat */
.showcase__sparks { position: absolute; inset: 0; z-index: 4; }
.showcase__sparks b {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--s); height: var(--s); margin: calc(var(--s) / -2);
  background: var(--accent); opacity: 0;
  clip-path: polygon(50% 0%, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0% 50%, 42% 42%);
  animation: showcase-twinkle 5.5s ease-in-out var(--delay) infinite;
}
.showcase__sparks b:nth-child(3) { background: var(--snow); }
.showcase__sparks b:nth-child(5) { background: var(--snow); }
@keyframes showcase-twinkle {
  0%, 68%, 100% { opacity: 0; transform: scale(0.2) rotate(-30deg); }
  78%           { opacity: 0.95; transform: scale(1) rotate(0deg); }
  88%           { opacity: 0.35; transform: scale(0.78) rotate(14deg); }
}

/* a little snow drifting around the scene */
.showcase__snow { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.showcase__snow i {
  position: absolute; top: -6%; left: var(--x);
  width: var(--s); height: var(--s); border-radius: 50%;
  background: var(--snow); opacity: 0.6;
  animation: showcase-snow var(--d) linear var(--delay) infinite;
}
@keyframes showcase-snow { to { transform: translate3d(var(--sway), 108%, 0); } }

@media (max-width: 879.98px) { .showcase { width: min(100%, 360px); } }

/* ---------- Snowy scene standing in the tree line ---------- */
/* room above the tree line so the snowman and the gifts are never cut off */
.xmas-ground { position: relative; overflow: hidden; padding-top: clamp(80px, 13vw, 170px); }
.scene-strip {
  position: absolute; left: 0; right: 0; bottom: clamp(26px, 4vw, 58px);
  z-index: 2; pointer-events: none;
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem;
  padding-inline: clamp(0.75rem, 6vw, 5rem);
}
.scene-strip > * { flex: 0 0 auto; }
.scene-strip .cane-wrap { align-self: flex-end; margin-bottom: 0.25rem; }
@media (max-width: 599.98px) {
  .scene-strip { padding-inline: 0.5rem; }
  .scene-strip .cane-wrap { display: none; }
}

/* ---------- Section ornament ---------- */
.ornament-head { display: grid; justify-items: center; gap: 0.35rem; text-align: center; margin-bottom: clamp(1.5rem, 4vw, 2.5rem); }
.ornament-head .section-title { margin-bottom: 0; }
.ornament-head .section-title::after { left: 50%; transform: translateX(-50%) scaleX(0); transform-origin: center; }
.is-in .ornament-head .section-title::after, .no-observer .ornament-head .section-title::after { transform: translateX(-50%) scaleX(1); }

/* Translucent so the fixed snow and northern lights carry on behind the products
   instead of stopping at the top of the page. Safe for contrast: the layer underneath
   is --bg, and text and links already pass against both --bg and --bg-2, so every
   blend of the two passes as well. */
.section--snow { position: relative; background: color-mix(in srgb, var(--bg-2) 74%, transparent); }
.section--snow::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(70% 60% at 50% 0%, rgb(255 220 147 / 0.07), transparent 70%);
}
.section--snow > * { position: relative; }

/* product page keeps the simple hero */
.xmas-hero { position: relative; overflow: hidden; padding-top: clamp(1.5rem, 4vw, 3rem); }
.xmas-hero__grid { display: grid; gap: clamp(1.75rem, 5vw, 3.5rem); align-items: center; padding-bottom: clamp(2rem, 5vw, 3.5rem); }
@media (min-width: 880px) { .xmas-hero__grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); } }
.xmas-hero__copy { display: grid; gap: 1.25rem; justify-items: start; }
.xmas-hero__copy .lede { font-size: clamp(1.05rem, 1rem + 0.35vw, 1.25rem); color: var(--muted); max-width: 46ch; }
.xmas-hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.xmas-hero__art { position: relative; display: grid; place-items: center; min-height: clamp(260px, 38vw, 400px); }
.xmas-hero__art .xm-tree { position: absolute; left: 0; bottom: -6%; width: clamp(132px, 34%, 258px); z-index: 0; }
.xmas-hero__art .product-art { position: relative; z-index: 1; width: min(100%, 420px); filter: drop-shadow(0 26px 38px rgb(0 0 0 / 0.5)); animation: art-float 7s ease-in-out infinite; }
@keyframes art-float { 0%, 100% { transform: translateY(0) rotate(-0.6deg); } 50% { transform: translateY(-10px) rotate(0.6deg); } }
@media (max-width: 879.98px) {
  .xmas-hero__art { min-height: 0; padding-left: 22%; }
  .xmas-hero__art .xm-tree { width: clamp(104px, 26%, 170px); left: -2%; bottom: -4%; }
}

@media (prefers-reduced-motion: reduce) {
  .showcase__stage, .showcase__glow, .showcase__shine, .showcase__snow i, .showcase__sparks b,
  .deal__badge, .deal__timer::before, .xmas-hero__art .product-art { animation: none; }
  .showcase__snow i { opacity: 0.45; }
  .showcase__sparks b { opacity: 0.6; transform: scale(0.85); }
}

/* ---------- A real calendar standing in the snow ----------
   The scene along the tree line used to be decoration only. This puts an actual
   product down there, propped in a market crate with a little sign, and the whole
   thing is a link through to that product's page. */
.stall { position: relative; width: clamp(128px, 15vw, 186px); pointer-events: auto; }
.stall__link {
  display: grid; justify-items: center;
  text-decoration: none;
  border-radius: var(--radius-card);
}
.stall__link:focus-visible { outline: 3px solid var(--focus); outline-offset: 6px; }

.stall__sign { display: block; width: 72%; margin-bottom: 2%; }
.stall__sign svg { width: 100%; height: auto; display: block; filter: drop-shadow(0 6px 10px rgb(0 0 0 / 0.35)); }
.stall__board { transform-origin: 50% 12%; animation: stall-swing 4.4s ease-in-out infinite; }
@keyframes stall-swing { 0%, 100% { transform: rotate(-2.6deg); } 50% { transform: rotate(2.6deg); } }

/* the product sits in the crate and lifts when you point at it */
.stall__box { display: block; width: 88%; margin-bottom: -26%; position: relative; z-index: 2; }
.stall__box img {
  width: 100%; height: auto; display: block;
  filter: drop-shadow(0 12px 16px rgb(0 0 0 / 0.5));
  transform-origin: 50% 100%;
  animation: stall-bob 5.5s ease-in-out infinite;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes stall-bob {
  0%, 100% { transform: translateY(0) rotate(-1.4deg); }
  50%      { transform: translateY(-5px) rotate(1.4deg); }
}
@media (hover: hover) {
  .stall__link:hover .stall__box img { animation-play-state: paused; transform: translateY(-9px) scale(1.06); }
  .stall__link:hover .stall__board { animation-play-state: paused; }
}

.stall__crate { display: block; width: 100%; position: relative; z-index: 1; }
.stall__crate svg { width: 100%; height: auto; display: block; }

@media (max-width: 599.98px) { .stall { width: 112px; } }

@media (prefers-reduced-motion: reduce) {
  .stall__board, .stall__box img { animation: none; }
  .stall__box img { transition: none; }
}

/* the whole showcase is a link through to the deal's own page */
.showcase__link { display: block; text-decoration: none; border-radius: var(--radius-card); }
.showcase__link:focus-visible { outline: 3px solid var(--focus); outline-offset: 8px; }
@media (hover: hover) {
  .showcase__link:hover .showcase__stage { animation-play-state: paused; }
  .showcase__link:hover .showcase__product { filter: drop-shadow(0 26px 34px rgb(0 0 0 / 0.55)) brightness(1.05); }
}
.showcase__product { transition: filter 0.3s ease; }

/* the way back, above the product name */
.back-row { margin-bottom: clamp(0.5rem, 1.6vh, 1.1rem); }
.back-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.35rem 0.75rem 0.35rem 0.6rem;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  color: var(--muted); text-decoration: none;
  font-size: 0.88rem; font-weight: 700;
  transition: color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.back-link:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); transform: translateX(-2px); }
.back-link:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
