/* =============================================================
   Folded Studio - "christmas-card" page type
   A cranberry linen table, a hanging paper parol, a cream
   envelope, and a pop-up card that comes out of it.
   Everything is paper: flat colors, creases and soft shadows.
   No glow, no gradients, no snow.

   Each step of the scene is a class on .scene, added by app.js:
     is-turning / is-turned  the envelope turns over
     is-back                 its back (flap side) is showing
     flap-open               the flap is lifted
     card-out                the card slides out
     card-free               the envelope drops away, card upright
     card-ready              "Tap the card" shows
     card-open               the cover swings open on its fold
     tree-up                 the pop-up tree rises
     note-open               the message panel folds down
   ============================================================= */

/* ---------- Colors (one block per theme) ----------
   To add a theme: copy this block, rename it, change the colors,
   and add the name to CARD_THEMES in scripts/build.js. */
:root,
[data-theme="classic"] {
  --table: #7A1F2B;       /* cranberry linen behind everything */
  --card: #F6F0E2;        /* warm cream card stock */
  --card-fold: #E4D8C0;   /* the shaded side of a fold */
  --env: #EFE5D0;         /* envelope paper, a touch warmer than the card */
  --env-fold: #E2D5BB;    /* envelope side flaps */
  --green: #1F4A36;       /* deep green */
  --green-print: #2F6A4C; /* green "ink" of the holly print */
  --tree-dark: #1A3F2E;   /* pop-up tree, shaded half */
  --tree-light: #275A42;  /* pop-up tree, lit half */
  --red: #A3313C;         /* cranberry paper */
  --red-fold: #84232D;    /* its shaded side */
  --red-ink: #6E1A24;
  --kraft: #B48A5E;       /* kraft paper */
  --kraft-ink: #7A5A3A;
  --ink: #3A2F2A;         /* postmark ink */
  --ink-text: #2E2924;    /* message and captions */
  --photo-paper: #FBF8F1; /* polaroid frame */
  --tape: rgba(196, 160, 112, 0.82);  /* kraft washi tape */
  --hint: #F2E6D3;        /* small text on the red table */
}

:root {
  --serif: "Fraunces", Georgia, serif;
  --sans: "Figtree", system-ui, sans-serif;

  /* Sizes, all from the closed card's width (--cw). */
  --cw: min(45vw, 220px, 28svh);
  --ch: calc(var(--cw) * 1.4);
  --ew: calc(var(--ch) + 44px);   /* the card lies sideways inside */
  --eh: calc(var(--cw) + 40px);

  /* The parol hangs at the top; everything else starts below it,
     so nothing overlaps on short phones. */
  --pw: min(52vw, 210px, 31svh);            /* parol width */
  --ph: calc(var(--pw) * 1.65);             /* parol height (viewBox 200 x 330) */
  --env-y: max(58svh, calc(var(--ph) + 16px + var(--eh) / 2));   /* envelope center */
  --card-center: max(50svh, calc(var(--ph) + 12px + var(--ch) / 2));
  --card-y: calc(var(--card-center) - var(--env-y)); /* card, from the envelope center */

  /* Shadows: paper lying on a table, and paper lifted off it. */
  --lying: 0 1px 2px rgba(40, 6, 12, 0.3), 0 8px 18px rgba(40, 6, 12, 0.28);
  --lifted: 0 2px 4px rgba(40, 6, 12, 0.28), 0 18px 34px rgba(40, 6, 12, 0.35);

  /* Very faint paper grain (an SVG noise filter, drawn by the browser). */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.35 0 0 0 0 0.3 0 0 0 0 0.22 0 0 0 0.09 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  /* Linen: a fine, short weave in both directions (dark and light
     threads, a few pixels long), so it reads as soft fabric. */
  --linen-h: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='h'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.2 1.1' numOctaves='2' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.12 0 0 0 0 0.02 0 0 0 0 0.04 0.2 0 0 0 -0.06'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23h)'/%3E%3C/svg%3E");
  --linen-v: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='v'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1 0.2' numOctaves='2' seed='9' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 0.9 0 0 0 0 0.85 0.16 0 0 0 -0.05'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23v)'/%3E%3C/svg%3E");
}

/* ---------- Base ---------- */
*,
*::before,
*::after { box-sizing: border-box; }

