/* Pepule (پەپوولە) — original work for Ahang, 2026-09-07.
   Loaded as a <link> for this template only; every rule is scoped by
   .pepule-scope / .pp-.

   THIS FILE IS PUBLIC AND THE GUEST DOWNLOADS EVERY BYTE, comments included.
   The reasoning lives in the components and in docs/templates/PEPULE_PLAN.md —
   only traps are written here.

   THE PAGE IS AN AISLE. The cover's flight ends on a white runner leading to a
   flowered arch over a lake, and the interior continues that runner: one fixed
   pale band down the centre of the page, behind everything, with the cards
   lying on it. That band is the whole composition. Take it away and this is
   eight centred blocks, which is the failure zerf's redesign diagnosed and that
   any template with eight sections walks into by default.

   THE ELEVATION RULE, inherited from zerf because it was right there:
   EVERYTHING PRINTED IS A CARD AND NOTHING ELSE IS. Every leaf carries the SAME
   shadow (--pp-lift), so no card competes with another, and nothing on this
   page is lifted that is not paper. Things ON a card are PRESSED INTO it — the
   calendar, the countdown cells, the form's fields — never raised off it.

   THE CONTRAST IS THE LIGHT WAY ROUND, and that is worth stating because hena's
   is inverted and the two files sit side by side. This is the lightest page in
   the catalogue: ink is 12.04:1 on the marble, and the accent gold is 2.97:1 and
   may therefore ONLY fill or hairline — never set a word.

   THE MARBLE IS #e2eaf1 AND NOT THE #f1f4f6 IT SHIPPED WITH, because at #f1f4f6
   the cards were invisible: paper is #ffffff, the page stood 1.10:1 off it, and
   the aisle then painted 72% white over the middle and closed even that. Every
   leaf was a white rectangle on a white ground with only a shadow to say where
   it ended. It is 1.22:1 now, the aisle is down to 34%, and every leaf carries
   a printed gold hairline of its own. */

.pepule-scope {
  /* ── the locked palette ───────────────────────────────────────────────
     Three copies of these must agree: TPL_VARS in PepuleTemplate.js, `vars` in
     lib/templates/themes.js, and this block. Darga's two copies drifted for two
     days and its gallery swatch advertised a palette the template had left.

     EVERY VALUE WAS SAMPLED OFF THE SHIPPED FRAMES, not chosen beside them —
     the header of PepuleTemplate.js lists which pixel each came from. */
  --pp-marble: #e2eaf1;
  --pp-marble-deep: #d3e0ea;
  --pp-paper: #ffffff;

  --pp-ink: #1e2a33;
  --pp-ink-soft: #3b5666;
  --pp-muted: #566978;

  --pp-lake: #2c5b76;
  --pp-lake-deep: #1f4257;
  --pp-gold: #a97f45;
  --pp-gold-soft: #e6cfa4;

  /* THE BLUSH IS NOT ONE OF THE TWELVE, ON PURPOSE. The --tpl-* tokens are what
     the SHARED components paint with — buttons, fields, the RSVP — and a pink
     submit button on a wedding invitation is a greetings card. It lives here
     instead, where it can be the wings, the petals and the hairlines and
     nothing that has to be clicked. Sampled off the ribbon in the cover frame
     (#cea68e) and lifted a step so it reads on white. */
  --pp-blush: #dda491;
  --pp-blush-soft: #f0cfc2;

  --pp-line: rgba(44, 91, 118, 0.22);
  --pp-line-soft: rgba(44, 91, 118, 0.11);

  /* THE ONE ELEVATION. Every leaf wears this and nothing else does. The third
     layer is a hairline, not a blur: a paper edge on a light ground is a line,
     and a shadow alone leaves the card looking printed on the aisle rather
     than laid on it. */
  --pp-lift:
    0 24px 46px -30px rgba(30, 42, 51, 0.46),
    0 2px 4px -2px rgba(30, 42, 51, 0.07),
    0 0 0 1px rgba(30, 42, 51, 0.045);
  --pp-lift-sm:
    0 10px 20px -14px rgba(30, 42, 51, 0.4),
    0 0 0 1px rgba(30, 42, 51, 0.04);

  --pp-r: 3px; /* paper corners are nearly square — a 14px radius reads as an app card */
  --pp-card-w: 32rem;
  --pp-gut: 0.85rem;   /* the page's outer margin — the CARDS want the width */
  --pp-pad: 1.85rem;   /* a leaf's own inner margin; the creases bleed by exactly this */

  /* ── the type and spacing rank, OPENED UP 2026-09-07 ──────────────────
     The founder's verdict on the first cut was that the sections were cramped,
     and they were: a 1.55rem heading is barely a step over 1rem body text, a
     2.4rem inner margin on a card that is 353px wide on a phone leaves the
     content boxed in, and 4.5rem between two white cards on a near-white page
     is not a gap, it is a seam. Every number in this block went up, and the
     page colour went DOWN (see --pp-marble) so the paper has something to lift
     off in the first place. */
  --pp-t-label: 0.78rem;
  --pp-t-body: 1.03rem;
  --pp-t-head: 1.85rem;
  --pp-t-display: 3.5rem;
  --pp-air: 6.5rem;    /* between two sections */
  --pp-air-in: 3.4rem; /* between two parts of one section */

  position: relative;
  width: 100%;
  min-height: 100vh;

  /* Under everything, so nothing is ever transparent onto the app's own page
     colour, and so the one frame before the hero decodes is marble and not
     white. */
  background-color: var(--pp-marble);

  color: var(--pp-ink);
  font-size: var(--pp-t-body);
  line-height: 1.85;
  overflow-x: clip;
  /* Never letter-space Kurdish: it pulls a joining script apart. */
  letter-spacing: normal;
}

/* ══ the insect ═══════════════════════════════════════════════════════════
   Geometry from components/templates/pepule/marks.js, markup from wings.js.
   Everything about how it LOOKS is here, so the same drawing can be a gold
   hairline at a heading and a blush butterfly on a card without the components
   knowing which. */

.pepule-scope .pp-wing-plate {
  fill: none;
  stroke: var(--pp-gold);
  stroke-width: 1.6;
  stroke-linejoin: round;
}
/* A FLAT FILL AND NOT A GRADIENT. An SVG gradient needs a <defs> with an id,
   and this mark is rendered more than once per page — two <defs> with one id is
   a document where the second one silently wins for both. The wing is 76px of a
   phone screen; a ramp across it is a difference nobody can see and one more
   thing to keep in step. */
