/* www.lettermatching.com — shared styles */

:root {
  --bg: #000000;
  /* rgba(255,255,255,0.5) over #000 = 5.28:1 — passes 4.5:1 (was 0.32 = 2.67:1, a fail) */
  --footer-text: rgba(255, 255, 255, 0.5);
  --footer-text-hover: rgba(255, 255, 255, 0.7);
  /* rgba(255,255,255,0.5) over #000 = 5.28:1 — passes 4.5:1 (was 0.18 = 1.54:1, a fail) */
  --watermark-text: rgba(255, 255, 255, 0.5);
  /* purely decorative glyphs only (aria-hidden separators) — exempt from 1.4.3 */
  --decorative-text: rgba(255, 255, 255, 0.18);
  --page-text: rgba(255, 255, 255, 0.72);
  --page-heading: rgba(255, 255, 255, 0.92);
  --page-link: rgba(255, 255, 255, 0.85);
  --focus-ring: rgba(255, 255, 255, 0.85);
}

* {
  box-sizing: border-box;
}

/* ---------- Accessibility utilities ---------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 100;
  background: #000;
  color: #fff;
  padding: 10px 16px;
  border-radius: 6px;
  font-size: 14px;
  text-decoration: underline;
  transition: top 150ms ease;
}

.skip-link:focus {
  top: 12px;
}

/* Visible, measured focus style used everywhere by default (3:1+ against
   black; see accessibility report for the full contrast table). Components
   with their own bespoke focus treatment override this locally. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 2px;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  height: 100%;
}

body {
  min-height: 100dvh;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  -webkit-tap-highlight-color: transparent;
}

/* ---------- Canvas (index) ---------- */

body.canvas-body {
  position: relative;
  min-height: 100dvh;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

/* Empty spacer so the AI disclosure sits just below the fold —
   reachable, but out of the way of the bubbles. */
.scroll-spacer {
  height: 125vh;
  pointer-events: none;
}

.ai-disclosure {
  position: relative;
  z-index: 2;
  background: var(--bg);
  max-width: 640px;
  margin: 0 auto;
  padding: 48px 24px 120px;
  user-select: text;
  -webkit-user-select: text;
}

.ai-disclosure h2 {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--page-heading);
  margin: 0 0 14px;
}

.ai-disclosure p {
  font-size: 13px;
  line-height: 1.7;
  color: var(--page-text);
  margin: 0;
}

.ai-disclosure a {
  color: var(--page-link);
}

.ai-disclosure a:hover,
.ai-disclosure a:focus-visible {
  color: var(--footer-text-hover);
}

#bubble-field {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: 1;
}

/* ---------- Rotating photo backdrop (js/backgrounds.js) ---------- */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.backdrop-layer {
  position: absolute;
  inset: 0;
  background-color: var(--bg);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity 2s ease;
}

.backdrop-layer.visible {
  opacity: 1;
}

/* Dims the photos so the white bubbles, letters, and footer text stay
   readable over bright skies and sand. */
.backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}

.bg-sunset1 { background-image: url("../assets/bg-sunset1.jpg"); }
.bg-dunes1  { background-image: url("../assets/bg-dunes1.jpg"); }
.bg-tide1   { background-image: url("../assets/bg-tide1.jpg"); }

/* Extra shade behind the footer links on the canvas page only, since they
   now sit over a photo instead of plain black. */
body.canvas-body .site-footer {
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 45%, rgba(0, 0, 0, 0.85) 100%);
  padding-top: 32px;
}

/* Phones (portrait, or landscape with a short screen): take the footer off
   the game screen. It drops into the normal page flow at the very bottom,
   below the AI disclosure, and the empty spacer above that grows by half a
   screen (125vh -> 175vh) so the links only show up after a deliberate
   scroll well past the game. */
@media (max-width: 700px), (pointer: coarse) and (max-height: 500px) {
  body.canvas-body .scroll-spacer {
    height: 175vh;
  }

  body.canvas-body .ai-disclosure {
    padding-bottom: 32px;
  }

  body.canvas-body .site-footer {
    position: relative;
    background: var(--bg);
    padding: 24px 16px 32px;
  }
}

/* ---------- Music toggle (persistent UI, not a pop-able bubble) ---------- */

/* ---------- Target letter bubble (top-left, not pop-able) ---------- */

