/* ══════════════════════════════════════════════════════════════════════════
   The park — the product page's setting.

   The PDP used to be a flat cream sheet: correct, on-brand, and completely
   inert. "Just like a notebook." So the page is now somewhere: a green field
   with a sky over it, the doodle cast out playing in it, and the product
   itself presented on sheets of paper laid on the grass.

   Deliberate constraint: the park is the SETTING, never the subject. The
   gallery, the buy box and every block of copy stay on high-contrast paper
   cards, because a product page that does not convert is just a poster.

   Loaded on the PDP only (product.html), on top of bazaar2.css.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  --pk-sky-top: #cfe9f5;
  --pk-sky-bot: #f3ecd8;
  --pk-hill-far: #cfe3c0;
  --pk-hill-near: #b7d79a;
  --pk-grass: #aad089;
  --pk-grass-deep: #9bc478;
  --pk-ink: #1a1714;
  --pk-path: #e2d2a8;
  /* Where the sky stops and the field starts, measured from the top of the
     page. Roughly the fold on desktop, so the hero sits against sky. */
  --pk-horizon: 720px;
}

/* ── The field ─────────────────────────────────────────────────────────── */

body.park {
  background-color: var(--pk-grass);
  background-image:
    /* mown stripes — barely there on purpose; at any more contrast they
       read as a barcode behind the product copy */
    repeating-linear-gradient(
      93deg,
      rgba(255, 255, 255, .028) 0 70px,
      rgba(0, 0, 0, .012) 70px 140px
    ),
    /* the hills at the horizon */
    radial-gradient(120% 130px at 22% var(--pk-horizon), var(--pk-hill-near) 0 60%, transparent 61%),
    radial-gradient(120% 165px at 78% calc(var(--pk-horizon) - 18px), var(--pk-hill-far) 0 60%, transparent 61%),
    /* sky */
    linear-gradient(var(--pk-sky-top) 0, var(--pk-sky-bot) 78%, var(--pk-grass) 100%);
  background-repeat: repeat, no-repeat, no-repeat, no-repeat;
  background-size: auto, 100% var(--pk-horizon), 100% var(--pk-horizon), 100% var(--pk-horizon);
  background-position: 0 var(--pk-horizon), 0 0, 0 0, 0 0;
  background-attachment: scroll;
}

/* The bunting under the header reads as strung across the park entrance. */
body.park .bunting-strip { filter: drop-shadow(0 2px 0 rgba(26, 23, 20, .12)); }

body.park .crumb {
  color: #4a4438;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
}
body.park .crumb a:hover { color: var(--pk-ink); }

/* ── Everything readable goes on paper ────────────────────────────────── */

/* .sec was a transparent block separated by a dashed rule. On grass that
   left body copy sitting on a green field — pretty, unreadable. Each one
   is now a sheet of paper, slightly rotated, as if set down on the lawn. */
body.park .sec {
  background: #fffdf6;
  border: 2.5px solid var(--pk-ink);
  border-top: 2.5px solid var(--pk-ink);
  border-radius: 22px 60px 20px 54px / 54px 20px 60px 22px;
  box-shadow: 5px 6px 0 var(--pk-ink);
  padding: 34px 32px;
  margin: 30px 0;
}
body.park .sec + .sec { margin-top: 30px; }
body.park .faq-section {
  background: #fffdf6;
  border: 2.5px solid var(--pk-ink);
  border-radius: 20px 54px 20px 54px / 54px 20px 54px 20px;
  box-shadow: 5px 6px 0 var(--pk-ink);
  padding: 30px 32px;
  margin: 30px 0;
}
body.park footer {
  background: #fffdf6;
  border-top: 2.5px solid var(--pk-ink);
  margin-top: 34px;
}

/* ── The park band: the cast, at play ─────────────────────────────────── */

.park-band {
  position: relative;
  width: 100%;
  margin: 8px 0 4px;
  overflow: hidden;
  line-height: 0;               /* kill the inline-SVG descender gap */
  pointer-events: none;         /* scenery must never eat a tap */
}
.park-band svg { display: block; width: 100%; height: auto; }

/* A caption in the shop's own voice, so the scene reads as intentional
   rather than as decoration someone forgot to remove. */
.park-note {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 4px;
  padding: 7px 14px;
  background: #fffdf6;
  border: 2px solid var(--pk-ink);
  border-radius: 14px 9px 16px 10px / 10px 16px 9px 14px;
  box-shadow: 3px 3px 0 var(--pk-ink);
  font-family: Nunito, ui-sans-serif, system-ui, sans-serif;
  font-size: 13.5px;
  font-weight: 700;
  color: #4a4438;
  line-height: 1.3;
}