.pepule-scope .pp-wing-plate.is-filled {
  fill: var(--pp-blush-soft);
  stroke: var(--pp-blush);
  stroke-width: 2;
}
.pepule-scope .pp-wing-vein {
  fill: none;
  stroke: var(--pp-gold);
  stroke-width: 0.9;
  stroke-linecap: round;
  opacity: 0.55;
}
.pepule-scope .pp-wing-plate.is-filled ~ .pp-wing-vein { stroke: var(--pp-blush); opacity: 0.7; }
.pepule-scope .pp-wing-spot { fill: #fffaf2; opacity: 0.9; }

.pepule-scope .pp-body path,
.pepule-scope .pp-body circle { fill: var(--pp-gold); }
.pepule-scope .pp-antenna {
  fill: none;
  stroke: var(--pp-gold);
  stroke-width: 1.4;
  stroke-linecap: round;
}

/* THE BEAT. Two groups at two phases — a hindwing lags a forewing, and a pair
   that moves as one block reads as a paper cut-out being waggled.

   `scaleX` about the thorax and nothing else: a wing seen from above closing
   toward the body foreshortens, it does not rotate in the page. The transform
   is applied INSIDE each wing group so it composes with the mirror on the left
   side rather than fighting it. */
@keyframes pp-beat {
  0%, 100% { transform: scaleX(1); }
  50% { transform: scaleX(0.9); }
}
.pepule-scope .pp-wing {
  transform-origin: 0 0;
  animation: pp-beat 3.4s ease-in-out infinite;
}
.pepule-scope .pp-wing--hind { animation-duration: 3.4s; animation-delay: -0.22s; }

.pepule-scope .pp-rule {
  display: block;
  inline-size: 8.75rem;
  block-size: auto;
  margin-inline: auto;
  overflow: visible;
}
.pepule-scope .pp-rule-line {
  fill: none;
  stroke: var(--pp-gold);
  stroke-width: 0.7;
  stroke-linecap: round;
  opacity: 0.85;
}

/* THE INSECT IN A RULE IS PLANTED AT scale(0.155), AND A STROKE SCALES WITH ITS
   TRANSFORM. Left alone, the 1.6-unit outline arrives as 0.25 of a unit in a
   140-unit box — a stroke so fine it disappears on a 1x screen and moires on a
   2x one. `non-scaling-stroke` takes the width out of the transform entirely, so
   the outline is a real hairline at whatever size the rule is rendered.

   The veins and the spots are not thinned here, they are not DRAWN here: at
   24px they clump into a brown blob, and `simple` on PepuleRule drops them. */
.pepule-scope .pp-rule-mark .pp-wing-plate {
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.pepule-scope .pp-rule-mark .pp-antenna {
  stroke-width: 0.8;
  vector-effect: non-scaling-stroke;
}

.pepule-scope .pp-mark {
  display: block;
  inline-size: 5.5rem;
  block-size: auto;
  margin-inline: auto;
  overflow: visible;
}

/* ══ the gate ═════════════════════════════════════════════════════════════ */

.pepule-scope .pp-gate {
  position: fixed;
  inset: 0;
  z-index: 60;
  overflow: hidden;
  /* svh, NOT dvh: dvh grows as a phone toolbar retracts and shoves the whole
     cover down mid-gesture, which on a press-and-release would move the scene
     under the thumb that is holding it. */
  height: 100svh;
  inline-size: 100%;
  padding: 0;
  border: 0;
  background: var(--pp-marble);
  /* The whole cover is the target, so nothing here may be interpreted as a
     scroll — a pointer stream the browser takes over never delivers its
     `pointerup`, and this cover opens on `pointerup`. */
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
  transition: opacity 760ms ease;
}

.pepule-scope .pp-gate--leaving {
  opacity: 0;
  pointer-events: none;
}

/* THE STAGE IS FIXED-ASPECT, AND THAT IS LOAD-BEARING.
   The butterfly is baked into the photograph, so the ring has to land on the
   photographed butterfly. `background-size: cover` crops by a different amount
   at every viewport, which would put the ring next to it instead of on it.

   CENTRED WITH left/transform, NOT with grid or flex centring. The stage is
   deliberately WIDER than the viewport, and an oversized centred grid item does
   not straddle its container — it overflows toward the inline START, which on
   an RTL page is the right. zerf measured the scene landing 42px off before it
   changed this. A physical left/translateX has no such opinion. */
.pepule-scope .pp-stage {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) scale(1);
  aspect-ratio: 720 / 1280;
  inline-size: max(100vw, calc(100svh * 720 / 1280));
  transition: transform 900ms cubic-bezier(0.22, 0.9, 0.28, 1);
}

/* THE BREATH. While a finger is down the scene draws in very slightly — 1.5%,
   which is under the threshold at which anyone can name what changed and well
   over the one at which they feel it. It is the only thing that distinguishes a
   hold from a tap, and a tap never reaches this state: `held` is behind a 220ms
   timer in PepuleGate. */
.pepule-scope .pp-gate--held .pp-stage { transform: translate(-50%, -50%) scale(1.015); }

.pepule-scope .pp-stage-img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* Tailwind preflight sets `img { max-width: 100% }`, which silently caps any
     width above the container. It bit the hena band for an hour. */
  max-inline-size: none;
  /* OUT OF HIT-TESTING, so the picture can never become a drag source and can
     never swallow the gesture. This is also the half that covers browsers which
     ignore `draggable={false}`. */
  pointer-events: none;
  -webkit-user-drag: none;
}

/* The flight, over the still and on the same box, so the two register exactly.
   Zero opacity until the release — and `pointer-events: none` with it, because
   darga shipped precisely this bug: an invisible <video> over the gate's button
   still ate every tap and the door never opened. */
.pepule-scope .pp-stage-clip {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  max-inline-size: none;
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease;
}
.pepule-scope .pp-gate--flying .pp-stage-clip,
.pepule-scope .pp-gate--leaving .pp-stage-clip { opacity: 1; }

/* THE PERCH — the ring of light over the real butterfly.
   `left`/`top`, NOT logical properties. These are coordinates in a photograph
   and must not flip with text direction: the butterfly does not move when the
   invitation is authored in Badini.

   Measured off rest.webp with a grid overlay — the insect spans x 250-460 and
   y 490-700 of the 720x1280 frame, so its thorax is at 49.3% / 46.5%. */
.pepule-scope .pp-perch {
  position: absolute;
  left: 49.3%;
  top: 46.5%;
  inline-size: 30%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgba(255, 250, 242, 0.34) 0%,
    rgba(255, 250, 242, 0.14) 42%,
    transparent 68%
  );
  animation: pp-perch 3.6s ease-in-out infinite;
}
@keyframes pp-perch {
  0%, 100% { opacity: 0.45; transform: translate(-50%, -50%) scale(0.94); }
  50% { opacity: 1; transform: translate(-50%, -50%) scale(1.06); }
}
/* Under a finger the halo comes up and stops pulsing: the butterfly is
   gathering, not waiting. */
.pepule-scope .pp-gate--held .pp-perch {
  animation: none;
  opacity: 1;
  transform: translate(-50%, -50%) scale(1.14);
  transition: transform 700ms cubic-bezier(0.22, 0.9, 0.28, 1), opacity 300ms ease;
}
.pepule-scope .pp-gate--flying .pp-perch,
.pepule-scope .pp-gate--leaving .pp-perch { opacity: 0; transition: opacity 200ms ease; }

