/* ============================================================================
   PAIRED CHALLENGE — states + the envelope reveal
   Loaded only by challenge.html; every selector is namespaced `pc-`.

   ANIMATION NOTES
   - This is a once-per-event moment, not everyday UI, so the choreography is
     deliberately cinematic. The <300ms rule applies to dropdowns and toasts,
     not to a reveal people will film. Interactive affordances (the seal press,
     buttons) still snap in 120-200ms.
   - Custom curves only. The built-in CSS easings are too weak to feel
     intentional: --pc-ease-out for entrances, --pc-ease-swing for the flap.
   - Nothing animates from scale(0) — the envelope enters at 0.94 with opacity,
     because nothing in the real world appears from nothing.
   - The overlay is modal-like, so it stays centred. Only the flap gets an
     origin (its top hinge) and the letter (its bottom edge, so it unfolds
     upward out of the pocket).

   GEOMETRY — read before touching the letter
   The stage is a fixed box. The envelope is pinned to its BOTTOM, and the
   letter travels up into the empty space above. The letter's height is capped
   (with internal scroll) so that `height + travel` can never exceed that empty
   space — an uncapped letter overflows the stage and ends up off-screen, which
   reads as "the animation is broken".

   STACKING — the pocket illusion depends on this exact order
     1  .pc-env-back    the envelope body
     2  .pc-letter      BETWEEN back and front, so the part still inside is hidden
     3  .pc-env-front   the bottom flap, painted OVER the letter
     6  .pc-env-flap    the top flap (drops to 1 once it is past vertical)
     8  .pc-seal
   ========================================================================== */

:root {
  --pc-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --pc-ease-swing: cubic-bezier(0.77, 0, 0.175, 1);
  --pc-lime: #c8ff00;
  /* Paper. Warm off-white, never pure #fff — pure white reads as a screen, not a
     sheet. Ink is a warm near-black, because true black on paper looks printed
     rather than written. */
  --pc-paper-1: #faf7f0;
  --pc-paper-2: #efe9dc;
  --pc-ink: #17150f;
  --pc-ink-soft: #5f594a;
  /* #736c5c, not a lighter grey: the labels and sign-off are small text, and
     anything lighter drops below 4.5:1 on this stock. Measured 4.87:1. */
  --pc-ink-faint: #736c5c;
  /* A pull, not a slide: a small tug to overcome the pocket, fast through the
     middle, then a long settle as the hand places it. */
  --pc-ease-paper: cubic-bezier(0.28, 0.06, 0.2, 1);
  /* A float, not a pull: almost no initial resistance, then a long glide out. */
  --pc-ease-float: cubic-bezier(0.16, 0.72, 0.12, 1);
  /* Brand lime envelope. The flap is a lighter tint so the fold reads without
     an outline; the body carries a barely-there gradient so a panel this large
     does not look like flat vector art. */
  --pc-env-flap-c: #dcff6b;
  --pc-env-body-c: #c8ff00;
  --pc-env-body-c2: #b6ea00;
  --pc-paper: #0a0a0a;
}

/* ── in-page states ───────────────────────────────────────────────────────── */

.pc-panel {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  margin: 22px 0;
}

.pc-eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.pc-h { font-size: 1.35rem; font-weight: 800; letter-spacing: -0.02em; margin: 0 0 8px; }
.pc-sub { color: var(--text-secondary); margin: 0; line-height: 1.6; }

.pc-countdown {
  font-variant-numeric: tabular-nums;
  font-size: clamp(2rem, 7vw, 3.2rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 6px 0 2px;
}

.pc-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: var(--accent);
  color: var(--text);
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  /* Specific properties, never `all`. */
  transition: transform 160ms var(--pc-ease-out), background 160ms ease,
              box-shadow 160ms ease;
}
.pc-btn:hover:not(:disabled) { background: var(--accent-dark); }
/* Instant feedback so the button feels like it heard the tap. */
.pc-btn:active:not(:disabled) { transform: scale(0.97); }
.pc-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.pc-btn--ghost { background: transparent; border-color: var(--border); color: var(--text); }
.pc-btn--ghost:hover:not(:disabled) { background: var(--bg-alt); }

.pc-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 18px; }

