/*
 * Global styles. Mobile-first, large type, high contrast — this product's
 * audience skews older and mostly on phones (see PLAN.md).
 */

:root {
  --color-bg: #fffaf3;
  --color-text: #2b2b2b;
  --color-primary: #d1495b;
  --color-primary-dark: #a8354a;
  --color-accent: #f2a541;
  --color-muted: #6b6b6b;
  --color-card-bg: #ffffff;
  --color-border: #e6dccb;
  font-size: 18px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
}

.container {
  max-width: 720px;
  margin: 0 auto;
  padding: 24px 20px;
}

h1, h2, h3 {
  line-height: 1.25;
}

h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }

a {
  color: var(--color-primary);
}

.btn {
  display: inline-block;
  font-size: 1.1rem;
  font-weight: 600;
  padding: 14px 28px;
  border-radius: 12px;
  border: none;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}

.btn-primary {
  background: var(--color-primary);
  color: #fff;
}

.btn-primary:hover { background: var(--color-primary-dark); }

.btn-whatsapp { background: #25d366; color: #fff; }
.btn-facebook { background: #1877f2; color: #fff; }

.btn-block { display: block; width: 100%; }

.field {
  margin-bottom: 20px;
}

.field label {
  display: block;
  font-weight: 600;
  margin-bottom: 6px;
}

.field input[type="text"],
.field input[type="email"],
.field input[type="date"],
.field input[type="url"],
.field textarea,
.field select {
  width: 100%;
  font-size: 1.1rem;
  padding: 12px;
  border: 2px solid var(--color-border);
  border-radius: 10px;
  background: var(--color-card-bg);
  color: var(--color-text);
}

.field textarea { min-height: 120px; }

.flash {
  padding: 14px 20px;
  text-align: center;
  font-weight: 600;
}

.flash-notice { background: #e8f5e9; color: #2e7d32; }
.flash-alert { background: #fdeceb; color: var(--color-primary-dark); }

.error-messages {
  background: #fdeceb;
  border: 2px solid var(--color-primary);
  border-radius: 10px;
  padding: 16px;
  margin-bottom: 20px;
}

.card-preview {
  position: relative;
  overflow: hidden;
  background: var(--color-card-bg);
  border-radius: 16px;
  border: 1px solid var(--color-border);
  padding: 24px;
  text-align: center;
}

.youtube-embed {
  margin: 16px 0;
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

.youtube-embed iframe {
  width: 100% !important;
  height: 100% !important;
  border: none;
  display: block;
}

.photo-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 16px 0;
}

.photo-grid img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 8px;
}

/* Photos are shown in full (object-fit: contain, never cropped) for the
   current 1-3 photo limit, with a layout tailored to the count. Counts
   above 3 are legacy cards saved before the limit was lowered from 7 and
   fall back to the cropped grid above. */
.photo-grid[data-count="1"] {
  grid-template-columns: 1fr;
}

.photo-grid[data-count="1"] img {
  aspect-ratio: 4 / 3;
  object-fit: contain;
  background: var(--color-bg);
}

.photo-grid[data-count="2"] {
  grid-template-columns: repeat(2, 1fr);
}

.photo-grid[data-count="2"] img {
  aspect-ratio: 1;
  object-fit: contain;
  background: var(--color-bg);
}

.photo-grid[data-count="3"] {
  grid-template-columns: repeat(3, 1fr);
}

.photo-grid[data-count="3"] img {
  aspect-ratio: 1;
  object-fit: contain;
  background: var(--color-bg);
}

.video-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-top: 8px;
}

@media (min-width: 600px) {
  .video-grid { grid-template-columns: repeat(3, 1fr); }
}

.video-option { text-align: center; }

.video-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 3px solid var(--color-border);
  border-radius: 10px;
  background: var(--color-card-bg);
  overflow: hidden;
  cursor: pointer;
}

.video-option.selected .video-thumb {
  border-color: var(--color-primary);
}

.video-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 1rem;
}

/* Video-picker preview overlay (VideoPreview in wizard.jsx). min-height
   keeps the player at YouTube's 200px minimum on narrow phones, where a
   16:9 box at full width would come out shorter than that and label-owned
   music videos would refuse to play. */
.video-preview {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.85);
}

.video-preview[hidden] { display: none; }

.video-preview-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: min(100%, 640px);
}

.video-preview-player {
  width: 100%;
  aspect-ratio: 16 / 9;
  min-height: 200px;
  border-radius: 10px;
  overflow: hidden;
  background: #000;
}

.video-preview-player iframe {
  width: 100% !important;
  height: 100% !important;
  border: none;
  display: block;
}

