/* 💌✨ THE ARRIVAL — the card arrives sealed (docs/plans/CARD-STYLES-2026-09-27.md §6; components/card-styles/Arrival.tsx).
   Every selector starts at the Arrival's OWN root, [data-arrival] (the sleeve and its burst carry it), and reaches only
   its own .dm-arrival* boxes: it can never reach the card, its sheets or its money. It rides over every style AND the
   classic card, and reads only the theme's tokens, so every shop's letter is in that shop's paper and wax.

   THE CLOCK — one number, --arr-pop (set by the component from POP_MS; 1,800 ms): the moment the seal lets go. Nothing
   moves until [data-go]: the letter is ready to be read (its face loaded, its seal and stamp decoded), or it never plays.
     0 → 650 ms ....... it lands: the letter settles onto the screen, pivoting on the seal.
     … → 1,420 ms ..... it holds still, to be read ("with love from", the shop, "for", the dog, the day's stamp); a
                        sheen crosses the wax once (800 → 1,400).
     1,420 → 1,570 .... light runs along the four seams into the seal (a soft 150 ms bloom); the seal's rim glows.
     1,580 → 1,800 .... the seal is pressed down into the paper (the paper dimples) and springs up.
     pop .............. it breaks: two rendered halves and three chips, lit from inside; warm light pours out of the
                        crack (additive, so it brightens every paper, never muddies it).
     pop → pop + 640 .. the flaps spring open (the envelope's lining shows along the seams), then swing away, their
                        shadows crossing the card; the words go with their flaps; the lining clears onto the card.
     pop + 110 ........ the burst leaves the crack, sideways and down (the component), off the face.
     pop + 300 ........ the card's own entrance begins (the component holds it until now).
     pop + 600 ........ the sleeve is gone (visibility: hidden). The burst has faded by pop + 900.
   Nothing loops. Every curve is a cubic-bezier. Reduced motion ⇒ no sleeve at all. No script ⇒ it leaves on its own. */

/* The ONE face, and the one the letter blocks on (its words are never read in a stand-in): Instrument Serif italic, cut
   to Latin + Latin-1 + the typographer's marks, unhinted (14 KB, was the kit's 22 KB). The shop's name is the app's own
   Nunito, already on the page. */
@font-face { font-family: "CS Arrival Instrument"; src: url("/card-styles/arrival/instrument-italic.woff2") format("woff2"); font-style: italic; font-display: block; }

/* ⛑️ FAIL-SAFE. The sleeve and its burst are rendered `display: none` inline: only this sheet shows them, and only for a
   phone that has not asked for less motion. A sheet that never arrives (a redirect, a 404, a slow network) shows
   NOTHING, never bare words over the card. */
@media (prefers-reduced-motion: no-preference) {
  [data-arrival].dm-arrival, [data-arrival].dm-arrival__burst { display: block !important; }
}
@media (prefers-reduced-motion: reduce) {
  [data-arrival][data-arrival] { display: none !important; animation: none !important; }
}

