:root {
  color-scheme: light dark;

  --color-bg: #F7F6F3;
  --color-text: #2B2B2B;
  --color-text-title: #4A4A4A;
  --color-text-muted: #8A8A8A;
  --color-border: #2B2B2B;
  --color-card-glass: rgba(255, 255, 255, 0.32);
  --color-btn-bg: rgba(255, 255, 255, 0.45);
  --color-btn-bg-active: rgba(255, 255, 255, 0.75);
  --color-specular: rgba(255, 255, 255, 0.9);
  /* Dial: strength of the secondary "far edge" light catch relative to the
     main one (0 = off, 1 = matches the main catch). See .card::after and
     .btn::after — the 0.9 in their calc() must be kept in sync with
     --color-specular's alpha above if that ever changes. */
  --spec-opposite-weight: 0.4;
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.02), 0 12px 28px -12px rgba(0, 0, 0, 0.06);
  /* Colors only — offsets are computed per-frame by tilt.js (see .btn::before)
     so the shadow direction stays correct in world space as a button spins. */
  --shadow-btn-color-1: rgba(0, 0, 0, 0.04);
  --shadow-btn-color-2: rgba(0, 0, 0, 0.11);

  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Helvetica, Arial, sans-serif;
}

/* Complementary dark scheme: same warm-paper hue family as light mode,
   with lightness inverted (warm near-black paper, off-white ink). */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #1B1A18;
    --color-text: #EDEBE6;
    --color-text-title: #C9C6BF;
    --color-text-muted: #94918A;
    --color-border: #EDEBE6;
    --color-card-glass: rgba(255, 255, 255, 0.04);
    --color-btn-bg: rgba(255, 255, 255, 0.06);
    --color-btn-bg-active: rgba(255, 255, 255, 0.14);
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.175), 0 12px 28px -12px rgba(0, 0, 0, 0.275);
    --shadow-btn-color-1: rgba(0, 0, 0, 0.2);
    --shadow-btn-color-2: rgba(0, 0, 0, 0.3);
  }
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  /* No legitimate content ever scrolls on this single-screen card — this
     kills the elastic/rubber-band overscroll bounce on mobile (iOS Safari
     16+, Chrome/Android) rather than just the spurious 100vh-driven scroll
     fixed above. Set on both html and body since browsers vary on which one
     they honor. */
  overscroll-behavior: none;
}

#confetti-canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  z-index: -1;
  pointer-events: none;
}

/* A minority of particles (see confetti.js FRONT_LAYER_CHANCE) render here
   instead, above the card and above thrown physics buttons (z-index: 50
   below), so some confetti visibly passes in front of them rather than
   always behind. */
#confetti-canvas-front {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  z-index: 100;
  pointer-events: none;
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
  font-family: var(--font-sans);
  color: var(--color-text);
  background-color: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.card {
  position: relative;
  isolation: isolate;
  width: 100%;
  max-width: 420px;
  border-radius: 10px;
  box-shadow: var(--shadow-card);
  will-change: transform;
  /* Without this, children can get flattened into a pre-transform 2D plane
     rather than genuinely warping in the same 3D space as .card's own
     perspective()/rotateX/rotateY (tilt.js) — backdrop-filter on .btn in
     particular is known to force its own compositing layer in a way that
     can otherwise escape the ancestor's 3D warp. */
  transform-style: preserve-3d;
}

.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  border-radius: inherit;
  /* Sheen layer reuses the same --spec-x/y/intensity light source as the
     .card::after bezel below, so both read as one coherent glint rather than
     two independently-moving highlights. Kept deliberately faint (the 0.0875
     multiplier, 25% of the original 0.35) — a broad, barely-there wash across
     the glass, not a spotlight. */
  background:
    radial-gradient(
      200% 200% at var(--spec-x, 50%) var(--spec-y, 50%),
      rgb(255 255 255 / calc(var(--spec-intensity, 0.35) * 0.0875)) 0%,
      rgb(255 255 255 / 0) 70%
    ),
    var(--color-card-glass);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Fake specular bezel: a thin gradient ring (not a fill) via the
   padding+mask-composite:exclude trick, so it reads as light catching the
   edge of a glass pane rather than a glow across the whole card. --spec-x/y/
   intensity are updated live by tilt.js from the same eased tilt angle
   driving the 3D rotation, so the glint tracks the card's actual visual
   tilt; the var() fallbacks here keep a plain, centered, static bezel when
   JS is disabled or tilt.js gates itself off (touch devices, reduced motion).
   A second, dimmer catch mirrors the main one through the center
   (calc(100% - var(--spec-x))) — like light passing through and catching
   the far edge of the glass too — giving the shape a clearer divide from
   its surroundings on the side away from the light. Weighted independently
   via --spec-opposite-weight (see :root) rather than sharing --spec-
   intensity outright, so the two catches can be balanced separately. */