.target-bubble {
  position: fixed;
  top: 16px;
  left: 16px;
  z-index: 5;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* Shared green "letter to match" bubble — the top-left target and the
   big new-letter flash use the same look so they read as the same thing. */
.target-bubble,
.new-letter-flash {
  background:
    radial-gradient(circle at 28% 24%, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.4) 5%, rgba(255, 255, 255, 0) 13%),
    radial-gradient(circle at 68% 72%, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 8%),
    radial-gradient(circle at 50% 50%, rgba(30, 170, 75, 0.88) 45%, rgba(50, 210, 100, 0.92) 76%, rgba(170, 255, 190, 0.95) 90%, rgba(60, 230, 110, 0.65) 100%);
  box-shadow:
    inset 0 0 14px rgba(170, 255, 190, 0.5),
    0 0 30px rgba(60, 230, 110, 0.55);
}

.target-letter,
.new-letter-flash-letter {
  font-weight: 800;
  line-height: 1;
  color: #ffffff;
  /* dark green outline keeps the white letter crisp on the green bubble */
  -webkit-text-stroke: 2px rgb(10, 90, 40);
  paint-order: stroke fill;
  text-shadow: 0 0 6px rgba(10, 90, 40, 0.8);
}

.target-letter {
  font-size: 52px;
}

.target-bubble.changed {
  animation: target-change 600ms ease-out;
}

@keyframes target-change {
  0%   { transform: scale(0.7); opacity: 0.3; }
  60%  { transform: scale(1.1); opacity: 1; }
  100% { transform: scale(1); }
}

/* ---------- New-letter flash (big green bubble, center screen) ---------- */

.new-letter-flash {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 6;
  width: min(60vw, 60vh, 320px);
  height: min(60vw, 60vh, 320px);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
}

.new-letter-flash-letter {
  font-size: min(32vw, 32vh, 170px);
  -webkit-text-stroke-width: 4px;
}

.new-letter-flash.show {
  animation: new-letter-flash 1800ms ease-out forwards;
}

@keyframes new-letter-flash {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
  15%  { opacity: 1; transform: translate(-50%, -50%) scale(1.08); }
  25%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  40%  { opacity: 0.6; }
  50%  { opacity: 1; }
  70%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.25); }
}

/* ---------- Streak starburst (5 matches in a row, js/bubbles.js) ---------- */

.streak-burst {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 7;
  width: min(80vw, 80vh, 420px);
  height: min(80vw, 80vh, 420px);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.3);
  filter: drop-shadow(0 0 24px rgba(255, 200, 60, 0.8));
}

.streak-burst-star {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, #fff7b0 0%, #ffd23f 45%, #ff9f1c 80%);
  clip-path: polygon(50% 0%, 58.5% 18.1%, 75% 6.7%, 73.3% 26.7%, 93.3% 25%, 81.9% 41.5%, 100% 50%, 81.9% 58.5%, 93.3% 75%, 73.3% 73.3%, 75% 93.3%, 58.5% 81.9%, 50% 100%, 41.5% 81.9%, 25% 93.3%, 26.7% 73.3%, 6.7% 75%, 18.1% 58.5%, 0% 50%, 18.1% 41.5%, 6.7% 25%, 26.7% 26.7%, 25% 6.7%, 41.5% 18.1%);
}

.streak-burst-text {
  position: relative;
  max-width: 62%;
  text-align: center;
  font-size: clamp(22px, 6vmin, 40px);
  font-weight: 800;
  line-height: 1.1;
  color: #ffffff;
  /* dark orange outline keeps white text readable on the yellow star */
  -webkit-text-stroke: 2px rgb(150, 60, 0);
  paint-order: stroke fill;
  text-shadow: 0 2px 6px rgba(120, 40, 0, 0.7);
}

.streak-burst.show {
  animation: streak-burst 1800ms ease-out forwards;
}

.streak-burst.show .streak-burst-star {
  animation: streak-spin 1800ms ease-out forwards;
}

@keyframes streak-burst {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
  15%  { opacity: 1; transform: translate(-50%, -50%) scale(1.1); }
  25%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  75%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.15); }
}

@keyframes streak-spin {
  0%   { transform: rotate(-20deg); }
  100% { transform: rotate(25deg); }
}

.music-toggle {
  position: fixed;
  top: 35px;
  left: 128px;
  z-index: 5;
  width: 58px;
  height: 58px;
  padding: 0;
  margin: 0;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  touch-action: manipulation;
  display: flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  outline: none;
  background-color: transparent;
  background-clip: padding-box;
  background:
    radial-gradient(circle at 28% 24%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.5) 5%, rgba(255, 255, 255, 0) 13%),
    radial-gradient(circle at 68% 72%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 8%),
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.05) 50%, rgba(210, 235, 255, 0.28) 76%, rgba(255, 255, 255, 0.45) 88%, rgba(255, 255, 255, 0.05) 100%);
  box-shadow:
    inset 0 0 6px rgba(255, 255, 255, 0.25),
    0 0 16px rgba(255, 255, 255, 0.08);
  transition: box-shadow 200ms ease;
}