html,
body {
  margin: 0;
  height: 100%;
  background-color: var(--table);
  background-image: var(--linen-h), var(--linen-v);
  color: var(--green);
  font-family: var(--sans);
  -webkit-tap-highlight-color: transparent;
}

#app { min-height: 100%; }

.stage {
  position: relative;
  min-height: 100%;
  overflow: hidden;
}
/* Once the message is out, the page scrolls: room for the watermark. */
.stage.is-long { padding-bottom: 56px; }

.progress { display: none; }

/* The first tap anywhere opens the envelope. */
.start {
  position: fixed;
  inset: 0;
  z-index: 20;
  cursor: pointer;
}
.start:focus-visible { outline: 2px solid var(--hint); outline-offset: -6px; }

/* One screen tall at first; app.js makes it taller when the
   message panel unfolds below the card. */
.scene {
  position: relative;
  height: 100svh;
}

/* Jump to a step without playing it (after a rebuild). */
.no-anim,
.no-anim * { transition: none !important; }

svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* ---------- Parol ---------- */
.parol {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--pw);
  height: var(--ph);
  margin-left: calc(var(--pw) / -2);
  transform-origin: 50% 0;
  animation: sway 6.5s ease-in-out infinite alternate;
  z-index: 1;
}
.parol-tail {
  transform-box: fill-box;
  transform-origin: 50% 0;
  animation: sway-tail 4.2s ease-in-out infinite alternate;
}
.parol-tail + .parol-tail { animation-duration: 5s; animation-delay: -1.5s; }
/* The fringe strands flutter a little on their own. */
.fringe {
  transform-box: fill-box;
  transform-origin: 50% 0;
  animation: sway-tail 2.6s ease-in-out infinite alternate;
}
.fringe:nth-child(2n) { animation-duration: 3.1s; animation-delay: -1s; }

@keyframes sway {
  from { transform: rotate(-2.2deg); }
  to { transform: rotate(2.2deg); }
}
@keyframes sway-tail {
  from { transform: rotate(-4deg); }
  to { transform: rotate(4deg); }
}

/* ---------- Envelope ---------- */
.envelope {
  position: absolute;
  left: 50%;
  top: var(--env-y);
  width: var(--ew);
  height: var(--eh);
  margin: calc(var(--eh) / -2) 0 0 calc(var(--ew) / -2);
  z-index: 2;
  transition: transform 0.26s ease-in;
}
/* Turning over: squeeze flat, swap to the back, open up again. */
.scene.is-turning .envelope { transform: scaleX(0); }
.scene.is-turned .envelope { transform: scaleX(1); transition-timing-function: ease-out; }

.env-front,
.env-inside {
  position: absolute;
  inset: 0;
  border-radius: 3px;
  background-color: var(--env);
  background-image: var(--grain);
  box-shadow: var(--lying);
}
.env-front { z-index: 5; }
.scene.is-back .env-front { display: none; }

.return-address {
  position: absolute;
  top: 9%;
  left: 7%;
  margin: 0;
  font-size: 11px;
  color: var(--kraft-ink);
}

.stamp {
  position: absolute;
  top: 8%;
  right: 6%;
  width: 16%;
  transform: rotate(2deg);
}
.postmark {
  position: absolute;
  top: 17%;
  right: 13%;
  width: 34%;
  opacity: 0.5;
  transform: rotate(-8deg);
}

.address {
  position: absolute;
  left: 14%;
  top: 50%;
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: calc(var(--ew) * 0.11);
  line-height: 1;
  color: var(--green);
}
.address-line { width: 110%; height: 0.32em; margin-top: 0.12em; }

/* A small fold: the bottom-left corner was bent once and flattened
   again. The corner is a shade darker, the crease line a little
   darker still, with a light edge where the paper bent. */
.crease {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 17%;
  aspect-ratio: 1;
  overflow: hidden;
  border-bottom-left-radius: 3px;
}
.crease::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(120, 85, 45, 0.07);
  clip-path: polygon(0 0, 100% 100%, 0 100%);
}
.crease::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 141.4%;
  height: 1px;
  background: rgba(90, 60, 30, 0.2);
  box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.6);
  transform: translate(-50%, -50%) rotate(45deg);
}