/* ── The cast moves ───────────────────────────────────────────────────── */

.pk-legf { animation: pk-legf .48s ease-in-out infinite; }
.pk-legb { animation: pk-legb .48s ease-in-out infinite; }
.pk-armf { animation: pk-legb .48s ease-in-out infinite; }
.pk-armb { animation: pk-legf .48s ease-in-out infinite; }
.pk-leg, .pk-arm { transform-box: fill-box; transform-origin: top center; }
@keyframes pk-legf { 0%, 100% { transform: rotate(24deg); } 50% { transform: rotate(-24deg); } }
@keyframes pk-legb { 0%, 100% { transform: rotate(-24deg); } 50% { transform: rotate(24deg); } }

/* the runner crosses the field and loops */
.pk-run { animation: pk-run 15s linear infinite; }
@keyframes pk-run { from { transform: translateX(-140px); } to { transform: translateX(1740px); } }

/* the frisbee flies between the two throwers, and the dog leaps for it */
.pk-disc { animation: pk-disc 3.6s ease-in-out infinite; transform-box: fill-box; }
@keyframes pk-disc {
  0%   { transform: translate(0, 0) rotate(0deg); }
  50%  { transform: translate(232px, -34px) rotate(540deg); }
  100% { transform: translate(0, 0) rotate(1080deg); }
}
.pk-leap { animation: pk-leap 3.6s ease-in-out infinite; }
@keyframes pk-leap { 0%, 100% { transform: translateY(0); } 46% { transform: translateY(-19px); } }

/* the sunbather breathes, the sun pulses */
.pk-breathe { animation: pk-breathe 4.4s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
@keyframes pk-breathe { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(1.035); } }
.pk-rays { animation: pk-rays 6s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
@keyframes pk-rays { 0%, 100% { opacity: .55; transform: scale(1); } 50% { opacity: .95; transform: scale(1.07); } }

/* the razor works its way up the cheek */
.pk-shave { animation: pk-shave 2.6s ease-in-out infinite; transform-box: fill-box; transform-origin: 20% 80%; }
@keyframes pk-shave {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  45%      { transform: translate(1px, -12px) rotate(-9deg); }
}
.pk-fleck { animation: pk-fleck 2.6s ease-out infinite; transform-box: fill-box; }
@keyframes pk-fleck {
  0%, 42%  { opacity: 0; transform: translate(0, 0); }
  62%      { opacity: .9; }
  100%     { opacity: 0; transform: translate(9px, 15px); }
}

/* kite, clouds, grass */
.pk-kite { animation: pk-kite 5.4s ease-in-out infinite; transform-box: fill-box; transform-origin: bottom center; }
@keyframes pk-kite { 0%, 100% { transform: rotate(-6deg) translateY(0); } 50% { transform: rotate(7deg) translateY(-13px); } }
.pk-cloud { animation: pk-cloud 90s linear infinite; }
@keyframes pk-cloud { from { transform: translateX(-220px); } to { transform: translateX(1760px); } }
.pk-sway { animation: pk-sway 4.8s ease-in-out infinite; transform-box: fill-box; transform-origin: bottom center; }
@keyframes pk-sway { 0%, 100% { transform: rotate(-2.2deg); } 50% { transform: rotate(2.2deg); } }
.pk-wag { animation: pk-wag .55s ease-in-out infinite; transform-box: fill-box; transform-origin: left center; }
@keyframes pk-wag { 0%, 100% { transform: rotate(-17deg); } 50% { transform: rotate(17deg); } }
.pk-steam { animation: pk-steam 4.6s ease-out infinite; transform-box: fill-box; }
@keyframes pk-steam {
  0%   { opacity: 0; transform: translateY(4px) scale(.7); }
  35%  { opacity: .65; }
  100% { opacity: 0; transform: translateY(-24px) scale(1.25); }
}

/* ── Small screens ────────────────────────────────────────────────────── */

@media (max-width: 900px) {
  :root { --pk-horizon: 460px; }
  body.park .sec,
  body.park .faq-section { padding: 24px 20px; margin: 22px 0; border-radius: 18px 40px 16px 36px / 36px 16px 40px 18px; }
  /* The band is 1600 units wide; on a phone it would shrink to a smear.
     Crop to the middle of the scene at a usable height instead. */
  .park-band svg { height: 190px; width: auto; min-width: 100%; }
}

/* ── Reduced motion: the park stays, the cast holds still ─────────────── */

@media (prefers-reduced-motion: reduce) {
  .pk-legf, .pk-legb, .pk-armf, .pk-armb, .pk-run, .pk-disc, .pk-leap,
  .pk-breathe, .pk-rays, .pk-shave, .pk-fleck, .pk-kite, .pk-cloud,
  .pk-sway, .pk-wag, .pk-steam { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Animation pass 2 — giving the cast weight.

   The first pass rotated two legs and called it a walk. What sells a
   hand-drawn figure as ALIVE is not the limbs, it is: a shadow that tells
   you where the ground is, a body that rises and falls on the stride, and
   the small involuntary things (a blink, a breath) that a loop does not
   usually bother with. All of it is still pure CSS on inline SVG, and all
   of it stops dead under prefers-reduced-motion.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Ground shadows: the single biggest "is it standing on anything?" cue ── */
.pk-shadow      { fill: rgba(26, 23, 20, .17); stroke: none; }
.pk-shadow-soft { fill: rgba(26, 23, 20, .11); stroke: none; }

/* A walking figure's shadow shrinks and lightens as the body lifts off the
   stride, which is what makes the bob read as weight rather than a float. */
.pk-shadow-walk {
  animation: pk-shadowwalk .48s ease-in-out infinite;
  transform-box: fill-box; transform-origin: center;
}
@keyframes pk-shadowwalk {
  0%, 100% { transform: scaleX(1);   opacity: 1; }
  50%      { transform: scaleX(.84); opacity: .6; }
}

/* ── Body rise and fall on the stride ── */
.pk-bob { animation: pk-bob .48s ease-in-out infinite; }
@keyframes pk-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4.5px); } }