[data-arrival].dm-arrival {
  /* Where the flaps meet and the seal sits: a touch above the middle, where the eye rests on a phone. (The seams' bloom
     below aims at 46 / 54 of the height: keep them with this number.) */
  --arr-cy: 46%;
  --arr-pop: 1800ms;
  --arr-ease: var(--cs-ease, cubic-bezier(.16, 1, .3, 1));
  /* THE PAPER is the theme's PANEL (§6: "a sleeve in the theme's panel colours"): the brand's own big tile, deep where
     the words sit, lit toward --panel on the flaps that face the light. A pale brand's panel is pale with a dark
     --on-panel, so its letter is pale with dark words: the trio already knows. */
  --arr-paper: var(--panel-deep, #0e5b68);
  --arr-paper-lit: color-mix(in oklab, var(--panel, #157e8e) 58%, var(--panel-deep, #0e5b68));
  /* The words: the panel's own ink, warmed a breath toward the house gold (a paper white, not a screen white). */
  --arr-ink: color-mix(in oklab, var(--on-panel, #fff) 94%, var(--accent2, #f6c66b));
  --arr-quiet: color-mix(in oklab, var(--on-panel, #fff) 90%, var(--panel-deep, #0e5b68));
  --arr-name: var(--arr-ink);
  /* THE WAX is the theme's ACCENT — a shop's SECOND brand colour (so a seasonal theme's season: Halloween's pumpkin on
     its indigo letter, Christmas's red on green) — taken a fifth of the way toward the letter's own ink. The ink is the
     colour the paper is built to be read against, so this pushes the wax AWAY from the paper whichever way the paper
     goes: lighter on a deep envelope, deeper on a pale one. Measured as rendered (the seal's mean against the paper
     around it): 3.35–5.7:1 on the six lab themes (round 2: 2.0–2.9). */
  --arr-wax: color-mix(in oklch, var(--accent, #ff7a59) 80%, var(--arr-ink));
  /* The light inside the letter: warm white, ADDED to what it falls on (plus-lighter): it brightens rose, teal or navy
     toward light, never mixes them to caramel or olive. */
  --arr-flare: rgb(255 246 230);
  --arr-bloom: color-mix(in oklab, #fff 64%, #ffcf7a);
  /* The envelope's lining, seen along the seams as the flaps spring: a soft tint of the accent, dotted with the accent
     and the house gold (confetti, a Paperless Post liner in the shop's colours). */
  --arr-lining: color-mix(in oklab, var(--accent, #ff7a59) 26%, #fffaf3);
  --arr-dot-a: color-mix(in oklab, var(--accent, #ff7a59) 82%, var(--panel-deep, #0e5b68));
  --arr-dot-b: var(--accent2, #f6c66b);
  /* A blind-embossed border pressed into the top and bottom flaps, 14 px inside their edges: a groove in shadow beside a
     ridge of light, no ink (the fine-stationery finish of a lined envelope). */
  --arr-groove: rgb(0 0 0 / .2);
  --arr-ridge: rgb(255 255 255 / .13);
  /* Paper tooth, BALANCED: equal light and dark specks (two alpha masks of one noise), so it reads as the same fine
     stock on a rose paper and a navy one. ≈0.6 KB, no request, tiles seamlessly. */
  --arr-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='2' seed='5' stitchTiles='stitch' result='n'/%3E%3CfeColorMatrix in='n' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1.7 0 0 0 -.93' result='w'/%3E%3CfeColorMatrix in='n' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.7 0 0 0 .77' result='b'/%3E%3CfeMerge%3E%3CfeMergeNode in='w'/%3E%3CfeMergeNode in='b'/%3E%3C/feMerge%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)' opacity='.2'/%3E%3C/svg%3E");
  position: fixed;
  inset: 0;
  z-index: 1000;
  overflow: hidden;
  pointer-events: none;
  -webkit-tap-highlight-color: transparent;
  /* The root is a size container: the seams' bloom aims itself with container units (any phone's proportions). */
  container-type: size;
  /* Sealed and waiting for its clock: the settle's own first frame, so the clock starts without a jump. */
  transform-origin: 50% var(--arr-cy);
  transform: scale(1.035);
}
/* 🌙 A DARK theme (color-scheme: dark — the Neon family and every dark twin) is read at night: its panel is a bright
   neon, so the letter is its night-blue surface lit a little by the panel, the words its own light ink, the dog's name
   in the neon (--primary-deep is the bright text shade there), the wax the accent taken toward that light ink, and the
   light a gentler glow, its bloom in the theme's second accent. */
@supports (color: light-dark(#000, #fff)) {
  [data-arrival].dm-arrival {
    --arr-paper: light-dark(var(--panel-deep, #0e5b68), color-mix(in oklab, var(--panel, #22d3ee) 13%, var(--surface, #0b1122)));
    --arr-paper-lit: light-dark(color-mix(in oklab, var(--panel, #157e8e) 58%, var(--panel-deep, #0e5b68)), color-mix(in oklab, var(--panel, #22d3ee) 21%, var(--surface, #0b1122)));
    --arr-ink: light-dark(color-mix(in oklab, var(--on-panel, #fff) 94%, var(--accent2, #f6c66b)), var(--ink, #e9f3ff));
    --arr-quiet: light-dark(color-mix(in oklab, var(--on-panel, #fff) 90%, var(--panel-deep, #0e5b68)), color-mix(in oklab, var(--ink, #e9f3ff) 78%, var(--surface, #0b1122)));
    --arr-name: light-dark(color-mix(in oklab, var(--on-panel, #fff) 94%, var(--accent2, #f6c66b)), var(--primary-deep, #6ee7fb));
    --arr-flare: light-dark(rgb(255 246 230), rgb(150 142 132));
    /* A night letter's bloom is its second accent, thinned toward white and dimmed by ALPHA (darkening a yellow toward
       black is how a glow turns olive). */
    --arr-bloom: light-dark(color-mix(in oklab, #fff 64%, #ffcf7a), color-mix(in oklab, color-mix(in oklab, var(--accent2, #fde047) 62%, #fff) 70%, transparent));
    --arr-lining: light-dark(color-mix(in oklab, var(--accent, #ff7a59) 26%, #fffaf3), color-mix(in oklab, var(--accent, #f24bc4) 20%, var(--surface, #0b1122)));
    --arr-dot-a: light-dark(color-mix(in oklab, var(--accent, #ff7a59) 82%, var(--panel-deep, #0e5b68)), var(--accent, #f24bc4));
  }
}
/* Where relative colour is understood, the wax takes back some of the colour the mix toward the ink washed out (and a
   breath of lightness, so a vivid accent pushed past the screen's gamut keeps its contrast): dense wax, not a pastel. */
@supports (color: oklch(from red calc(l + .015) calc(c * 1.22) h)) {
  [data-arrival].dm-arrival { --arr-wax-rich: oklch(from var(--arr-wax) calc(l + .015) calc(c * 1.22) h); }
  [data-arrival][data-arrival] :is(.dm-arrival__whole > .dm-arrival__wax, .dm-arrival__piece) { background-color: var(--arr-wax-rich); }
}
/* The clock. dm-arr-out is the sleeve's own clock (other styles read it: comic, sticker book); it ends as the sleeve
   leaves. With no script, the letter never gets its clock, so it leaves on its own (dm-arr-late) rather than stand. */
[data-arrival].dm-arrival[data-go] { animation: dm-arr-settle 650ms var(--arr-ease) both, dm-arr-out 1ms calc(var(--arr-pop) + 600ms) both; }
[data-arrival].dm-arrival:not([data-go]) { animation: dm-arr-late 220ms cubic-bezier(.3, 0, .2, 1) 1200ms both; }
/* Not ready in time (its face or its seal still on the way): it never plays half-drawn; it steps aside. */
[data-arrival].dm-arrival[data-late] { animation: dm-arr-late 220ms cubic-bezier(.3, 0, .2, 1) both; }
/* A press, a key, a wheel: out of the way, now. */
[data-arrival].dm-arrival[data-skip] { animation: dm-arr-skip 180ms cubic-bezier(.3, 0, .2, 1) both; }
[data-arrival].dm-arrival[data-skip] * { pointer-events: none; }
@keyframes dm-arr-settle { from { transform: scale(1.035); } to { transform: none; } }
@keyframes dm-arr-out { to { visibility: hidden; } }
@keyframes dm-arr-skip { from { transform: none; } to { opacity: 0; visibility: hidden; transform: none; } }
@keyframes dm-arr-late { from { transform: scale(1.035); } to { opacity: 0; visibility: hidden; transform: scale(1.035); } }

[data-arrival] .dm-arrival__stage { position: absolute; inset: 0; perspective: 1500px; perspective-origin: 50% var(--arr-cy); }

/* ---------------------------------------------------------------- inside the envelope: its lining
   Under the flaps. It shows along the seams as the flaps spring open, lit by the light pouring out of the crack, and
   clears onto the card as they swing away. Painted once (gradients), then only its opacity moves. */
[data-arrival] .dm-arrival__inside {
  position: absolute; inset: 0; z-index: 0;
  background:
    var(--arr-grain) 0 0 / 220px 220px,
    radial-gradient(circle, var(--arr-dot-a) 0 2.1px, transparent 2.6px) 0 0 / 24px 24px,
    radial-gradient(circle, var(--arr-dot-b) 0 1.5px, transparent 2px) 12px 12px / 24px 24px,
    var(--arr-lining);
}
[data-arrival][data-go] .dm-arrival__inside { animation: dm-arr-fade 110ms cubic-bezier(.4, 0, .6, 1) calc(var(--arr-pop) + 150ms) both; }
@keyframes dm-arr-fade { to { opacity: 0; } }

/* ---------------------------------------------------------------- the flaps' shadows, on whatever is under them
   A flap lifted toward the reader throws its shadow down and to the right (the light is above, at the top-left): the top
   flap's shadow reaches PAST its own tip and leans right as it rises, the left flap's reaches past its edge and leans
   down; the bottom and right flaps' fall mostly under themselves. Each shadow is the flap's triangle blurred once (an
   SVG, rasterised at paint), then only scaled and skewed about its hinge as the flap swings, and gone as it leaves. */
[data-arrival] .dm-arrival__shade { position: absolute; z-index: 1; opacity: 0; background: var(--arr-shade) center / 100% 100% no-repeat; }
[data-arrival][data-go] .dm-arrival__shade { animation: var(--arr-shade-name) 640ms linear var(--arr-shade-at) both; }
[data-arrival] .dm-arrival__shade--t { left: 0; top: 0; width: 100%; height: var(--arr-cy); transform-origin: 50% 0; --arr-shade-name: dm-arr-shade-t; --arr-shade-at: var(--arr-pop);
  --arr-shade: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cfilter id='b' x='-.2' y='-.2' width='1.4' height='1.4'%3E%3CfeGaussianBlur stdDeviation='2.3 2.3'/%3E%3C/filter%3E%3Cpolygon points='0,0 100,0 50,100' fill='%23000' filter='url(%23b)'/%3E%3C/svg%3E"); }
[data-arrival] .dm-arrival__shade--b { left: 0; top: var(--arr-cy); width: 100%; height: calc(100% - var(--arr-cy)); transform-origin: 50% 100%; --arr-shade-name: dm-arr-shade-b; --arr-shade-at: calc(var(--arr-pop) + 80ms);
  --arr-shade: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cfilter id='b' x='-.2' y='-.2' width='1.4' height='1.4'%3E%3CfeGaussianBlur stdDeviation='2.3 2'/%3E%3C/filter%3E%3Cpolygon points='0,100 50,0 100,100' fill='%23000' filter='url(%23b)'/%3E%3C/svg%3E"); }
[data-arrival] .dm-arrival__shade--l { left: 0; top: 0; width: 50%; height: 100%; transform-origin: 0 50%; --arr-shade-name: dm-arr-shade-l; --arr-shade-at: calc(var(--arr-pop) + 40ms);
  --arr-shade: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cfilter id='b' x='-.2' y='-.2' width='1.4' height='1.4'%3E%3CfeGaussianBlur stdDeviation='4.4 1.1'/%3E%3C/filter%3E%3Cpolygon points='0,0 100,46 0,100' fill='%23000' filter='url(%23b)'/%3E%3C/svg%3E"); }
[data-arrival] .dm-arrival__shade--r { right: 0; top: 0; width: 50%; height: 100%; transform-origin: 100% 50%; --arr-shade-name: dm-arr-shade-r; --arr-shade-at: calc(var(--arr-pop) + 40ms);
  --arr-shade: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cfilter id='b' x='-.2' y='-.2' width='1.4' height='1.4'%3E%3CfeGaussianBlur stdDeviation='4.4 1.1'/%3E%3C/filter%3E%3Cpolygon points='100,0 100,100 0,46' fill='%23000' filter='url(%23b)'/%3E%3C/svg%3E"); }
@keyframes dm-arr-shade-t { 0% { opacity: 0; transform: none; } 12% { opacity: .5; transform: skewX(6deg) scaleY(1.12); } 24% { opacity: .5; transform: skewX(5deg) scaleY(1.1); } 47% { opacity: .4; transform: skewX(14deg) scaleY(.92); } 62% { opacity: .16; transform: skewX(16deg) scaleY(.6); } 72%, 100% { opacity: 0; transform: skewX(16deg) scaleY(.45); } }
@keyframes dm-arr-shade-b { 0% { opacity: 0; transform: none; } 12% { opacity: .35; transform: skewX(-5deg) scaleY(.96); } 47% { opacity: .28; transform: skewX(-12deg) scaleY(.6); } 72%, 100% { opacity: 0; transform: skewX(-14deg) scaleY(.3); } }
@keyframes dm-arr-shade-l { 0% { opacity: 0; transform: none; } 12% { opacity: .42; transform: skewY(9deg) scaleX(1.05); } 24% { opacity: .42; transform: skewY(8deg) scaleX(1.04); } 47% { opacity: .34; transform: skewY(20deg) scaleX(.9); } 62% { opacity: .14; transform: skewY(24deg) scaleX(.62); } 72%, 100% { opacity: 0; transform: skewY(25deg) scaleX(.45); } }
@keyframes dm-arr-shade-r { 0% { opacity: 0; transform: none; } 12% { opacity: .3; transform: skewY(-8deg) scaleX(.96); } 47% { opacity: .22; transform: skewY(-18deg) scaleX(.6); } 72%, 100% { opacity: 0; transform: skewY(-22deg) scaleX(.3); } }

/* ---------------------------------------------------------------- the four flaps
   Each flap is a box hinged on its screen edge; its paper is a clip-path inside it. The side flaps run a few px under
   their neighbours so no seam can open between them, and the top flap is laid last. At the pop each SPRINGS open a
   little (20° in 100 ms, the paper's own spring), sags back a touch, then swings toward the reader and out past the
   screen's edge (100°, the back face hidden past 90°: every flap is gone by pop + 500). The seams' edges carry a hair
   of light below the cut and a soft shadow beyond it: the paper's thickness. */
[data-arrival] .dm-arrival__flap {
  --arr-flap-dur: 640ms;
  position: absolute;
  pointer-events: auto;
  backface-visibility: hidden;
  will-change: transform;
}
[data-arrival][data-go] .dm-arrival__flap { animation: var(--arr-flap-name) var(--arr-flap-dur) linear var(--arr-flap-at) both; }
[data-arrival] .dm-arrival__flap--l { left: 0; top: 0; width: calc(50% + 10px); height: 100%; transform-origin: 0 50%; z-index: 2;
  --arr-flap-name: dm-arr-open-l; --arr-flap-at: calc(var(--arr-pop) + 40ms); }
[data-arrival] .dm-arrival__flap--r { right: 0; top: 0; width: calc(50% + 10px); height: 100%; transform-origin: 100% 50%; z-index: 3;
  --arr-flap-name: dm-arr-open-r; --arr-flap-at: calc(var(--arr-pop) + 40ms); }
[data-arrival] .dm-arrival__flap--b { left: 0; top: var(--arr-cy); width: 100%; height: calc(100% - var(--arr-cy)); transform-origin: 50% 100%; z-index: 4;
  --arr-flap-name: dm-arr-open-b; --arr-flap-at: calc(var(--arr-pop) + 80ms);
  filter: drop-shadow(0 -.75px 0 rgb(255 255 255 / .12)) drop-shadow(0 -1px 1.3px rgb(0 0 0 / .2)); }
[data-arrival] .dm-arrival__flap--t { left: 0; top: 0; width: 100%; height: var(--arr-cy); transform-origin: 50% 0; z-index: 5;
  --arr-flap-name: dm-arr-open-t; --arr-flap-at: var(--arr-pop);
  /* Two hairline shadows, the paper's edge. A third, 18 px wide, was dropped (#302, "smooth"): a wide blur on a
     half-screen flap that turns in 3D is the costliest thing a phone repaints in the open, and at that size it read as
     haze, not depth. */
  filter: drop-shadow(0 .75px 0 rgb(255 255 255 / .16)) drop-shadow(0 1.5px 1.5px rgb(0 0 0 / .26)); }

[data-arrival] .dm-arrival__paper {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background-color: var(--arr-paper);
  background-repeat: repeat, no-repeat;
  background-size: 220px 220px, 100% 100%;
}
/* Lit from the top-left, as the seal is: the top and left flaps catch the light, the right and bottom fall away. */
[data-arrival] .dm-arrival__flap--l .dm-arrival__paper { clip-path: polygon(0 -10px, 100% var(--arr-cy), 0 calc(100% + 10px));
  background-image: var(--arr-grain), linear-gradient(90deg, var(--arr-paper-lit), color-mix(in oklab, var(--arr-paper-lit) 45%, var(--arr-paper)) 55%, var(--arr-paper)); }
[data-arrival] .dm-arrival__flap--r .dm-arrival__paper { clip-path: polygon(100% -10px, 100% calc(100% + 10px), 0 var(--arr-cy));
  background-image: var(--arr-grain), linear-gradient(270deg, color-mix(in oklab, var(--arr-paper) 86%, #000), var(--arr-paper) 60%); }
[data-arrival] .dm-arrival__flap--b .dm-arrival__paper { clip-path: polygon(0 100%, 50% 0, 100% 100%);
  background-image: var(--arr-emboss-l), var(--arr-emboss-r), var(--arr-grain), linear-gradient(0deg, color-mix(in oklab, var(--arr-paper) 88%, #000), var(--arr-paper) 45%);
  background-repeat: no-repeat, no-repeat, repeat, no-repeat;
  background-size: 50% 100%, 50% 100%, 220px 220px, 100% 100%;
  background-position: left top, right top, 0 0, 0 0;
  --arr-emboss-l: linear-gradient(to bottom right, transparent calc(50% + 13px), var(--arr-groove) calc(50% + 13.6px), var(--arr-groove) calc(50% + 14.2px), var(--arr-ridge) calc(50% + 14.8px), var(--arr-ridge) calc(50% + 15.4px), transparent calc(50% + 16px));
  --arr-emboss-r: linear-gradient(to bottom left, transparent calc(50% + 13px), var(--arr-groove) calc(50% + 13.6px), var(--arr-groove) calc(50% + 14.2px), var(--arr-ridge) calc(50% + 14.8px), var(--arr-ridge) calc(50% + 15.4px), transparent calc(50% + 16px)); }
[data-arrival] .dm-arrival__flap--t .dm-arrival__paper { clip-path: polygon(0 0, 100% 0, 50% 100%);
  background-image: var(--arr-emboss-l), var(--arr-emboss-r), var(--arr-grain), linear-gradient(180deg, var(--arr-paper-lit), color-mix(in oklab, var(--arr-paper-lit) 50%, var(--arr-paper)) 60%, var(--arr-paper));
  background-repeat: no-repeat, no-repeat, repeat, no-repeat;
  background-size: 50% 100%, 50% 100%, 220px 220px, 100% 100%;
  background-position: left top, right top, 0 0, 0 0;
  --arr-emboss-l: linear-gradient(to top right, transparent calc(50% + 13px), var(--arr-groove) calc(50% + 13.6px), var(--arr-groove) calc(50% + 14.2px), var(--arr-ridge) calc(50% + 14.8px), var(--arr-ridge) calc(50% + 15.4px), transparent calc(50% + 16px));
  --arr-emboss-r: linear-gradient(to top left, transparent calc(50% + 13px), var(--arr-groove) calc(50% + 13.6px), var(--arr-groove) calc(50% + 14.2px), var(--arr-ridge) calc(50% + 14.8px), var(--arr-ridge) calc(50% + 15.4px), transparent calc(50% + 16px)); }

/* As a flap swings up it turns to the light (top, left) or away from it (right, bottom). */
[data-arrival] .dm-arrival__paper::after { content: ""; position: absolute; inset: 0; opacity: 0; }
[data-arrival][data-go] .dm-arrival__paper::after { animation: dm-arr-lit var(--arr-flap-dur) cubic-bezier(.4, 0, .6, 1) var(--arr-flap-at) both; }
[data-arrival] .dm-arrival__flap--t .dm-arrival__paper::after { background: #fff; --arr-lit: .2; }
[data-arrival] .dm-arrival__flap--l .dm-arrival__paper::after { background: #fff; --arr-lit: .12; }
[data-arrival] .dm-arrival__flap--r .dm-arrival__paper::after { background: #000; --arr-lit: .16; }
[data-arrival] .dm-arrival__flap--b .dm-arrival__paper::after { background: #000; --arr-lit: .18; }
@keyframes dm-arr-lit { to { opacity: var(--arr-lit); } }

/* The spring, then the swing: a snap to 20° (the seal let go), a sag back to 17°, then away toward the reader (90° at
   ~65% of the run: the top flap is out of sight by pop + 420, the bottom one by pop + 500). */
@keyframes dm-arr-open-t { 0% { transform: none; animation-timing-function: cubic-bezier(.2, .8, .3, 1); } 15% { transform: rotateX(20deg); animation-timing-function: cubic-bezier(.4, 0, .6, 1); } 28% { transform: rotateX(17deg); animation-timing-function: cubic-bezier(.3, .2, .2, 1); } 100% { transform: rotateX(100deg); } }
@keyframes dm-arr-open-b { 0% { transform: none; animation-timing-function: cubic-bezier(.2, .8, .3, 1); } 15% { transform: rotateX(-20deg); animation-timing-function: cubic-bezier(.4, 0, .6, 1); } 28% { transform: rotateX(-17deg); animation-timing-function: cubic-bezier(.3, .2, .2, 1); } 100% { transform: rotateX(-100deg); } }
@keyframes dm-arr-open-l { 0% { transform: none; animation-timing-function: cubic-bezier(.2, .8, .3, 1); } 15% { transform: rotateY(-20deg); animation-timing-function: cubic-bezier(.4, 0, .6, 1); } 28% { transform: rotateY(-17deg); animation-timing-function: cubic-bezier(.3, .2, .2, 1); } 100% { transform: rotateY(-100deg); } }
@keyframes dm-arr-open-r { 0% { transform: none; animation-timing-function: cubic-bezier(.2, .8, .3, 1); } 15% { transform: rotateY(20deg); animation-timing-function: cubic-bezier(.4, 0, .6, 1); } 28% { transform: rotateY(17deg); animation-timing-function: cubic-bezier(.3, .2, .2, 1); } 100% { transform: rotateY(100deg); } }

/* ---------------------------------------------------------------- the words (the only copy: "with love from", "for")
   Printed on their flaps and held still to be read; at the pop they go with the paper, fading in the spring (never
   seen squashed edge-on). */
[data-arrival][data-go] :is(.dm-arrival__from, .dm-arrival__to) { animation: dm-arr-fade 140ms cubic-bezier(.25, .6, .4, 1) var(--arr-pop) both; }
/* The top flap is a triangle that narrows toward the seal, so the lockup is anchored by its LAST line, at 41% of the
   flap's height, where the paper is still ~59% of the screen wide: a logo or a second line grows upward, into the
   wider paper, and the name's max-width (58%) always fits inside the triangle at that line (375 × 667 → 430 × 932). */
[data-arrival] .dm-arrival__from {
  position: absolute; left: 0; right: 0; bottom: 59%;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center;
}
/* The shop's mark on the house's white tile (the ShopLockup treatment): the whole logo, contained, never cropped. */
[data-arrival] .dm-arrival__logo {
  box-sizing: border-box; width: 52px; height: 52px; margin-bottom: 8px; padding: 6px; border-radius: 14px;
  background-color: #fff; background-position: center; background-size: contain; background-repeat: no-repeat;
  background-origin: content-box;
  box-shadow: 0 1px 2px rgb(0 0 0 / .16), 0 6px 16px rgb(0 0 0 / .14);
}
[data-arrival] .dm-arrival__love { font: italic 400 22px/1 "CS Arrival Instrument", "Iowan Old Style", Georgia, serif; color: var(--arr-quiet); letter-spacing: .01em; }
[data-arrival] .dm-arrival__shop {
  max-width: 58%;
  font: 800 14px/1.35 var(--font-nunito), system-ui, sans-serif;
  letter-spacing: .2em; text-transform: uppercase; text-wrap: balance;
  color: var(--arr-ink);
}
/* A longer name gives up tracking before it gives up size, and wraps (balanced) rather than run past the paper. */
[data-arrival] .dm-arrival__from[data-len="m"] .dm-arrival__shop { font-size: 12.5px; letter-spacing: .15em; }
[data-arrival] .dm-arrival__from[data-len="l"] .dm-arrival__shop { font-size: 12px; letter-spacing: .09em; line-height: 1.3; }
/* A short phone (an SE is 667 tall) has a shorter flap: a smaller tile and tighter lines keep a logo + a three-line
   name on the paper. */
@media (max-height: 740px) {
  [data-arrival] .dm-arrival__from { gap: 5px; }
  [data-arrival] .dm-arrival__logo { width: 40px; height: 40px; margin-bottom: 5px; border-radius: 11px; padding: 4px; }
  [data-arrival] .dm-arrival__love { font-size: 18px; }
  [data-arrival] .dm-arrival__from[data-len="l"] .dm-arrival__shop { font-size: 11px; }
  [data-arrival] .dm-arrival__stamp { top: 12px; right: 12px; transform: rotate(4deg) scale(.84); transform-origin: 100% 0; }
}
/* A long shop name (up to three lines) or a short phone needs the flap's width: the postmark keeps its ring on the
   stamp and loses the wavy lines that would run across the name. */
@media (max-height: 740px) { [data-arrival] .dm-arrival__postmark { clip-path: inset(0 0 0 54%); } }
[data-arrival] .dm-arrival__flap--t:has(.dm-arrival__from[data-len="l"]) .dm-arrival__postmark { clip-path: inset(0 0 0 54%); }
/* "for {Dog}", on the bottom flap: it rides the flap (not clipped by the paper, so a long name may cross a seam). */
[data-arrival] .dm-arrival__to {
  position: absolute; left: 0; right: 0; top: 55%;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
[data-arrival] .dm-arrival__for { font: italic 400 23px/1 "CS Arrival Instrument", "Iowan Old Style", Georgia, serif; color: var(--arr-quiet); }
[data-arrival] .dm-arrival__name {
  max-width: 76%;
  font: italic 400 72px/1.02 "CS Arrival Instrument", "Iowan Old Style", Georgia, serif;
  letter-spacing: -.01em; text-wrap: balance;
  color: var(--arr-name);
  padding: 0 .08em;     /* the italic's overhang */
}
[data-arrival] .dm-arrival__to[data-len="m"] .dm-arrival__name { font-size: 54px; }
[data-arrival] .dm-arrival__to[data-len="l"] .dm-arrival__name { font-size: 40px; line-height: 1.1; }

/* ---------------------------------------------------------------- the stamp (the joke): the day's glyph, franked by a paw
   A rendered stamp (arrival/stamp.webp: the perforated paper with its own soft shadow, one corner lifting) in the top
   flap's corner, where a letter's stamp goes. Its printed field is the shop's accent over the paper (multiply, so the
   paper's tooth shows through the ink), the day's glyph sits in it, and a rubber-stamp postmark with a paw where the
   town would be cancels it, its wavy lines running off across the envelope. It rides away with its flap. */
[data-arrival] .dm-arrival__stamp {
  position: absolute; top: 24px; right: 22px; width: 72px; height: 82px;
  transform: rotate(4deg);
  background: url("/card-styles/arrival/stamp.webp") 0 0 / 174px 82px no-repeat;
}
[data-arrival] .dm-arrival__stamp-field {
  position: absolute; left: 11.5px; top: 11px; width: 45px; height: 55px;
  display: grid; place-items: center;
  background: color-mix(in oklab, var(--accent, #ff7a59) 34%, #fffaf2);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--accent, #ff7a59) 72%, #000 14%), inset 0 0 0 3px color-mix(in oklab, var(--accent, #ff7a59) 34%, #fffaf2), inset 0 0 0 3.8px color-mix(in oklab, var(--accent, #ff7a59) 55%, #fffaf2);
  mix-blend-mode: multiply;
}
[data-arrival] .dm-arrival__stamp-glyph { position: absolute; left: 11.5px; top: 11px; width: 45px; height: 55px; display: grid; place-items: center; font-size: 27px; line-height: 1; }
[data-arrival] .dm-arrival__postmark {
  position: absolute; left: -61px; top: 34px; width: 100px; height: 50px;
  background: url("/card-styles/arrival/stamp.webp") -74px 0 / 174px 82px no-repeat;
  transform: rotate(-9deg);
  mix-blend-mode: multiply;
  opacity: .72;
}

/* ---------------------------------------------------------------- the bloom: light runs along the seams into the seal
   Four soft streaks, one down each seam from its corner, arrive at the seal together in 150 ms (then the seal's rim
   glows: the light has gathered where it will break out). Each is one small element, its head on the seal, aimed and
   flown by the root's own proportions (container units + atan2, so it runs along the real seam on any phone); only its
   transform and opacity move. */
[data-arrival] .dm-arrival__comet {
  position: absolute; left: 50%; top: var(--arr-cy); width: 230px; height: 44px; margin: -22px 0 0 -230px;
  z-index: 6;
  opacity: 0;
  transform-origin: 100% 50%;
  transform: rotate(var(--arr-comet-a));
  background: radial-gradient(farthest-side at 100% 50%, color-mix(in oklab, var(--arr-bloom) 85%, transparent), color-mix(in oklab, var(--arr-bloom) 30%, transparent) 38%, transparent 80%);
  filter: blur(5px);
  mix-blend-mode: plus-lighter;
}
/* The fallback aim is a 430 × 932 phone's, flown by the viewport; atan2() and container units make it exact. */
[data-arrival] .dm-arrival__comet--tl { --arr-comet-a: 63.4deg; --arr-comet-x: -50vw; --arr-comet-y: -46vh; }
[data-arrival] .dm-arrival__comet--tr { --arr-comet-a: 116.6deg; --arr-comet-x: 50vw; --arr-comet-y: -46vh; }
[data-arrival] .dm-arrival__comet--bl { --arr-comet-a: -66.8deg; --arr-comet-x: -50vw; --arr-comet-y: 54vh; }
[data-arrival] .dm-arrival__comet--br { --arr-comet-a: -113.2deg; --arr-comet-x: 50vw; --arr-comet-y: 54vh; }
@supports (transform: rotate(atan2(1px, 1px))) and (width: 1cqw) {
  [data-arrival] .dm-arrival__comet--tl { --arr-comet-a: atan2(46cqh, 50cqw); --arr-comet-x: -50cqw; --arr-comet-y: -46cqh; }
  [data-arrival] .dm-arrival__comet--tr { --arr-comet-a: atan2(46cqh, -50cqw); --arr-comet-x: 50cqw; --arr-comet-y: -46cqh; }
  [data-arrival] .dm-arrival__comet--bl { --arr-comet-a: atan2(-54cqh, 50cqw); --arr-comet-x: -50cqw; --arr-comet-y: 54cqh; }
  [data-arrival] .dm-arrival__comet--br { --arr-comet-a: atan2(-54cqh, -50cqw); --arr-comet-x: 50cqw; --arr-comet-y: 54cqh; }
}
[data-arrival][data-go] .dm-arrival__comet { animation: dm-arr-comet 150ms cubic-bezier(.5, 0, .75, .7) calc(var(--arr-pop) - 380ms) both; }
@keyframes dm-arr-comet {
  0%   { opacity: 0; transform: translate(var(--arr-comet-x), var(--arr-comet-y)) rotate(var(--arr-comet-a)); }
  30%  { opacity: .8; }
  82%  { opacity: .8; }
  100% { opacity: 0; transform: rotate(var(--arr-comet-a)); }
}
/* …and the light pools at the seal: its rim glows through the press, then the flare takes over. */
[data-arrival] .dm-arrival__halo {
  position: absolute; left: 50%; top: var(--arr-cy);
  width: 230px; height: 230px; margin: -115px 0 0 -115px;
  z-index: 7;
  border-radius: 50%;
  background: radial-gradient(closest-side, transparent 50%, color-mix(in oklab, var(--arr-bloom) 70%, transparent) 62%, color-mix(in oklab, var(--arr-bloom) 22%, transparent) 78%, transparent);
  mix-blend-mode: plus-lighter;
  opacity: 0;
}
[data-arrival][data-go] .dm-arrival__halo { animation: dm-arr-halo 320ms cubic-bezier(.4, 0, .6, 1) calc(var(--arr-pop) - 250ms) both; }
@keyframes dm-arr-halo { 0% { opacity: 0; transform: scale(1.12); } 55% { opacity: .55; transform: scale(.98); } 88% { opacity: .5; transform: scale(.96); } 100% { opacity: 0; transform: scale(1.05); } }

/* ---------------------------------------------------------------- the paper, pressed under the seal
   As the seal is pushed down the paper around it dimples: a soft shadow ring hugging the wax and a faint lit bulge
   beyond it. It springs back at the pop. */
[data-arrival] .dm-arrival__dimple {
  position: absolute; left: 50%; top: var(--arr-cy);
  width: 220px; height: 220px; margin: -110px 0 0 -110px;
  z-index: 7;
  border-radius: 50%;
  background: radial-gradient(closest-side, transparent 56%, rgb(0 0 0 / .30) 64%, rgb(0 0 0 / .10) 73%, rgb(255 255 255 / .09) 82%, transparent 95%);
  opacity: 0;
}
[data-arrival][data-go] .dm-arrival__dimple { animation: dm-arr-dimple 260ms calc(var(--arr-pop) - 220ms) both; }
@keyframes dm-arr-dimple {
  0%   { opacity: 0; transform: scale(.96); animation-timing-function: cubic-bezier(.5, 0, .9, .6); }
  55%  { opacity: 1; transform: scale(1); }
  80%  { opacity: 1; transform: scale(1); animation-timing-function: cubic-bezier(.2, .6, .4, 1); }
  100% { opacity: 0; transform: scale(1.04); }
}

/* ---------------------------------------------------------------- the light that pours out as the seal breaks
   ADDED to the paper and the lining under it (plus-lighter), warm white: it brightens every theme toward light and
   never mixes to brown. Drawn at 64vmax and scaled up (a soft gradient loses nothing to the upscale; its layer is a
   sixth of the memory of a full-size one). */
[data-arrival] .dm-arrival__glow {
  position: absolute; left: 50%; top: var(--arr-cy);
  width: 64vmax; height: 64vmax; margin: -32vmax 0 0 -32vmax;
  z-index: 8;
  background: radial-gradient(closest-side, var(--arr-flare), color-mix(in oklab, var(--arr-flare) 55%, transparent) 12%, color-mix(in oklab, var(--arr-flare) 16%, transparent) 30%, transparent 48%);
  mix-blend-mode: plus-lighter;
  opacity: 0;
  transform: scale(.15);
}
[data-arrival][data-go] .dm-arrival__glow { animation: dm-arr-glow 560ms calc(var(--arr-pop) - 30ms) var(--arr-ease) both; }
@keyframes dm-arr-glow {
  0%   { opacity: 0; transform: scale(.15); }
  14%  { opacity: .85; }
  100% { opacity: 0; transform: scale(2.3); }
}

/* ---------------------------------------------------------------- the seal
   Blender's greyscale light maps (arrival/seal.webp, the whole seal; arrival/seal-broken.webp, its two halves and three
   chips in one atlas: shape in the alpha, shading in the grey) hard-lit over the wax colour: 50% grey IS the colour,
   darker is the wax's shadow, lighter its gloss, white a specular. One set of files, every shop's wax. */
[data-arrival] .dm-arrival__seal {
  position: absolute; left: 50%; top: var(--arr-cy);
  width: 144px; height: 144px; margin: -72px 0 0 -72px;
  z-index: 9;
  pointer-events: auto;
  filter: drop-shadow(1px 2.5px 1.5px rgb(0 0 0 / .36)) drop-shadow(3px 8px 10px rgb(0 0 0 / .2));
}
[data-arrival][data-go] .dm-arrival__seal { animation: dm-arr-press 220ms calc(var(--arr-pop) - 220ms) both; }
[data-arrival] .dm-arrival__seal > div { position: absolute; }
[data-arrival] .dm-arrival__whole { inset: 0; }
[data-arrival] .dm-arrival__whole > .dm-arrival__wax {
  position: absolute; inset: 0;
  -webkit-mask: url("/card-styles/arrival/seal.webp") center / 100% 100% no-repeat;
          mask: url("/card-styles/arrival/seal.webp") center / 100% 100% no-repeat;
  /* The sheen is the wax's own top layer (overlay over the lit wax, inside the same mask), so it follows the form. */
  background:
    linear-gradient(115deg, transparent 40%, rgb(255 255 255 / .24) 47%, rgb(255 255 255 / .42) 50%, rgb(255 255 255 / .24) 53%, transparent 60%) 86% 0 / 300% 100% no-repeat,
    url("/card-styles/arrival/seal.webp") center / 100% 100% no-repeat,
    var(--arr-wax);
  background-blend-mode: overlay, hard-light, normal;
}
[data-arrival][data-go] .dm-arrival__whole > .dm-arrival__wax { animation: dm-arr-glint 600ms 800ms cubic-bezier(.35, .1, .45, 1) both; }
/* 86% → 14% carries the band from off one edge to off the other (a third of the image's 300% width). */
@keyframes dm-arr-glint {
  from { background-position: 86% 0, center, 0 0; }
  to   { background-position: 14% 0, center, 0 0; }
}
/* The press: the seal is pushed into the paper, then springs up off it as it lets go. */
@keyframes dm-arr-press {
  0%   { transform: none; animation-timing-function: cubic-bezier(.5, 0, .9, .6); }
  68%  { transform: scale(.93); filter: drop-shadow(.5px 1.2px 1px rgb(0 0 0 / .42)) drop-shadow(1.5px 4px 6px rgb(0 0 0 / .18)); animation-timing-function: cubic-bezier(.2, .9, .3, 1); }
  100% { transform: translateY(-5px) scale(1.06); filter: drop-shadow(4px 11px 7px rgb(0 0 0 / .22)) drop-shadow(8px 20px 18px rgb(0 0 0 / .12)); }
}

/* THE BREAK: at the pop the whole seal is swapped for the rendered pieces — two halves already tipped up at the crack
   (their broken faces show as lit, grainy wax with real thickness) and three chips. The halves part (the light comes
   out between them), then are flung out sideways and a little down, off the face and off the screen; the chips fly
   out, spinning. */
[data-arrival][data-go] .dm-arrival__whole { animation: dm-arr-hide 1ms calc(var(--arr-pop) - 10ms) both; }
@keyframes dm-arr-hide { to { opacity: 0; } }
[data-arrival] .dm-arrival__piece {
  opacity: 0;
  -webkit-mask: url("/card-styles/arrival/seal-broken.webp") var(--arr-ax) var(--arr-ay) / 136px 153px no-repeat;
          mask: url("/card-styles/arrival/seal-broken.webp") var(--arr-ax) var(--arr-ay) / 136px 153px no-repeat;
  background: url("/card-styles/arrival/seal-broken.webp") var(--arr-ax) var(--arr-ay) / 136px 153px no-repeat, var(--arr-wax);
  background-blend-mode: hard-light, normal;
}
/* Each piece's box in the seal's 144 px frame and in the atlas (printed by arrival-pack.mjs). */
[data-arrival] .dm-arrival__piece--l { left: 7px; top: 10px; width: 65px; height: 128px; --arr-ax: 0px; --arr-ay: 0px; transform-origin: 70% 55%; }
[data-arrival] .dm-arrival__piece--r { left: 72px; top: 9px; width: 69px; height: 129px; --arr-ax: -67px; --arr-ay: 0px; transform-origin: 30% 55%; }
[data-arrival] .dm-arrival__piece--c0 { left: 58px; top: 24px; width: 26px; height: 18px; --arr-ax: 0px; --arr-ay: -131px; }
[data-arrival] .dm-arrival__piece--c1 { left: 58px; top: 64px; width: 20px; height: 22px; --arr-ax: -28px; --arr-ay: -131px; }
[data-arrival] .dm-arrival__piece--c2 { left: 61px; top: 103px; width: 23px; height: 22px; --arr-ax: -50px; --arr-ay: -131px; }
/* `forwards`, never `both`: before the pop a piece must not exist (it would show through the whole seal). */
[data-arrival][data-go] .dm-arrival__piece--l { animation: dm-arr-half-l 420ms calc(var(--arr-pop) - 10ms) forwards; }
[data-arrival][data-go] .dm-arrival__piece--r { animation: dm-arr-half-r 420ms calc(var(--arr-pop) - 10ms) forwards; }
[data-arrival][data-go] .dm-arrival__piece--c0 { animation: dm-arr-chip 380ms cubic-bezier(.2, .7, .5, 1) calc(var(--arr-pop) - 10ms) forwards; --arr-cx: 64px; --arr-cy2: 14px; --arr-cr: 250deg; }
[data-arrival][data-go] .dm-arrival__piece--c1 { animation: dm-arr-chip 380ms cubic-bezier(.2, .7, .5, 1) calc(var(--arr-pop) + 10ms) forwards; --arr-cx: -70px; --arr-cy2: 30px; --arr-cr: -290deg; }
[data-arrival][data-go] .dm-arrival__piece--c2 { animation: dm-arr-chip 380ms cubic-bezier(.2, .7, .5, 1) calc(var(--arr-pop) + 20ms) forwards; --arr-cx: 52px; --arr-cy2: 58px; --arr-cr: 320deg; }
/* The halves are flung out sideways (a little down) and leave the screen whole: wax is never seen turning to a ghost. */
@keyframes dm-arr-half-l {
  0%   { opacity: 1; transform: none; animation-timing-function: cubic-bezier(.15, .85, .3, 1); }
  14%  { transform: translate(-16px, -2px) rotate(-7deg); animation-timing-function: cubic-bezier(.45, 0, .85, .55); }
  88%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-340px, 128px) rotate(-78deg) scale(.86); }
}
@keyframes dm-arr-half-r {
  0%   { opacity: 1; transform: none; animation-timing-function: cubic-bezier(.15, .85, .3, 1); }
  14%  { transform: translate(16px, -2px) rotate(7deg); animation-timing-function: cubic-bezier(.45, 0, .85, .55); }
  88%  { opacity: 1; }
  100% { opacity: 0; transform: translate(350px, 120px) rotate(80deg) scale(.86); }
}
@keyframes dm-arr-chip {
  0%   { opacity: 1; transform: none; }
  62%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--arr-cx), var(--arr-cy2)) rotate(var(--arr-cr)) scale(.85); }
}

/* ---------------------------------------------------------------- the burst (the component's own emitter), over everything
   Paper in the theme's colours and a sprinkle of the day's glyph (one in nine), thrown SIDEWAYS AND DOWN out of the
   crack. Each piece's flight is computed (drag, gravity, spin, flutter) and played by the compositor. A soft mask keeps
   the photo's face band clear: nothing flies across where a face would be, whatever the photo. */
[data-arrival].dm-arrival__burst {
  position: fixed; inset: 0; z-index: 1001; pointer-events: none; overflow: hidden; contain: strict;
  -webkit-mask: radial-gradient(ellipse 26% 25% at 50% 31%, transparent 72%, #000 100%);
          mask: radial-gradient(ellipse 26% 25% at 50% 31%, transparent 72%, #000 100%);
}
[data-arrival] .dm-arrival__bit { position: absolute; left: 0; top: 0; border-radius: 1.5px; opacity: 0; will-change: transform, opacity; }
/* The glyph wears a thin white rim, as a sticker does, so a dark one (🎓, 🦮) still reads over a dark photo. Static: the
   compositor moves it with its layer, never re-filters it per frame. */
[data-arrival] .dm-arrival__bit--glyph { font-size: 29px; line-height: 1; border-radius: 0; filter: drop-shadow(0 0 1.1px rgb(255 255 255 / .9)) drop-shadow(0 1px 2px rgb(0 0 0 / .2)); }
/* Version B: the 3D kit's own glyph, drawn at the same size. */
[data-arrival] .dm-arrival__bit--art { width: 29px; height: 29px; background: center / contain no-repeat; }