/* Secondary action, sat beside the primary one. "Go solo" is a real choice, not
   a warning, so it reads as a calm outline rather than a destructive red -- but
   it must never compete with "Find my collaborator", which is the path we want
   most people to take. Same size and baseline, lower contrast. */
.pc-btn-ghost {
  background: transparent;
  border-color: var(--pc-rule, rgba(0, 0, 0, .18));
  color: var(--text);
  font-weight: 600;
}
.pc-btn-ghost:hover:not(:disabled) {
  background: var(--bg-alt, #f4f4f4);
  border-color: rgba(0, 0, 0, .32);
}
.pc-btn-ghost:disabled { opacity: .55; cursor: not-allowed; }

/* Sits under the two buttons and explains what going solo costs the room. */
.pc-solo-note { margin-top: 12px; font-size: .86rem; opacity: .8; }

.pc-spinner {
  width: 18px; height: 18px;
  border: 2px solid var(--border);
  border-top-color: var(--text);
  border-radius: 50%;
  animation: pc-spin 700ms linear infinite;
}
@keyframes pc-spin { to { transform: rotate(360deg); } }

.pc-waiting { display: flex; align-items: center; gap: 12px; }

/* Big enough to shout across a loud room. */
.pc-pairno {
  font-variant-numeric: tabular-nums;
  font-size: clamp(2.4rem, 9vw, 4rem);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 1;
}
.pc-partner { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.pc-partner img,
.pc-partner .pc-avatar-fallback {
  width: 52px; height: 52px; border-radius: 50%; object-fit: cover;
  background: var(--bg-alt);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; color: var(--text-muted);
  flex: none;
}
.pc-partner-name { font-weight: 700; font-size: 1.05rem; }
.pc-partner-role { color: var(--text-muted); font-size: 0.85rem; }

.pc-brief { display: grid; gap: 16px; margin-top: 18px; }
.pc-brief-row { border-left: 3px solid var(--accent); padding: 2px 0 2px 14px; }
.pc-brief-label {
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-muted);
}
.pc-brief-value { font-weight: 700; font-size: 1.05rem; margin-top: 2px; }
.pc-brief-body { color: var(--text-secondary); font-size: 0.92rem; margin-top: 4px; }

/* ── overlay ────────────────────────────────────────────────────────────── */

.pc-overlay {
  position: fixed; inset: 0;
  z-index: 1100;                       /* the established full-screen tier */
  display: none;
  align-items: center; justify-content: center;
  padding: 20px;
  /* A GREEN glow, not a grey haze.
     Low-alpha lime (#c8ff00) over near-black lifts red and green almost equally,
     so it desaturates to grey-white — which is exactly what the first attempt
     did. The fix is a SATURATED mid-green with red kept low, layered as a tight
     core plus a wide wash. */
  background:
    radial-gradient(ellipse 40% 32% at 50% 48%, rgba(120, 235, 30, 0.26) 0%,
                    rgba(96, 200, 20, 0.11) 44%, rgba(70, 160, 15, 0) 72%),
    radial-gradient(ellipse 86% 78% at 50% 50%, rgba(38, 120, 16, 0.34) 0%,
                    rgba(10, 26, 8, 0.95) 58%, rgba(3, 9, 3, 0.99) 100%);
  overflow: auto;                      /* safety net on very short viewports */
  perspective: 1600px;
}
.pc-overlay.open { display: flex; }
.pc-overlay-fade { animation: pc-fade-in 320ms var(--pc-ease-out) both; }
@keyframes pc-fade-in { from { opacity: 0; } to { opacity: 1; } }

.pc-close {
  position: fixed; top: 20px; right: 20px;
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: #fff; font-size: 1.1rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  z-index: 2;
  transition: background 160ms ease, transform 160ms var(--pc-ease-out);
}
.pc-close:hover { background: rgba(255, 255, 255, 0.16); }
.pc-close:active { transform: scale(0.94); }

/* The stage is the fixed box the whole reveal happens inside. The envelope sits
   at the bottom; everything above is the letter's runway. */
.pc-stage {
  position: relative;
  /* SIZING, in two acts:
     - Sealed: the stage is exactly the envelope's height, so the envelope shows
       at its full size (~66vw, matching the reference) with nothing above it.
     - Open: layoutOpen() grows the height to make a runway for the letter and,
       if the composed scene is taller than the viewport, scales the whole thing
       down. That is the only way to satisfy "big envelope" + "letter taller than
       the envelope" + "fits on a laptop screen" at once.
     Both are transitioned, so the scene settles as the flap swings. */
  transform-origin: center center;
  width: min(1150px, 92vw);
  flex: none;
  transform-style: preserve-3d;
  transition: height 700ms var(--pc-ease-swing),
              transform 700ms var(--pc-ease-swing);
}

/* ── envelope ──────────────────────────────────────────────────────────── */

.pc-env-wrap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  aspect-ratio: 1.75;
  transform-style: preserve-3d;
  /* Enters from 0.94 — never from nothing. NOTE: no rotate() here. A tilt
     rotates the letter too, and since the letter rises far above the rotation
     centre it ends up visibly off-centre. Depth comes from the rim light. */
  animation: pc-env-in 760ms var(--pc-ease-out) both;
}
@keyframes pc-env-in {
  from { opacity: 0; transform: translateY(26px) scale(0.94); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Slow idle drift while it waits. Killed the instant it opens so the drift
   cannot fight the choreography. */
.pc-stage:not(.is-open) .pc-env-wrap {
  animation: pc-env-in 760ms var(--pc-ease-out) both,
             pc-float 7s ease-in-out 900ms infinite;
}
@keyframes pc-float {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(-11px) scale(1); }
}

/* the body of the envelope */
.pc-env-back {
  position: absolute; inset: 0;
  z-index: 1;
  border-radius: 22px;
  background:
    linear-gradient(168deg, var(--pc-env-body-c) 0%, var(--pc-env-body-c2) 100%);
  box-shadow:
    0 50px 110px rgba(0, 0, 0, 0.6),
    0 0 120px rgba(200, 255, 0, 0.14),
    /* paper thickness: lit top edge, shadowed bottom edge */
    0 1px 0 rgba(255, 255, 255, 0.5) inset,
    0 -2px 0 rgba(0, 0, 0, 0.14) inset,
    /* the shadowed interior, just under the open flap */
    0 20px 30px -22px rgba(0, 0, 0, 0.55) inset;
}

/* The bottom flap, painted OVER the letter so the letter appears to sit inside
   the pocket. Its top edge peaks in the middle, like a real envelope. */
.pc-env-front {
  position: absolute; inset: 0;
  z-index: 3;
  border-radius: 22px;
  overflow: hidden;
  pointer-events: none;
}
.pc-env-front::before {
  content: '';
  position: absolute; inset: 0;
  background:
    /* a hairline of light along the folded edge */
    linear-gradient(172deg, rgba(255, 255, 255, 0.55) 0 2px, transparent 2px),
    linear-gradient(172deg, var(--pc-env-body-c) 0%, var(--pc-env-body-c2) 96%);
  clip-path: polygon(0 62%, 50% 34%, 100% 62%, 100% 100%, 0 100%);
  /* lifts the pocket off the body */
  filter: drop-shadow(0 -3px 6px rgba(0, 0, 0, 0.22));
}
/* the seam where the two side folds meet the bottom flap */
.pc-env-front::after {
  content: '';
  position: absolute; inset: 0;
  background:
    /* left and right folds, very slightly darker than the face */
    linear-gradient(to bottom right, rgba(0,0,0,0.055) 0 49.9%, transparent 50%),
    linear-gradient(to bottom left,  rgba(0,0,0,0.03) 0 49.9%, transparent 50%);
}



/* the top flap — hinged on its top edge */
.pc-env-flap {
  position: absolute; top: 0; left: 0; right: 0;
  height: 68%;
  z-index: 6;
  transform-origin: top center;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  border-radius: 22px 22px 0 0;
  background:
    /* hinge crease */
    linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0 2px, transparent 2px),
    linear-gradient(176deg, var(--pc-env-flap-c) 0%, #cff442 100%);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.20));
  transition: transform 700ms var(--pc-ease-swing);
}
.pc-stage.is-open .pc-env-flap {
  transform: rotateX(-178deg);
  /* Drop behind everything once past vertical, or it clips the rising paper.
     Stepped, not eased — z-index cannot tween. */
  z-index: 1;
  transition: transform 700ms var(--pc-ease-swing), z-index 0ms linear 330ms;
}

/* ── the tap target ──────────────────────────────────────────────────────────
   The reference design has no wax seal, so the ENVELOPE is the affordance. A
   real transparent <button> covers it: keyboard-focusable and screen-reader
   labelled, which a click handler on a div would not be. It stops intercepting
   pointer events once open so the letter underneath becomes interactive.      */

.pc-env-open {
  position: absolute; inset: 0;
  z-index: 9;
  border: 0;
  padding: 0;
  border-radius: 22px;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 200ms var(--pc-ease-out);
}
.pc-env-open:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 6px;
}
/* Press feedback on the whole envelope, so it feels like it heard the tap. */
.pc-env-open:active { transform: scale(0.988); }
.pc-stage.is-open .pc-env-open { pointer-events: none; }