/* THE COPY, AND IT BRINGS ITS OWN GROUND.
   There is no quiet region anywhere on this frame — the 6x6 variance map in the
   header of PepuleGate.js finds 21.3 at its calmest against a mean of 45. So
   this is perde's answer and not gulbaran's: a scrim, feathered so it has no
   edge, dark enough to carry ivory type over blossom and marble alike. */
.pepule-scope .pp-gate-copy {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  padding: 7rem 1.5rem calc(2.6rem + env(safe-area-inset-bottom));
  text-align: center;
  background: linear-gradient(
    to top,
    rgba(20, 34, 44, 0.86) 0%,
    rgba(20, 34, 44, 0.72) 34%,
    rgba(20, 34, 44, 0.34) 66%,
    transparent 100%
  );
  transition: opacity 420ms ease;
}
/* It goes as the butterfly does. Watching a flight past a block of type is
   watching two things. */
.pepule-scope .pp-gate--flying .pp-gate-copy,
.pepule-scope .pp-gate--leaving .pp-gate-copy { opacity: 0; }

.pepule-scope .pp-gate-hi {
  font-size: 0.85rem;
  letter-spacing: normal;
  color: var(--pp-gold-soft);
}

.pepule-scope .pp-gate-names {
  font-size: clamp(2rem, 9vw, 2.9rem);
  line-height: 1.32;
  color: #fffaf2;
  /* The one text-shadow on the page. The scrim carries the type, but the
     names cross the widest part of the frame and a blossom under a descender
     is still a blossom. */
  text-shadow: 0 1px 14px rgba(12, 24, 32, 0.55);
}

/* TWO LINES IN ONE BOX, and only one is ever readable. Both are rendered and
   stacked so the box never changes height — a hint that grew a line when a
   finger went down would shift the couple's names under the guest's own
   thumb. */
/* `inline-size: 100%`, AND WITHOUT IT THE HINT COLLAPSES TO NOTHING. Both lines
   inside are absolutely positioned, so this box has no in-flow content and no
   intrinsic width — and as a centred flex item that resolves to zero, which
   wraps "دەست بدە بۆ کردنەوە" to one word per line and pushes it off the bottom
   of the cover. Screenshotted before it was believed. */
.pepule-scope .pp-gate-hint {
  position: relative;
  display: block;
  inline-size: 100%;
  margin-block-start: 0.35rem;
  min-block-size: 1.5rem;
  font-size: 0.9rem;
  color: rgba(255, 250, 242, 0.9);
}
.pepule-scope .pp-hint {
  position: absolute;
  inset-inline: 0;
  top: 0;
  transition: opacity 260ms ease;
}
.pepule-scope .pp-hint--lift { opacity: 0; }
.pepule-scope .pp-gate--held .pp-hint--tap { opacity: 0; }
.pepule-scope .pp-gate--held .pp-hint--lift { opacity: 1; }

.pepule-scope .pp-gate:focus-visible {
  outline: 3px solid var(--pp-gold-soft);
  outline-offset: -6px;
}

/* The interior, held back until the cover goes. `visibility` and not
   `display`: the hero has to have LAID OUT and resolved its fonts before it is
   uncovered, or the first thing the guest sees after the flight is a reflow. */
.pepule-scope .pp-page {
  opacity: 0;
  visibility: hidden;
  transition: opacity 620ms ease 120ms;
}
.pepule-scope .pp-page.is-open { opacity: 1; visibility: visible; }

/* ══ the hero ═════════════════════════════════════════════════════════════ */

.pepule-scope .pp-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* CENTRED, at the founder's direction 2026-09-07. It was anchored to the TOP
     of the frame, on the measurement in PepuleHero.js: the sky in the top fifth
     is the only part of this picture flat enough to print on unaided, so the
     names went there and took no scrim.

     That was true and it was still wrong. Pinned to the top of a 100svh hero
     the couple's names read as a caption over a landscape — small, high, and
     with the whole picture below them competing. The names are the invitation.
     They go in the middle of the screen, and since the middle of THIS picture
     is the arch (stddev 33-70), they bring their own ground: see
     .pp-hero-copy::before. */
  justify-content: center;
  min-block-size: 100svh;
  padding: 4rem 1.25rem 5rem;
  overflow: hidden;
  isolation: isolate;
}

.pepule-scope .pp-hero-scene {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--pp-marble);
}

.pepule-scope .pp-hero-img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  max-inline-size: none;
  /* THE ONLY MOVEMENT ON THIS SCREEN. 24 seconds, 4%, and it does not start
     until lib/coverOpen says the guest is actually in — a 24-second move
     started under the cover means the guest arrives part-way through one.
     There is no video here; the reasoning is in PepuleHero.js. */
  transform: scale(1);
  transition: transform 24s linear;
}
.pepule-scope .pp-hero.is-live .pp-hero-img { transform: scale(1.04); }

/* One feather, at the foot, and it works the OPPOSITE way from the dark
   templates': this picture is bright and the type is ink, so the wash lifts the
   page colour up over the grass rather than pushing the picture back with
   black. There is NONE at the top — the sky there measures 3.2-15.7 out of 255
   (the map in PepuleHero.js), and a veil over it would be a grey cloud painted
   on clean weather. */
.pepule-scope .pp-hero-veil {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  block-size: 34%;
  background: linear-gradient(to top, var(--pp-marble) 0%, rgba(241, 244, 246, 0.72) 42%, transparent 100%);
}

.pepule-scope .pp-hero-copy {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
  text-align: center;
  max-inline-size: 24rem;
}

/* THE HAZE. The copy's own ground, and the price of moving it off the measured
   quiet sky into the middle of the picture.

   It is the PAGE COLOUR at 94% falling to nothing, not a grey box and not a
   dark scrim: painted in the marble the interior is printed on, it reads as
   light lying over the water rather than as a panel bolted onto a photograph —
   and it keeps the type dark, which is this template's whole arrangement
   (the ink measures 12.04:1 on it, against about 4.9 and wildly variable if
   the names were set straight on the arch).

   Feathered to zero well inside its own box so it has no edge anywhere. An
   edge here would be the ornament-rectangle problem by another route. */
.pepule-scope .pp-hero-copy::before {
  content: "";
  position: absolute;
  /* Overflowing the copy on both axes so the falloff has room to reach zero
     INSIDE its own box. A haze that is still opaque where its box ends has a
     straight edge, and the eye finds a straight edge on a photograph instantly.
     That is exactly what a second, linear layer did here on the first attempt:
     two hard horizontal lines across the middle of the picture and the arch
     wiped out between them. ONE ellipse, nothing else. */
  inset-block: -4.5rem;
  inset-inline: -6rem;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    74% 64% at 50% 50%,
    rgba(226, 234, 241, 0.93) 0%,
    rgba(226, 234, 241, 0.85) 40%,
    rgba(226, 234, 241, 0.44) 68%,
    rgba(226, 234, 241, 0) 88%
  );
}