.music-icon {
  font-size: 22px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.55);
  user-select: none;
  transition: color 200ms ease, filter 200ms ease;
}

.music-toggle.playing .music-icon {
  color: rgba(255, 255, 255, 0.92);
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.5));
}

/* ---------- Settings toggle + panel (B2: larger / slower / forgiving) ---------- */

.settings-toggle {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 5;
  width: 58px;
  height: 58px;
  padding: 0;
  margin: 0;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  touch-action: manipulation;
  display: flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  outline: none;
  background-color: transparent;
  background-clip: padding-box;
  background:
    radial-gradient(circle at 28% 24%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.5) 5%, rgba(255, 255, 255, 0) 13%),
    radial-gradient(circle at 68% 72%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 8%),
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.05) 50%, rgba(210, 235, 255, 0.28) 76%, rgba(255, 255, 255, 0.45) 88%, rgba(255, 255, 255, 0.05) 100%);
  box-shadow:
    inset 0 0 6px rgba(255, 255, 255, 0.25),
    0 0 16px rgba(255, 255, 255, 0.08);
}

.settings-icon {
  font-size: 22px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.55);
  user-select: none;
  transition: color 200ms ease, filter 200ms ease;
}

.settings-toggle[aria-expanded="true"] .settings-icon {
  color: rgba(255, 255, 255, 0.92);
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.5));
}

.settings-panel {
  position: fixed;
  top: 86px;
  right: 20px;
  z-index: 5;
  width: 240px;
  max-width: calc(100vw - 40px);
  background: rgba(0, 0, 0, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 14px;
  padding: 16px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6);
}

.settings-panel[hidden] {
  display: none;
}

.settings-panel-title {
  margin: 0 0 12px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--page-heading);
}

.settings-option {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--page-text);
  cursor: pointer;
}

.settings-option:last-child {
  margin-bottom: 0;
}

.settings-option input[type="checkbox"] {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  accent-color: #ffffff;
}

/* .bubble is the real hit target (a <button>) — it owns position and the
   float/fall animation. Its visible "glass" sphere is a separate,
   pointer-events:none child sized to the visual diameter, so the tap
   target (B1: 24x24 min, larger with the "forgiving hit area" setting)
   can be bigger than what's drawn without distorting the artwork. */
.bubble {
  position: absolute;
  top: -12vh;
  border: none;
  margin: 0;
  padding: 0;
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  border-radius: 50%;
  cursor: pointer;
  /* "none" so a finger can drag a bubble without scrolling the page */
  touch-action: none;
  will-change: transform, opacity;
  animation-name: float;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}

/* A non-matching bubble being held and dragged aside (js/bubbles.js).
   It stops falling while held and sits above the other bubbles. */
.bubble.held {
  z-index: 3;
  cursor: grabbing;
}

/* After release, a dragged bubble keeps falling from where it was dropped. */
@keyframes fall-rest {
  0%   { transform: translateY(0); opacity: var(--held-op, 0.85); }
  90%  { opacity: var(--held-op, 0.85); }
  100% { transform: translateY(var(--fall-dist)); opacity: 0; }
}

.bubble:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}

.bubble.popping {
  animation-play-state: paused;
}

.bubble-glass {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(circle at 28% 24%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.5) 5%, rgba(255, 255, 255, 0) 13%),
    radial-gradient(circle at 68% 72%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 8%),
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.03) 50%, rgba(210, 235, 255, 0.28) 76%, rgba(255, 255, 255, 0.45) 88%, rgba(255, 255, 255, 0.05) 100%);
  box-shadow:
    inset 0 0 6px rgba(255, 255, 255, 0.25),
    0 0 16px rgba(255, 255, 255, 0.08);
}

@keyframes float {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 0; }
  8%   { opacity: var(--op, 0.85); }
  25%  { transform: translate(calc(var(--drift) * 0.5), 35vh) rotate(calc(var(--rotate) * 0.3)); }
  50%  { transform: translate(calc(var(--drift) * -0.6), 70vh) rotate(calc(var(--rotate) * -0.4)); }
  75%  { transform: translate(calc(var(--drift) * 0.8), 105vh) rotate(calc(var(--rotate) * 0.6)); }
  92%  { opacity: var(--op, 0.85); }
  100% { transform: translate(calc(var(--drift) * -0.3), 140vh) rotate(var(--rotate)); opacity: 0; }
}

.bubble.popping .bubble-glass {
  animation: pop 220ms ease-out forwards;
}

@keyframes pop {
  0%   { transform: translate(-50%, -50%) scale(1); opacity: 1; filter: blur(0); }
  55%  { transform: translate(-50%, -50%) scale(1.35); opacity: 0.55; filter: blur(0.5px); }
  100% { transform: translate(-50%, -50%) scale(1.7); opacity: 0; filter: blur(1px); }
}