/* the head lags a beat behind the body — cheap, and it reads as momentum */
.pk-head-lead { animation: pk-head .48s ease-in-out infinite; transform-box: fill-box; transform-origin: bottom center; }
@keyframes pk-head { 0%, 100% { transform: rotate(-2.5deg); } 50% { transform: rotate(2.5deg); } }

/* ── Blink. Rare, quick, offset per character so they never blink in sync ── */
.pk-blink { animation: pk-blink 7s infinite; transform-box: fill-box; transform-origin: center; }
@keyframes pk-blink { 0%, 95%, 100% { transform: scaleY(1); } 97.5% { transform: scaleY(.08); } }

/* ── Breathing, for anyone standing or sitting still ── */
.pk-idle { animation: pk-idle 5.2s ease-in-out infinite; transform-box: fill-box; transform-origin: bottom center; }
@keyframes pk-idle { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(1.022); } }

/* ── A second walker, crossing the other way and slower = parallax depth ── */
.pk-walk2 { animation: pk-walk2 34s linear infinite; }
@keyframes pk-walk2 { from { transform: translateX(1720px); } to { transform: translateX(-220px); } }

/* ── Butterflies and foreground grass: the field itself moving ── */
.pk-flutter { animation: pk-flutter 19s ease-in-out infinite; }
@keyframes pk-flutter {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(340px, -34px); }
  50%  { transform: translate(700px, 12px); }
  75%  { transform: translate(1080px, -26px); }
  100% { transform: translate(1460px, 4px); }
}
.pk-wing { animation: pk-wing .22s ease-in-out infinite; transform-box: fill-box; transform-origin: right center; }
@keyframes pk-wing { 0%, 100% { transform: scaleX(1); } 50% { transform: scaleX(.35); } }

.pk-tuft { animation: pk-tuft 4.2s ease-in-out infinite; transform-box: fill-box; transform-origin: bottom center; }
@keyframes pk-tuft { 0%, 100% { transform: skewX(-5deg); } 50% { transform: skewX(5deg); } }

/* ── The short strip used above the product grid ──
   Same scene, cropped to the ground: `slice` throws away the sky rather
   than scaling the cast down to nothing on a listing page. */
/* The strip carries its own 1600x190 viewBox, so it sizes itself — no fixed
   pixel height to fight with, and nothing gets sliced off at wide widths. */
.park-band.is-strip svg { width: 100%; height: auto; }