.pc-seal-hint {
  position: absolute;
  left: 0; right: 0; bottom: -42px;
  text-align: center;
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  z-index: 8;
  transition: opacity 240ms ease;
}
.pc-stage.is-open .pc-seal-hint { opacity: 0; }

/* A slow breath on the whole envelope while it waits to be opened. */
.pc-stage:not(.is-open) .pc-env-wrap { cursor: pointer; }

/* ── the letter ────────────────────────────────────────────────────────── */

.pc-letter {
  position: absolute;
  left: 16%; right: 16%;
  top: 50%;
  z-index: 7;
  padding: 58px 62px 50px;
  text-align: left;
  border-radius: 3px;                    /* paper barely rounds; 3px, not 10px */
  color: var(--pc-ink);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* PAPER, in four layers:
     1. the two fold creases it arrived with (a letter keeps its creases — fading
        them out would be less realistic, not more)
     2. long fibres running the sheet's length
     3. a soft sheen, as if lit from the top-left
     4. the warm base stock                                                    */
  background-image:
    linear-gradient(180deg, transparent calc(33.33% - 1px), rgba(90, 78, 55, 0.11) 33.33%, transparent calc(33.33% + 1px)),
    linear-gradient(180deg, transparent calc(66.66% - 1px), rgba(90, 78, 55, 0.09) 66.66%, transparent calc(66.66% + 1px)),
    repeating-linear-gradient(92deg, rgba(120, 105, 75, 0.035) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(2deg, rgba(120, 105, 75, 0.028) 0 1px, transparent 1px 4px),
    linear-gradient(148deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0) 46%),
    linear-gradient(168deg, var(--pc-paper-1) 0%, var(--pc-paper-2) 100%);
  /* Cast shadow onto the envelope, plus a warm edge so the sheet has thickness. */
  box-shadow:
    0 42px 70px -22px rgba(0, 0, 0, 0.55),
    0 8px 18px -8px rgba(0, 0, 0, 0.3),
    0 0 0 1px rgba(120, 105, 75, 0.16),
    0 1px 0 rgba(255, 255, 255, 0.9) inset;
  opacity: 0;
  /* Resting deep in the pocket: tilted back, pushed away, and SHORT because it
     is still folded. */
  /* Starts ABOVE, at the envelope's mouth, and floats DOWN to centre.
     The previous version started at +16% — BELOW the centre line — so however
     much wobble it had, the eye read it as rising from the bottom. Direction of
     travel is what the eye reads first; everything else is detail.
     transform-origin stays centred: this is a sheet floating on air, not one
     hinged at an edge. */
  transform:
    translate3d(0, -104%, -50px)
    rotateX(-7deg) rotateZ(-1.3deg)
    scaleY(0.9);
  transform-origin: center center;
  backface-visibility: hidden;
  pointer-events: none;
}