.pepule-scope .pp-hero-kicker {
  font-size: 0.86rem;
  color: var(--pp-ink-soft);
}

.pepule-scope .pp-hero-names {
  font-size: clamp(2.7rem, 13vw, 4.4rem);
  line-height: 1.26;
  color: var(--pp-ink);
}

.pepule-scope .pp-hero-tag {
  font-size: 1rem;
  color: var(--pp-ink-soft);
  max-inline-size: 20rem;
}

.pepule-scope .pp-hero-rule { margin-block-start: 1rem; inline-size: 9rem; }

.pepule-scope .pp-hero-cue {
  position: absolute;
  inset-inline: 0;
  bottom: calc(1.6rem + env(safe-area-inset-bottom));
  font-size: 0.78rem;
  color: var(--pp-muted);
  text-align: center;
  animation: pp-cue 2.6s ease-in-out infinite;
}
@keyframes pp-cue {
  0%, 100% { transform: translateY(0); opacity: 0.7; }
  50% { transform: translateY(4px); opacity: 1; }
}

/* ══ the body, and the aisle ══════════════════════════════════════════════ */

.pepule-scope .pp-body {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--pp-air);
  padding: var(--pp-air) var(--pp-gut) 5.5rem;
}

/* THE AISLE. Light down the centre of the page, behind everything, carrying the
   white runner the hero ends on into the interior. It is what the guest sees
   between the cards, and it is the whole composition.

   A SOFT RAMP AND NOT A BAND, and this was measured the hard way. The first cut
   was a band with edges at `calc(50% +/- 15rem)` — 30rem wide, which on a
   390px phone resolves to -45px and 435px. The runner covered the entire
   viewport, the marble never showed at all, and the composition simply was not
   there on the only device that matters. A ramp in percentages cannot have that
   failure at any width: the middle is always lit and the outer fifths are
   always the marble.

   `position: fixed` and not a background on .pp-body: a background would scroll
   with the content and read as a stripe of paper, where this is meant to be the
   FLOOR the cards are lying on. Fixed also means it paints once.
   `background-attachment: fixed` would do the same thing and is a repaint per
   frame on iOS, which is why that property is effectively unusable on a
   phone. */
.pepule-scope .pp-aisle {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.12) 17%,
    rgba(255, 255, 255, 0.34) 50%,
    rgba(255, 255, 255, 0.12) 83%,
    rgba(255, 255, 255, 0) 100%
  );
}

/* ══ a leaf of stationery ═════════════════════════════════════════════════ */

.pepule-scope .pp-leaf {
  position: relative;
  inline-size: 100%;
  max-inline-size: var(--pp-card-w);
  padding: 3.4rem var(--pp-pad) 3.6rem;
  border-radius: var(--pp-r);
  background: var(--pp-paper);
  box-shadow: var(--pp-lift);
  text-align: center;
}

/* THE PRINTED EDGE. A hairline of the seal's gold, inset by the same margin a
   real card's border is, and drawn with a pseudo-element so it does not fight
   the one shadow every leaf wears. Without it a leaf is a white rectangle,
   which on a page of white rectangles is an app panel — the founder's verdict
   on the first cut, and this is half the answer to it. The other half is that
   the page underneath went a shade deeper. */
.pepule-scope .pp-leaf::before {
  content: "";
  position: absolute;
  inset: 0.6rem;
  border: 1px solid rgba(169, 127, 69, 0.26);
  border-radius: 1px;
  pointer-events: none;
}

.pepule-scope .pp-leaf-label {
  font-size: var(--pp-t-label);
  color: var(--pp-muted);
  margin-block-end: 2rem;
}

.pepule-scope .pp-leaf-mark { inline-size: 5.5rem; margin-block-end: 1.6rem; }

/* THE CREASE. A hairline with the light catching one side of it — a card that
   carries three things is folded, not cut into three. It bleeds to the card's
   own inner margin on both sides, which is what makes it a fold in the paper
   rather than a rule printed on it. */
.pepule-scope .pp-fold {
  display: block;
  block-size: 2px;
  margin: var(--pp-air-in) calc(var(--pp-pad) * -1);
  background: linear-gradient(to bottom, var(--pp-line-soft) 0 1px, rgba(255, 255, 255, 0.9) 1px 2px);
}

.pepule-scope .pp-part { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; }
/* When the day is and how long until it are one thought, and a card cannot
   change its ground to say so — it says it with distance instead. */
.pepule-scope .pp-part--tight { margin-block-start: -1.6rem; }

.pepule-scope .pp-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
}

.pepule-scope .pp-h2 {
  font-size: var(--pp-t-head);
  line-height: 1.45;
  color: var(--pp-ink);
}

.pepule-scope .pp-lede {
  font-size: 0.92rem;
  color: var(--pp-muted);
  max-inline-size: 26rem;
}

/* ── the invitation leaf ─────────────────────────────────────────────── */

.pepule-scope .pp-bismillah,
.pepule-scope .pp-verse {
  font-size: 1.05rem;
  line-height: 2;
  color: var(--pp-ink-soft);
}
.pepule-scope .pp-verse { margin-block-start: 0.6rem; color: var(--pp-muted); }

.pepule-scope .pp-names {
  margin-block: 2.2rem 1.5rem;
  font-size: var(--pp-t-display);
  line-height: 1.3;
  color: var(--pp-lake-deep);
}

.pepule-scope .pp-invite-msg { color: var(--pp-ink-soft); }

.pepule-scope .pp-hosts {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-block-start: 2.2rem;
  list-style: none;
  padding: 0;
}
.pepule-scope .pp-host { display: flex; flex-direction: column; gap: 0.15rem; }
.pepule-scope .pp-host-lbl { font-size: var(--pp-t-label); color: var(--pp-muted); }
.pepule-scope .pp-host-name { font-size: 1.05rem; color: var(--pp-ink); }
.pepule-scope .pp-host-name--solo { margin-block-start: 1.6rem; }

/* ══ a plate — not stationery, straight on the marble ═════════════════════ */

.pepule-scope .pp-plate {
  inline-size: 100%;
  max-inline-size: var(--pp-card-w);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.9rem;
  text-align: center;
}
.pepule-scope .pp-plate--wide { max-inline-size: 40rem; }

/* ══ the shared components, in this template's clothes ════════════════════
   Class names come from components/templates/shared/* via `prefix="pp"`. What
   follows is only what those components leave to the template: they ship their
   own structure and their own accessibility, and rebuilding either here would
   be the fastest way to lose both. */

/* ── the date plate ──────────────────────────────────────────────────────
   NO MONTH GRID. This drew one until 2026-09-07 (shared/CalendarLeaf) and the
   founder's direction was to make it like darga's, which has none — the whole
   argument is at the top of PepuleDate.js. All the .pp-cal-* rules that used to
   live here went with it: forty-odd lines of grid, weekday heads and cell
   states that nothing renders any more, and a stylesheet is downloaded in full
   by every guest. */

