/* ==========================================================================
   COHORT COMPLETION CERTIFICATE
   Self-sufficient stylesheet: carries its own @font-face set because the
   print/PDF shell loads ONLY this file (never styles.css / Google CDN).
   Canvas: fixed 1123x794px (A4 landscape @ 96dpi), scaled to fit on screen.
   ========================================================================== */

/* --- Fonts (self-hosted; see assets/fonts/) ------------------------------ */

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/cormorant-garamond-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/cormorant-garamond-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/cormorant-garamond-400italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/cormorant-garamond-400italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/cormorant-garamond-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/cormorant-garamond-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Space Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/space-mono-400-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Space Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/space-mono-700-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'PP Neue Bit';
  src: url('/assets/fonts/ppneuebit-bold.otf') format('opentype');
  font-weight: 700;
  font-display: swap;
}

/* --- Tokens --------------------------------------------------------------- */

.cert-main,
.cert-print {
  --cert-ivory: #FDFBF4;
  --cert-ivory-deep: #F6F1E4;
  --cert-env: #F3EDDC;
  --cert-env-shade: #EAE2CC;
  --cert-ink: #2B2517;
  --cert-ink-soft: #6B5F49;
  --cert-gold: #B98B2E;
  --cert-gold-deep: #8F6A1C;
  --cert-gold-pale: rgba(185, 139, 46, 0.55);
  --cert-blue: #4040FF;
  --cert-blue-deep: #2424A8;
  --cert-serif: 'Cormorant Garamond', 'Times New Roman', serif;
  --cert-mono: 'Space Mono', 'Courier New', monospace;
  --cert-scale: 1;
}

/* CSS-only scale fallback (no JS). js/certificate.js sets the precise value. */
@media (max-width: 1220px) { .cert-main { --cert-scale: 0.8; } }
@media (max-width: 940px)  { .cert-main { --cert-scale: 0.62; } }
@media (max-width: 720px)  { .cert-main { --cert-scale: 0.5; } }
@media (max-width: 580px)  { .cert-main { --cert-scale: 0.4; } }
@media (max-width: 460px)  { .cert-main { --cert-scale: 0.32; } }

/* --- Page layout (web shell) ---------------------------------------------- */

/* The dropping envelope must vanish at the page bottom without stretching the
   scrollable area — clip, never scroll. Only certificate pages load this file. */
.page-wrapper { overflow: clip; }

/* Tight vertical padding: every pixel here comes off the certificate's size,
   because js/certificate.js scales it to fit whatever height is left over. */
.cert-main {
  width: 100%;
  max-width: 1220px;
  margin: 0 auto;
  padding: 28px 16px 20px;
  text-align: center;
}

.cert-main--404 {
  max-width: 680px;
  padding-top: 96px;
  padding-bottom: 64px;
}

.cert-page-kicker {
  font-family: var(--cert-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.22em;
  color: #AAAAAA;
  margin-bottom: 24px;
}

/* --- Scene ----------------------------------------------------------------- */

.cert-scene {
  position: relative;
  width: calc(1123px * var(--cert-scale));
  height: calc(794px * var(--cert-scale));
  margin: 0 auto;
}

/* z-index 3: sandwiched between the envelope interior (.env-back, z1) and the
   envelope front + flap (z6/z8), so the certificate can slide out of the mouth. */
.cert-holder {
  position: absolute;
  inset: 0;
  z-index: 3;
}

.cert-holder .cert-stage {
  transform: scale(var(--cert-scale));
  transform-origin: top left;
}

/* --- The certificate artifact (fixed 1123x794 canvas) ---------------------- */

.cert-stage {
  position: relative;
  width: 1123px;
  height: 794px;
  text-align: center;
  color: var(--cert-ink);
  background-color: var(--cert-ivory);
  background-image:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 255, 255, 0.9), transparent 60%),
    radial-gradient(circle at 12% 8%, rgba(120, 100, 60, 0.05), transparent 45%),
    radial-gradient(circle at 88% 92%, rgba(120, 100, 60, 0.06), transparent 45%),
    repeating-linear-gradient(112deg, rgba(160, 140, 100, 0.022) 0 2px, transparent 2px 5px);
  box-shadow:
    0 1px 2px rgba(43, 37, 23, 0.08),
    0 6px 18px rgba(43, 37, 23, 0.1),
    0 24px 48px -18px rgba(43, 37, 23, 0.18),
    inset 0 0 0 1px rgba(185, 139, 46, 0.08);
}