.bubble-letter {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  line-height: 1;
  /* fallback; js/bubbles.js gives each bubble its own bright color */
  color: rgba(255, 255, 255, 0.92);
  /* dark outline + shadow keep colored letters readable over the photos */
  -webkit-text-stroke: 1px rgba(0, 0, 0, 0.55);
  paint-order: stroke fill;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75);
}

/* Wrong letter: the bubble jiggles and ripples but stays intact. */
.bubble.wobble .bubble-glass {
  animation: jiggle 420ms ease-in-out;
}

@keyframes jiggle {
  0%   { transform: translate(-50%, -50%) scale(1, 1); }
  15%  { transform: translate(calc(-50% - 6px), -50%) scale(1.08, 0.92); }
  30%  { transform: translate(calc(-50% + 5px), -50%) scale(0.93, 1.07); }
  45%  { transform: translate(calc(-50% - 4px), -50%) scale(1.05, 0.95); }
  60%  { transform: translate(calc(-50% + 3px), -50%) scale(0.97, 1.03); }
  80%  { transform: translate(calc(-50% - 1px), -50%) scale(1.01, 0.99); }
  100% { transform: translate(-50%, -50%) scale(1, 1); }
}

@keyframes wobble {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.45); }
}

/* Bubble movement is the essential mechanic (2.3.3) and stays; the
   decorative pop-burst scale/blur and the wobble flicker are not
   essential, so they're replaced with a plain opacity fade. */
@media (prefers-reduced-motion: reduce) {
  .bubble.popping .bubble-glass {
    animation: pop-reduced 180ms ease-out forwards;
  }

  .bubble.wobble .bubble-glass {
    animation: wobble 280ms ease-in-out;
  }

  .target-bubble.changed {
    animation: none;
  }

  .new-letter-flash.show {
    animation: new-letter-flash-reduced 1800ms ease-out forwards;
  }

  .streak-burst.show {
    animation: streak-burst-reduced 1800ms ease-out forwards;
  }

  .streak-burst.show .streak-burst-star {
    animation: none;
  }

  @keyframes streak-burst-reduced {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(1); }
    15%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    75%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
  }

  @keyframes new-letter-flash-reduced {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(1); }
    15%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    70%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
  }

  @keyframes pop-reduced {
    0%   { transform: translate(-50%, -50%); opacity: 1; }
    100% { transform: translate(-50%, -50%); opacity: 0; }
  }
}

/* ---------- Footer (shared across all pages) ---------- */

.site-footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 14px 16px 10px;
  text-align: center;
  pointer-events: none;
}

.site-footer * {
  pointer-events: auto;
}

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  letter-spacing: 0.04em;
}

.footer-nav a {
  color: var(--footer-text);
  text-decoration: none;
  transition: color 200ms ease;
  padding: 4px 2px;
}

.footer-nav a:hover,
.footer-nav a:focus-visible {
  color: var(--footer-text-hover);
}

.footer-nav span {
  color: var(--decorative-text);
  font-size: 10px;
}

.watermark {
  margin: 6px 0 0;
  font-size: 10px;
  font-weight: 300;
  letter-spacing: 0.06em;
  color: var(--watermark-text);
}

/* ---------- Content pages (terms, privacy, contact, sitemap, whitepaper) ---------- */

body.page-body {
  padding: 64px 20px 140px;
  display: flex;
  justify-content: center;
}

.page-wrap {
  width: 100%;
  max-width: 640px;
}

.page-back {
  display: inline-block;
  margin-bottom: 28px;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--footer-text);
  text-decoration: none;
}

.page-back:hover,
.page-back:focus-visible {
  color: var(--footer-text-hover);
}

.page-wrap h1 {
  font-size: 22px;
  font-weight: 500;
  color: var(--page-heading);
  margin: 0 0 6px;
}

.page-updated {
  font-size: 11px;
  color: var(--watermark-text);
  margin: 0 0 32px;
}

.page-wrap h2 {
  font-size: 15px;
  font-weight: 600;
  color: var(--page-heading);
  margin: 28px 0 8px;
}

.page-wrap p,
.page-wrap li {
  font-size: 14px;
  line-height: 1.7;
  color: var(--page-text);
}

.page-wrap ul {
  padding-left: 20px;
}

.page-wrap a {
  color: var(--page-link);
}

.page-wrap a:hover,
.page-wrap a:focus-visible {
  color: var(--footer-text-hover);
}

.sitemap-list {
  list-style: none;
  padding: 0;
}

.sitemap-list li {
  margin-bottom: 10px;
}