.card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  padding: 1.5px;
  opacity: var(--spec-intensity, 0.35);
  background:
    radial-gradient(
      140% 140% at var(--spec-x, 50%) var(--spec-y, 50%),
      var(--color-specular) 0%,
      rgba(255, 255, 255, 0) 45%
    ),
    radial-gradient(
      140% 140% at calc(100% - var(--spec-x, 50%)) calc(100% - var(--spec-y, 50%)),
      rgb(255 255 255 / calc(0.9 * var(--spec-opposite-weight, 0.4))) 0%,
      rgb(255 255 255 / 0) 45%
    );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}

.card__body {
  padding: 2rem 1.5rem 2.25rem;
}

.card__name {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-text);
}

.card__title {
  margin: 0.45rem 0 0;
  font-size: 0.95rem;
  color: var(--color-text-title);
}

.card__company {
  margin: 0.35rem 0 0;
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

.card__divider {
  border: none;
  height: 2px;
  margin: 1.5rem 0;
  background: var(--color-text-title);
}

.card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* <noscript> isn't reliably block-level by default across browsers. */
noscript {
  display: block;
}

.card__noscript-note {
  margin: 0.75rem 0 0;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.btn {
  position: relative;
  flex: 1 1 auto;
  text-align: center;
  padding: 0.75rem 1.1rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  border-radius: 14px;
  border: none;
  background: var(--color-btn-bg);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  color: var(--color-text);
  transition: background-color 0.15s ease;
  white-space: nowrap;
  -webkit-user-drag: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

/* Drop shadow lives on its own invisible (no background) pseudo-element so
   its box-shadow offsets can be individually driven by custom properties.
   Its SHAPE rotates naturally with .btn (no counter-rotation) — it should
   hug the button's true rotated silhouette. What tilt.js corrects instead is
   the light DIRECTION: each frame it rotates the fixed world-space shadow
   vector (straight down) into the button's current local frame using its
   live physics angle, the same "rotate world offset into local frame" trick
   confetti.js uses for push points. Once the browser then applies the
   button's actual rotation to this whole (already pre-rotated) box, the
   shadow lands back pointing the same fixed world-space direction — as if
   cast by a light that isn't spinning along with the object, the way a real
   flat object's shadow direction doesn't change from spinning in its own
   plane. var(..., fallback) defaults (straight down) apply whenever tilt.js
   hasn't run for this button yet (not yet activated, or tilt.js gated off). */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  border-radius: inherit;
  box-shadow:
    var(--shadow-btn-x1, 0px) var(--shadow-btn-y1, 1px) 2px var(--shadow-btn-color-1),
    var(--shadow-btn-x2, 0px) var(--shadow-btn-y2, 8px) 18px -8px var(--shadow-btn-color-2);
}

/* Same specular bezel technique as .card::after, replacing the old solid
   var(--color-border) stroke. Same world-space-correction as .btn::before
   above: the ring's SHAPE rotates naturally with .btn (traces the button's
   true rotated edges), but tilt.js pre-rotates the cursor-relative light
   direction into the button's local frame each frame before writing
   --spec-x/y, so the glint's apparent position stays correct in world space
   rather than spinning as a decal glued to the button's surface. The
   var(..., fallback) defaults keep a plain, centered, static bezel when
   tilt.js never runs (touch devices, reduced motion, JS disabled). Also has
   the same dimmer, mirrored "far edge" catch as .card::after, weighted via
   the shared --spec-opposite-weight dial. */
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  padding: 1.5px;
  opacity: var(--spec-intensity, 0.35);
  background:
    radial-gradient(
      140% 140% at var(--spec-x, 50%) var(--spec-y, 50%),
      var(--color-specular) 0%,
      rgba(255, 255, 255, 0) 45%
    ),
    radial-gradient(
      140% 140% at calc(100% - var(--spec-x, 50%)) calc(100% - var(--spec-y, 50%)),
      rgb(255 255 255 / calc(0.9 * var(--spec-opposite-weight, 0.4))) 0%,
      rgb(255 255 255 / 0) 45%
    );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}

.btn:hover,
.btn:focus-visible {
  background: var(--color-btn-bg-active);
}

.btn:focus-visible {
  outline: 2px solid var(--color-border);
  outline-offset: 2px;
}

.btn--physics {
  margin: 0;
  z-index: 50;
  will-change: transform;
  transform-origin: 50% 50%;
  cursor: grab;
  /* Belt-and-suspenders alongside onPointerDown's e.preventDefault(): stops
     the browser's native touch pan/scroll gesture from ever contesting a
     touch-drag on an activated button. */
  touch-action: none;
}

.btn--physics.is-dragging {
  cursor: grabbing;
  transition: none;
  background: var(--color-btn-bg-active);
}

@media (max-width: 420px) {
  .card__actions {
    flex-direction: column;
  }
}