/* Fibre grain. An inline SVG turbulence is the cheapest thing that actually
   reads as paper rather than as a gradient, and being a data URI it costs no
   request. Multiply so it darkens the tooth without greying the sheet. */
.pc-letter::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 3px;
  pointer-events: none;
  opacity: 0.5;
  mix-blend-mode: multiply;
  background-image: 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='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.32'/%3E%3C/svg%3E");
}

.pc-stage.is-open .pc-letter {
  animation: pc-letter-out 1750ms var(--pc-ease-float) 260ms both;
  pointer-events: auto;
}

/**
 * Out of the envelope's mouth at the TOP, then floating down to centre.
 *
 * DIRECTION FIRST. The eye reads direction of travel before it reads anything
 * else, so the sheet has to START above the centre line and descend. The earlier
 * version began below it and rose, and no amount of wobble could stop that
 * reading as "from the bottom".
 *
 * LIGHTWEIGHT, which means the motion has to be mostly HORIZONTAL in effort and
 * mostly gravity in fall:
 *  1. Eight stops. A straight interpolation between two poses is what makes
 *     motion look rigid; a sheet on air changes direction several times.
 *  2. No two properties finish together — the descent settles before the tilt,
 *     the tilt before the sway. Everything landing on one frame is the tell.
 *  3. A DECAYING SWAY: rotateZ crosses zero four times with shrinking amplitude
 *     (-1.3 -> +1.0 -> -0.6 -> +0.35 -> -0.18 -> 0), the way a light sheet rocks
 *     itself to a stop.
 *  4. It DRIFTS PAST centre and floats back up, rather than braking into place.
 *     Paper has almost no mass, so it overshoots gently and returns.
 */