/* The back: kraft lining, the card, the pocket, the flap. */
.env-inside {
  z-index: 0;
  background-color: var(--kraft);
  visibility: hidden;
}
.env-pocket {
  position: absolute;
  inset: 0;
  z-index: 3;
  visibility: hidden;
  filter: drop-shadow(0 -0.5px 0.5px rgba(60, 30, 10, 0.18));
}
.pocket-bottom { filter: drop-shadow(0 -1px 1px rgba(60, 30, 10, 0.2)); }

.env-flap {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 71%;
  z-index: 4;
  visibility: hidden;
  transform-origin: 50% 0;
  transform-style: preserve-3d;
  transform: perspective(800px) rotateX(0deg);
  /* Halfway through opening, the flap goes behind the card. */
  transition: transform 0.6s ease-in-out, z-index 0s linear 0.3s;
}
.flap-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  filter: drop-shadow(0 1.5px 1.5px rgba(60, 30, 10, 0.25));
}
.flap-in { transform: rotateX(180deg); }

/* A round paper sticker sealing the flap. */
.seal {
  position: absolute;
  left: 50%;
  bottom: -4%;
  width: 14%;
  aspect-ratio: 1;
  padding: 2.5%;
  margin-left: -7%;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 1px 2px rgba(40, 6, 12, 0.35);
}

.scene.is-back .env-inside,
.scene.is-back .env-pocket,
.scene.is-back .env-flap,
.scene.is-back .card { visibility: visible; }

.scene.flap-open .env-flap {
  transform: perspective(800px) rotateX(180deg);
  z-index: 1;
}

/* The envelope falls away once the card is out, then it's gone
   (the page gets taller later, so it must not land on the photos). */
.scene.card-free .env-inside,
.scene.card-free .env-pocket,
.scene.card-free .env-flap {
  transform: translateY(110svh);
  visibility: hidden;
  transition: transform 0.8s ease-in, visibility 0s linear 0.8s;
}
.scene.card-free .env-flap { transform: translateY(110svh) perspective(800px) rotateX(180deg); }

/* ---------- Card ---------- */
.card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--cw);
  height: var(--ch);
  margin: calc(var(--ch) / -2) 0 0 calc(var(--cw) / -2);
  z-index: 2;
  visibility: hidden;
  /* Lying sideways inside the envelope. */
  transform: translateY(4px) rotate(-90deg);
  transition: transform 0.85s cubic-bezier(0.45, 0, 0.25, 1);
  outline: none;
}
.scene.card-out .card { transform: translateY(calc(var(--cw) * -1 - 30px)) rotate(-90deg); }
.scene.card-free .card {
  z-index: 6;
  transform: translateY(var(--card-y)) rotate(0deg);
  transition-duration: 0.95s;
}
.scene.card-ready:not(.card-open) .card { cursor: pointer; }
.scene.card-open .card {
  transform: translate(calc(var(--cw) / 2), var(--card-y));
  transition-duration: 1.1s;
}
.card:focus-visible .cover-front { outline: 2px solid var(--hint); outline-offset: 4px; }

.card-inside,
.cover-front,
.cover-back {
  position: absolute;
  inset: 0;
  background-color: var(--card);
  background-image: var(--grain);
}
.card-inside {
  z-index: 0;
  border-radius: 0 2px 2px 0;
  box-shadow: var(--lying),
    inset 12px 0 12px -12px rgba(70, 40, 20, 0.28); /* shade beside the fold */
}

.card-cover {
  position: absolute;
  inset: 0;
  z-index: 3;
  transform-origin: 0 50%;
  transform-style: preserve-3d;
  transform: perspective(1600px) rotateY(0deg);
  /* Halfway through opening, the cover goes under the tree. */
  transition: transform 1.1s cubic-bezier(0.5, 0, 0.2, 1), z-index 0s linear 0.55s;
}
.scene.card-open .card-cover {
  transform: perspective(1600px) rotateY(-177deg);
  z-index: 1;
}