/* Zero-specificity guard: nothing from styles.css leaks into the artifact. */
.cert-stage :where(h1, h2, h3, p, span, div, dl, dt, dd, small, em, strong, img, i) {
  margin: 0;
  padding: 0;
  font: inherit;
  letter-spacing: normal;
  text-transform: none;
  line-height: inherit;
  color: inherit;
}

.cert-frame-outer {
  position: absolute;
  inset: 26px;
  border: 2px solid transparent;
  border-image: linear-gradient(135deg, #B98B2E, #E8C77A 18%, #F4E4B4 34%, #C99B3C 52%, #8F6A1C 70%, #E3C57C 86%, #B98B2E) 1;
  pointer-events: none;
}

.cert-frame-inner {
  position: absolute;
  inset: 40px;
  border: 1px solid var(--cert-gold-pale);
  pointer-events: none;
}

.cert-corner {
  position: absolute;
  width: 34px;
  height: 34px;
  color: var(--cert-gold);
  pointer-events: none;
}
.cert-corner--tl { top: 34px; left: 34px; transform: rotate(90deg); }
.cert-corner--tr { top: 34px; right: 34px; transform: rotate(180deg); }
.cert-corner--br { bottom: 34px; right: 34px; transform: rotate(270deg); }
.cert-corner--bl { bottom: 34px; left: 34px; }

.cert-micro {
  position: absolute;
  left: 80px;
  right: 80px;
  bottom: 29px;
  font-family: var(--cert-mono);
  font-size: 5px;
  letter-spacing: 0.28em;
  white-space: nowrap;
  overflow: hidden;
  color: rgba(185, 139, 46, 0.45);
  pointer-events: none;
}

/* One-shot foil shimmer overlay (armed on reveal; inert in print). */
.cert-foil {
  position: absolute;
  inset: 26px;
  pointer-events: none;
  overflow: hidden;
}
.cert-foil::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 40%, rgba(255, 245, 214, 0.35) 50%, transparent 60%);
  background-size: 250% 100%;
  background-position: -150% 0;
  opacity: 0;
}

/* --- Certificate body ------------------------------------------------------ */

.cert-body {
  position: absolute;
  inset: 74px 92px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
}

.cert-issuer {
  font-family: var(--cert-mono);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.34em;
  text-indent: 0.34em; /* optical centre for letterspaced text */
  color: var(--cert-ink-soft);
}

.cert-issuer-rule {
  width: 72px;
  height: 1px;
  margin: 14px 0 26px;
  background: var(--cert-gold-pale);
}

.cert-kicker {
  font-family: var(--cert-mono);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.38em;
  text-indent: 0.38em;
  text-transform: uppercase;
  color: var(--cert-gold-deep);
  margin-bottom: 34px;
}

.cert-presented {
  font-family: var(--cert-serif);
  font-style: italic;
  font-size: 17px;
  color: var(--cert-ink-soft);
  margin-bottom: 10px;
}

.cert-name {
  font-family: var(--cert-serif);
  font-weight: 600;
  font-size: var(--cert-name-size, 62px);
  line-height: 1.08;
  color: #1A1710;
  max-width: 900px;
}

.cert-name-rule {
  position: relative;
  width: 62%;
  height: 1px;
  margin: 16px 0 18px;
  background: linear-gradient(90deg, transparent, var(--cert-gold-pale) 18%, var(--cert-gold-pale) 82%, transparent);
}
.cert-name-rule i,
.cert-name-rule::before,
.cert-name-rule::after {
  content: '';
  position: absolute;
  top: -2.5px;
  width: 6px;
  height: 6px;
  background: var(--cert-gold);
  transform: rotate(45deg);
}
.cert-name-rule::before { left: 15%; }
.cert-name-rule::after { right: 15%; }
.cert-name-rule i { display: none; }