@keyframes pc-letter-out {
  0% {
    opacity: 0;
    transform: translate3d(0, -104%, -50px) rotateX(-7deg) rotateZ(-1.3deg) scaleY(0.9);
  }
  /* clear of the mouth and catching the light */
  10% { opacity: 1; }
  /* falling away from the envelope, leaning the other way */
  32% {
    transform: translate3d(0, -86%, -28px) rotateX(-4.5deg) rotateZ(1deg) scaleY(0.945);
  }
  /* mid-descent; the fold has nearly relaxed */
  54% {
    transform: translate3d(0, -68%, -12px) rotateX(-2deg) rotateZ(-0.6deg) scaleY(0.978);
  }
  /* almost home, level */
  74% {
    transform: translate3d(0, -52%, 2px) rotateX(0.6deg) rotateZ(0.35deg) scaleY(0.997);
  }
  /* DRIFTS PAST centre — no mass, so it does not brake */
  86% {
    transform: translate3d(0, -45.5%, 4px) rotateX(0.2deg) rotateZ(-0.18deg) scaleY(1.004);
  }
  /* floats back up */
  95% {
    transform: translate3d(0, -51.4%, 1px) rotateX(-0.1deg) rotateZ(0.07deg) scaleY(1.001);
  }
  /* at rest, flat and centred */
  100% {
    opacity: 1;
    transform: translate3d(0, -50%, 0) rotateX(0deg) rotateZ(0deg) scaleY(1);
  }
}

/**
 * THE BEND. A sheet held by one corner sags — CSS cannot curve a plane, but the
 * giveaway of a bend is the way light travels across it, so we animate a sheen
 * sweeping the sheet instead. Reads as flex without any geometry.
 */
.pc-letter::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 3px;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(176deg,
    rgba(255,255,255,0) 26%, rgba(255,255,255,0.72) 50%, rgba(255,255,255,0) 74%);
}
.pc-stage.is-open .pc-letter::after {
  animation: pc-letter-sheen 1750ms var(--pc-ease-float) 260ms both;
}
/* Light runs DOWN the sheet as it rises — the direction of travel, so it reads as
   the sheet flexing rather than as a highlight wiped across it. */
@keyframes pc-letter-sheen {
  0%   { opacity: 0;    transform: translateY(-52%); }
  20%  { opacity: 0.5; }
  64%  { opacity: 0.28; }
  100% { opacity: 0;    transform: translateY(44%); }
}

/* Lines are built into the DOM up front (so the letter can be measured before it
   moves) but held invisible until the envelope is opened. */
.pc-letter-line.pc-hold { opacity: 0; animation: none; }

.pc-letter-line {
  opacity: 0;
  transform: translateY(6px);
  animation: pc-line-in 520ms var(--pc-ease-out) both;
}
@keyframes pc-line-in { to { opacity: 1; transform: translateY(0); } }

/* Letterhead. Monochrome ink — a real letterhead is printed in one colour, and
   lime on this stock measures 1.11:1 so it would read as washed out. */
