/* ------------------------------------------------------------------
   Procedural forest banner — 8 parallax layers rendered in WebGL.
   Companion styles for static/js/forest.js. The banner replaces the
   home-page hero block; see templates/index.html.
   ------------------------------------------------------------------ */

.fr-banner {
  position: relative;
  margin: 0 0 56px;
  border-bottom: 1px solid var(--rule);
  background: #7fb4d4;
  overflow: hidden;
  height: clamp(620px, 80vh, 840px);
  isolation: isolate;
}

/* Thin variant used as a header strip on archive/section pages.
   No bio card, just a sliver of forest. */
.fr-banner--thin {
  height: 200px;
  margin: 0 0 40px;
}
.fr-banner--thin .fr-outer { display: none; }

.fr-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  display: block;
  cursor: crosshair;
}

.fr-outer {
  position: relative;
  z-index: 2;
  padding: 80px max(20px, 4vw);
  max-width: 72rem;
  margin: 0 auto;
  pointer-events: none;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.fr-card {
  max-width: 44rem;          /* matches the .wrap below */
  width: 100%;
  padding: 40px 44px 36px;
  background: var(--paper);
  border: 0;
  pointer-events: auto;
  box-shadow:
    0 0 0 1px rgba(35, 50, 50, 0.05),
    0 22px 44px -18px rgba(15, 28, 26, 0.40),
    0 6px 16px -6px rgba(15, 28, 26, 0.22);
}

.fr-card h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(28px, 4vw, 36px);
  line-height: 1.18;
  letter-spacing: -0.018em;
  margin: 0 0 22px;
  text-wrap: balance;
}

.fr-card p {
  font-size: 17.5px;
  line-height: 1.65;
  margin: 0 0 16px;
  text-wrap: pretty;
}

.fr-card p:last-child { margin-bottom: 0; }

.fr-caption {
  position: absolute;
  bottom: 14px;
  left: max(20px, 4vw);
  z-index: 3;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: #1d2422;
  pointer-events: none;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  background: rgba(253, 253, 251, 0.55);
  padding: 4px 10px;
  backdrop-filter: blur(2px);
}

.fr-caption .swatch {
  display: inline-block;
  width: 8px;
  height: 8px;
  background: var(--accent);
}

.fr-hint {
  position: absolute;
  bottom: 14px;
  right: max(20px, 4vw);
  z-index: 3;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: #1d2422;
  pointer-events: none;
  background: rgba(253, 253, 251, 0.55);
  padding: 4px 10px;
  backdrop-filter: blur(2px);
}

.fr-hint kbd {
  font-family: var(--mono);
  font-size: 10px;
  border: 1px solid var(--rule);
  background: var(--paper);
  padding: 1px 6px;
  border-radius: 2px;
  color: var(--ink-2);
}

/* Mobile / no-hover devices: skip WebGL, show a baked-in still image.
   The script never instantiates Three.js below this breakpoint. */
.fr-banner[data-fallback] .fr-canvas,
.fr-banner[data-fallback] .fr-hint {
  display: none;
}
.fr-banner[data-fallback] {
  background-image: url(/images/forest/forest-fallback.jpg);
  background-size: cover;
  background-position: center;
}

/* Touch devices see this CTA over the static forest fallback; tapping
   it lazy-loads the runner and clears `data-fallback` so the canvas
   takes over (see templates/index.html). */
.fr-tap-to-play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 14px 22px;
  background: rgba(253, 253, 251, 0.94);
  color: var(--ink);
  border: 0;
  border-radius: 2px;
  cursor: pointer;
  pointer-events: auto;
  box-shadow:
    0 0 0 1px rgba(35, 50, 50, 0.08),
    0 12px 28px -10px rgba(15, 28, 26, 0.45);
}
.fr-tap-to-play:disabled { opacity: 0.7; cursor: default; }