.pepule-scope .pp-date {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* THE GAP THE FOUNDER ASKED FOR. The button used to sit inside the same box
     as the date; it is a separate thought and it stands off the plate by the
     page's own interval. */
  gap: var(--pp-air-in);
  inline-size: 100%;
}

/* The plate: a panel of the page's own marble pressed into the white leaf.
   PRESSED, not lifted — the elevation rule at the top of this file. */
.pepule-scope .pp-date-plate {
  position: relative;
  inline-size: 100%;
  max-inline-size: 19rem;
  padding: 1.9rem 1.5rem 2rem;
  border-radius: var(--pp-r);
  background: var(--pp-marble);
  box-shadow: inset 0 0 0 1px var(--pp-line-soft);
  text-align: center;
}

.pepule-scope .pp-date-band {
  font-size: 1.02rem;
  line-height: 1.4;
  color: var(--pp-lake-deep);
}
.pepule-scope .pp-date-year { color: var(--pp-muted); font-size: 0.82rem; }

.pepule-scope .pp-date-wd {
  margin-block-start: 0.35rem;
  font-size: var(--pp-t-label);
  color: var(--pp-muted);
}

/* THE DAY. Everything else on this plate is a caption to it, so it is the one
   numeral on the page allowed to be this size. Tabular figures because a 1 and
   a 7 at 4rem should not shift the whole plate's balance between two events. */
.pepule-scope .pp-date-num {
  font-size: 4.4rem;
  line-height: 1.08;
  color: var(--pp-lake);
  font-variant-numeric: tabular-nums lining;
}

/* The page's own ornament, doing structural work — the same figure every
   heading carries. Smaller here: it is furniture on a plate, not a heading. */
.pepule-scope .pp-date-rule { inline-size: 7rem; margin-block: 0.5rem 0.7rem; }

.pepule-scope .pp-date-time { font-size: 0.92rem; color: var(--pp-ink-soft); }
.pepule-scope .pp-date-clock { font-variant-numeric: tabular-nums; }

/* The action. It is a BUTTON and not a chip: a chip is one of several things a
   guest might do, and this is the only thing this card asks of them. */
.pepule-scope .pp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  min-block-size: 2.9rem;
  padding: 0.7rem 1.6rem;
  border: 0;
  border-radius: 999px;
  background: var(--pp-lake);
  color: #fbfcfd; /* 7.14:1 */
  font: inherit;
  font-size: 0.92rem;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
  transition: background 160ms ease, transform 120ms ease;
}
.pepule-scope .pp-btn:hover { background: var(--pp-lake-deep); }
.pepule-scope .pp-btn:active { transform: scale(0.97); }
.pepule-scope .pp-btn:focus-visible { outline: 2px solid var(--pp-lake); outline-offset: 3px; }
.pepule-scope .pp-btn-ico { display: inline-flex; flex: none; }


/* ── chips ───────────────────────────────────────────────────────────── */

.pepule-scope .pp-acts { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; }

.pepule-scope .pp-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.6rem 1.15rem;
  border-radius: 999px;
  border: 1px solid var(--pp-line);
  background: var(--pp-paper);
  color: var(--pp-lake-deep);
  font-size: 0.86rem;
  text-decoration: none;
  transition: background 160ms ease, border-color 160ms ease;
  touch-action: manipulation;
}
.pepule-scope .pp-chip:hover { background: var(--pp-marble-deep); border-color: var(--pp-lake); }
.pepule-scope .pp-chip:focus-visible { outline: 2px solid var(--pp-lake); outline-offset: 2px; }
.pepule-scope .pp-chip--full { inline-size: 100%; justify-content: center; }
.pepule-scope .pp-chip-ico { inline-size: 1rem; block-size: 1rem; flex: none; }

/* ── the countdown ───────────────────────────────────────────────────── */

.pepule-scope .pp-cd { display: flex; justify-content: center; gap: 0.55rem; }
.pepule-scope .pp-cd-unit {
  min-inline-size: 4rem;
  padding: 0.75rem 0.4rem;
  border-radius: var(--pp-r);
  background: var(--pp-marble);
  box-shadow: inset 0 0 0 1px var(--pp-line-soft);
  text-align: center;
}
.pepule-scope .pp-cd-num {
  display: block;
  font-size: 1.5rem;
  line-height: 1.2;
  color: var(--pp-lake-deep);
  font-variant-numeric: tabular-nums;
}
.pepule-scope .pp-cd-lbl { display: block; font-size: 0.68rem; color: var(--pp-muted); }
.pepule-scope .pp-arrived { color: var(--pp-lake-deep); font-size: 1.05rem; }

/* ── the venue ───────────────────────────────────────────────────────── */

.pepule-scope .pp-venue { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.pepule-scope .pp-venue-name { font-size: 1.15rem; color: var(--pp-ink); }
.pepule-scope .pp-venue-addr { font-size: 0.9rem; color: var(--pp-muted); }

.pepule-scope .pp-map {
  position: relative;
  inline-size: 100%;
  margin-block-start: 1rem;
  border-radius: var(--pp-r);
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--pp-line-soft);
  background: var(--pp-marble);
}
.pepule-scope .pp-map-frame { display: block; inline-size: 100%; aspect-ratio: 16 / 10; border: 0; }
.pepule-scope .pp-map-hit {
  display: grid;
  place-items: center;
  gap: 0.5rem;
  inline-size: 100%;
  aspect-ratio: 16 / 10;
  border: 0;
  background: none;
  color: var(--pp-lake-deep);
  cursor: pointer;
}
.pepule-scope .pp-map-pin { inline-size: 1.4rem; block-size: 1.4rem; color: var(--pp-gold); }
.pepule-scope .pp-map-lbl { font-size: 0.86rem; }

/* ── the programme ─────────────────────────────────────────────────────
   IT LIGHTS AS THE GUEST SCROLLS, AND IT WAS NOT DOING SO. shared/Timeline has
   driven this since 2026-08-19: it toggles `.is-on` on each row as the row
   crosses the reading line, and it sets --prog-p on the wrapper — a 0-to-1
   fraction for how far down the evening the guest has read. Every other
   template fills a rail with that number.

   This one never declared the rail. So the only thing that moved was a 7px dot
   changing colour, which on a page a guest is scrolling past is nothing at all,
   and the founder's verdict was that the programme was not interactive. It was
   interactive; it had nothing to show for it.

   Three things move now, all of them REVERSIBLE — a one-shot reveal looks
   identical on the way down and dead on the way back up, which is the whole
   reason Timeline is written as a scroll handler rather than an observer:

     the rail    fills from the first node to the last, scaleY(--prog-p)
     the node    fills and grows a ring
     the row     lifts from muted to ink, and its hour goes gold

   Under `prefers-reduced-motion` Timeline sets every row .is-on and --prog-p to
   1 on mount, so the evening arrives already lit and nothing moves. */

.pepule-scope .pp-prog-wrap { inline-size: 100%; max-inline-size: 23rem; }