.pc-letter-brand {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  flex: none;
  width: 100%; max-width: 46ch;
  margin: 0 auto 26px;
  padding-bottom: 16px;
  border-bottom: 1px solid rgba(90, 78, 55, 0.2);
}
.pc-letter-id { display: flex; align-items: center; gap: 9px; }
.pc-letter-mark { width: 26px; height: 26px; fill: var(--pc-ink); flex: none; }
.pc-letter-date {
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em;
  color: var(--pc-ink-faint); white-space: nowrap;
}
.pc-letter-word {
  font-size: 0.95rem; font-weight: 800; letter-spacing: -0.02em;
  color: var(--pc-ink);
}

/* The shared measure. Capping all three sections to the same width and centring
   them is what makes the block read as one centred column of left-aligned text. */
.pc-letter-head,
.pc-letter-mid,
.pc-letter-foot {
  width: 100%;
  max-width: 46ch;
  margin-left: auto;
  margin-right: auto;
}
.pc-letter-head { flex: none; }
.pc-salutation {
  font-size: 1.18rem; font-weight: 700; letter-spacing: -0.015em; margin: 0 0 10px;
  color: var(--pc-ink);
}
.pc-letter p { margin: 0; line-height: 1.6; color: var(--pc-ink-soft); }
.pc-letter-intro { font-size: 0.98rem; }

/* Takes up the slack between the head and the sign-off. */
.pc-letter-mid {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 34px;
  padding: 34px 0;
  min-height: 0;
  align-items: stretch;
}

.pc-letter-rule {
  height: 1px; flex: none;
  background: linear-gradient(90deg, rgba(90, 78, 55, 0.34), rgba(90, 78, 55, 0));
}
.pc-letter-label {
  font-size: 0.66rem; font-weight: 800; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--pc-ink-faint);
}
.pc-letter-value {
  font-size: 1.32rem; font-weight: 800; letter-spacing: -0.025em;
  margin: 5px 0 0; color: var(--pc-ink); line-height: 1.28;
}
.pc-letter-body {
  font-size: 0.92rem; margin: 8px 0 0;
  color: var(--pc-ink-soft); line-height: 1.55;
}

.pc-letter-foot {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding-top: 26px;
  border-top: 1px solid rgba(90, 78, 55, 0.16);
}
.pc-letter-signoff {
  font-size: 0.86rem; color: var(--pc-ink-faint); line-height: 1.5;
}
.pc-letter-foot .pc-btn { background: var(--pc-lime); flex: none; }

/* ── responsive ────────────────────────────────────────────────────────── */

@media (max-width: 560px) {
  .pc-letter { padding: 30px 24px 26px; left: 8%; right: 8%; }
  .pc-letter-mid { gap: 18px; padding: 16px 0; }
  .pc-letter-value { font-size: 1.1rem; }
  .pc-letter-foot { flex-direction: column; align-items: stretch; }
}

/* Touch devices fire hover on tap, which would leave the glow stuck on. */
@media (hover: hover) and (pointer: fine) {
  .pc-seal:hover {
    box-shadow:
      0 10px 26px rgba(0, 0, 0, 0.75),
      0 0 0 1px rgba(200, 255, 0, 0.9),
      0 0 0 10px rgba(200, 255, 0, 0.10);
  }
}

/* Reduced motion: keep the opacity story (it carries meaning), drop the travel.
   styles.css already zeroes durations globally; this pins the resting state so
   nothing is left mid-flight. */
@media (prefers-reduced-motion: reduce) {
  .pc-stage:not(.is-open) .pc-env-wrap { animation: none; }
  .pc-env-wrap { transform: none; opacity: 1; }
  .pc-stage.is-open .pc-env-flap { transform: rotateX(-178deg); }
  .pc-stage.is-open .pc-letter {
    animation: none;
    opacity: 1;
    transform: translate3d(0, -50%, 0) rotateX(0) rotateY(0) rotateZ(0) scaleY(1);
  }
  .pc-stage.is-open .pc-letter::after { animation: none; opacity: 0; }
  .pc-letter-line { opacity: 1; transform: none; animation: none; }
}