.cert-completed {
  font-family: var(--cert-serif);
  font-style: italic;
  font-size: 17px;
  color: var(--cert-ink-soft);
  margin-bottom: 8px;
}

.cert-course {
  font-family: var(--cert-serif);
  font-weight: 600;
  font-size: 30px;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
  color: var(--cert-ink);
}

.cert-cohort {
  font-family: var(--cert-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: var(--cert-ink-soft);
  margin-top: 10px;
}

/* Typewriter voice: the one body passage in mono, for legibility against the
   ceremonial serifs around it. */
.cert-learned {
  font-family: var(--cert-mono);
  font-size: 11.5px;
  line-height: 1.75;
  letter-spacing: 0.02em;
  color: var(--cert-ink-soft);
  max-width: 700px;
  margin-top: 26px;
}

/* --- Certificate footer: signature | seal | meta --------------------------- */

.cert-footer {
  margin-top: auto;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  gap: 40px;
}

.cert-sig {
  justify-self: start;
  text-align: left;
}
.cert-sig-img {
  display: block;
  height: 46px;
  width: auto;
  max-width: 200px;
  margin-bottom: 6px;
}
.cert-sig-rule {
  width: 190px;
  height: 1px;
  background: rgba(43, 37, 23, 0.3);
  margin-bottom: 8px;
}
.cert-sig-name {
  font-family: var(--cert-mono);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cert-ink);
}
.cert-sig-role {
  font-family: var(--cert-serif);
  font-style: italic;
  font-size: 12px;
  color: var(--cert-ink-soft);
  margin-top: 3px;
}

.cert-meta {
  justify-self: end;
  text-align: right;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.cert-meta div {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  align-items: baseline;
}
.cert-meta dt {
  font-family: var(--cert-mono);
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(107, 95, 73, 0.75);
}
.cert-meta dd {
  font-family: var(--cert-mono);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cert-ink);
}
/* Verify row: smaller + tighter so the longest slug URLs stay on one line. */
.cert-meta div:last-child dd {
  font-size: 8.5px;
  letter-spacing: 0.02em;
}

/* --- The blue wax seal ------------------------------------------------------ */

