/* Invitation card for every design. Colours, fonts and sizes come from the per-invitation
   <style> block (src/builder/render.js): --bg --paper --ink --accent --orn --muted, .f-<field>. */
* { box-sizing: border-box; }
html { min-width: 280px; background: var(--bg); }
body {
  margin: 0; min-height: 100svh; background: var(--bg); color: var(--ink);
  font-family: var(--font-body); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
body.is-sealed, body.is-opening { overflow: hidden; }
p, h1 { margin: 0; font-weight: inherit; }
:root { --ease: cubic-bezier(.22, .61, .36, 1); }

/* ---------- page backgrounds ---------- */
.page-velvet { background: radial-gradient(ellipse at 50% 25%, color-mix(in srgb, var(--bg), #fff 16%), var(--bg) 60%, color-mix(in srgb, var(--bg), #000 35%)) fixed; }
.page-spotlight { background: radial-gradient(ellipse at 50% 0%, #f7dfc8 0%, color-mix(in srgb, var(--bg), #fff 40%) 28%, var(--bg) 70%) fixed; }
.page-linen { background: repeating-linear-gradient(0deg, #ffffff22 0 1px, transparent 1px 4px), repeating-linear-gradient(90deg, #0000000a 0 1px, transparent 1px 4px), var(--bg) fixed; }
.page-tiles { background: linear-gradient(color-mix(in srgb, var(--bg) 90%, transparent), color-mix(in srgb, var(--bg) 90%, transparent)), url('/b/tile.svg') 0 0 / 120px 120px; }
.page-stars, .page-bokeh { background: radial-gradient(ellipse at 50% -10%, color-mix(in srgb, var(--bg), #fff 18%), var(--bg) 70%) fixed; }
.page-extras { position: fixed; inset: 0; pointer-events: none; overflow: hidden; }
.page-extras i { position: absolute; border-radius: 50%; }
.page-extras--stars i { background: #fff; opacity: .7; animation: twinkle 5s ease-in-out infinite; }
.page-extras--bokeh i { background: radial-gradient(circle, color-mix(in srgb, var(--orn) 40%, transparent), transparent 70%); filter: blur(1px); animation: drift 16s linear infinite; }

/* ---------- the card ---------- */
.card { position: relative; padding: clamp(28px, 7vw, 64px) clamp(14px, 4vw, 40px); outline: none; }
.paper {
  position: relative; width: min(100%, 520px); margin: 0 auto; container-type: inline-size;
  background: var(--paper); box-shadow: 0 1px 2px #0000000f, 0 30px 70px -30px #00000066;
}
.content { position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 12cqw 10cqw; overflow-wrap: anywhere; }
.content > * { max-width: 100%; }
.caps { text-transform: uppercase; letter-spacing: .3em; padding-left: .3em; }
.f-title, .f-date, .f-venue, .f-times { font-weight: 500; }
.f-intro { margin-top: 4cqw; line-height: 1.45; }
.names { display: flex; flex-direction: column; align-items: center; margin: 5cqw 0 3cqw; font-weight: 400; line-height: 1.05; }
.names .amp { margin: .6cqw 0; font-family: var(--font-title); font-style: italic; font-size: 6.4cqw; line-height: 1.2; color: var(--orn); }
.f-date { letter-spacing: .08em; font-variant-numeric: lining-nums; }
.f-receptionLabel { line-height: 1.5; }
.f-times { margin-top: 1.4cqw; font-variant-numeric: lining-nums; letter-spacing: .06em; }
.f-venue { margin-top: 2cqw; line-height: 1.3; }
.map {
  margin-top: 3.6cqw; padding: 2cqw 5.4cqw; border-radius: 999px; border: 1px solid var(--accent); color: var(--accent);
  font-family: var(--font-body); font-size: 2.9cqw; letter-spacing: .2em; text-transform: uppercase; text-decoration: none;
  transition: background-color .2s var(--ease), color .2s var(--ease);
}
.map:hover, .map:focus-visible { background: var(--accent); color: var(--paper); outline: none; }
.f-closing { margin-top: 7cqw; line-height: 1.7; }
.f-welcome { margin-top: 3cqw; line-height: 1.15; }

/* Dividers */
.divider { width: 16cqw; height: 1px; margin: 6cqw 0 5cqw; background: var(--orn); opacity: .6; }
.divider--deco { width: 40cqw; height: auto; background: none; opacity: 1; color: var(--orn); }
.divider--deco svg { display: block; width: 100%; }
.divider--star { display: flex; align-items: center; gap: 3cqw; width: auto; height: auto; background: none; opacity: 1; color: var(--orn); }
.divider--star::before, .divider--star::after { content: ''; width: 14cqw; height: 1px; background: currentColor; opacity: .6; }
.divider--star svg { width: 3.4cqw; height: 3.4cqw; fill: currentColor; }
.divider--leaf { width: 30cqw; height: 4cqw; opacity: .75; background:
  radial-gradient(ellipse 22% 38% at 50% 50%, var(--orn) 96%, transparent) center / 8cqw 4cqw no-repeat,
  linear-gradient(var(--orn), var(--orn)) center / 100% 1px no-repeat; }

/* ---------- frames ---------- */
/* Florals down both sides, the printed proportions of the original Ftesa card. */
.frame-florals .paper { display: flex; flex-direction: column; aspect-ratio: 813 / 1204; box-shadow: 0 0 90px #3d2c2212; }
.frame-florals .content { flex: 1; justify-content: center; padding: 12cqw 19cqw; }
.fl {
  position: absolute; top: 0; bottom: 0; width: 20cqw; background: var(--orn); pointer-events: none;
  -webkit-mask-image: url('/b/florals.webp'), linear-gradient(transparent, #000 4%, #000 94%, transparent), linear-gradient(90deg, transparent, #000 35%);
  mask-image: url('/b/florals.webp'), linear-gradient(transparent, #000 4%, #000 94%, transparent), linear-gradient(90deg, transparent, #000 35%);
  -webkit-mask-size: auto 100%, 100% 100%, 100% 100%; mask-size: auto 100%, 100% 100%, 100% 100%;
  -webkit-mask-position: right center, 0 0, 0 0; mask-position: right center, 0 0, 0 0;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-composite: source-in; mask-composite: intersect;
}
.fl--r { right: 0; }
.fl--l { left: 0; transform: scaleX(-1); }

/* Double gold rule, with corner flourishes or stars. */
.frame-double .paper::before, .frame-double .paper::after, .frame-stars .paper::before, .frame-stars .paper::after { content: ''; position: absolute; pointer-events: none; }
.frame-double .paper::before, .frame-stars .paper::before { inset: 3.4cqw; border: 1px solid color-mix(in srgb, var(--orn) 65%, transparent); }
.frame-double .paper::after, .frame-stars .paper::after { inset: 4.6cqw; border: 1px solid color-mix(in srgb, var(--orn) 30%, transparent); }
.frame-double .content, .frame-stars .content { padding: 14cqw 12cqw 13cqw; }
.corners .corner { position: absolute; width: 17cqw; height: 17cqw; color: var(--orn); top: 2cqw; left: 2cqw; }
.corners .corner:nth-child(2) { left: auto; right: 2cqw; transform: scaleX(-1); }
.corners .corner:nth-child(3) { top: auto; bottom: 2cqw; transform: scaleY(-1); }
.corners .corner:nth-child(4) { top: auto; left: auto; bottom: 2cqw; right: 2cqw; transform: scale(-1); }
.corner-stars .star { position: absolute; width: 5cqw; height: 5cqw; fill: var(--orn); }
.corner-stars .star:nth-child(1) { top: 1.4cqw; left: 1.4cqw; } .corner-stars .star:nth-child(2) { top: 1.4cqw; right: 1.4cqw; }
.corner-stars .star:nth-child(3) { bottom: 1.4cqw; left: 1.4cqw; } .corner-stars .star:nth-child(4) { bottom: 1.4cqw; right: 1.4cqw; }

/* Arch: a rounded-top panel with a double gold edge. */
.frame-arch .paper {
  border: 1px solid color-mix(in srgb, var(--orn) 55%, transparent); border-radius: 50cqw 50cqw 2cqw 2cqw;
  box-shadow: inset 0 0 0 2.4cqw var(--paper), inset 0 0 0 calc(2.4cqw + 1px) color-mix(in srgb, var(--orn) 35%, transparent), 0 0 80px color-mix(in srgb, var(--orn) 18%, transparent), 0 40px 80px -30px #000;
}
.frame-arch .content { padding: 12cqw 11cqw 12cqw; }

/* Tile bands top and bottom. */
.band { height: 20cqw; background: url('/b/tile.svg') 0 0 / 20cqw 20cqw repeat-x; border-bottom: 3px solid var(--orn); }
.band--bottom { border-bottom: 0; border-top: 3px solid var(--orn); }
.frame-band .content { padding: 7cqw 10cqw 9cqw; }

/* Olive branches above and below, drawing themselves once the card is shown. */
.olive { fill: none; stroke: var(--orn); stroke-width: 1.2; stroke-linecap: round; }
.olive-wrap { width: 54cqw; }
.olive-wrap svg { display: block; width: 100%; }
.olive-wrap--top { margin-bottom: 5cqw; }
.olive-wrap--bottom { margin-top: 7cqw; transform: scale(-1, -1); }
.olive-wrap path, .olive-wrap ellipse { stroke-dasharray: 1; }
body:not(.is-sealed):not(.is-opening) .olive-wrap path, body:not(.is-sealed):not(.is-opening) .olive-wrap ellipse { animation: draw 2200ms 300ms cubic-bezier(.45, 0, .2, 1) both; }
body:not(.is-sealed):not(.is-opening) .olive-wrap--bottom path, body:not(.is-sealed):not(.is-opening) .olive-wrap--bottom ellipse { animation-delay: 1200ms; }

/* Minimal: no paper, just generous space and two hairlines. */
.frame-minimal .paper { background: transparent; box-shadow: none; }
.frame-minimal .content { padding: 10cqw 4cqw; border-top: 1px solid color-mix(in srgb, var(--ink) 20%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--ink) 20%, transparent); }

/* ---------- emblems ---------- */
.emblem { margin-bottom: 5cqw; color: var(--orn); }
.emblem--sunburst svg { display: block; width: 44cqw; }
.emblem--lemon svg { display: block; width: 19cqw; filter: drop-shadow(0 4px 6px #c8961a40); }
.emblem--monogram {
  display: flex; align-items: center; justify-content: center; gap: 2.4cqw; width: 17cqw; height: 17cqw; border-radius: 50%;
  border: 1px solid var(--orn); box-shadow: 0 0 0 1.2cqw var(--paper), 0 0 0 calc(1.2cqw + 1px) color-mix(in srgb, var(--orn) 40%, transparent);
  font-family: var(--font-title); font-size: 5.6cqw; line-height: 1; color: var(--accent);
}
.emblem--monogram i { width: 1px; height: 6.5cqw; background: var(--orn); rotate: 20deg; }
.emblem--seal .seal { display: block; width: 24cqw; aspect-ratio: 1; margin-top: -2cqw; }
.seal { background: var(--seal) center / contain no-repeat; filter: drop-shadow(0 1px 1px #4a32187a) drop-shadow(1px 5px 6px #4a321847); }

/* ---------- rendered artwork (designs with `art`, images from tools/art) ---------- */
/* The page backdrop sits on a fixed layer: background-attachment: fixed is ignored on iOS. */
body.page-art { background: transparent; }
.page-art::before { content: ''; position: fixed; inset: 0; z-index: -1; background: var(--art-page); }
.paper-art .paper { background: var(--art-paper); }
/* A minimal design over a scene still needs a card to read on. */
.paper-art .frame-minimal .paper { background: var(--art-paper); border-radius: 3cqw; box-shadow: 0 30px 70px -30px #0000004d; }
.paper-art .frame-minimal .content { border: 0; padding: 12cqw 9cqw; }
/* 9-slice frame: corners keep their shape, edges stretch or repeat. Drawn on its own layer
   so the frame width can be in cqw of the paper. */
.frame-art .paper { box-shadow: 0 2px 4px #0000001a, 0 30px 70px -30px #00000073; }
.frame-art .content { padding: calc(var(--frame-pad) + 6cqw) calc(var(--frame-pad) + 4cqw); }
.art-frame { position: absolute; inset: 0; pointer-events: none; border-style: solid; border-width: var(--frame-w); border-image: var(--frame-src) var(--frame-slice) / var(--frame-w) var(--frame-repeat); }
/* A painted border a 9-slice can't keep (art.card), e.g. a ring of roses: the top and bottom garlands
   keep their shape and only the side leaves between them stretch with the texts. */
.frame-painted .paper { box-shadow: 0 2px 4px #0000001a, 0 30px 70px -30px #00000073; }
/* On a layer inside the paper, so cqw is the paper's width (the paper is the size container). */
.frame-painted .paper::before { content: ''; position: absolute; inset: 0; pointer-events: none; background: var(--card-art); }
.frame-painted .content { justify-content: center; min-height: var(--card-min); padding: var(--card-pad); }
.emblem--art { width: var(--emblem-w); aspect-ratio: var(--emblem-ratio); background: var(--emblem-src) center / contain no-repeat; }
.emblem--butterfly svg, .emblem--heart svg { display: block; width: 16cqw; fill: var(--orn); }
.emblem--heart svg { width: 9cqw; }
.divider--star .heart { width: 3.6cqw; fill: currentColor; }
.divider--butterfly svg { width: 7cqw; height: auto; fill: currentColor; }

/* Age (birthdays): a large numeral with a small unit under it. */
.age { display: flex; flex-direction: column; align-items: center; margin-top: 3cqw; line-height: .9; }
.f-age { font-variant-numeric: lining-nums; }
.age-unit { margin-top: 1cqw; font-family: var(--font-title); font-size: 3.2cqw; letter-spacing: .4em; padding-left: .4em; text-transform: uppercase; color: var(--muted); }
.names--one { margin-top: 3cqw; }

/* Floating butterflies, confetti, petals, balloons or hearts, drifting over the page. */
.floaters { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
/* They drift behind the card, so the text stays clean. */
.card { z-index: 1; }
.floaters i { position: absolute; top: 100%; display: block; scale: var(--s); animation: float-up 14s linear infinite; }
.floaters--confetti i, .floaters--petals i { top: -6%; animation-name: fall; }
.floaters svg { display: block; width: 34px; fill: var(--orn); }
.floaters--hearts svg { width: 18px; opacity: .7; }
.floaters--butterflies i { animation-name: flutter-by; }
.floaters--butterflies .wing { transform-box: fill-box; animation: flap .32s ease-in-out infinite alternate; }
.floaters--butterflies .wing--l { transform-origin: 100% 50%; }
.floaters--butterflies .wing--r { transform-origin: 0 50%; }
.floaters--confetti b { display: block; width: 7px; height: 12px; border-radius: 1px; background: var(--c); rotate: var(--r); animation: tumble 1.8s linear infinite; }
.floaters--petals b { display: block; width: 16px; height: 12px; border-radius: 80% 0 80% 0; background: radial-gradient(ellipse at 30% 30%, #fff8f6, var(--orn)); opacity: .85; animation: tumble 4s ease-in-out infinite; }
.floaters--balloons b { position: relative; display: block; width: 38px; height: 46px; border-radius: 50% 50% 48% 48% / 56% 56% 44% 44%; opacity: .92;
  background: radial-gradient(circle at 34% 28%, #ffffffcc 0 6%, transparent 22%), radial-gradient(circle at 50% 45%, var(--c), color-mix(in srgb, var(--c), #000 22%)); }
.floaters--balloons b::after { content: ''; position: absolute; left: 50%; top: 100%; width: 1px; height: 46px; background: color-mix(in srgb, var(--ink) 35%, transparent); }
/* Only once the card is showing, and never in the generator's still thumbnails. */
.is-sealed .floaters, .is-opening .floaters, [data-preview="sealed"] .floaters { display: none; }

/* ---------- hero: the full-screen opening scene (designs with a rendered backdrop) ---------- */
/* It cancels the card's padding so the scene fills the screen edge to edge. */
.hero {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 100svh; margin: calc(-1 * clamp(28px, 7vw, 64px)) calc(-1 * clamp(14px, 4vw, 40px)) clamp(28px, 7vw, 64px);
  padding: 72px 20px 96px; text-align: center;
}
.hero-inner { position: relative; width: min(100%, 520px); container-type: inline-size; display: flex; flex-direction: column; align-items: center; gap: 2.4cqw; }
/* Hero text uses the design's colour for text on its backdrop (light on velvet or night sky, dark on
   plaster or sky), over a soft halo of the backdrop's own tone, so it reads on any scene. */
.hero { --hero-ink: var(--cv-on-dark); }
.hero-inner::before {
  content: ''; position: absolute; inset: -18% -16%; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--cv-dark) 70%, transparent), color-mix(in srgb, var(--cv-dark) 30%, transparent) 62%, transparent);
  filter: blur(10px);
}
.hero .f-name1, .hero .f-name2 { color: var(--hero-ink); }
.hero-emblem { margin-bottom: 2cqw; }
.hero-title { font-family: var(--font-title); font-size: 3.4cqw; letter-spacing: .36em; padding-left: .36em; text-transform: uppercase; color: var(--hero-ink); opacity: .82; }
.hero-names { display: flex; flex-direction: column; align-items: center; line-height: 1.02; zoom: 1.35; }
.hero-names .amp { font-family: var(--font-title); font-style: italic; font-size: 6cqw; color: var(--hero-ink); opacity: .75; margin: .4cqw 0; }
.hero-date { font-family: var(--font-title); font-size: 4.2cqw; letter-spacing: .24em; padding-left: .24em; text-transform: uppercase; color: var(--hero-ink); }
.hero-scroll {
  position: absolute; left: 50%; bottom: max(28px, env(safe-area-inset-bottom)); translate: -50% 0;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  font-family: var(--font-title); font-size: 12px; letter-spacing: .3em; padding-left: .3em; text-transform: uppercase; color: var(--hero-ink);
  text-shadow: 0 0 12px var(--cv-dark), 0 0 2px var(--cv-dark);
}
.hero-scroll svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.4; animation: nudge 2.2s ease-in-out infinite; }
/* Akuarel: the names up in the painted sky, clear of the village and the couple. */
.design-akuarel .hero { justify-content: flex-start; padding-top: max(5svh, 32px); }
.design-akuarel .hero-names { zoom: 1.12; }
/* Kristal: the chandelier is the opening scene only (it scrolls away; the page behind the card is
   plain paper) and the names sit on the paper under it. */
.design-kristal .hero {
  justify-content: flex-end; padding-bottom: max(16svh, 96px);
  background: linear-gradient(transparent 82%, #f6f0e4), url('/b/art/kristal/hero.webp') 50% 0 / cover no-repeat #faf5ea;
}
/* Wide screens: the (portrait) scene at full height in the middle, its sides fading into the paper,
   and the names kept at phone size so they stay on the paper under the chandelier. */
@media (min-aspect-ratio: 3/4) {
  .design-kristal .hero { background: #faf5ea; }
  .design-kristal .hero::before {
    content: ''; position: absolute; top: 0; bottom: 0; left: 50%; translate: -50% 0; aspect-ratio: 1200 / 2143;
    background: url('/b/art/kristal/hero.webp') 50% 0 / 100% 100% no-repeat; pointer-events: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  }
  .design-kristal .hero-inner { width: min(100%, 380px); }
}

/* ---------- sections under the card: photos, the place, dress code ---------- */
/* Torn-paper sheets: the paper colour with ragged top and bottom edges. */
.sec { position: relative; width: min(100%, 520px); margin: clamp(28px, 7vw, 56px) auto 0; text-align: center; color: var(--ink); }
.sec--sheet {
  display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 46px 28px 50px;
  background: var(--paper); filter: drop-shadow(0 18px 30px #00000026);
  -webkit-mask: url('/b/torn.svg') top / 100% 16px no-repeat, linear-gradient(#000, #000) 0 15px / 100% calc(100% - 30px) no-repeat, url('/b/torn-b.svg') bottom / 100% 16px no-repeat;
  mask: url('/b/torn.svg') top / 100% 16px no-repeat, linear-gradient(#000, #000) 0 15px / 100% calc(100% - 30px) no-repeat, url('/b/torn-b.svg') bottom / 100% 16px no-repeat;
}
.paper-art .sec--sheet { background: var(--art-paper); }
.sec-icon { width: 30px; height: 30px; fill: none; stroke: var(--orn); stroke-width: 1.3; stroke-linejoin: round; }
.sec-title { margin: 0; font-family: var(--font-names); font-weight: 400; font-size: 40px; line-height: 1.1; color: var(--accent); }
.sec-lead { font-family: var(--font-title); font-size: 21px; line-height: 1.3; }
.sec-text { font-family: var(--font-body); font-size: 17px; line-height: 1.55; color: var(--muted); max-width: 34ch; }
.sec-meta { font-family: var(--font-title); font-size: 15px; letter-spacing: .14em; color: var(--accent); font-variant-numeric: lining-nums; }
.sec .map { margin-top: 14px; padding: 12px 26px; font-size: 12px; }
/* A design's own paintings (art.place, art.dress): the venue as a small print, the dress code
   washed into the sheet (its painted paper matches the design's paper). */
.sec-art { display: block; width: 100%; height: auto; margin: 6px 0 10px; }
.sec-art--print { width: 68%; margin: 10px 0 18px; rotate: -1.5deg; box-shadow: 0 1px 2px #3c28141f, 0 14px 30px -6px #3c281433; }
.sec-art--wash { width: calc(100% + 28px); max-width: none; -webkit-mask-image: linear-gradient(transparent, #000 8%, #000 90%, transparent); mask-image: linear-gradient(transparent, #000 8%, #000 90%, transparent); }

/* Photos as prints, slightly askew, overlapping when there are several. */
.sec--photos { display: flex; justify-content: center; align-items: center; padding: 10px 0; }
.photo { margin: 0; padding: 10px 10px 34px; background: #fffdf8; box-shadow: 0 2px 4px #0000001f, 0 22px 40px -14px #00000066; rotate: var(--r, -2deg); }
.photo img { display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; background: color-mix(in srgb, var(--paper), var(--ink) 8%); }
.sec--n1 .photo { width: min(78%, 360px); --r: -1.5deg; }
.sec--n2 .photo, .sec--n3 .photo { width: 52%; }
.sec--n2 .photo:nth-child(1) { --r: -5deg; margin-right: -8%; translate: 0 12px; }
.sec--n2 .photo:nth-child(2) { --r: 4deg; z-index: 1; }
.sec--n3 .photo { width: 42%; }
.sec--n3 .photo:nth-child(1) { --r: -7deg; margin-right: -10%; translate: 0 18px; }
.sec--n3 .photo:nth-child(2) { --r: 1deg; z-index: 1; }
.sec--n3 .photo:nth-child(3) { --r: 6deg; margin-left: -10%; translate: 0 22px; }

/* Signed off at the very end. */
.sign-off { margin: clamp(36px, 9vw, 64px) auto 8px; text-align: center; }
.so-names { font-family: var(--font-names); font-size: 36px; line-height: 1.15; color: var(--accent); }
.so-date { margin-top: 6px; font-family: var(--font-title); font-size: 13px; letter-spacing: .3em; padding-left: .3em; text-transform: uppercase; color: var(--muted); }
.report { display: inline-block; margin-top: 28px; padding: 8px; font-family: var(--font-body); font-size: 11px; letter-spacing: .04em; color: var(--muted); opacity: .7; }
.page-art .report { color: var(--cv-on-dark); }
.page-art .so-names, .page-art .so-date { color: var(--cv-on-dark); text-shadow: 0 0 14px var(--cv-dark), 0 0 3px var(--cv-dark); }

/* ---------- scroll reveals (invite.js marks what scrolled into view) ---------- */
/* Only once the script is running, so the page never stays hidden without it. */
.js-reveal [data-reveal] { opacity: 0; translate: 0 26px; transition: opacity 900ms var(--ease), translate 900ms var(--ease); transition-delay: calc(var(--i, 0) * 140ms); }
.js-reveal .photo[data-reveal] { translate: 0 40px; rotate: calc(var(--r, 0deg) * 3); transition-property: opacity, translate, rotate; }
.js-reveal [data-reveal].is-in { opacity: 1; translate: 0 0; rotate: var(--r, 0deg); }

/* ---------- music toggle (cards without a cover) ---------- */
.music {
  position: fixed; z-index: 3; right: max(16px, env(safe-area-inset-right)); bottom: max(16px, env(safe-area-inset-bottom));
  width: 48px; height: 48px; display: grid; place-items: center; padding: 0; cursor: pointer;
  border: 1px solid var(--orn); border-radius: 50%; background: color-mix(in srgb, var(--paper) 88%, transparent); color: var(--accent);
  box-shadow: 0 6px 18px #0000004d; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.music svg { width: 20px; height: 20px; }
.music[aria-pressed="true"] { background: var(--accent); color: var(--paper); }
.music:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* The card rises in once there is no cover in front of it. */
.is-open .card { animation: rise 900ms var(--ease) both; }
.is-sealed .card, .is-opening .card { visibility: visible; }

.no-motion *, .no-motion *::before, .no-motion *::after { animation: none !important; transition: none !important; }

@keyframes rise { from { opacity: 0; translate: 0 18px; } }
@keyframes draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes twinkle { 50% { opacity: .2; } }
@keyframes nudge { 50% { translate: 0 6px; } }
@keyframes float-up { to { transform: translateY(calc(-100vh - 120%)) translateX(6vw); } }
@keyframes fall { to { transform: translateY(calc(100vh + 120%)) translateX(-8vw); } }
@keyframes flutter-by { 0% { transform: translate(0, 0) rotate(-8deg); } 25% { transform: translate(8vw, -30vh) rotate(10deg); } 50% { transform: translate(-4vw, -60vh) rotate(-6deg); } 75% { transform: translate(10vw, -85vh) rotate(8deg); } 100% { transform: translate(0, calc(-100vh - 120%)) rotate(0); } }
@keyframes flap { to { transform: scaleX(.25); } }
@keyframes tumble { 50% { transform: rotateX(70deg) rotateY(40deg); } 100% { transform: rotateX(0) rotateY(360deg); } }
@keyframes drift { 0% { transform: translateY(0); opacity: 0; } 15%, 85% { opacity: 1; } 100% { transform: translateY(-40vh); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .olive-wrap path, .olive-wrap ellipse { stroke-dashoffset: 0; }
}

/* ---------- per-design touches ---------- */
/* Disko: neon script names and a night-club card. */
.design-disko .f-name1, .design-disko .f-name2, .design-disko .f-welcome {
  text-shadow: 0 0 2px #fff, 0 0 8px currentColor, 0 0 18px currentColor, 0 0 36px color-mix(in srgb, currentColor 60%, transparent);
}
.design-disko .paper { box-shadow: 0 0 0 1px #9b7bff55, 0 0 60px #ff5fc133, 0 30px 70px -30px #000; }
.design-disko .frame-minimal .content { border-color: #9b7bff55; }