/* ── slot machine: landing on your partner ─────────────────────────────────────
   A physical reel, not a scrolling list. What sells it:
   - a recessed window (inset shadow) rather than a raised card
   - CURVATURE: the rows above and below the centre are scaled down, dimmed and
     tilted in 3D, so the strip reads as wrapping around a drum
   - the centre row is the only one at full size, full brightness and brand colour
   - motion blur while spinning, removed on landing
   - a lime bracket on the winning row, and tick marks down each flank
   The landing itself is deterministic — see playSlotMachine().               */

.pc-slot-wrap { margin-top: 22px; }

/* Housing — the window is set into a surround, so it reads as a machine part
   rather than a floating card. */
.pc-slot-housing {
  padding: 12px;
  border-radius: 22px;
  background: linear-gradient(180deg, #1b1b1b 0%, #0e0e0e 100%);
  border: 1px solid rgba(255, 255, 255, 0.07);
  box-shadow:
    0 22px 50px rgba(0, 0, 0, 0.45),
    0 1px 0 rgba(255, 255, 255, 0.06) inset;
  transition: box-shadow 300ms ease, border-color 300ms ease;
}
.pc-slot-housing.is-locked {
  border-color: rgba(200, 255, 0, 0.35);
  box-shadow:
    0 22px 50px rgba(0, 0, 0, 0.45),
    0 0 44px rgba(200, 255, 0, 0.14),
    0 1px 0 rgba(255, 255, 255, 0.06) inset;
}

/* The pair number, revealed once the name locks in. */
.pc-slot-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 12px; padding: 0 4px;
  min-height: 20px;
}
.pc-slot-pool {
  font-size: 0.78rem; color: var(--text-muted);
}
.pc-slot-pair {
  font-size: 0.78rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text);
  opacity: 0; transform: translateY(4px);
  transition: opacity 300ms var(--pc-ease-out) 220ms,
              transform 300ms var(--pc-ease-out) 220ms;
}
.pc-slot-pair.is-shown { opacity: 1; transform: translateY(0); }

.pc-slot-label {
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 12px;
  transition: color 240ms ease;
}
.pc-slot {
  position: relative;
  height: 168px;                         /* 3 rows x 56px */
  overflow: hidden;
  border-radius: 16px;
  background:
    linear-gradient(180deg, #0a0a0a 0%, #131313 50%, #0a0a0a 100%);
  border: 1px solid rgba(255, 255, 255, 0.09);
  /* recessed, like a window cut into the panel */
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.4),
    0 2px 10px rgba(0, 0, 0, 0.5) inset,
    0 -1px 0 rgba(255, 255, 255, 0.05) inset;
  /* the drum the rows curve around */
  perspective: 460px;
  perspective-origin: 50% 50%;
}

/* Tick marks down each flank, like a mechanical reel. */
.pc-slot::before {
  content: '';
  position: absolute; inset: 0;
  z-index: 4;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(180deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 14px),
    repeating-linear-gradient(180deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 14px);
  background-size: 7px 100%, 7px 100%;
  background-position: left center, right center;
  background-repeat: no-repeat;
}