@media (max-width: 720px) {
  /* Ambient (no game): banner sizes to the bio card so the masthead and
     posts list don't clip the intro on phones. */
  .fr-banner:not(.fr-banner--thin) {
    height: auto;
    min-height: 320px;
  }
  .fr-outer {
    padding: 28px max(20px, 4vw);
    flex-direction: column;
    gap: 22px;
  }
  .fr-card { padding: 26px 22px 22px; }

  /* Sit below the intro card rather than over it. */
  .fr-tap-to-play {
    position: static;
    transform: none;
  }

  /* Game mode on touch: expand the banner to give real play space and
     stop the browser hijacking vertical drags for page scroll. dvh
     dodges iOS toolbar resize jank. */
  .fr-banner[data-game-mode="on"] {
    height: clamp(440px, 75dvh, 720px);
    overscroll-behavior: contain;
  }
  /* Apply touch-action whenever the canvas is live (i.e. the runner
     has booted and we're past the fallback overlay), so even the first
     tap during the ambient scene doesn't get stolen for page scroll. */
  .fr-banner:not([data-fallback]) .fr-canvas,
  .fr-banner:not([data-fallback]) .fr-game-ui {
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
  }

  /* Tighter HUD + death card so they fit a narrow viewport. */
  .fr-hud-score { font-size: 32px; }
  .fr-hud-best  { font-size: 10px; }
  .fr-deathcard {
    padding: 22px 24px 20px;
    max-width: 92vw;
  }
  .fr-deathcard__score { font-size: 24px; }
  .fr-deathcard__entry input {
    width: 1.6em;
    font-size: 22px;
    padding: 8px 0;
  }
  .fr-leaderboard { font-size: 12px; }
}

/* ------------------------------------------------------------------
   Forest Runner — game UI layered over the banner.
   The bio card slides up out of view when the runner takes over;
   the canvas's `overflow: hidden` on the banner clips it cleanly,
   reading as the card "sliding behind" the masthead above.
   ------------------------------------------------------------------ */

.fr-card {
  transition: transform 600ms cubic-bezier(.22, .61, .36, 1),
              opacity   400ms ease-out;
  will-change: transform;
}
.fr-banner[data-game-mode="on"] .fr-card {
  transform: translateY(-130%);
  opacity: 0;
  pointer-events: none;
}
.fr-banner[data-game-mode="on"] .fr-canvas {
  cursor: pointer;
}

.fr-game-ui {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.fr-hud {
  position: absolute;
  top: 24px;
  right: max(20px, 4vw);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  font-family: var(--mono);
  color: #ffffff;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 0 6px rgba(0, 0, 0, 0.25);
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 280ms ease-out, transform 280ms ease-out;
}
.fr-game-ui[data-state="starting"] .fr-hud,
.fr-game-ui[data-state="running"]  .fr-hud,
.fr-game-ui[data-state="gameover"] .fr-hud {
  opacity: 1;
  transform: none;
}

.fr-hud-score {
  font-family: var(--mono);
  font-size: 44px;
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: inherit;
}
.fr-hud-best {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: inherit;
  opacity: 0.85;
}

.fr-deathcard {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  background: var(--paper);
  padding: 28px 40px 26px;
  text-align: center;
  font-family: var(--serif);
  box-shadow:
    0 0 0 1px rgba(35, 50, 50, 0.05),
    0 22px 44px -18px rgba(15, 28, 26, 0.40),
    0 6px 16px -6px rgba(15, 28, 26, 0.22);
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease-out, transform 240ms cubic-bezier(.22,.61,.36,1);
}
.fr-game-ui[data-state="gameover"] .fr-deathcard {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.fr-deathcard__title {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.22em;
  color: var(--accent);
  margin-bottom: 8px;
}
.fr-deathcard__score {
  font-family: var(--mono);
  font-size: 28px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-bottom: 2px;
}
.fr-deathcard__best {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.10em;
  color: var(--ink-2);
  margin-bottom: 14px;
}
.fr-deathcard__hint {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.10em;
  color: var(--ink-3);
}

.fr-game-ui[data-state="gameover"] .fr-deathcard {
  pointer-events: auto;
}

.fr-deathcard__entry {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 4px 0 14px;
}
.fr-deathcard__entry[hidden] { display: none; }
.fr-deathcard__entry-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.fr-deathcard__entry-inputs {
  display: inline-flex;
  gap: 8px;
}
.fr-deathcard__entry input {
  width: 1.4em;
  padding: 4px 0;
  font-family: var(--mono);
  font-size: 24px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-align: center;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--ink-3);
  outline: none;
  transition: border-color 120ms ease-out;
}
.fr-deathcard__entry input:focus {
  border-bottom-color: var(--accent);
}
.fr-deathcard__entry-actions {
  display: inline-flex;
  gap: 14px;
  align-items: center;
}
.fr-deathcard__entry-submit,
.fr-deathcard__entry-skip {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  background: transparent;
  border: 0;
  padding: 4px 8px;
  cursor: pointer;
}
.fr-deathcard__entry-submit { color: var(--accent); }
.fr-deathcard__entry-skip   { color: var(--ink-3); }
.fr-deathcard__entry-skip:hover,
.fr-deathcard__entry-submit:hover { opacity: 0.75; }
.fr-deathcard__entry-submit:disabled,
.fr-deathcard__entry-skip:disabled {
  opacity: 0.4;
  cursor: default;
}

.fr-leaderboard__title {
  margin: 14px 0 6px;
  padding-top: 12px;
  border-top: 1px solid rgba(35, 50, 50, 0.10);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: center;
}
.fr-leaderboard__title[hidden] { display: none; }

.fr-leaderboard {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 180px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-2);
}
.fr-leaderboard:empty {
  display: none;
}
.fr-leaderboard__row {
  display: grid;
  grid-template-columns: 1.6em 1fr auto;
  align-items: baseline;
  gap: 8px;
  padding: 2px 4px;
  font-variant-numeric: tabular-nums;
}
.fr-leaderboard__rank {
  color: var(--ink-3);
  text-align: right;
}
.fr-leaderboard__initials {
  text-align: left;
  letter-spacing: 0.12em;
}
.fr-leaderboard__score {
  text-align: right;
  color: var(--ink);
}
.fr-leaderboard__row--me {
  background: rgba(35, 50, 50, 0.06);
  color: var(--accent);
  font-weight: 600;
}
.fr-leaderboard__row--me .fr-leaderboard__rank,
.fr-leaderboard__row--me .fr-leaderboard__score {
  color: var(--accent);
}