/* The rail is on the LIST, not on each row, so it can run continuously from the
   first node to the last however tall the rows happen to be. Two layers: the
   spent track, and the fill scaled over it. */
.pepule-scope .pp-prog {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}
.pepule-scope .pp-prog::before,
.pepule-scope .pp-prog::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  /* Top and bottom stop level with the FIRST and LAST nodes rather than the
     list's own edges, or the rail starts a row-height above the first dot. */
  top: 1.35rem;
  bottom: 1.35rem;
  inline-size: 2px;
  border-radius: 2px;
}
.pepule-scope .pp-prog::before { background: var(--pp-line-soft); }
.pepule-scope .pp-prog::after {
  background: var(--pp-gold);
  transform-origin: top;
  transform: scaleY(var(--prog-p, 0));
}

.pepule-scope .pp-prog-row {
  position: relative;
  display: grid;
  grid-template-columns: 5.4rem 1fr;
  gap: 0.85rem;
  align-items: baseline;
  padding: 0.95rem 0;
  /* Drawn on the INLINE-START edge so it follows the text direction. Unlike the
     gate's coordinates, this one has no photograph to register against. */
  padding-inline-start: 1.5rem;
  text-align: start;
  transition: opacity 320ms ease;
  /* The entrance stagger, which is a different thing from .is-on. */
  transition-delay: calc(var(--i, 0) * 60ms);
  opacity: 0.55;
}
.pepule-scope .pp-prog-row.is-on { opacity: 1; }

/* The node. It sits on the rail, level with the hour. */
.pepule-scope .pp-prog-row::after {
  content: "";
  position: absolute;
  inset-inline-start: -3px;
  top: 1.35rem;
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
  background: var(--pp-marble);
  box-shadow: inset 0 0 0 1.5px var(--pp-gold);
  transition: background 320ms ease, box-shadow 320ms ease, transform 320ms ease;
}
.pepule-scope .pp-prog-row.is-on::after {
  background: var(--pp-gold);
  transform: scale(1.25);
  /* The ring is the page colour, so the node reads as a bead ON the rail rather
     than as a dot beside it. */
  box-shadow: 0 0 0 3px var(--pp-paper), inset 0 0 0 1.5px var(--pp-gold);
}

.pepule-scope .pp-prog-time {
  font-size: 0.84rem;
  color: var(--pp-muted);
  font-variant-numeric: tabular-nums;
  transition: color 320ms ease;
}
.pepule-scope .pp-prog-row.is-on .pp-prog-time { color: var(--pp-gold); }

.pepule-scope .pp-prog-title { color: var(--pp-ink-soft); transition: color 320ms ease; }
.pepule-scope .pp-prog-row.is-on .pp-prog-title { color: var(--pp-ink); }

/* ── the gallery: a pile of prints ────────────────────────────────────────
   components/templates/pepule/PepuleGallery.js. NOT shared/Slider — the founder
   asked for a gallery unlike the filmstrip every other template uses, and none
   of the .pp-slide/.pp-lb/.pp-dot rules that used to be here survived. They
   were forty lines describing a scroll-snap track and a lightbox that this
   template no longer renders, and a stylesheet is downloaded in full by every
   guest.

   A REAL PRINT HAS A WHITE MARGIN. That border is not decoration; it is what
   separates a photograph lying on marble from a photograph pasted onto it, and
   it is the only thing making the pile read as objects rather than as layers. */

.pepule-scope .pp-pile-wrap {
  inline-size: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
}

/* The table the pile lies on. Fixed aspect, so the section does not resize as
   prints are dealt — a gallery that changes height on every tap shifts the rest
   of the page under the guest's thumb. */
.pepule-scope .pp-pile {
  position: relative;
  /* CONTAINS ITS OWN STACK. The prints below carry z-index 1-10 and a dealt one
     carries 20, and without this they are not the pile's business at all —
     they resolve against the page's root stack and paint straight through the
     front butterfly layer at 4. Isolating puts the whole pile back on one
     rung, where a section of a page belongs. */
  isolation: isolate;
  inline-size: 100%;
  max-inline-size: 21rem;
  aspect-ratio: 4 / 5;
  /* The pile is stacked with transforms, and the corners of the cards behind
     the top one reach outside the box. */
  overflow: visible;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

/* One print. `--depth` is 0 for the top card and counts backwards into the
   pile; everything below is derived from it, so the fan is described once
   rather than written out per card. */
.pepule-scope .pp-print {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0.55rem 0.55rem 2.4rem;
  border-radius: 2px;
  background: var(--pp-paper);
  box-shadow: var(--pp-lift);
  /* Deeper cards sit lower in the stack, step back, and tip a little further.
     EVERY VALUE ARRIVES FROM THE COMPONENT ALREADY CARRYING ITS UNIT — --tilt
     and --drot in deg, --dx in px — because nothing in a transform may do
     arithmetic across two units. The first cut computed the tilt here with
     `mod()` and added `var(--drag) * 0.035` to it inside `rotate()`: a length
     plus a degree, which made the whole declaration invalid, so every print
     rendered at `transform: none` and the pile stacked dead flat. A custom
     property is substituted rather than computed, so the unit error announced
     itself nowhere at all. See the note in PepuleGallery.js. */
  transform:
    translate3d(var(--dx, 0px), calc(var(--depth) * 7px), 0)
    scale(calc(1 - var(--depth) * 0.035))
    rotate(calc(var(--tilt, 0deg) + var(--drot, 0deg)));
  z-index: calc(10 - var(--depth));
  transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 260ms ease;
  cursor: grab;
}
.pepule-scope .pp-print:not(:first-of-type) { cursor: default; }

/* While a finger is down the card follows it with no transition, or every
   pointermove would be chasing a 260ms ease and the drag would feel like glue.
   A CLASS and not `[style*="--drag"]`: matching on the serialised style string
   is a selector that breaks the moment another custom property is added. */
.pepule-scope .pp-print.is-dragging { transition: none; cursor: grabbing; }

/* Dealt. It leaves toward the inline END and turns as it goes, which is the arc
   a card actually takes off a pile when it is flicked. `--pp-deal-x` is a
   physical direction on purpose: this is an object being thrown, not text being
   laid out, and it must not reverse with the writing direction. */
.pepule-scope .pp-print.is-flying {
  transform: translate3d(120%, -14%, 0) rotate(16deg) scale(0.94);
  opacity: 0;
  transition: transform 420ms cubic-bezier(0.4, 0, 0.7, 0.4), opacity 420ms ease-in;
  pointer-events: none;
  z-index: 20;
}

.pepule-scope .pp-print-img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  border-radius: 1px;
  /* A PHOTOGRAPH HAS A RECTANGLE and no blend hides it, so it is given a hairline
     rather than being feathered into the paper. */
  box-shadow: inset 0 0 0 1px rgba(30, 42, 51, 0.06);
  background: var(--pp-marble);
  -webkit-user-drag: none;
}