/* Top and bottom fades, so the strip disappears into the drum. */
.pc-slot::after {
  content: '';
  position: absolute; inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(180deg, #0b0b0b 0%, rgba(11,11,11,0.72) 38%, rgba(11,11,11,0) 56%),
    linear-gradient(0deg,   #0b0b0b 0%, rgba(11,11,11,0.72) 38%, rgba(11,11,11,0) 56%);
}

/* The winning row: a lime bracket rather than a filled block, so the name stays
   the brightest thing in the window. */
.pc-slot-band {
  position: absolute;
  left: 10px; right: 10px; top: 56px; height: 56px;
  z-index: 1;
  border-radius: 10px;
  background: linear-gradient(90deg,
    rgba(200,255,0,0.05), rgba(200,255,0,0.09), rgba(200,255,0,0.05));
  border-top: 1px solid rgba(200, 255, 0, 0.30);
  border-bottom: 1px solid rgba(200, 255, 0, 0.30);
  transition: background 260ms ease, border-color 260ms ease, box-shadow 260ms ease;
}
.pc-slot.is-locked .pc-slot-band {
  background: linear-gradient(90deg,
    rgba(200,255,0,0.10), rgba(200,255,0,0.18), rgba(200,255,0,0.10));
  border-color: rgba(200, 255, 0, 0.85);
  box-shadow: 0 0 30px rgba(200, 255, 0, 0.22), 0 0 0 1px rgba(200,255,0,0.18);
}

.pc-slot-reel {
  position: relative;
  z-index: 2;
  will-change: transform;
  transform: translateY(0);
  transform-style: preserve-3d;
}

.pc-slot-item {
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 26px;
  font-size: 1.06rem;
  font-weight: 700;
  letter-spacing: -0.012em;
  color: rgba(255, 255, 255, 0.30);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Curvature: every row is pushed back and shrunk a little; the centre row is
     brought forward by the rule below. Cheap 3D, and it is what stops this
     looking like a plain scrolling list. */
  transform: translateZ(-26px) scale(0.9);
  opacity: 0.7;
  transition: color 200ms ease, opacity 200ms ease, transform 200ms ease;
}
/* The row sitting in the band. */
.pc-slot-item.is-winner {
  color: var(--pc-lime);
  opacity: 1;
  transform: translateZ(0) scale(1);
  text-shadow: 0 0 18px rgba(200, 255, 0, 0.35);
}

.pc-slot.is-locked .pc-slot-item.is-winner {
  animation: pc-slot-pop 460ms var(--pc-ease-out);
}
@keyframes pc-slot-pop {
  0%   { transform: translateZ(0) scale(1); }
  36%  { transform: translateZ(0) scale(1.06); }
  100% { transform: translateZ(0) scale(1); }
}

@media (max-width: 560px) {
  .pc-slot { height: 144px; }
  .pc-slot-band { top: 48px; height: 48px; }
  .pc-slot-item { height: 48px; font-size: 0.98rem; }
}

@media (prefers-reduced-motion: reduce) {
  .pc-slot-reel { transition: none !important; }
  .pc-slot.is-locked .pc-slot-item.is-winner { animation: none; }
}

/* ── the reel IS the waiting state ────────────────────────────────────────────
   There is no separate "Finding your partner…" spinner any more. The moment you
   tap Find my partner the reel starts spinning, and when your partner arrives it
   decelerates onto their name. One continuous motion, one thing to look at.

   The idle loop translates by exactly PC_SLOT_LOOP_ROWS * 48px, so a reel built
   from repeated blocks of that many names loops seamlessly.                    */

.pc-slot-reel.is-spinning {
  animation: pc-slot-loop 620ms linear infinite;
  /* A real drum smears at speed. Cleared on landing so the name lands crisp. */
  filter: blur(1.5px);
}
.pc-slot-reel { transition: filter 320ms var(--pc-ease-out); }
@keyframes pc-slot-loop {
  from { transform: translateY(0); }
  /* Distance = SLOT_LOOP_ROWS x the MEASURED row height, set by startIdleSpin().
     Hardcoding it breaks the seamless wrap as soon as the row height changes at
     a breakpoint. */
  to   { transform: translateY(var(--pc-loop, -672px)); }
}

/* While spinning, no single row is "the" winner. */
.pc-slot-reel.is-spinning .pc-slot-item { color: rgba(255, 255, 255, 0.42); }

@media (prefers-reduced-motion: reduce) {
  .pc-slot-reel.is-spinning { animation: none; }
}

/* ── check-in field ────────────────────────────────────────────────────────── */

.pc-field { margin-top: 18px; max-width: 380px; }
.pc-label {
  display: block;
  font-size: 0.72rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 8px;
}
.pc-input {
  width: 100%;
  padding: 13px 15px;
  border: 2px solid var(--border);
  border-radius: 11px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.pc-input:focus {
  outline: none;
  border-color: var(--text);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.pc-input::placeholder { color: var(--text-muted); opacity: 0.7; }
.pc-error { font-size: 0.84rem; color: #ef4444; margin: 7px 0 0; min-height: 1em; }

/* Roster autocomplete. The list itself never reaches the browser — the callable
   returns at most 8 matches — so this is a suggestion list, not a picker. */
.pc-suggest { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
.pc-suggest:empty { display: none; }
.pc-suggest-item {
  text-align: left;
  padding: 10px 13px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease;
}
.pc-suggest-item:hover, .pc-suggest-item:focus-visible {
  background: var(--accent-soft);
  border-color: var(--accent-dark);
  outline: none;
}
