/* ==========================================================================
   Maggie's card. Colour lives in the tokens; canvas effects read --c-* too (readPalette).
   ========================================================================== */
:root {
  --c-bg: #0B1026;
  --c-bg-2: #26336f;
  --c-ink: #FFF8EC;
  --c-muted: #C3C9E2;
  --c-accent: #F2B84B;      /* gold: the sung word */
  --c-accent-2: #8FB0E8;    /* disco blue */
  --c-accent-3: #18A86B;    /* Irish emerald */
  --c-accent-4: #FF7EB6;
  --c-paper: #f6ecdc;

  --font-display: 'Fraunces', 'Iowan Old Style', Georgia, serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'DM Mono', ui-monospace, Menlo, monospace;

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --gutter: clamp(16px, 5vw, 48px);
  /* the envelope, sized to the screen's short side */
  --env-w: min(76vw, 52vh, 440px);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; background: var(--c-bg); color: var(--c-ink); }
body { font-family: var(--font-body); overflow: hidden; overscroll-behavior: none; -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent;
  /* a phone: no double-tap zoom (a second tap on the seal would zoom the card), no long-press menus or text selection */
  touch-action: manipulation; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
html { touch-action: manipulation; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }   /* Safari: no bigger text when the phone turns */
button { font: inherit; color: inherit; }

/* ---------- layers: 3D → DOM scenes → 2D effects → UI ---------- */
#bg, #gl, #fx { position: fixed; inset: 0; width: 100%; height: 100%; display: block; }
#bg { z-index: 0; display: none; }
#gl { z-index: 1; pointer-events: none; opacity: 0; }
#stage { position: fixed; inset: 0; z-index: 2; pointer-events: none; }
#stage .is-interactive { pointer-events: auto; }
#fx { z-index: 3; pointer-events: none; }
#ui { position: fixed; z-index: 40; top: calc(var(--safe-t) + 12px); right: calc(env(safe-area-inset-right, 0px) + 12px); display: flex; gap: 8px; align-items: center; }
.scene { position: absolute; inset: 0; }

/* ---------- the envelope (gate and opening share it) ---------- */
.env { position: absolute; left: 50%; top: 50%; width: var(--env-w); aspect-ratio: 76 / 54; translate: -50% -55%; }
.env > * { position: absolute; inset: 0; }
.env-back { border-radius: 2.1% / 3%; background: linear-gradient(180deg, #dccbb0, #cfbb9a); box-shadow: 0 6vmin 14vmin rgba(0,0,0,.5); }
.env-front { border-radius: 2.1% / 3%; background: linear-gradient(170deg, #f8efe1 0%, #ede0ca 100%); clip-path: polygon(0 0, 50% 56%, 100% 0, 100% 100%, 0 100%); }
.env-flap { bottom: auto; height: 60%; transform-origin: 50% 0; background: linear-gradient(180deg, #f5ebd9, #e9dbc1); clip-path: polygon(0 0, 100% 0, 50% 100%); }
.env-flap.in { background: linear-gradient(0deg, #cdb894, #c1ab86); }
.env-seal {
  position: absolute; left: 50%; top: calc(50% - var(--env-w) * 54 / 76 * 0.55 + var(--env-w) * 54 / 76 * 0.6);
  width: calc(var(--env-w) * 0.22); height: calc(var(--env-w) * 0.22); translate: -50% -50%;
  border-radius: 50%; border: 0; padding: 0; cursor: pointer; display: grid; place-items: center;
  background: radial-gradient(circle at 36% 32%, #d0574b 0%, #a3281f 52%, #6f150f 100%);
  box-shadow: 0 1.6vmin 4vmin rgba(0,0,0,.45), inset 0 0 0 calc(var(--env-w) * .017) rgba(0,0,0,.14), inset 0 0 0 calc(var(--env-w) * .025) rgba(255,255,255,.06);
}
.env-seal svg { width: 38%; height: 38%; margin-left: 7%; fill: #fbe9d6; }
/* written by hand on the flap, above the seal: the gate writes it on; opening, it turns away with the flap and fades */
.env-to { position: absolute; left: 0; right: 0; top: 24%; bottom: auto; height: auto; text-align: center; white-space: nowrap; font: 400 calc(var(--env-w) * .08)/1.8 'Homemade Apple', 'Bradley Hand', 'Segoe Script', cursive; color: #22305f; pointer-events: none; }
.env-to span { display: inline-block; rotate: -4deg; opacity: .92; }
.env--gate .env-to { translate: 0 -50%; }
.env-to--write { -webkit-mask: linear-gradient(90deg, #000 45%, transparent 55%) 100% 0 / 260% 100% no-repeat; mask: linear-gradient(90deg, #000 45%, transparent 55%) 100% 0 / 260% 100% no-repeat; }
#gate .env-to { -webkit-mask: linear-gradient(90deg, #000 45%, transparent 55%) 100% 0 / 260% 100% no-repeat; mask: linear-gradient(90deg, #000 45%, transparent 55%) 100% 0 / 260% 100% no-repeat; }
#gate.is-ready .env-to, #gate.is-opening .env-to { animation: env-write 1.5s cubic-bezier(.45, .05, .4, 1) .25s both; }
@keyframes env-write { to { -webkit-mask-position: 0 0; mask-position: 0 0; } }
.env--probe { visibility: hidden; pointer-events: none; }
.env-seal:focus { outline: none; }
html.kbd .env-seal:focus-visible { outline: 3px solid var(--c-accent); outline-offset: 5px; }   /* after Tab only: no ring on a phone */
.env-seal:disabled { cursor: progress; opacity: .85; }

#gate { position: fixed; inset: 0; z-index: 10; cursor: pointer; background: radial-gradient(95% 60% at 50% 46%, var(--c-bg-2) 0%, var(--c-bg) 78%); }
#gate.is-ready .env-seal, #gate.is-opening .env-seal { animation: seal-pulse 2.4s ease-in-out infinite; }
#gate.is-opening .env-seal { animation-play-state: paused; }   /* held where the tap caught it; the opening starts from there */
#gate.is-gone { display: none; }
.gate__progress { position: absolute; left: 50%; bottom: calc(var(--safe-b) + 12vh); width: 120px; height: 2px; translate: -50% 0; border-radius: 2px; background: rgba(255,255,255,.12); overflow: hidden; transition: opacity .4s; }
.gate__progress::after { content: ''; display: block; height: 100%; width: calc(var(--progress, 0) * 100%); background: var(--c-accent); transition: width .3s ease; }
#gate.is-ready .gate__progress { opacity: 0; }
@keyframes seal-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.045); } }

/* the opening scene: same envelope, then the photo, then its dissolve into 3D */
.scene--opening { overflow: hidden; }
.scene--opening .bgfill { position: absolute; inset: 0; background: radial-gradient(95% 60% at 50% 46%, var(--c-bg-2) 0%, var(--c-bg) 78%); }
.scene--opening .env-seal { pointer-events: none; }
/* the photo: laid out once at its full-screen size, then only moved and scaled (no layout work per frame) */
.open-clip { position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: hidden; will-change: opacity; }
.open-photo { position: absolute; left: 0; top: 0; transform-origin: 0 0; will-change: transform; box-shadow: 0 1vmin 3vmin rgba(0,0,0,.35); border-radius: 0.6%/0.5%; }
.scene--opening .env-back, .scene--opening .env-front, .scene--opening .env-flap, .scene--opening .env-seal, .scene--opening .env-to { will-change: transform, opacity; }

/* ---------- the phrase: one short chunk at a time ---------- */
.scene--phrase { pointer-events: none; }
.phrase { position: absolute; left: var(--gutter); right: var(--gutter); top: calc(var(--safe-t) + 7vh); height: 18vh; font-family: var(--font-display); font-weight: 650; color: #fff; text-shadow: 0 2px 18px rgba(0,0,0,.42); }
.phrase .chunk { position: absolute; left: 0; right: 0; top: 0; text-align: center; white-space: nowrap; font-size: min(10vw, 7.2vh, 76px); line-height: 1.15; opacity: 0; transform-origin: 50% 60%; will-change: transform, opacity, filter; }
.phrase .w { display: inline-block; opacity: 0; will-change: transform, opacity, filter; }

/* ---------- after the song: the menu ---------- */
#keepsake { position: fixed; inset: 0; z-index: 6; pointer-events: none; }
#keepsake.is-visible { pointer-events: none; }
.menu { position: absolute; left: var(--gutter); right: var(--gutter); bottom: calc(var(--safe-b) + 3.5vh); max-width: 560px; margin: 0 auto; opacity: 0; transform: translateY(14px); transition: opacity .7s ease .3s, transform .7s var(--ease-out) .3s; pointer-events: auto; }
#keepsake.is-visible .menu { opacity: 1; transform: none; }
.menu__label { display: flex; justify-content: space-between; align-items: center; gap: 12px; font: 500 clamp(11px, 3vw, 13px) var(--font-mono); letter-spacing: .12em; text-transform: uppercase; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.8); margin-bottom: 10px; }
.replay { font: 600 clamp(13px, 3.6vw, 15px) var(--font-body); letter-spacing: 0; text-transform: none; color: #1d1606; background: var(--c-accent); border: 0; border-radius: 999px; padding: 8px 16px; min-height: 40px; cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,.3); }
.menu__tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.tile { display: block; padding: 0; border: 1px solid rgba(255,255,255,.22); border-radius: 14px; overflow: hidden; background: rgba(8,12,30,.86); color: #fff; cursor: pointer; text-align: left; box-shadow: 0 0 0 2px var(--c) inset, 0 10px 30px rgba(0,0,0,.35); font: inherit; }
.tile img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.tile span { display: block; padding: 9px 12px 11px; font: 600 clamp(14px, 3.9vw, 17px) var(--font-body); }
.tile small { display: block; font: 500 clamp(11px, 2.9vw, 12px) var(--font-mono); color: rgba(255,255,255,.72); margin-top: 3px; }

/* ---------- a sequence, full screen and always landscape ---------- */
#seq { position: fixed; inset: 0; z-index: 50; background: #000; overflow: hidden; }
html.is-seq #ui { display: none; }
/* the landscape frame: on a phone held upright it is turned on its side, so the video is always wide */
.seq__frame { position: absolute; left: 0; top: 0; width: 100%; height: 100%; container-type: size; }
@media (orientation: portrait) {
  .seq__frame { width: 100vh; height: 100vw; width: 100dvh; height: 100dvw; transform-origin: 0 0; transform: translateX(100vw) rotate(90deg); }
  @supports (width: 100dvw) { .seq__frame { transform: translateX(100dvw) rotate(90deg); } }
}
.seq__frame video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
.seq__kicker { position: absolute; left: max(6cqw, calc(env(safe-area-inset-left, 0px) + 8px)); top: 6cqh; font: 500 3.2cqh var(--font-mono); letter-spacing: .12em; text-transform: uppercase; color: #fff; background: rgba(0,0,0,.45); border-left: 3px solid var(--c, #fff); padding: .5em .8em; }
.seq__song { position: absolute; left: max(6cqw, calc(env(safe-area-inset-left, 0px) + 8px)); bottom: max(8cqh, calc(env(safe-area-inset-bottom, 0px) + 8px)); max-width: 70cqw; padding-left: 2.2cqh; border-left: .55cqh solid var(--c, #fff); opacity: 0; text-shadow: 0 2px 14px rgba(0,0,0,.6); }
.seq__title { font: 650 8.2cqh/1.05 var(--font-display); color: #fff; }
.seq__artist { margin-top: 1.2cqh; font: 500 3.1cqh/1.3 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: rgba(255,255,255,.82); }
.seq__close { position: absolute; right: max(5cqw, calc(env(safe-area-inset-right, 0px) + 8px)); top: 5cqh; width: 44px; height: 44px; width: max(44px, 11cqh); height: max(44px, 11cqh); padding: 0; border-radius: 50%; color: #fff; background: rgba(0,0,0,.45); border: 1px solid rgba(255,255,255,.3); cursor: pointer; }
.seq__close svg { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; width: 42%; height: 42%; }
.seq__sound { position: absolute; left: 50%; top: 5cqh; translate: -50% 0;   /* top centre: clear of the song's title and artist */ border: 0; border-radius: 999px; padding: .8em 1.4em; min-height: 44px; font: 600 max(15px, 4cqh) var(--font-body); color: #1d1606; background: var(--c-accent); cursor: pointer; box-shadow: 0 8px 26px rgba(0,0,0,.4); }
.seq__end { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 3cqw; background: rgba(0,0,0,.55); }
.seq__end button, .seq__end a { border: 0; border-radius: 999px; padding: .8em 1.4em; min-height: 44px; font: 600 max(15px, 4cqh) var(--font-body); text-decoration: none; cursor: pointer; }
.seq__again { color: #1d1606; background: var(--c-accent); }
.seq__back { color: #fff; background: transparent; box-shadow: inset 0 0 0 1px rgba(255,255,255,.4); }
.seq__loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: #05060c; font: 500 max(13px, 3.6cqh) var(--font-mono); letter-spacing: .14em; color: #fff; text-transform: uppercase; }
.seq__soon { position: absolute; inset: 0; display: grid; place-items: center; align-content: center; gap: 4cqh; background: #04060d; }
.seq__soon img { height: 52cqh; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 14px; filter: saturate(.8) brightness(.8); box-shadow: 0 0 0 2px var(--c, #fff) inset; }
.seq__soon p { margin: 0; font: 600 7cqh var(--font-display); color: #fff; }
/* a sequence's own page (disco/, irish/) */
body.route { background: #000; }

/* ---------- UI ---------- */
.icon-btn { width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(255,255,255,.18); background: rgba(10,6,20,.38); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); position: relative; padding: 0; cursor: pointer; opacity: 0; transition: opacity .6s ease; }
.is-open .icon-btn { opacity: .8; }
/* the icons are centred by position, not by the button's own layout, which older Safari does its own way */
.icon-btn svg { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; width: 20px; height: 20px; }
.icon-btn .off { display: none; }
.icon-btn[aria-pressed='true'] .on { display: none; }
.icon-btn[aria-pressed='true'] .off { display: block; }
.pill { border: 0; border-radius: 999px; padding: 10px 16px; background: var(--c-ink); color: var(--c-bg); font-weight: 600; cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,.35); }

.file-warning, html.is-file .file-warning { display: none; }
html.is-file .file-warning { display: block; position: fixed; inset: auto 12px 12px; z-index: 99; background: #fff; color: #111; padding: 14px 16px; border-radius: 10px; font: 14px/1.45 system-ui, sans-serif; }
@media (prefers-reduced-motion: reduce) { #gate.is-ready .env-seal, #gate.is-opening .env-seal { animation: none; } #gate .env-to { -webkit-mask: none; mask: none; animation: none !important; } }