/* Everything past the fourth card. Rendered so it never remounts and never
   re-decodes, painted nowhere. */
.pepule-scope .pp-print-hidden { display: none; }

.pepule-scope .pp-pile-hint {
  font-size: var(--pp-t-label);
  color: var(--pp-muted);
  min-block-size: 1.2rem;
}

.pepule-scope .pp-pile-acts {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.pepule-scope .pp-pile-btn {
  display: grid;
  place-items: center;
  inline-size: 2.6rem;
  block-size: 2.6rem;
  border: 1px solid var(--pp-line);
  border-radius: 50%;
  background: var(--pp-paper);
  color: var(--pp-lake-deep);
  cursor: pointer;
  touch-action: manipulation;
  transition: background 160ms ease, border-color 160ms ease, transform 120ms ease;
}
.pepule-scope .pp-pile-btn:hover { background: var(--pp-marble-deep); border-color: var(--pp-lake); }
.pepule-scope .pp-pile-btn:active { transform: scale(0.94); }
.pepule-scope .pp-pile-btn:focus-visible { outline: 2px solid var(--pp-lake); outline-offset: 3px; }

.pepule-scope .pp-pile-count {
  min-inline-size: 6.5rem;
  text-align: center;
  font-size: 0.84rem;
  color: var(--pp-muted);
  font-variant-numeric: tabular-nums;
}

/* ── the reply ───────────────────────────────────────────────────────── */

/* ONE LINE, AND IT IS THE ONLY THING THIS TEMPLATE DOES TO THE FORM.
   On a leaf, --tpl-surface IS the paper the form is printed on, so fields
   painted with it would vanish. They take the pressed marble instead. */
.pepule-scope .pp-rsvp { inline-size: 100%; --tpl-surface: var(--pp-marble); }

.pepule-scope .pp-notes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.pepule-scope .pp-note {
  position: relative;
  padding-inline-start: 1.4rem;
  text-align: start;
  color: var(--pp-ink-soft);
  font-size: 0.94rem;
}
.pepule-scope .pp-note::before {
  content: "";
  position: absolute;
  inset-inline-start: 0.3rem;
  top: 0.85em;
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: var(--pp-gold);
}

.pepule-scope .pp-contact { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.pepule-scope .pp-contact-name { font-size: 1.1rem; color: var(--pp-ink); }
.pepule-scope .pp-contact-role { font-size: 0.85rem; color: var(--pp-muted); }

/* ── the wishes ──────────────────────────────────────────────────────── */

.pepule-scope .pp-wishes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1rem; inline-size: 100%; }
.pepule-scope .pp-wish {
  position: relative;
  padding: 1.25rem 1.35rem 1.1rem;
  border-radius: var(--pp-r);
  background: var(--pp-paper);
  box-shadow: var(--pp-lift-sm);
  text-align: start;
}
/* A small blush dot at the head of each note — the wax seal from the
   photograph, at the one size where repeating it is a texture rather than a
   stamp. */
.pepule-scope .pp-wish-dot {
  position: absolute;
  inset-inline-start: 1.35rem;
  top: -5px;
  inline-size: 10px;
  block-size: 10px;
  border-radius: 50%;
  background: var(--pp-blush);
}
.pepule-scope .pp-wish-msg { color: var(--pp-ink-soft); font-size: 0.95rem; }
.pepule-scope .pp-wish-name { margin-block-start: 0.5rem; font-size: 0.82rem; color: var(--pp-muted); }

/* ══ the drifting butterflies ═════════════════════════════════════════════
   components/templates/pepule/PepuleDrift.js. It was shared/Motes drifting
   three abstract blobs; the founder asked for butterflies, in darga's terms.

   FIXED, NOT ABSOLUTE, which is what makes the count mean anything: scattered
   down an eight-screen page these distribute evenly, so the number you can SEE
   would be total / 8. darga measured that at thirteen leaves and two on screen.
   Fixed, the sixteen ARE the sixteen you are looking at.

   TWO LAYERS, one behind the stationery and one in front of it. The component
   builds both and the only thing that separates them here is z-index and a
   shadow; everything else about them — size, speed, opacity — is rolled per
   plane in PepuleDrift.js, where the reasoning for each range is written.

   Nothing is declared here for reduced motion: PepuleDrift returns null under
   `reduce`, so there is no element to hide. That is stricter than a
   `display: none`, which would still have fetched four pictures in order to
   hide them. */

.pepule-scope .pp-drift {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  /* HIDDEN UNLESS IT IS RUNNING, and this is not the same statement as the
     pause below. A paused animation still PAINTS: each element sits frozen at
     whatever height its negative delay gave it. That went unnoticed for as
     long as every butterfly was behind an opaque hero photograph, and it stops
     being invisible the moment one of these layers is in front of the page. */
  opacity: 0;
  transition: opacity 520ms ease;
}
.pepule-scope .pp-drift.is-running { opacity: 1; }

/* BEHIND THE STATIONERY. Above the aisle (-1) and under every card, which is
   where a butterfly seen through the gap between two leaves belongs. */
.pepule-scope .pp-drift--back { z-index: 0; }

/* IN FRONT OF IT, at the founder's direction 2026-09-08: *I want the
   butterflies to go over the white parts, the sections also — I mean the
   texts.*
   4 clears the cards (auto) and the pile's own internal stack, and stays well
   under the music button at 40 and the cover at 60 — a decoration that can
   cover a control is a bug, however pretty. */
.pepule-scope .pp-drift--front { z-index: 4; }

/* THE SHADOW IS THE WHOLE TRICK. Over bare marble a cut-out reads as a
   butterfly either way; over a white card it reads as something PRINTED on the
   card unless it casts something. One soft shadow, offset down the way a light
   above the table would throw it, and the same shape is suddenly an object
   flying a few centimetres over the paper. Only the front plane pays for it. */
.pepule-scope .pp-drift--front .pp-drift-el {
  filter: drop-shadow(0 3px 4px rgba(30, 42, 51, 0.22));
}

/* THE FALLER. It carries the drift down the glass and nothing else — the beat
   is on the two children, so neither animation has to know the other's phase.
   The box is the WIDE pose's aspect; the narrow pose is centred inside it. */
.pepule-scope .pp-drift-el {
  position: absolute;
  top: -10vh;
  display: block;
  aspect-ratio: 240 / 151;
  opacity: 0;
  /* Paused until the interior is on screen — a hidden infinite animation is not
     throttled just because you cannot see it. The class is toggled by the
     component's own observer, and it must reach the CHILDREN too. */
  animation: pp-flit var(--pp-dur, 32s) linear var(--pp-delay, 0s) infinite paused;
  will-change: transform, opacity;
}
.pepule-scope .pp-drift.is-running .pp-drift-el,
.pepule-scope .pp-drift.is-running .pp-pose { animation-play-state: running; }

/* THE BEAT — two poses cross-fading. See the header of PepuleDrift.js for why
   this is two images and not a filmstrip.

   Both poses are 151 tall and centred on the same body, so the thorax does not
   move between them; only the wings do. `inset-inline: 0; margin-inline: auto`
   rather than a left/translate pair, because that combination resolves to
   `right: 50%` on this RTL page and shifts the whole element a full width off
   centre — the trap ADDING_A_TEMPLATE.md spells out. */
.pepule-scope .pp-pose {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  margin-inline: auto;
  block-size: auto;
  /* Tailwind's preflight sets `img { max-width: 100% }`, which silently caps
     any width above the container. It bit the hena band for an hour. */
  max-inline-size: none;
  animation: pp-beat-open var(--pp-beat, 1.2s) ease-in-out infinite paused;
}
.pepule-scope .pp-pose--open { inline-size: 100%; }
/* 139 / 240 — the narrow pose's true proportion, so it is never stretched. */
.pepule-scope .pp-pose--shut {
  inline-size: 57.92%;
  animation-name: pp-beat-shut;
}

/* The pair never both fade to nothing at once, or the butterfly blinks. They
   cross at 50% with both partly present, which is what reads as a wing moving
   THROUGH a position rather than as two pictures alternating. */
@keyframes pp-beat-open {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
@keyframes pp-beat-shut {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}

/* THE PEAK ARRIVES FROM THE COMPONENT, per element and per plane, because the
   two planes are looked at against different grounds: the back plane is seen
   against bare marble, where darga's ivory-on-ivory ceiling would be a smudge,
   and the front plane is seen against white paper WITH WORDS ON IT, where the
   same number is a sticker. 0.34-0.62 behind, 0.30-0.48 in front.

   The ramp in and out is what keeps either from ever appearing or vanishing on
   screen: at 0% and 100% the element is off the glass anyway, so the fade only
   ever happens past the edge. */
@keyframes pp-flit {
  0% { transform: translate3d(0, 0, 0) rotate(0deg); opacity: 0; }
  12% { opacity: var(--pp-do, 0.28); }
  84% { opacity: var(--pp-do, 0.28); }
  100% {
    transform: translate3d(var(--pp-dx, 40px), var(--pp-dy, 116vh), 0)
      rotate(var(--pp-dr, 20deg));
    opacity: 0;
  }
}

/* ══ reveals ══════════════════════════════════════════════════════════════ */

.pepule-scope .pp-reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 700ms ease, transform 700ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.pepule-scope .pp-reveal.is-visible { opacity: 1; transform: none; }
/* Without JS nothing ever adds `.is-visible`, so the whole page would be
   invisible. `.js` is set on <html> by the app shell; this is the belt. */
html:not(.js) .pepule-scope .pp-reveal { opacity: 1; transform: none; }

/* ══ the closing ══════════════════════════════════════════════════════════
   The one place the page goes dark, and the only one. It is the lake at the end
   of the aisle, and it carries the second and last of the page's full-size
   butterflies — leaving, which is what the guest watched it do to get in. */

.pepule-scope .pp-closing {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  padding: 5rem 1.5rem calc(5.5rem + env(safe-area-inset-bottom));
  text-align: center;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(44, 91, 118, 0.55) 0%, transparent 62%),
    linear-gradient(to bottom, var(--pp-lake-deep) 0%, #16323f 100%);
  color: var(--pp-gold-soft);
}

/* The gold line drawing on a dark ground — the one place the unfilled mark is
   large. It rises as it beats, which is the whole animation. */
.pepule-scope .pp-closing-mark { inline-size: 6.5rem; animation: pp-leave 7s ease-in-out infinite; }
@keyframes pp-leave {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-10px); }
}
.pepule-scope .pp-closing .pp-wing-plate,
.pepule-scope .pp-closing .pp-wing-vein,
.pepule-scope .pp-closing .pp-antenna { stroke: var(--pp-gold-soft); }
.pepule-scope .pp-closing .pp-body path,
.pepule-scope .pp-closing .pp-body circle { fill: var(--pp-gold-soft); }

