@layer components {
  /* ── POND ────────────────────────────────────────────────────────
     The lodge home's frosted page treated as a still pond. Click on
     the white expanse and a small, soft, round patch blooms — a hint
     of scenery seen through frost that thinned for a moment. The
     welcome headline shivers (subtle → less subtle) for ten clicks,
     then its letters scatter with a small stipple of spaces and the
     surface goes inert until the page is refreshed.

     Layered architecture (only when .pond-active is set):
       .stay-landscape--portal.pond-active   (transparent stage)
         .pond-frost                         (#f0f0f0 veil)
         .pond-ripple (×N)                   (added per click; behind
                                              cards via the parent's
                                              z-index of 0)
         .shell-portal                       (the orb; unchanged)
  ────────────────────────────────────────────────────────────────── */

  /* Pond mode swaps the solid #f0f0f0 background of .stay-landscape
     for a transparent stage; the .pond-frost child paints the veil. */
  .stay-landscape--portal.pond-active {
    background: transparent;
  }

  .pond-frost {
    position: absolute;
    inset: 0;
    background: #f0f0f0;
    /* Frost itself doesn't catch clicks — the controller listens at
       the document level so clicks anywhere not on a card / link / etc.
       count as "touching the pond." */
    pointer-events: none;
  }

  /* ── ORB IMAGE in pond mode ──
     The orb keeps its original circular shape and position (clamp size,
     centered horizontally, sitting low). What changes is the IMAGE
     inside it: instead of being sized to fit the orb (object-fit: cover),
     it's sized to the viewport at the same scale ripples use, then
     offset so its (0,0) lines up with the viewport's (0,0). The result:
     the slice of land showing through the orb matches the slice a
     ripple would reveal at that same spot, in continuity. */
  .stay-landscape--portal.pond-active .shell-portal > img {
    --portal-size: clamp(680px, 108vw, 1160px);
    position: absolute;
    width: 100vw;
    height: 100vh;
    object-fit: cover;
    /* Shift up-and-left so the image's top-left aligns with the
       viewport's top-left, undoing the orb's center-bottom offset. */
    left: calc(-50vw + var(--portal-size) / 2);
    top:  calc(-100vh + var(--portal-size) * 0.48);
  }
  @media (min-width: 900px) {
    .stay-landscape--portal.pond-active .shell-portal > img {
      top: calc(-100vh + var(--portal-size) * 0.38);
    }
  }

  /* The lodge home is treated as a touchable surface — clicks are for
     the pond, not for selecting words. Lock everything inside the
     lodge shell to no-select so accidental double / triple clicks
     don't highlight headlines, cards, or drawer text mid-play. (Card
     links still work; only text-selection is suppressed.)

     Gated on a pond-active landscape inside the shell, so a future
     lodge view rendered without pond mode keeps copy-paste available. */
  .stay-shell.lodge-shell:has(.stay-landscape--portal.pond-active),
  .stay-shell.lodge-shell:has(.stay-landscape--portal.pond-active) * {
    -webkit-user-select: none;
            user-select: none;
  }

  /* ── RIPPLE ──
     A small fixed-size box pinned at the click point. The visible
     region is shaped entirely by a soft radial mask whose maximum
     alpha is well below 1 — even at peak the disc is a *translucent
     wash*, not a cutout, so the frost still feels intact and just
     gets temporarily damp. The mask grows from a 0-px dot to fill
     the box, which is what produces the "spreading" feel.

     `--peak` is the per-tier max alpha (the "how much scenery shows
     through" knob). The radial gradient softly tapers from `--peak`
     at the center down to 0 at the rim — mostly feather, only a small
     core of "max" alpha. Even tier 3's peak stays low so the reveal
     reads as gentle translucence.
       --1  whisper  · clicks 1–3 · the frost barely registers a touch
       --2  audible  · clicks 4–6 · a soft warm haze, scenery hinted
       --3  present  · clicks 7+  · the clearest gentle reveal */
  .pond-ripple {
    --ripple-size: 200px;
    --peak: 0.08;
    position: fixed;
    width: var(--ripple-size);
    height: var(--ripple-size);
    pointer-events: none;
    /* Same scenery photo as the orb. Viewport-anchored sizing keeps
       the photo from scaling with the disc — we want the disc to grow,
       not zoom in on the picture. */
    background-size: 100vw 100vh;
    background-position: top left;
    background-attachment: fixed;
    background-repeat: no-repeat;
    /* The mask is what makes the disc round AND feathered — most of
       the gradient is taper, only a tiny core sits at full --peak.
       The corners of the 200×200 box land beyond the gradient's 95%
       stop, so they are fully transparent (= mask hides them) — the
       visible patch is a soft circle with no hard edge. */
    -webkit-mask-image: radial-gradient(circle closest-side at center,
      rgba(0,0,0, var(--peak))                  0%,
      rgba(0,0,0, calc(var(--peak) * 0.78))    25%,
      rgba(0,0,0, calc(var(--peak) * 0.42))    55%,
      rgba(0,0,0, calc(var(--peak) * 0.14))    80%,
      rgba(0,0,0, 0)                           98%);
            mask-image: radial-gradient(circle closest-side at center,
      rgba(0,0,0, var(--peak))                  0%,
      rgba(0,0,0, calc(var(--peak) * 0.78))    25%,
      rgba(0,0,0, calc(var(--peak) * 0.42))    55%,
      rgba(0,0,0, calc(var(--peak) * 0.14))    80%,
      rgba(0,0,0, 0)                           98%);
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: 0 0;
            mask-size: 0 0;
    animation: pond-ripple-bloom 1.9s cubic-bezier(.22,.7,.3,1) forwards;
    will-change: mask-size;
  }

  /* Tier 1 — whisper. Quiet but actually perceptible. This is most
     of the 37-click budget, so it should feel like a soft, gentle
     touch — not nothing. */
  .pond-ripple--1 { --peak: 0.10; }
  /* Tier 2 — audible. A soft warm haze; scenery clearly hinted. */
  .pond-ripple--2 { --peak: 0.18; }
  /* Tier 3 — present. The clearest reveal in this budget — and still
     a wash, not a window. Most of the frost stays in place; you sense
     the land, you don't see it sharply. */
  .pond-ripple--3 { --peak: 0.30; }

  @keyframes pond-ripple-bloom {
    0%   { -webkit-mask-size: 0 0;
                   mask-size: 0 0; }
    100% { -webkit-mask-size: var(--ripple-size) var(--ripple-size);
                   mask-size: var(--ripple-size) var(--ripple-size); }
  }

  /* ── HEADLINE SHIVERS ──
     Three escalating intensities. None of them ever go "crazy" — the
     biggest movement is ~2px. Each is a single-shot keyframe; the JS
     restarts it on every click by removing the class, forcing reflow,
     and re-adding it.

     The shake combines a tiny translate with a hair of rotation so the
     letters feel like they're being nudged by something tactile, not
     mechanically dithered. */
  /* Tier 1 — sub-perceptual. A fraction of a pixel; you only catch
     it out of the corner of your eye, if at all. Tuned for the long
     opening stretch (clicks 1–22) where the headline should mostly
     feel undisturbed. */
  @keyframes pond-shiver-1 {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(-0.15px, 0.1px); }
  }
  /* Tier 2 — a hair more present. Still under 1px. */
  @keyframes pond-shiver-2 {
    0%, 100% { transform: translate(0, 0) rotate(0); }
    30%      { transform: translate(-0.5px, 0.3px) rotate(-0.05deg); }
    65%      { transform: translate(0.4px, -0.25px) rotate(0.05deg); }
  }
  /* Tier 3 — the most this ever moves. ~1px max. Still tasteful. */
  @keyframes pond-shiver-3 {
    0%, 100% { transform: translate(0, 0) rotate(0); }
    20%      { transform: translate(-0.9px, 0.5px) rotate(-0.1deg); }
    50%      { transform: translate(0.8px, -0.4px) rotate(0.1deg); }
    80%      { transform: translate(-0.4px, 0.3px) rotate(-0.04deg); }
  }

  .lodge-greeting h1.pond-shiver-1 {
    animation: pond-shiver-1 200ms ease-in-out;
  }
  .lodge-greeting h1.pond-shiver-2 {
    animation: pond-shiver-2 240ms ease-in-out;
  }
  .lodge-greeting h1.pond-shiver-3 {
    animation: pond-shiver-3 280ms ease-in-out;
  }

  /* Pre-scramble shake — fires once on click 36, the click immediately
     before the page scrambles. Bigger and longer than tier 3, so the
     headline visibly braces itself before the whole page comes apart.
     Still bounded (~5px max, 600ms) — dramatic, not ridiculous. */
  @keyframes pond-shiver-pre {
    0%, 100% { transform: translate(0, 0) rotate(0); }
    8%       { transform: translate(-3px, 1.5px) rotate(-0.4deg); }
    18%      { transform: translate(3.5px, -1.8px) rotate(0.5deg); }
    30%      { transform: translate(-4px, 2px)   rotate(-0.6deg); }
    44%      { transform: translate(4.5px, -2px) rotate(0.6deg); }
    58%      { transform: translate(-3.5px, 1.6px) rotate(-0.4deg); }
    72%      { transform: translate(2.8px, -1.2px) rotate(0.3deg); }
    86%      { transform: translate(-1.6px, 0.8px) rotate(-0.15deg); }
  }
  .lodge-greeting h1.pond-shiver-pre {
    animation: pond-shiver-pre 600ms cubic-bezier(.36,.07,.19,.97);
  }

  /* When the headline has fragmented (10th click), give the new
     non-breaking spaces a tiny visual softening so the letters feel
     like they drifted apart, not like they were just word-spaced. */
  .lodge-greeting h1.pond-fragmented {
    transition: letter-spacing 800ms ease;
  }

  /* Honor reduced-motion: keep the ripple visual but skip the shiver. */
  @media (prefers-reduced-motion: reduce) {
    .lodge-greeting h1.pond-shiver-1,
    .lodge-greeting h1.pond-shiver-2,
    .lodge-greeting h1.pond-shiver-3,
    .lodge-greeting h1.pond-shiver-pre {
      animation: none;
    }
  }
}