.seal-disc {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  background: radial-gradient(circle at 34% 28%, #6E6EFF, #4040FF 46%, #2424A8 100%);
  border-radius: 47% 53% 51% 49% / 52% 48% 52% 48%;
  box-shadow:
    inset 0 -3px 6px rgba(0, 0, 0, 0.28),
    inset 0 3px 6px rgba(255, 255, 255, 0.25),
    inset 0 0 0 2px rgba(255, 255, 255, 0.18),
    0 3px 7px rgba(36, 36, 168, 0.35);
}
.seal-mark {
  font-family: 'PP Neue Bit', var(--cert-mono);
  font-weight: 700;
  font-size: 24px;
  letter-spacing: 0.06em;
  text-indent: 0.06em;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
}

.cert-seal {
  justify-self: center;
  transform: rotate(-4deg);
}
.cert-seal .seal-disc {
  width: 76px;
  height: 76px;
}
.cert-seal .seal-mark { font-size: 22px; }

/* ==========================================================================
   ENVELOPE (web only — never rendered in the print shell)

   Layering is the whole trick: .env deliberately carries NO transform,
   z-index, or perspective, so it never becomes a stacking context. Its
   layers therefore interleave with the sibling .cert-holder (z3) inside
   .cert-scene: interior (.env-back, z1) BEHIND the certificate, front and
   flap (z6/z8) IN FRONT of it — the certificate genuinely slides out of the
   envelope mouth. The moment the drop animation transforms .env, it becomes
   a stacking context and the whole envelope falls BEHIND the certificate.
   ========================================================================== */

.env {
  position: absolute;
  width: min(620px, 94vw);
  aspect-ratio: 620 / 400;
  /* Centered without transform (0.32258 = half of 400/620). */
  left: calc(50% - min(620px, 94vw) / 2);
  top: calc(50% - min(620px, 94vw) * 0.32258);
  --seal-size: min(84px, 18vw); /* scales down with the envelope on phones */
  cursor: pointer;
  outline-offset: 6px;
}
.env[hidden] { display: none; }

/* Envelope interior — visible through the mouth once the flap opens. */
.env-back {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, #E3D9BE 0%, var(--cert-env) 36%, var(--cert-env-shade) 100%);
  border-radius: 8px;
  box-shadow:
    inset 0 12px 18px -10px rgba(80, 65, 35, 0.35),
    0 1px 2px rgba(43, 37, 23, 0.1),
    0 10px 24px rgba(43, 37, 23, 0.14),
    0 30px 50px -20px rgba(43, 37, 23, 0.22);
}

.env-front {
  position: absolute;
  inset: 0;
  z-index: 6;
  background:
    linear-gradient(115deg, rgba(255, 255, 255, 0.28), transparent 42%),
    repeating-linear-gradient(115deg, rgba(160, 140, 100, 0.028) 0 2px, transparent 2px 5px),
    linear-gradient(180deg, var(--cert-env) 30%, var(--cert-env-shade));
  border-radius: 8px;
  clip-path: polygon(0 0, 50% 30%, 100% 0, 100% 100%, 0 100%);
  box-shadow:
    inset 0 0 0 1px rgba(120, 100, 60, 0.1),
    inset 0 -1px 0 rgba(255, 255, 255, 0.4);
  filter: drop-shadow(0 -1px 0 rgba(120, 100, 60, 0.2));
}

/* Folded-paper construction: two side flaps, then the bottom flap over them.
   (These live inside .env-front, so its mouth clip also clips them.) */
.env-fold {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.env-fold--left {
  clip-path: polygon(0 4%, 49.5% 64%, 0 100%);
  background: linear-gradient(64deg, rgba(120, 100, 60, 0.1), rgba(120, 100, 60, 0.02) 46%, rgba(120, 100, 60, 0.05) 100%);
  filter: drop-shadow(1.5px 0.5px 1px rgba(110, 92, 55, 0.16));
}
.env-fold--right {
  clip-path: polygon(100% 4%, 50.5% 64%, 100% 100%);
  background: linear-gradient(-64deg, rgba(120, 100, 60, 0.1), rgba(120, 100, 60, 0.02) 46%, rgba(120, 100, 60, 0.05) 100%);
  filter: drop-shadow(-1.5px 0.5px 1px rgba(110, 92, 55, 0.16));
}
.env-fold--bottom {
  clip-path: polygon(0 100%, 50% 54%, 100% 100%);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.08) 40%, rgba(120, 100, 60, 0.06) 100%);
  filter: drop-shadow(0 -1.5px 1px rgba(110, 92, 55, 0.18));
}

.env-address {
  position: absolute;
  left: 10%;
  right: 10%;
  top: calc(56% + var(--seal-size) / 2 + 8px); /* always just below the seal */
  font-family: var(--cert-serif);
  font-style: italic;
  font-size: clamp(16px, 3.6vw, 23px);
  color: var(--cert-ink-soft);
  text-align: center;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
  pointer-events: none;
}

/* The flap is an unclipped wrapper (so its seal half isn't cut by the
   triangle); the visible triangle lives on .env-flap-face. */
.env-flap {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 56%;
  z-index: 8;
  transform-origin: top center;
}
.env-flap-face {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.32), transparent 30%),
    repeating-linear-gradient(115deg, rgba(160, 140, 100, 0.028) 0 2px, transparent 2px 5px),
    linear-gradient(180deg, var(--cert-env-shade), var(--cert-env) 88%);
  border-radius: 8px 8px 0 0;
  /* Slightly blunted point — wax gathers at the tip of a real flap. */
  clip-path: polygon(0 0, 100% 0, 51.5% 96%, 50% 100%, 48.5% 96%);
  filter: drop-shadow(0 2px 2px rgba(43, 37, 23, 0.14));
}