.pepule-scope .pp-closing-msg { font-size: 1.15rem; line-height: 2; max-inline-size: 26rem; }
.pepule-scope .pp-hashtag { font-size: 0.9rem; color: rgba(230, 207, 164, 0.72); }

/* ══ wider than a phone ═══════════════════════════════════════════════════ */

@media (min-width: 48rem) {
  .pepule-scope {
    --pp-t-display: 4.1rem;
    --pp-t-head: 2.05rem;
    --pp-pad: 3rem;
    --pp-air: 8rem;
    --pp-air-in: 4rem;
  }
  /* The pile gets more table on a wider screen; the print's own proportion is
     fixed, so this is the only thing the breakpoint has to say about it. */
  .pepule-scope .pp-pile { max-inline-size: 25rem; }
}

/* ══ reduced motion ═══════════════════════════════════════════════════════
   NOT the same product with the animation switched off. Where a mechanic is
   carried entirely by movement, the mechanic goes too — a guest tapping into
   silence is worse than a guest who was never offered the trick.

     the flight        does not play at all (PepuleGate returns early); the
                       cover fades and the page is simply there.
     the perch ring    removed. The hint line carries the affordance alone,
                       which is why it is a sentence and not an icon.
     the wing-beat     stopped, everywhere. The insect is a drawing.
     the butterflies   never built at all — PepuleDrift returns null under
                       `reduce`, so there is no element here to hide. Stricter
                       than a `display: none`, which would still have
                       constructed eleven SVGs in order to hide them.
     the programme     arrives already lit: Timeline sets every row `.is-on`
                       and --prog-p to 1 on mount, so the rail is full and
                       nothing travels. The information is identical; only the
                       journey through it is gone.
     the hero's scale  never starts (PepuleHero returns early).
     the reveals       shown immediately, by ScrollReveal itself. */
@media (prefers-reduced-motion: reduce) {
  .pepule-scope .pp-perch { display: none; }
  .pepule-scope .pp-wing,
  .pepule-scope .pp-closing-mark,
  .pepule-scope .pp-hero-cue { animation: none; }
  .pepule-scope .pp-stage,
  .pepule-scope .pp-hero-img { transition: none; }
  .pepule-scope .pp-reveal { opacity: 1; transform: none; transition: none; }
  /* The rail and the nodes still SHOW their end state — set by Timeline — they
     just do not travel to it. */
  .pepule-scope .pp-prog-row,
  .pepule-scope .pp-prog-row::after,
  .pepule-scope .pp-prog-time,
  .pepule-scope .pp-prog-title,
  .pepule-scope .pp-btn { transition: none; }

  /* THE PILE KEEPS ITS PILE. The cards behind the top one still step back and
     still tip, because a stack of tilted prints is the only thing saying this
     is a pile rather than one photograph — and it says it without moving. What
     goes is the travel: PepuleGallery advances instantly instead of flying the
     card away, so a tap is a swap rather than a tap into 420ms of nothing. */
  .pepule-scope .pp-print,
  .pepule-scope .pp-pile-btn { transition: none; }
}