.video-preview-close {
  background: #fff;
  color: #000;
}

.video-thumb-custom,
.video-thumb-none {
  flex-direction: column;
  gap: 4px;
  font-size: 0.95rem;
  font-weight: 600;
}

.video-thumb-icon {
  font-size: 1.5rem;
  line-height: 1;
}

/* "Outro vídeo" reads as an add/upload action — dashed border, accent
   color, on the page background so it doesn't look like a filled-in
   thumbnail. */
.video-thumb-custom {
  color: var(--color-primary);
  background: var(--color-bg);
  border-style: dashed;
}

/* "Sem vídeo" is the opt-out — muted and flat, closer to a disabled
   control than a choice, so it doesn't compete visually with the videos. */
.video-thumb-none {
  color: var(--color-muted);
  background: var(--color-border);
  border-style: solid;
}

.video-thumb-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px;
}

.video-title {
  margin: 6px 0 0;
  font-size: 0.9rem;
}

.effect-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-top: 8px;
}

@media (min-width: 600px) {
  .effect-grid { grid-template-columns: repeat(3, 1fr); }
}

.effect-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 100%;
  padding: 16px 8px;
  border: 3px solid var(--color-border);
  border-radius: 10px;
  background: var(--color-card-bg);
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 600;
  /* Stays put while an effect preview plays over it (see wizard.jsx) —
     no layout shift while confetti/stickers/etc. render on top. */
  position: relative;
  overflow: hidden;
}

.effect-tile.selected {
  border-color: var(--color-primary);
}

.effect-tile-icon {
  font-size: 1.75rem;
  line-height: 1;
}

.share-buttons {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 20px 0;
}

.qr-code {
  margin: 20px auto;
  max-width: 220px;
}

.muted { color: var(--color-muted); font-size: 0.95rem; }

.preview-banner {
  background: var(--color-card-bg);
  border: 2px solid var(--color-accent);
  border-radius: 16px;
  padding: 24px;
  text-align: center;
  margin: 20px 0;
}

.expired-page {
  text-align: center;
}

.spinner {
  display: inline-block;
  width: 32px;
  height: 32px;
  border: 4px solid var(--color-border);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------------ */
/* Stickers effect (app/javascript/effects/sticker_slapper.js) — ported */
/* from codepen.io/shubniggurath/pen/MYJoxvY. `--p` (0 = rolled up,     */
/* 1 = flat) drives a clip-path "fold line" and a mirrored flap layer   */
/* to fake a paper sticker peeling on. See that file for the physics.  */
/* ------------------------------------------------------------------ */

@property --p {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.sticker {
  position: absolute;
  user-select: none;
  pointer-events: none;
  filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.16)) drop-shadow(0 5px 10px rgba(0, 0, 0, 0.05));

  --fold: calc(var(--minp) + var(--span) * var(--p));
  --half-plane: polygon(
    calc((var(--fold) * var(--u0) + var(--big) * var(--w0)) * 1px)
      calc((var(--fold) * var(--u1) + var(--big) * var(--w1)) * 1px),
    calc((var(--fold) * var(--u0) - var(--big) * var(--w0)) * 1px)
      calc((var(--fold) * var(--u1) - var(--big) * var(--w1)) * 1px),
    calc((var(--fold) * var(--u0) - var(--big) * var(--w0) - var(--big) * var(--u0)) * 1px)
      calc((var(--fold) * var(--u1) - var(--big) * var(--w1) - var(--big) * var(--u1)) * 1px),
    calc((var(--fold) * var(--u0) + var(--big) * var(--w0) - var(--big) * var(--u0)) * 1px)
      calc((var(--fold) * var(--u1) + var(--big) * var(--w1) - var(--big) * var(--u1)) * 1px)
  );
}

.sticker__layer { position: absolute; inset: 0; }

.sticker__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.sticker__flat,
.sticker__flap-clip {
  clip-path: var(--half-plane);
}

.sticker__flap-inner {
  transform-origin: 0 0;
  transform: matrix(
    var(--a), var(--b), var(--b), var(--d),
    calc(2 * var(--fold) * var(--u0)), calc(2 * var(--fold) * var(--u1))
  );
}

.sticker__flap-fill {
  -webkit-mask-image: var(--src);
  mask-image: var(--src);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  mask-mode: alpha;
  background-image: linear-gradient(
    calc(var(--ca) * 1deg),
    #cdcdcd calc(var(--p) * 100%),
    #f3f3f3 calc((var(--p) + (1 - var(--p)) * 0.1) * 100%),
    #ffffff calc((var(--p) + (1 - var(--p)) * 0.45) * 100%),
    #f0f0f0 calc((var(--p) + (1 - var(--p)) * 0.82) * 100%),
    #c6c6c6 100%
  );
}