/* Seal halves, registered on the flap tip (56% of envelope height).
   The break line is a shared jagged polyline (bottom overlaps up 0.6% so the
   seam is invisible while sealed) — cracking reveals the irregular edge. */
.seal-half {
  position: absolute;
  left: 50%;
  width: var(--seal-size, 84px);
  height: var(--seal-size, 84px);
  margin-left: calc(var(--seal-size, 84px) / -2);
  pointer-events: none;
}
.seal-half .seal-disc {
  width: 100%;
  height: 100%;
}
.seal-half .seal-mark {
  font-size: calc(var(--seal-size, 84px) * 0.286);
}
.seal--top {
  bottom: calc(var(--seal-size, 84px) / -2);
  backface-visibility: hidden; /* rides the flap; hidden once it swings past 90° */
  clip-path: polygon(0% 0%, 100% 0%, 100% 52%, 91% 46%, 80% 55%, 68% 47%, 55% 57%, 45% 46%, 33% 56%, 20% 45%, 9% 55%, 0% 49%);
}
.seal--bottom {
  top: calc(56% - var(--seal-size, 84px) / 2);
  clip-path: polygon(0% 48.4%, 9% 54.4%, 20% 44.4%, 33% 55.4%, 45% 45.4%, 55% 56.4%, 68% 46.4%, 80% 54.4%, 91% 45.4%, 100% 51.4%, 100% 100%, 0% 100%);
}

/* Wax crumbs that break loose when the seal cracks. */
.env-crumbs {
  position: absolute;
  left: 50%;
  top: 56%;
  width: 0;
  height: 0;
  z-index: 9;
  pointer-events: none;
}
.env-crumbs i {
  position: absolute;
  left: -3px;
  top: -3px;
  display: block;
  width: 6px;
  height: 6px;
  background: radial-gradient(circle at 35% 30%, #6E6EFF, #2E2EC8 75%);
  border-radius: 55% 45% 60% 40% / 50% 60% 40% 60%;
  opacity: 0;
}
.env-crumbs i:nth-child(2) { width: 4px; height: 5px; }
.env-crumbs i:nth-child(3) { width: 7px; height: 6px; border-radius: 40% 60% 45% 55%; }
.env-crumbs i:nth-child(4) { width: 4px; height: 4px; }

.env-hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -46px;
  font-family: var(--cert-mono);
  font-size: 10px;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  color: #AAAAAA;
  animation: env-hint-pulse 2.4s ease-in-out 1.6s infinite;
}

/* --- Actions ---------------------------------------------------------------- */

.cert-actions {
  margin-top: 28px;
}
.cert-actions-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}
.cert-btn { min-width: 220px; }

.cert-actions-fallback {
  font-family: var(--cert-mono);
  font-size: 0.625rem;
  letter-spacing: 0.12em;
  color: #AAAAAA;
  margin-top: 18px;
}
.cert-id-copy {
  color: #7E7E7E;
  user-select: all;
}


/* ==========================================================================
   STATE MACHINE
   Default (no JS): envelope hidden via [hidden], certificate fully visible.
   JS adds .cert--js on <html> -> envelope shown, certificate tucked inside.
   .is-opening runs the sequence; .is-open is the settled end state.

   Emergence geometry (--tuck-y / --rise-y in px, --emerge-s) is measured and
   set by js/certificate.js; the var() fallbacks only cover the frames before
   it runs.

   Timeline (ms from click):
     0-180    hint fades
     130-590  wax strains, then cracks along the jagged line; crumbs fall
     650-1270 flap swings open (z-steps behind the envelope at 90°)
     900-1700 certificate slides up out of the mouth (still envelope-sized)
     1750-2500 envelope drops off the page (.is-dropping, added by JS);
              the certificate grows into place
     2500     .is-open -> foil shimmer + actions stagger in
   ========================================================================== */