.fr-tip {
  position: absolute;
  bottom: 18px;
  right: max(20px, 4vw);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: #ffffff;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 0 6px rgba(0, 0, 0, 0.25);
  background: rgba(15, 25, 25, 0.32);
  backdrop-filter: blur(2px);
  padding: 6px 10px;
  border-radius: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms ease-out;
}
.fr-tip__row { display: inline-flex; gap: 6px; align-items: baseline; }
.fr-tip__arrow { font-weight: 600; letter-spacing: 0; }

.fr-game-ui[data-state="starting"] .fr-tip,
.fr-game-ui[data-state="running"]  .fr-tip,
.fr-game-ui[data-state="gameover"] .fr-tip {
  opacity: 1;
}

/* Ambient-state cue mirroring the in-game .fr-tip layout, telling the
   visitor the canvas is interactive while the bio card is still up. */
.fr-startcue {
  position: absolute;
  bottom: 18px;
  right: max(20px, 4vw);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: #ffffff;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 0 6px rgba(0, 0, 0, 0.25);
  background: rgba(15, 25, 25, 0.32);
  backdrop-filter: blur(2px);
  padding: 6px 10px;
  border-radius: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms ease-out;
}
.fr-startcue__row { display: inline-flex; gap: 6px; align-items: baseline; }
.fr-game-ui[data-state="ambient"] .fr-startcue {
  opacity: 1;
  animation: fr-startcue-pulse 2.4s ease-in-out infinite;
}
@keyframes fr-startcue-pulse {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1; }
}

.fr-restore-arrow {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: 36px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: rgba(253, 253, 251, 0.65);
  backdrop-filter: blur(3px);
  color: var(--ink-2);
  cursor: pointer;
  padding: 0;
  border-radius: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 240ms ease-out, color 160ms;
}
.fr-game-ui[data-state="starting"] .fr-restore-arrow,
.fr-game-ui[data-state="running"]  .fr-restore-arrow,
.fr-game-ui[data-state="gameover"] .fr-restore-arrow {
  opacity: 1;
  pointer-events: auto;
}
.fr-restore-arrow:hover { color: var(--accent); }
.fr-restore-arrow svg { width: 22px; height: 22px; display: block; }

/* The death card is the one element inside .fr-game-ui that needs
   to receive its own clicks (otherwise the canvas would consume them
   and treat the click as restart anyway — which is in fact what we
   want, so we leave pointer-events untouched). */