@media (prefers-reduced-motion: reduce) {
  .pk-shadow-walk, .pk-bob, .pk-head-lead, .pk-blink, .pk-idle,
  .pk-walk2, .pk-flutter, .pk-wing, .pk-tuft { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Listing pages (browse.py: /shop, /c/<dept>, /c/<dept>/<sub>, /search).

   These were the pages the founder actually screenshotted — graph-paper
   background, no cast, "only notepad like background". They share the park
   with the product pages, with two differences: the content column is
   narrow, so the bands break out to full width or they read as a picture in
   a box; and the horizon sits higher, because the hero here is a heading
   rather than a full-height gallery.
   ══════════════════════════════════════════════════════════════════════════ */

body.park-list { --pk-horizon: 430px; }

/* Break the band out of the centred content column to the full viewport.
   100vw includes the scrollbar, hence max-width + the negative half-margin
   trick rather than a plain width:100vw (which would cause h-scroll). */
body.park-list .park-band {
  width: 100vw;
  max-width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
body.park-list .park-note { margin: 10px 0 2px; }

/* Product cards already sit on white; on grass they just need their ink
   edge back so they read as cards laid on the field. */
body.park-list .card { box-shadow: 4px 5px 0 var(--pk-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   Daybreak — the strip's arrival, and the pointer response.

   The strip used to be cropped to the ground with the page showing through
   above it, which left a pale washed band across the top of every category
   page and cut the cast's heads off. It has its own sky and sun now, and it
   ARRIVES: the sun comes up, a band of light crosses the field, and each of
   the cast wakes as the light reaches them. Once, on load - it is a greeting,
   not a loop, and a scene that keeps re-announcing itself is a nuisance.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The sun rises and settles ── */
.pk-sun {
  transform-box: fill-box; transform-origin: center;
  animation: pk-sunrise 1.5s cubic-bezier(.16,.9,.3,1) both;
}
@keyframes pk-sunrise {
  0%   { opacity: 0; transform: translateY(46px) scale(.62); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
/* rays keep turning, slowly, forever */
.pk-sunrays {
  transform-box: fill-box; transform-origin: center;
  animation: pk-raysturn 44s linear infinite, pk-rayspulse 5.5s ease-in-out infinite;
}
@keyframes pk-raysturn  { to { transform: rotate(360deg); } }
@keyframes pk-rayspulse { 0%,100% { opacity: .6; } 50% { opacity: 1; } }

/* ── The band of light crossing the field, once ── */
.pk-daybreak {
  animation: pk-daybreak 2.1s cubic-bezier(.3,.7,.4,1) .15s both;
  pointer-events: none;
}
@keyframes pk-daybreak {
  0%   { transform: translateX(0);     opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translateX(2400px); opacity: 0; }
}

/* ── Each of the cast wakes as the light reaches them ── */
.pk-wake { animation: pk-wake .85s cubic-bezier(.18,.9,.32,1) both; }
@keyframes pk-wake {
  0%   { opacity: 0; transform: translateY(16px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* ── Pointer response (js/park-scene.js sets --pk-px / --pk-py) ──
   Layers shift by their own depth, so the sky drifts less than the field:
   the same trick the flat town uses, which is what makes a flat drawing
   feel like it has air in it. */
.park-band [data-pk-depth] {
  transition: transform .5s cubic-bezier(.2,.8,.3,1);
  will-change: transform;
}
.park-band.is-live [data-pk-depth] { transition: transform .12s linear; }

/* Hovering the field brings the sun up a notch and lifts the cast. */
.park-band.is-strip:hover .pk-sunrays { animation-duration: 22s, 2.4s; opacity: 1; }
.park-band.is-strip { transition: filter .45s ease; }
.park-band.is-strip:hover { filter: saturate(1.06) brightness(1.02); }

@media (prefers-reduced-motion: reduce) {
  .pk-sun, .pk-sunrays, .pk-daybreak, .pk-wake { animation: none; }
  .pk-daybreak { opacity: 0; }
  .park-band [data-pk-depth] { transition: none; transform: none !important; }
  .park-band.is-strip:hover { filter: none; }
}

/* ── The strip on a phone ──────────────────────────────────────────────────
   At 390px the full 1600-unit scene scales to 0.24, which rendered the cast
   about twenty pixels tall: a row of grey specks, not characters. With
   `slice` on the SVG we can give the band a real height and let it CROP to
   the middle of the field instead of shrinking the whole thing - two or
   three doodles at a readable size beats eight you cannot make out. */
@media (max-width: 900px) {
  .park-band.is-strip svg { height: 190px; width: 100%; }
}
@media (max-width: 560px) {
  .park-band.is-strip svg { height: 170px; }
}