.sticker__flap-shadow {
  filter: drop-shadow(
    calc(var(--u0) * var(--s) * -0.05 * (1 - var(--p)) * 1px)
    calc((var(--u1) * -0.05 + 0.04) * var(--s) * (1 - var(--p)) * 1px)
    calc(var(--s) * 0.025 * 1px) rgba(0, 0, 0, calc(0.35 * (1 - var(--p))))
  );
}

/* ============================================================
   Card opening: the full-screen tap-to-reveal overlay, one style
   per Card#opening (splash / envelope / gift / folded_card /
   curtains — see app/views/cards/openings/ and
   card_opening_controller.js). All five share the same overlay
   mechanics and reveal timing; only the illustration and
   background differ per style, toggled by the
   .card-opening-<style> modifier class show.html.erb adds.

   NOTE: propshaft serves app/assets/stylesheets/application.css
   as a single file (no auto-bundling of a sibling stylesheet), so
   this lives here rather than in a separate file.
   ============================================================ */

.card-opening {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 24px;
  text-align: center;
  overflow: hidden;
}

/* [hidden] alone would lose to the display: flex above — the cascade is
   UA-stylesheet < author stylesheet regardless of selector specificity, so
   this has to be spelled out here to actually hide the overlay on open. */
.card-opening[hidden] { display: none; }

.card-opening-content {
  max-width: 360px;
  width: 100%;
}

/* Once a style's morph element (see card_opening_controller.js) starts
   growing to cover the screen, the "toque para abrir" caption underneath
   it has done its job — fade it rather than let it linger or flash
   through the growing card. Splash has no morph target, so this never
   fires for it (is-morphing is only ever added right before a morph
   actually starts — see open()/morphIntoCard()). */
.card-opening.is-morphing .card-opening-content {
  opacity: 0;
  transition: opacity 0.25s ease;
}

.card-opening-icon {
  display: block;
  font-size: 3rem;
  margin-bottom: 12px;
}

.card-opening-content h2 { color: var(--color-text); margin: 0 0 8px; }
.card-opening-content p { color: var(--color-muted); margin: 0 0 20px; }

/* Splash and curtains sit on a dark/saturated background, so their text
   needs to flip to light — the other three styles keep the default dark
   text over their pastel backgrounds. */