.cert--js .cert-scene:not(.is-open) .cert-holder {
  opacity: 0;
  transform: translateY(var(--tuck-y, 9%)) scale(var(--emerge-s, 0.5));
}
.cert--js .cert-scene .cert-holder {
  transform-origin: center center;
}

/* Idle affordance: one gentle wiggle after 1s. */
.cert--js .cert-scene:not(.is-opening):not(.is-open) .env {
  animation: env-wiggle 600ms ease-in-out 1000ms 1;
}

/* Phase A: the hint fades; the envelope body itself stays still.
   CRITICAL: no animation may touch .env during the opening — Chrome promotes
   an element with any attached transform animation (even one waiting out its
   delay) to a stacking context, which would flatten the envelope above the
   certificate and break the slide-out. The drop is therefore triggered by a
   separate JS-timed .is-dropping class, never by a delayed animation here. */
.cert-scene.is-opening .env-hint {
  animation: hint-out 180ms ease-out forwards;
}

/* The wax strains, then snaps along the jagged break line. */
.cert-scene.is-opening .seal-half {
  filter: drop-shadow(0 1px 1px rgba(20, 20, 90, 0.35));
}
/* The vanish fires as the flap passes edge-on (~90° at 950ms): the flap's
   children are flattened (no preserve-3d), so backface-visibility alone can't
   cull the seal half once the flap has flipped — this hides it reliably. */
.cert-scene.is-opening .seal--top {
  animation:
    seal-crack-top 460ms cubic-bezier(0.5, 0, 0.6, 1) 130ms forwards,
    seal-top-vanish 20ms linear 940ms forwards;
}
.cert-scene.is-opening .seal--bottom {
  animation: seal-crack-bottom 460ms cubic-bezier(0.5, 0, 0.6, 1) 130ms forwards;
}
.cert-scene.is-opening .env-crumbs i {
  animation: crumb-fall 620ms cubic-bezier(0.45, 0.05, 0.85, 0.5) 430ms forwards;
}
.cert-scene.is-opening .env-crumbs i:nth-child(1) { --crumb-x: -26px; --crumb-y: 46px; --crumb-r: -160deg; }
.cert-scene.is-opening .env-crumbs i:nth-child(2) { --crumb-x: 16px;  --crumb-y: 54px; --crumb-r: 120deg;  animation-delay: 450ms; }
.cert-scene.is-opening .env-crumbs i:nth-child(3) { --crumb-x: 34px;  --crumb-y: 32px; --crumb-r: 200deg;  animation-delay: 440ms; }
.cert-scene.is-opening .env-crumbs i:nth-child(4) { --crumb-x: -12px; --crumb-y: 62px; --crumb-r: -90deg;  animation-delay: 465ms; }

/* Phase B: flap swings open, carrying the top half of the seal. */
.cert-scene.is-opening .env-flap {
  animation: flap-open 620ms cubic-bezier(0.3, 0.75, 0.25, 1) 650ms forwards;
}

/* Phase C+D on the certificate: slide out of the mouth, then grow into place. */
.cert-scene.is-opening .cert-holder {
  animation:
    cert-emerge 800ms cubic-bezier(0.22, 1, 0.36, 1) 900ms both,
    cert-settle 750ms cubic-bezier(0.45, 0.05, 0.2, 1) 1750ms forwards;
}

/* Phase D on the envelope: added by JS at 1750ms. Animating .env makes it a
   stacking context, so the emptied envelope falls BEHIND the certificate. */
.cert-scene.is-dropping .env {
  animation: env-drop 700ms cubic-bezier(0.5, 0, 0.85, 0.35) forwards;
}

.cert-scene.is-open .env { display: none; }
.cert-scene.is-open .cert-holder {
  opacity: 1;
  transform: none;
}

/* Actions stagger in once open. */
.cert--js .cert-actions {
  opacity: 0;
}
.cert--js .cert-actions > * {
  opacity: 0;
  transform: translateY(12px);
}
.cert--js .cert-actions.is-in {
  opacity: 1;
}
.cert--js .cert-actions.is-in > * {
  opacity: 1;
  transform: none;
  transition: opacity 320ms ease-out, transform 320ms ease-out;
}
.cert--js .cert-actions.is-in > *:nth-child(1) { transition-delay: 0ms; }
.cert--js .cert-actions.is-in > *:nth-child(2) { transition-delay: 70ms; }