.cover-front,
.cover-back {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.cover-front {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(var(--cw) * 0.07);
  padding: 12%;
  border-radius: 0 2px 2px 0;
  box-shadow: var(--lifted),
    inset 2px 0 0 rgba(70, 40, 20, 0.1); /* the folded spine on the left */
}
.cover-back {
  transform: rotateY(180deg);
  border-radius: 2px 0 0 2px;
  box-shadow: var(--lying),
    inset -12px 0 12px -12px rgba(70, 40, 20, 0.28);
}

.holly { width: 58%; }

.headline {
  margin: 0;
  font-family: var(--serif);
  font-weight: 600;
  font-size: calc(var(--cw) * 0.135);
  line-height: 1.12;
  text-align: center;
  color: var(--green);
  text-wrap: balance;
}
.headline-a,
.headline-b { display: block; }
.headline-b {
  font-weight: 400;
  font-style: italic;
  font-size: 1.3em;
  color: var(--red);
}

/* ---------- Pop-up tree ----------
   Sits on the fold (the card's left edge). Sizes are fractions of
   the tree's width (--tw). Each layer stands up from the page,
   one after another (--i = its turn). */
.tree {
  --tw: calc(var(--cw) * 1.05);
  position: absolute;
  left: 0;
  bottom: 9%;
  width: var(--tw);
  height: calc(var(--tw) * 1.03);
  margin-left: calc(var(--tw) / -2);
  z-index: 2;
  pointer-events: none;
}
.tier {
  position: absolute;
  left: 50%;
  width: 100%;
  aspect-ratio: 100 / 44;
  transform-origin: 50% 100%;
  transform: translateX(-50%) perspective(500px) rotateX(80deg);
  opacity: 0;
  filter: drop-shadow(0 2px 1.5px rgba(30, 15, 5, 0.38));
  transition:
    transform 0.6s cubic-bezier(0.3, 1.35, 0.55, 1) calc(var(--i) * 170ms),
    opacity 0.12s linear calc(var(--i) * 170ms);
}
.scene.tree-up .tier {
  transform: translateX(-50%) perspective(500px) rotateX(0deg);
  opacity: 1;
}
.trunk {
  bottom: 0;
  width: 8%;
  aspect-ratio: auto;
  height: 10%;
  background: var(--kraft);
}
.tier-1 { bottom: 7.8%; }
.tier-2 { bottom: 33.9%; width: 80%; }
.tier-3 { bottom: 55%; width: 62%; }
.tier-4 { bottom: 71.1%; width: 44%; }
.tree-star { bottom: 84%; width: 15%; aspect-ratio: 1; }

/* A soft shadow on the page where the tree is glued in. */
.tree-shadow {
  position: absolute;
  left: 18%;
  right: 18%;
  bottom: -2px;
  height: 6px;
  border-radius: 50%;
  background: rgba(50, 25, 10, 0.2);
  filter: blur(3px);
  opacity: 0;
  transition: opacity 0.4s;
}
.scene.tree-up .tree-shadow { opacity: 1; }

/* ---------- Message panel ----------
   A third panel of the card, hinged on the spread's bottom edge.
   It starts folded up behind the card and swings down toward you. */
.note {
  position: absolute;
  top: 100%;
  left: calc(var(--cw) * -1);
  width: calc(var(--cw) * 2);
  max-height: 80svh; /* very long messages scroll inside the panel */
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 22px 22px 24px;
  border-radius: 0 0 2px 2px;
  background-color: var(--card);
  background-image: var(--grain);
  box-shadow: var(--lying),
    inset 0 12px 12px -12px rgba(70, 40, 20, 0.3); /* shade under the fold */
  visibility: hidden;
  transform-origin: 50% 0;
  transform: perspective(1400px) rotateX(180deg);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transition: transform 0.9s cubic-bezier(0.4, 0, 0.2, 1);
  font-family: var(--serif);
  color: var(--ink-text);
}
.scene.card-open .note { visibility: visible; }
.scene.note-open .note { transform: perspective(1400px) rotateX(0deg); }

.note-greeting {
  margin: 0 0 12px;
  font-style: italic;
  font-size: 19px;
  color: var(--green);
}
.note-body p {
  margin: 0 0 12px;
  font-size: 15.5px;
  line-height: 1.55;
}
.note-signoff {
  margin: 16px 0 0;
  text-align: right;
  font-style: italic;
  font-size: 18px;
  line-height: 1.3;
  white-space: pre-line;
  color: var(--red);
}

/* ---------- Polaroids ----------
   Scattered on the table under the card: tilted opposite ways,
   offset left and right, each one overlapping the one before by
   about a fifth. The first is tucked under the message panel's
   bottom edge, as if it was slipped into the card. (The card is
   on a higher layer, so the photos go under it.) */
.photos {
  --pol-w: min(64vw, 250px);
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: -30px;  /* tuck the first photo under the panel */
  padding: 0 16px 8px;
}
.photos[hidden] { display: none; }

.polaroid {
  position: relative;
  width: var(--pol-w);
  margin: 0;
  padding: 9px 9px 0;
  background-color: var(--photo-paper);
  background-image: var(--grain);
  /* "Set down" on the table when it scrolls into view. */
  transform: translateX(var(--shift)) rotate(calc(var(--tilt) + 4deg)) translateY(-14px) scale(1.04);
  box-shadow: var(--lifted);
  transition: transform 0.7s cubic-bezier(0.3, 1.2, 0.5, 1), box-shadow 0.7s;
}
/* About 20% overlap: a polaroid is about 1.25 x its width + 33px tall. */
.polaroid + .polaroid { margin-top: calc(var(--pol-w) * -0.25 - 7px); }
/* The first one slides out from under the panel instead. */
.polaroid:first-child {
  transform: translateX(var(--shift)) translateY(-40%) rotate(var(--tilt));
  box-shadow: var(--lying);
  transition-duration: 0.9s;
  transition-timing-function: cubic-bezier(0.25, 0.8, 0.3, 1);
}
.polaroid:first-child { z-index: 2; } /* lies on top, so its caption shows */
.polaroid:first-child .tape { display: none; }
.polaroid.is-placed {
  transform: translateX(var(--shift)) rotate(var(--tilt));
  box-shadow: var(--lying);
}
.polaroid img {
  display: block;
  width: 100%;
  height: auto; /* the width/height attributes only reserve space */
  aspect-ratio: 4 / 5;
  object-fit: cover;
  background: var(--card-fold);
}
.polaroid figcaption {
  padding: 10px 4px 14px;
  text-align: center;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 14px;
  line-height: 1.3;
  color: var(--ink-text);
  text-wrap: balance;
  transform: rotate(-1deg);
}

/* Washi tape: translucent paper with torn ends, across the top-right
   corner (the part that sticks out from under the photo above it). */
.tape {
  position: absolute;
  top: 4px;
  right: -22px;
  width: 38%;
  height: 24px;
  background-color: var(--tape);
  background-image: var(--grain);
  transform: rotate(var(--tape-tilt));
  clip-path: polygon(0 8%, 4% 0, 8% 10%, 12% 0, 88% 0, 92% 8%, 96% 0, 100% 10%,
    100% 92%, 96% 100%, 92% 90%, 88% 100%, 12% 100%, 8% 92%, 4% 100%, 0 90%);
}

/* ---------- Hints ---------- */
.hint {
  position: absolute;
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  font-size: 14px;
  font-weight: 500;
  color: var(--hint);
  transition: opacity 0.4s;
}
.hint-open { top: calc(var(--env-y) + var(--eh) / 2 + 22px); }
.hint-card {
  top: calc(var(--card-center) + var(--ch) / 2 + 22px);
  opacity: 0;
  pointer-events: none;
}
.scene.is-turning .hint-open,
.scene.is-back .hint-open { opacity: 0; }
.scene.card-ready .hint-card { opacity: 1; }
.scene.card-open .hint-card { opacity: 0; }

/* ---------- Mute button and watermark ---------- */
.mute {
  position: fixed;
  top: max(12px, env(safe-area-inset-top));
  right: 12px;
  z-index: 30;
  padding: 6px 12px;
  border: 0;
  border-radius: 2px;
  background-color: var(--card);
  background-image: var(--grain);
  box-shadow: var(--lying);
  font: 500 13px var(--sans);
  color: var(--green);
  transform: rotate(2deg);
  cursor: pointer;
}

.watermark {
  position: absolute;
  left: 0;
  right: 0;
  bottom: max(14px, env(safe-area-inset-bottom));
  z-index: 3;
  text-align: center;
  font-size: 12px;
  color: var(--hint);
  opacity: 0.7;
}

/* ---------- Less motion ----------
   No sway and no animations: app.js shows the card open at once. */
@media (prefers-reduced-motion: reduce) {
  .parol,
  .parol-tail,
  .fringe { animation: none; }
  .scene,
  .scene *,
  .polaroid { transition: none !important; }
}