.card-opening-splash .card-opening-content h2,
.card-opening-curtains .card-opening-content h2 { color: #fff; }

.card-opening-splash .card-opening-content p,
.card-opening-curtains .card-opening-content p { color: rgba(255, 255, 255, 0.9); }

.card-opening .btn[disabled],
.opening-trigger[disabled] {
  opacity: 0.6;
  cursor: default;
}

.opening-trigger {
  background: none;
  border: none;
  padding: 0;
  margin: 0 0 4px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

@media (prefers-reduced-motion: reduce) {
  /* Belt and braces: card_opening_controller.js already skips straight to
     the reveal under reduced motion without adding .is-opening, but this
     keeps any animation from playing even if that check is ever bypassed. */
  .card-opening.is-opening * {
    animation: none !important;
    transition: none !important;
  }
}

/* --- Splash: the original style, unchanged --- */

.card-opening-splash {
  background: linear-gradient(160deg, var(--color-primary), var(--color-accent));
}

/* --- Envelope: a letter tucked in an envelope, flap swings open --- */

.card-opening-envelope {
  background: linear-gradient(160deg, #fff6e6, #f7e3c2);
}

.opening-envelope {
  position: relative;
  display: block;
  width: 240px;
  height: 160px;
  perspective: 800px;
}

.opening-envelope-back {
  position: absolute;
  inset: 0;
  background: #e8c98f;
  border-radius: 8px;
  box-shadow: 0 16px 30px rgba(43, 27, 10, 0.25);
}

.opening-envelope-card {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 10px;
  height: 120px;
  background: #fffaf3;
  border-radius: 6px;
  box-shadow: 0 6px 14px rgba(43, 27, 10, 0.18);
  transition: transform 0.8s ease 0.4s;
}

.opening-envelope-flap {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 0;
  border-left: 120px solid transparent;
  border-right: 120px solid transparent;
  border-top: 88px solid #f4dba8;
  transform-origin: top center;
  transform: rotateX(0deg);
  transition: transform 0.7s ease;
  z-index: 2;
  filter: drop-shadow(0 4px 6px rgba(43, 27, 10, 0.15));
}

.opening-envelope-seal {
  position: absolute;
  top: 58px;
  left: 50%;
  width: 26px;
  height: 26px;
  margin-left: -13px;
  border-radius: 50%;
  background: var(--color-primary);
  z-index: 3;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.card-opening.is-opening .opening-envelope-flap { transform: rotateX(-175deg); }
.card-opening.is-opening .opening-envelope-seal { opacity: 0; transform: scale(0.4); }
.card-opening.is-opening .opening-envelope-card { transform: translateY(-64px); }

/* Once .opening-envelope-card (data-card-opening-target="morph" — see
   card_opening_controller.js) starts growing into the real card, the rest
   of the envelope has nothing left to do but get out of the way. */
.card-opening.is-morphing .opening-envelope-back,
.card-opening.is-morphing .opening-envelope-flap {
  opacity: 0;
  transition: opacity 0.3s ease;
}

/* --- Gift: a wrapped box, bow and ribbon come off, lid pops --- */

.card-opening-gift {
  background: linear-gradient(160deg, #ffe9ee, #ffd3dc);
}

.opening-gift {
  position: relative;
  display: block;
  width: 220px;
  height: 190px;
}

.opening-gift-box {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 130px;
  background: #f2637a;
  border-radius: 8px;
  box-shadow: 0 16px 28px rgba(70, 15, 30, 0.25);
}

/* The card itself, sitting inside the box (data-card-opening-target=
   "morph" — see card_opening_controller.js) — hidden until the wrapping
   is gone, then it rises up out of the box (translateY below) before
   morphIntoCard() takes over and grows it into the real card. Roughly
   matches the box's own footprint so it reads as "inside" it. */
.opening-gift-card {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 4px;
  height: 122px;
  background: var(--color-card-bg);
  border-radius: 8px;
  box-shadow: 0 16px 28px rgba(70, 15, 30, 0.25);
  opacity: 0;
  transform: translateY(8px) scale(0.92);
  transition: opacity 0.4s ease 0.45s, transform 0.5s ease 0.45s;
  z-index: 1;
}

.card-opening.is-opening .opening-gift-card {
  opacity: 1;
  transform: translateY(-58px) scale(1);
}

.opening-gift-lid {
  position: absolute;
  left: -8px;
  right: -8px;
  top: 50px;
  height: 34px;
  background: #d1495b;
  border-radius: 8px;
  box-shadow: 0 8px 14px rgba(70, 15, 30, 0.2);
  transition: transform 0.8s ease, opacity 0.6s ease 0.4s;
  z-index: 3;
}

.opening-gift-ribbon-v {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 24px;
  margin-left: -12px;
  background: #ffe08a;
  z-index: 2;
  transition: opacity 0.5s ease 0.2s;
}

.opening-gift-ribbon-h {
  /* Sits on the box front, below the lid (which spans 50-84px) — not
     behind it, so the band is actually visible before the box opens. */
  position: absolute;
  left: 0;
  right: 0;
  top: 106px;
  height: 24px;
  background: #ffe08a;
  z-index: 2;
  transition: transform 0.6s ease, opacity 0.5s ease;
}

.opening-gift-bow {
  position: absolute;
  left: 50%;
  top: 30px;
  width: 56px;
  height: 34px;
  margin-left: -28px;
  z-index: 4;
  transition: transform 0.6s ease, opacity 0.5s ease;
}

.opening-gift-bow::before,
.opening-gift-bow::after {
  content: "";
  position: absolute;
  top: 0;
  width: 26px;
  height: 26px;
  background: #ffe08a;
  border-radius: 50% 50% 50% 0;
}

.opening-gift-bow::before { left: 0; transform: rotate(-45deg); }
.opening-gift-bow::after { right: 0; transform: rotate(135deg); }

.card-opening.is-opening .opening-gift-bow {
  transform: translateY(-30px) scale(0.5) rotate(-25deg);
  opacity: 0;
}

.card-opening.is-opening .opening-gift-ribbon-v,
.card-opening.is-opening .opening-gift-ribbon-h {
  opacity: 0;
}

.card-opening.is-opening .opening-gift-ribbon-h { transform: scaleX(1.4) translateY(-10px); }

.card-opening.is-opening .opening-gift-lid {
  transform: translateY(-46px) rotate(-8deg);
  opacity: 0;
}

/* Once .opening-gift-card starts growing into the real card, the box
   itself (already unwrapped by is-opening above) has nothing left to do
   but get out of the way too. */
.card-opening.is-morphing .opening-gift-box {
  opacity: 0;
  transition: opacity 0.3s ease;
}

/* --- Folded card: a closed card, cover swings open like a book --- */

.card-opening-folded_card {
  background: linear-gradient(160deg, #fff6e6, #f2e0c9);
}

.opening-folded-card {
  position: relative;
  display: block;
  width: 220px;
  height: 280px;
  perspective: 1200px;
}

/* The card's inner page — already the real card's own colors (#fffaf3
   matches --color-card-bg), revealed as the cover flips away, and reused
   directly as the morph target (data-card-opening-target="morph" — see
   card_opening_controller.js) that then grows to become the real card. */
.opening-folded-card-inner {
  position: absolute;
  inset: 0;
  background: #fffaf3;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  box-shadow: 0 16px 30px rgba(43, 27, 10, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
}

.opening-folded-card-icon {
  font-size: 3rem;
  transition: opacity 0.3s ease;
}

.card-opening.is-morphing .opening-folded-card-icon { opacity: 0; }

/* Same warm gradient splash/curtains use, tying the folded-card opening
   to the rest of the app's visual language. */
.opening-folded-card-cover {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  background-color: var(--color-primary);
  background-image: linear-gradient(160deg, var(--color-primary), var(--color-accent));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  transform-origin: left center;
  transform: rotateY(0deg);
  transition: transform 1s ease;
  box-shadow: 0 16px 30px rgba(43, 27, 10, 0.25);
  display: flex;
  align-items: flex-end;
  padding: 16px;
  backface-visibility: hidden;
}

.opening-folded-card-cover-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -60%);
  font-size: 3.5rem;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
}

.opening-folded-card-title {
  display: block;
  color: #fff;
  font-weight: 700;
  font-size: 1.1rem;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

.card-opening.is-opening .opening-folded-card-cover { transform: rotateY(-165deg); }

/* --- Curtains: theater curtains part to reveal the card --- */

.card-opening-curtains {
  background: #2b1116;
}

.opening-curtains {
  position: relative;
  display: block;
  width: 260px;
  height: 220px;
  border-radius: 8px;
  overflow: hidden;
  background: #1a0d10;
  box-shadow: 0 16px 30px rgba(0, 0, 0, 0.35);
}

.opening-curtains-valance {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 26px;
  background: repeating-linear-gradient(90deg, #8a1622 0 14px, #a82030 14px 28px);
  z-index: 3;
}

.opening-curtain {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 52%;
  overflow: hidden;
  /* Narrower pleats that ramp light → mid → dark → mid per cycle, rather
     than a flat two-tone stripe, so each one reads as a rounded, gathered
     fold of fabric catching the light instead of a flat printed pattern —
     "fluffier". */
  background: repeating-linear-gradient(
    90deg,
    #7a121c 0,
    #a8203a 5px,
    #c23652 9px,
    #a8203a 13px,
    #7a121c 18px
  );
  box-shadow: inset 0 0 24px rgba(0, 0, 0, 0.35);
  transition: transform 0.9s ease;
  z-index: 2;
}

.opening-curtain-left { left: 0; border-radius: 0 0 45% 0; }
.opening-curtain-right { right: 0; border-radius: 0 0 0 45%; }

/* A gold trim band with a scalloped tassel fringe hanging off it along
   the curtain's hem — `overflow: hidden` on .opening-curtain above keeps
   it clipped to that swooping bottom-corner border-radius rather than
   poking past it as a hard rectangle. */
.opening-curtain::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 18px;
  background:
    repeating-radial-gradient(circle at 7px 2px, var(--color-accent) 0 3px, transparent 3.5px 14px),
    linear-gradient(var(--color-accent), var(--color-accent));
  background-size: 14px 14px, 100% 7px;
  background-position: 0 8px, 0 0;
  background-repeat: repeat-x, no-repeat;
}

/* The card, presented on the stage behind the curtains (data-card-
   opening-target="morph" — see card_opening_controller.js) — no reveal
   transition of its own needed, since the curtains sliding away is what
   uncovers it. */
.opening-curtains-card {
  position: absolute;
  top: 30px;
  left: 50%;
  width: 150px;
  height: 170px;
  margin-left: -75px;
  background: var(--color-card-bg);
  border-radius: 8px;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.35);
  z-index: 1;
}

.card-opening.is-opening .opening-curtain-left { transform: translateX(-105%); }
.card-opening.is-opening .opening-curtain-right { transform: translateX(105%); }

/* Once .opening-curtains-card starts growing into the real card, the
   valance (which never moves — only the two panels slide away) has
   nothing left to do but get out of the way too. */
.card-opening.is-morphing .opening-curtains-valance {
  opacity: 0;
  transition: opacity 0.3s ease;
}