/* Foil shimmer: one pass, on reveal only. */
.cert-scene.is-open .cert-foil::after {
  animation: foil-sweep 800ms ease-in-out 150ms 1 forwards;
}

/* --- Keyframes -------------------------------------------------------------- */

@keyframes env-wiggle {
  0% { transform: rotate(0deg); }
  30% { transform: rotate(-1deg); }
  65% { transform: rotate(1.2deg); }
  100% { transform: rotate(0deg); }
}

@keyframes hint-out {
  to { opacity: 0; }
}

@keyframes seal-top-vanish {
  to { opacity: 0; }
}

@keyframes seal-crack-top {
  0% { transform: none; }
  55% { transform: scale(1.05) rotate(0.6deg); }
  70% { transform: translate(-2px, -3px) rotate(-3.6deg); }
  100% { transform: translate(-1.5px, -2px) rotate(-3deg); }
}
@keyframes seal-crack-bottom {
  0% { transform: none; }
  55% { transform: scale(1.05) rotate(-0.5deg); }
  70% { transform: translate(1.5px, 2.5px) rotate(2.4deg); }
  100% { transform: translate(1px, 1.5px) rotate(2deg); }
}

@keyframes crumb-fall {
  0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
  85% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--crumb-x, -20px), var(--crumb-y, 50px)) rotate(var(--crumb-r, 140deg)); }
}

/* Self-contained perspective: .env must stay free of perspective/transforms. */
@keyframes flap-open {
  0% { transform: perspective(800px) rotateX(0deg); z-index: 8; }
  49% { z-index: 8; }
  50% { z-index: 0; }
  100% { transform: perspective(800px) rotateX(-176deg); z-index: 0; }
}

@keyframes cert-emerge {
  0% { opacity: 0; transform: translateY(var(--tuck-y, 9%)) scale(var(--emerge-s, 0.5)); }
  7% { opacity: 1; }
  100% { opacity: 1; transform: translateY(var(--rise-y, -36%)) scale(var(--emerge-s, 0.5)); }
}

@keyframes cert-settle {
  0% { opacity: 1; transform: translateY(var(--rise-y, -36%)) scale(var(--emerge-s, 0.5)); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes env-drop {
  0% { transform: translateY(0) rotate(0deg); opacity: 1; }
  80% { opacity: 1; }
  100% { transform: translateY(130vh) rotate(6deg); opacity: 0; }
}

@keyframes foil-sweep {
  0% { opacity: 1; background-position: -150% 0; }
  100% { opacity: 0; background-position: 250% 0; }
}

@keyframes env-hint-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* --- Reduced motion: skip the theatre entirely. ----------------------------- */

@media (prefers-reduced-motion: reduce) {
  .cert--js .cert-scene .env { display: none; }
  .cert--js .cert-scene .cert-holder {
    opacity: 1;
    transform: none;
  }
  .cert--js .cert-actions,
  .cert--js .cert-actions > * {
    opacity: 1;
    transform: none;
  }
  .cert-foil::after { animation: none; }
  .env-hint { animation: none; }
}

/* ==========================================================================
   PRINT / PDF SHELL
   Loaded standalone by scripts/build-certificates.mjs (assets inlined) and
   by /certificate/:slug?print=1. No styles.css, no chrome, no envelope.
   ========================================================================== */

@page {
  size: 1123px 794px;
  margin: 0;
}

.cert-print {
  margin: 0;
  padding: 0;
  background: #ffffff;
}
.cert-print * {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}
.cert-print .cert-stage {
  transform: none;
  box-shadow: none;
  margin: 0;
}
.cert-print .cert-foil { display: none; }
/* Quartz (macOS Preview) rasterizes box-shadow + irregular border-radius as a
   square block — the wax depth comes from the gradient alone in the PDF. */
.cert-print .seal-disc { box-shadow: none; }
