/* ============================================================================
   NEKOLAND — style.css
   ----------------------------------------------------------------------------
   Everything about how Nekoland LOOKS lives here: colours, fonts, layout and
   the CSS animations (door opening, branches drawing, leaves fluttering).

   ✏️ Search this file for "TWEAK" (Ctrl+F in Zed) to jump to every spot that
   is safe to change by hand.

   Easiest things to change:
     • Colours  -> the variables in :root just below (e.g. --gold)
     • Fonts    -> --font-display / --font-body
     • Speeds   -> search for "animation" or "transition"

   Sections in this file:
     1.  Colour & font variables
     2.  Page basics + background
     3.  Scenes (door & tree) and how they fade between each other
     4.  The door
     5.  The tree (branches, leaves, labels, cats)
     6.  Grow-in animation (branches drawing themselves)
     7.  Buttons, dialog, captions
     8.  Small screens (phones)
     9.  Reduced motion (accessibility)
   ============================================================================ */


/* ---------------------------------------------------------------------------
   1. COLOUR & FONT VARIABLES — change these to re-theme the whole site
   ✏️ TWEAK: every colour below is used all over the site. Change one here
   and it changes everywhere. (Colours are written as #RRGGBB or rgba().)
   --------------------------------------------------------------------------- */
:root {
  --bg:          #0b1410;   /* deep forest, almost black            */
  --bg-raised:   #111d17;   /* slightly lighter, used for fills      */
  --bark-fill:   #13221b;   /* inside of trunk & branches            */
  --ivory:       #e9e2cf;   /* main line & text colour               */
  --ivory-soft:  rgba(233, 226, 207, 0.72);
  --ivory-faint: rgba(233, 226, 207, 0.32);
  --sage:        #93a98c;   /* leaves                                */
  --sage-deep:   #5f7a63;
  --gold:        #c9a45c;   /* warm accent (hover, light, knob)      */
  --leaf-line:   #93c983;   /* ✏️ TWEAK: leaf outline green           */
  --leaf-fill:   rgba(108, 176, 96, .40);   /* ✏️ TWEAK: leaf inside green (last number = strength) */
  --leaf-vein:   rgba(150, 205, 130, .70);
  --gold-soft:   #e2c784;

  /* Fonts already on Windows / Mac — nothing is downloaded. The first two
     (Cormorant Garamond, EB Garamond) are used only if the visitor happens to
     have them installed; otherwise Palatino / Georgia are used.
     Windows will normally use "Palatino Linotype"; Mac uses "Palatino". */
  --font-display: "Cormorant Garamond", "EB Garamond", "Palatino Linotype",
                  "Book Antiqua", Palatino, Georgia, serif;
  --font-body:    "Palatino Linotype", "Book Antiqua", Palatino, Georgia, serif;

  --ease-soft:  cubic-bezier(.33, .1, .25, 1);
  --ease-grow:  cubic-bezier(.45, .05, .35, 1);
}


/* ---------------------------------------------------------------------------
   2. PAGE BASICS + BACKGROUND
   --------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;          /* the experience is one screen; no scrollbars */
}

body {
  background: var(--bg);
  color: var(--ivory);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
}

/* soft vignette: lighter green in the middle, darker at the edges */
.backdrop {
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 60% at 50% 45%, #15251d 0%, rgba(11, 20, 16, 0) 70%),
    radial-gradient(ellipse 120% 90% at 50% 50%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, .55) 100%),
    var(--bg);
}

/* subtle paper grain, drawn by a tiny inline SVG noise filter (no image file) */
.backdrop__grain {
  position: absolute;
  inset: 0;
  opacity: .5;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.92  0 0 0 0 0.89  0 0 0 0 0.8  0 0 0 .07 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

/* drifting fireflies */
/* ✏️ TWEAK: FIREFLIES
     • how MANY and WHERE they start: the <span> lines in index.html
       (inside <div class="backdrop__motes">) — copy a line to add one.
     • their SPEED: the --d value on each of those lines (seconds; smaller = faster)
     • their SIZE / GLOW: width, height and box-shadow just below
     • how far they DRIFT: the translate(...) numbers in @keyframes mote */
.backdrop__motes span {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--gold-soft);
  box-shadow: 0 0 8px 2px rgba(226, 199, 132, .35);
  opacity: 0;
  animation: mote var(--d) ease-in-out var(--delay) infinite;
}
@keyframes mote {
  0%   { transform: translate(0, 0);        opacity: 0; }
  20%  { opacity: .7; }
  50%  { transform: translate(18px, -40px); opacity: .25; }
  80%  { opacity: .6; }
  100% { transform: translate(-10px, -80px); opacity: 0; }
}

/* .eyebrow = the small italic line (door screen & pop-up); .wordmark = the big title on the door */
.eyebrow {
  margin: 0;
  font-family: var(--font-body);
  font-style: italic;
  font-size: .95rem;
  letter-spacing: .08em;
  color: var(--ivory-soft);
}
.wordmark {
  margin: .1em 0 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 5vw, 3.1rem);   /* ✏️ TWEAK: "Nekoland" title size (min, preferred, max) */
  letter-spacing: .32em;
  text-indent: .32em;        /* keeps letter-spaced text visually centred */
  color: var(--ivory);
}


/* ---------------------------------------------------------------------------
   3. SCENES — both scenes sit on top of each other; only one is visible.
   script.js adds / removes the class "is-visible".
   --------------------------------------------------------------------------- */
.scene {
  position: fixed;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity .9s var(--ease-soft), visibility 0s linear .9s;   /* ✏️ TWEAK: .9s = scene cross-fade speed (change both .9s here and in the rule below) */
}
.scene.is-visible {
  opacity: 1;
  visibility: visible;
  transition: opacity .9s var(--ease-soft), visibility 0s;
}
/* PERFORMANCE: while a scene is hidden, freeze all its animations (Chrome
   would otherwise keep running them in the background) */
.scene:not(.is-visible) *,
.scene:not(.is-visible) *::before,
.scene:not(.is-visible) *::after { animation-play-state: paused !important; }


/* ---------------------------------------------------------------------------
   4. THE DOOR
   --------------------------------------------------------------------------- */
.scene--door {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(.4rem, 1.6vh, 1.1rem);       /* ✏️ TWEAK: space between title, mural, scroll and hint */
  padding: 1rem;
  text-align: center;
}

/* the door is a <button>; we remove the default grey button look */
.door {
  position: relative;
  width: var(--door-w, 160px);            /* size: set by --door-w on .door-portal below */
  aspect-ratio: 2 / 3;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  border-radius: 50% 50% 6px 6px / 34% 34% 6px 6px;
  animation: door-float 7s ease-in-out infinite;     /* ✏️ TWEAK: 7s = speed of the gentle idle float */
  -webkit-tap-highlight-color: transparent;
}
@keyframes door-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }   /* ✏️ TWEAK: how high the door floats */
}

/* --- THE MURAL around the door, and the elvish scroll -------------------- */
.door-portal {
  --door-w: clamp(112px, 17vmin, 190px);  /* ✏️ TWEAK: DOOR SIZE (smallest, normal, largest) — the mural grows with it */
  position: relative;
  width: calc(var(--door-w) * 2.7);       /* the mural is 2.7 doors wide… */
  aspect-ratio: 540 / 600;                /* …and matches its drawing's shape */
  flex: none;
}
.door-portal .door {
  position: absolute;
  left: calc(50% - var(--door-w) / 2);    /* centred at the bottom of the mural */
  bottom: 1.6%;
}
.mural {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  /* ✏️ TWEAK: the faint white glow (bigger px = softer, wider glow) */
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, .35)) drop-shadow(0 0 10px rgba(220, 230, 255, .18));
  opacity: .62;                           /* ✏️ TWEAK: how bright the mural is while the door is locked */
  transition: opacity 1.6s ease;
  animation: mural-breathe 7s ease-in-out infinite;
}
@keyframes mural-breathe { 0%, 100% { opacity: .55; } 50% { opacity: .72; } }
/* the word was right: the mural brightens GRADUALLY, starting from wherever its
   breathing glow happens to be (no sudden jump). --awaken is DOOR_ELVISH.awakenSeconds. */
.scene--door.is-unlocked .mural {
  animation: mural-breathe 7s ease-in-out infinite,
             mural-wake calc(var(--awaken, 3.2s) * .9) ease calc(var(--awaken, 3.2s) * .25) forwards;
}
@keyframes mural-wake { to { opacity: .95; } }

/* FLOWING LIGHT: a bright spark runs up the left pillar, round the arch and down
   the right pillar, drawing a thin gold line behind it */
.mural__flow {
  fill: none;
  stroke: #f6dc96;
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-dasharray: .06 1.2;
  stroke-dashoffset: .06;
  opacity: 0;
}
.mural__flow--trail {
  stroke: rgba(240, 206, 128, .7);
  stroke-width: 1.1;
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
}
.scene--door.is-unlocked .mural__flow {
  animation: flow-run calc(var(--awaken, 3.2s) * .75) linear calc(var(--awaken, 3.2s) * .18) forwards;
}
.scene--door.is-unlocked .mural__flow--trail {
  animation: flow-trail calc(var(--awaken, 3.2s) * .75) linear calc(var(--awaken, 3.2s) * .18) forwards;
}
@keyframes flow-run {
  0%   { opacity: 0; stroke-dashoffset: .06; }
  6%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { opacity: 0; stroke-dashoffset: -1.02; }
}
@keyframes flow-trail {
  0%   { opacity: .9; stroke-dashoffset: 1; }
  100% { opacity: .45; stroke-dashoffset: 0; }
}

/* your elvish (script.js: DOOR_ELVISH): ivory while locked; each piece turns gold
   in turn as the light reaches it (its own --elv-delay) */
.elv-ivory { opacity: .82; transition: opacity 1.2s ease var(--elv-delay, 0s); }
.elv-gold  { opacity: 0;   transition: opacity 1.2s ease var(--elv-delay, 0s); }
.scene--door.is-unlocked .elv-ivory { opacity: 0; }
.scene--door.is-unlocked .elv-gold  { opacity: 1; }
.mural__line  { fill: none; stroke: rgba(244, 243, 255, .85); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.mural__line--fine { stroke-width: .8; stroke: rgba(244, 243, 255, .55); }
.mural__line--dots { stroke-width: 1.2; stroke-dasharray: 1 7; stroke: rgba(244, 243, 255, .6); }
.mural__vine  { fill: none; stroke: rgba(244, 243, 255, .7); stroke-width: 1.3; stroke-linecap: round; }
.mural__vine--fine { stroke-width: .8; }
.mural__leaves path { fill: rgba(244, 243, 255, .08); stroke: rgba(244, 243, 255, .7); stroke-width: .9; }
.mural__flowers path { fill: rgba(244, 243, 255, .12); stroke: rgba(244, 243, 255, .7); stroke-width: .8; }
.mural__stars path { fill: rgba(255, 255, 255, .85); }
.mural__runes path { fill: none; stroke: rgba(244, 243, 255, .85); stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; }

/* the elvish scroll with the box to type in */
.word-scroll {
  position: relative;
  width: min(420px, 88vw);                /* ✏️ TWEAK: scroll width */
  aspect-ratio: 440 / 120;
  margin: 0;
  display: grid;
  place-items: center;
  flex: none;
}
.word-scroll__art { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.word-scroll__paper { fill: rgba(233, 226, 207, .07); stroke: rgba(244, 243, 255, .6); stroke-width: 1.2; }
.word-scroll__roll  { fill: #101b16; stroke: rgba(244, 243, 255, .6); stroke-width: 1.2; }
.word-scroll__line  { fill: none; stroke: rgba(244, 243, 255, .35); stroke-width: .9; }
.word-scroll__runes path { fill: none; stroke: rgba(244, 243, 255, .7); stroke-width: 1; stroke-linecap: round; stroke-linejoin: round; }
.word-scroll__input {
  position: relative;
  top: 16%;
  width: 58%;
  padding: .25em .4em;
  font-family: var(--font-display);
  font-size: clamp(.95rem, 2.4vmin, 1.15rem);
  font-style: italic;
  letter-spacing: .14em;
  text-align: center;
  color: var(--ivory);
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(244, 243, 255, .35);
  outline: none;
  caret-color: var(--gold-soft);
  transition: border-color .4s ease, box-shadow .4s ease;
}
.word-scroll__input::placeholder { color: rgba(233, 226, 207, .45); }
.word-scroll__input:focus-visible { border-bottom-color: var(--gold-soft); box-shadow: 0 6px 12px -8px rgba(226, 199, 132, .8); }
.word-scroll__speak {
  position: absolute;
  right: 14%;
  top: 58%;
  transform: translateY(-50%);
  width: 1.9rem;
  height: 1.9rem;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--gold-soft);
  font-size: 1rem;
  cursor: pointer;
}
.word-scroll__speak:focus-visible { outline: 1px solid var(--gold-soft); }
/* a wrong word: the scroll shivers */
.word-scroll.is-wrong { animation: scroll-shiver .5s ease-in-out; }
@keyframes scroll-shiver {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-6px); }
  40%, 80% { transform: translateX(6px); }
}
/* the right word: the runes light up gold */
.scene--door.is-unlocked .word-scroll__runes path { stroke: var(--gold-soft); }
.scene--door.is-unlocked .word-scroll__input { color: var(--gold-soft); border-bottom-color: rgba(226, 199, 132, .6); transition: color 1.2s ease, border-color 1.2s ease; }
/* clicking the door while it's locked: it rattles against its latch */
.door.is-rattling .door__leaf { animation: door-rattle .55s ease-in-out; }
@keyframes door-rattle {
  0%, 100% { transform: rotateY(0deg); }
  20%, 60% { transform: rotateY(-5deg); }
  40%, 80% { transform: rotateY(-1deg); }
}

/* keyboard focus ring on the door */
.door:focus-visible {
  outline: 1px solid var(--gold-soft);
  outline-offset: 14px;
}
.door:focus:not(:focus-visible) { outline: none; }

/* the stage tilts toward the cursor using --tilt-x / --tilt-y (set by JS) */
.door__stage {
  position: absolute;
  inset: 0;
  display: block;
  perspective: 700px;
  transform: rotateX(var(--tilt-y, 0deg)) rotateY(var(--tilt-x, 0deg));
  transform-origin: 50% 62%;
  transition: transform .8s var(--ease-soft);
}

.door__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.door__void  { fill: #050a07; }
.door__light {
  opacity: .55;
  animation: light-breathe 4.5s ease-in-out infinite;
  transition: opacity .8s ease;
}
/* ✏️ TWEAK: doorway glow pulse — .45 = dimmest, .8 = brightest */
@keyframes light-breathe {
  0%, 100% { opacity: .45; }
  50%      { opacity: .8; }
}
.door__line {
  fill: none;
  stroke: var(--ivory-soft);
  stroke-width: 1.3;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.door__line--fine { stroke-width: .8; stroke: var(--ivory-faint); }
.door__vine {
  fill: none;
  stroke: var(--sage-deep);
  stroke-width: 1.1;
  stroke-linecap: round;
}
.door__vine-leaf {
  fill: rgba(147, 169, 140, .28);
  stroke: var(--sage);
  stroke-width: .7;
  transform-origin: var(--o);
  animation: vine-sway 5s ease-in-out infinite;
}
.door__vine-leaf:nth-child(2n) { animation-duration: 6.5s; animation-delay: -2s; }
.door__vine-leaf:nth-child(3n) { animation-duration: 4.2s; animation-delay: -1s; }
@keyframes vine-sway {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(calc(var(--r) * .25)); }
}

/* the door panel that swings open (hinged on the left) */
.door__leaf {
  position: absolute;
  left: 21%;   top: 18%;      /* matches the doorway inside the 200x300 frame */
  width: 58%;  height: 78%;
  display: block;
  transform-origin: 0% 50%;   /* 0% = the left edge = the hinge */
  transform: rotateY(0deg);
  transform-style: preserve-3d;   /* lets the inner layers turn in real 3D */
  transition: transform 1.25s cubic-bezier(.55, .05, .3, 1);   /* ✏️ TWEAK: 1.25s = how long the door takes to swing open on click */
}
.door__leaf svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* ---- DOOR BREEZE & LEAN ------------------------------------------------
   The numbers that control these (how far, how fast) are set from
   DOOR_MOTION at the top of script.js, which writes them into these CSS
   variables:  --sway-angle  --sway-speed  --hover-sway-boost
               --lean-speed  --hover-speed
   (--door-lean is calculated live by updateDoor() from approachAngle & hoverAngle)
   The fallback values after the commas are used only if JavaScript is off.

   @property "registers" a variable so the browser can animate it smoothly.
   We use it so the breeze can grow / fade out gently instead of jumping. */
@property --sway-amp {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

.door__leaf-lean,
.door__leaf-breeze {
  position: absolute;
  inset: 0;
  display: block;
  transform-origin: 0% 50%;        /* every layer turns on the same hinge */
  transform-style: preserve-3d;
}

/* LEAN: opens a little as the cursor approaches, a bit more on hover.
   script.js only changes --door-lean when the mouse moves; the CSS
   transition does the smooth easing, so nothing runs while you're still. */
.door__leaf-lean {
  transform: rotateY(var(--door-lean, 0deg));
  transition: transform var(--lean-speed, 1.4s) cubic-bezier(.25, .8, .25, 1);
}
.door.is-hovered .door__leaf-lean {
  /* swing out with a soft start and a long, slow settle = the brief pause */
  transition: transform var(--hover-speed, 1.1s) cubic-bezier(.3, .65, .15, 1);
}

/* BREEZE: a slow sway out (negative = toward you) and in (positive = away).
   It lingers near the outward point (28% -> 42%) like a door held by air. */
.door__leaf-breeze {
  --sway-amp: 1;
  animation: door-breeze var(--sway-speed, 6.5s) ease-in-out infinite;
  transition: --sway-amp 1.6s ease;
}
@keyframes door-breeze {
  0%, 100% { transform: rotateY(0deg); }
  28%      { transform: rotateY(calc(var(--sway-angle, 2.4deg) * var(--sway-amp) * -1)); }
  42%      { transform: rotateY(calc(var(--sway-angle, 2.4deg) * var(--sway-amp) * -0.88)); }
  74%      { transform: rotateY(calc(var(--sway-angle, 2.4deg) * var(--sway-amp) * 0.55)); }
}
/* on hover the breeze gets a little livelier… */
.door.is-hovered .door__leaf-breeze { --sway-amp: var(--hover-sway-boost, 1.7); }
/* …and while the door is opening it calms to nothing, so it can't fight
   the opening swing (kept AFTER the hover rule so it always wins) */
.scene--door.is-opening .door__leaf-breeze { --sway-amp: 0; }
.leaf__body    { fill: #16261e; stroke: var(--ivory-soft); stroke-width: 1.2; }
.leaf__planks  { fill: none; stroke: rgba(233, 226, 207, .14); stroke-width: .8; }
.leaf__iron    { fill: none; stroke: rgba(233, 226, 207, .45); stroke-width: 1.4; stroke-linecap: round; }
.leaf__window  { opacity: .75; animation: light-breathe 4.5s ease-in-out infinite; }
.leaf__line    { fill: none; stroke: var(--ivory-soft); stroke-width: 1; }
.leaf__line--fine { stroke: var(--ivory-faint); stroke-width: .8; }
.leaf__knob    { fill: var(--gold); stroke: var(--gold-soft); stroke-width: .6; }
.leaf__keyhole { fill: none; stroke: var(--gold); stroke-width: 1; stroke-linecap: round; }

/* hover / keyboard focus: the light inside grows (the door's lean is done
   by .door__leaf-lean above; script.js adds "is-hovered" for mouse & keyboard) */
.door.is-hovered .door__light,
.door:focus-visible .door__light { animation: none; opacity: .95; }

/* the "click the door" hint */
.door-hint {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: .6em;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: .06em;
  color: var(--ivory-soft);
}
.door-hint__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 0 rgba(201, 164, 92, .6);
  animation: hint-pulse 2.4s ease-out infinite;   /* ✏️ TWEAK: pulse speed of the hint dot */
}
@keyframes hint-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(201, 164, 92, .55); }
  100% { box-shadow: 0 0 0 10px rgba(201, 164, 92, 0); }
}

/* AJAR: once the secret word is right, the door eases open a crack and a
   sliver of light spills out — it waits for your click to open fully.
   ✏️ TWEAK: -14deg = how far ajar (more negative = wider), 2.2s = how slowly */
.scene--door.is-unlocked .door__leaf {
  transform: rotateY(-14deg);
  /* it waits for the flowing light to reach it (--awaken), then eases ajar */
  transition: transform 2.2s cubic-bezier(.3, .6, .2, 1) calc(var(--awaken, 3.2s) * .8);
}
/* the light in the doorway swells gently once the flow arrives (no sudden switch-on) */
.scene--door.is-unlocked .door__light {
  animation: light-breathe 4.5s ease-in-out infinite,
             light-wake calc(var(--awaken, 3.2s) * .7) ease calc(var(--awaken, 3.2s) * .75) forwards;
}
@keyframes light-wake { to { opacity: .95; } }

/* --- OPENING SEQUENCE (classes added by script.js) --- */
.scene--door.is-opening .door        { animation: none; }
.scene--door.is-opening .door__leaf  {
  transform: rotateY(-104deg);       /* ✏️ TWEAK: how wide the door opens on click */
  transition: transform 1.25s cubic-bezier(.55, .05, .3, 1);
}
.scene--door.is-opening .door__light { animation: none; opacity: 1; }
.scene--door.is-opening .door-hint,
.scene--door.is-opening .door-heading {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .6s ease, transform .6s ease;
}
/* after the door is open we "walk through" by zooming into the doorway */
.scene--door.is-entering .door__stage {
  transform: scale(9);                 /* ✏️ TWEAK: how far we zoom into the doorway */
  opacity: 0;                          /* the door dissolves into the light */
  transition: transform 1.5s cubic-bezier(.65, 0, .35, 1),
              opacity 1.1s ease .25s;
}
/* coming back: the door scene settles in from slightly zoomed */
.scene--door.is-returning .door__stage { animation: door-return 1.1s var(--ease-soft) both; }   /* ✏️ TWEAK: return speed (also change 1100 in returnToDoor in script.js) */
@keyframes door-return {
  from { transform: scale(1.25); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}

/* the golden light that fills the screen as you step through */
.light-veil {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  /* ✏️ TWEAK: colour / strength of the golden walk-through flash */
  background: radial-gradient(circle at 50% 55%, rgba(246, 226, 168, .55), rgba(201, 164, 92, .18) 40%, rgba(11, 20, 16, 0) 75%);
  transition: opacity 1.1s ease;
}
.light-veil.is-lit { opacity: 1; transition: opacity .7s ease; }


/* ---------------------------------------------------------------------------
   5. THE TREE
   --------------------------------------------------------------------------- */
.scene--tree { overflow: hidden; }

/* THE WORLD — the sky and the tree, taller than the screen by --sky-extra.
   Normally its bottom part shows; scrolling up slides it down by --pan
   (set by script.js, SKY_PAN) to reveal the sky above. */
.tree-world {
  --sky-extra: 34vh;                   /* ✏️ TWEAK: how much more sky there is above (keep equal to SKY_PAN.extra) */
  position: absolute;
  left: 0;
  right: 0;
  top: calc(-1 * var(--sky-extra));
  height: calc(100% + var(--sky-extra));
  transform: translateY(var(--pan, 0px));
  transition: transform .9s cubic-bezier(.22, .8, .25, 1);
  will-change: transform;
}
.tree-world > .tree {
  inset: auto 0 0 0;
  height: calc(100% - var(--sky-extra));
}
/* the stars drift a little slower than the tree: a touch of depth */
.tree-world > .night-sky {
  transform: translateY(calc(var(--pan, 0px) * -0.3));
  transition: transform .9s cubic-bezier(.22, .8, .25, 1), opacity .8s ease, visibility 0s;
}

.tree {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: manipulation;          /* taps are quick on phones */
  -webkit-tap-highlight-color: transparent;
  /* --u is set by script.js: "how many SVG units = 1 screen pixel".
     It keeps lines readable when the tree is drawn small. */
  --u: 1;
}

/* decorative layer: rings, ground, grass */
.tree .deco-ring      { fill: none; stroke: rgba(201, 164, 92, .10); stroke-width: 1; }
.tree .deco-ring--dot { stroke-dasharray: 1 9; stroke: rgba(201, 164, 92, .22); stroke-linecap: round; }
.tree .ground-line    { fill: none; stroke: var(--ivory-faint); stroke-width: max(1px, calc(var(--u) * .9px)); }
.tree .grass          { fill: none; stroke: var(--sage-deep); stroke-width: max(1px, calc(var(--u) * .8px)); stroke-linecap: round; }
.tree .earth          { fill: url(#earthFade); pointer-events: none; }

/* trunk, branches & roots are drawn as two outline strokes + a dark fill */
.tree .bark-fill { fill: var(--bark-fill); }
.tree .bark-line {
  fill: none;
  stroke: var(--ivory-soft);
  stroke-width: max(1.1px, calc(var(--u) * 1px));
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke .5s ease;
}
.tree .bark-grain {
  fill: none;
  stroke: rgba(233, 226, 207, .22);
  stroke-width: max(.8px, calc(var(--u) * .7px));
  stroke-linecap: round;
}
.tree .twig {
  fill: none;
  stroke: var(--ivory-soft);
  stroke-width: max(1px, calc(var(--u) * 1px));
  stroke-linecap: round;
  transition: stroke .5s ease;
}
.tree .root-line { stroke: rgba(233, 226, 207, .5); }

/* nothing decorative should steal the mouse — only hit areas do */
.tree .deco, .tree .trunk, .tree .roots, .tree .sway, .tree .leaf { pointer-events: none; }

/* --- leaves ---
   Each leaf is nested groups (see buildLeaf in script.js):
     .leaf (position) > .leaf-grow (pop-in) > react group (breeze + cursor)
   The flutter itself is done in script.js (✏️ TWEAK: LEAF_MOTION there) —
   one animation loop for the whole tree is much lighter than hundreds of
   separate CSS animations. */
.tree .leaf-grow {
  transform-origin: 0 0;              /* = the leaf's stem (each leaf is drawn from 0,0) — cheap, no measuring */
}
.tree .leaf-shape {                    /* ✏️ TWEAK: leaf colours — set by --leaf-fill / --leaf-line at the top */
  fill: var(--leaf-fill);
  stroke: var(--leaf-line);
  stroke-width: max(.8px, calc(var(--u) * .8px));
  transition: fill .5s ease, stroke .5s ease;
}
.tree .leaf-shape.is-gold { fill: rgba(201, 164, 92, .2); stroke: var(--gold); }
.tree .leaf-vein {
  fill: none;
  stroke: var(--leaf-vein);
  stroke-width: max(.6px, calc(var(--u) * .6px));
}

/* --- the clickable destination branches --- */
.tree .limb { cursor: pointer; outline: none; }
.tree .limb-hit {
  fill: none;
  stroke: transparent;                 /* invisible, but generous to click */
  stroke-width: 46;                    /* ✏️ TWEAK: how wide the invisible click area around each branch is */
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: stroke;
  transition: stroke .4s ease;
}
/* HOVER / FOCUS: the whole branch warms to gold
   ✏️ TWEAK: change var(--gold-soft) here to give hovered branches another colour */
.tree .limb.is-active .bark-line,
.tree .limb.is-active .twig       { stroke: var(--gold-soft); }
.tree .limb.is-active .leaf-shape { fill: rgba(201, 164, 92, .26); stroke: var(--gold-soft); }
/* KEYBOARD FOCUS: also show a faint glow along the branch's hit area */
.tree .limb.is-focused .limb-hit  { stroke: rgba(201, 164, 92, .10); }   /* ✏️ TWEAK: keyboard-focus glow (.10 faint, .25 clearer) */

/* --- labels (drawn above everything else so they stay readable) --- */
.tree .label { cursor: pointer; }
.tree .label-hit { fill: transparent; pointer-events: all; }
.tree .label-name {
  font-family: var(--font-display);
  font-size: var(--label-size, 26px);
  letter-spacing: .06em;
  fill: var(--ivory);
  opacity: .82;                         /* ✏️ TWEAK: brightness of branch names before hover (0 – 1) */
  /* a dark halo behind the letters keeps them readable over leaves */
  paint-order: stroke;
  stroke: var(--bg);
  stroke-width: calc(var(--label-size, 26px) * .28);
  stroke-linejoin: round;
  transition: fill .45s ease, opacity .45s ease;
}
.tree .label-hint {
  font-family: var(--font-body);
  font-style: italic;
  font-size: calc(var(--label-size, 26px) * .58);
  letter-spacing: .12em;
  fill: var(--gold-soft);
  opacity: 0;
  paint-order: stroke;
  stroke: var(--bg);
  stroke-width: calc(var(--label-size, 26px) * .2);
  transform: translateY(-6px);
  transition: opacity .4s ease, transform .4s ease;
}
.tree .label-rule {
  stroke: var(--gold);
  stroke-width: max(1px, calc(var(--u) * 1px));
  opacity: 0;
  transition: opacity .4s ease;
}
.tree .label-cue {
  fill: none;
  stroke: var(--gold-soft);
  stroke-width: max(1px, calc(var(--u) * 1px));
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
.tree .label.is-active .label-name { fill: var(--gold-soft); opacity: 1; }
.tree .label.is-active .label-hint { opacity: .95; transform: translateY(0); }
.tree .label.is-active .label-cue  { animation: cue-pulse 1.8s ease-out infinite; }
.tree .label.is-focused .label-rule { opacity: .9; }
@keyframes cue-pulse {
  0%   { opacity: .9; transform: scale(.4); }
  100% { opacity: 0;  transform: scale(1.6); }
}

/* --- cats --- */
.tree .cat-image { pointer-events: none; }
.tree .cat-placeholder {
  fill: none;
  stroke: rgba(233, 226, 207, .35);
  stroke-width: max(1px, calc(var(--u) * .9px));
  stroke-dasharray: 3 4;
  stroke-linecap: round;
  stroke-linejoin: round;
}


/* --- clickable cats on the branches --- */
.tree .cat-hit { fill: transparent; pointer-events: all; cursor: pointer; }
.tree .cat-poke { transform-box: fill-box; transform-origin: 50% 100%; }
.tree .cat-poke.is-poked { animation: cat-hop .62s cubic-bezier(.3, .7, .4, 1); }   /* ✏️ TWEAK: .62s = hop speed */
@keyframes cat-hop {           /* ✏️ TWEAK: the hop — -14% = how high it jumps */
  0%   { transform: translateY(0) scale(1, 1); }
  18%  { transform: translateY(0) scale(1.06, .92); }   /* crouch */
  45%  { transform: translateY(-14%) scale(.96, 1.05); } /* hop!  */
  75%  { transform: translateY(0) scale(1.04, .95); }   /* land  */
  100% { transform: translateY(0) scale(1, 1); }
}
.tree .cat-word {
  font-family: var(--font-body);
  font-style: italic;
  fill: var(--gold-soft);              /* ✏️ TWEAK: colour of the cat's little word */
  paint-order: stroke;
  stroke: var(--bg);
  stroke-width: 4px;
  pointer-events: none;
  animation: cat-word 1.6s ease-out forwards;
}
@keyframes cat-word {
  0%   { opacity: 0; transform: translateY(4px); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-18px); }
}

/* --- sleeping cats on the forest floor (settings: SLEEPING_CATS in script.js) --- */
.tree .sleepers { pointer-events: none; }
.tree .sleeper-line {
  fill: none;
  stroke: rgba(233, 226, 207, .55);    /* ✏️ TWEAK: line colour / brightness of sleeping cats */
  stroke-width: max(.9px, calc(var(--u) * .9px));
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tree .sleeper-body {
  transform-box: fill-box;
  transform-origin: 50% 100%;          /* breathe from the ground up */
  animation: sleeper-breathe var(--breath, 4.5s) ease-in-out var(--breath-delay, 0s) infinite;
}
@keyframes sleeper-breathe {           /* ✏️ TWEAK: 1.05 = how much they swell when breathing in */
  0%, 100% { transform: scale(1, 1); }
  45%      { transform: scale(1.012, 1.05); }
}
.tree .sleeper-z {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 9px;                      /* ✏️ TWEAK: size of the floating "z" */
  fill: var(--ivory-soft);
  opacity: 0;
  animation: sleeper-z var(--z-dur, 10s) ease-out var(--z-delay, 0s) infinite;
}
@keyframes sleeper-z {                 /* a "z" rises & fades, then rests until next time */
  0%   { opacity: 0;   transform: translate(0, 0); }
  6%   { opacity: .8; }
  22%  { opacity: 0;   transform: translate(-7px, -16px); }
  100% { opacity: 0;   transform: translate(-7px, -16px); }
}

/* --- sleeping cats WAKING UP (script.js → wakeRandomCat adds these classes) ---
   .is-awake   = head up, eyes open      .is-looking = glancing about
   .is-purring = tiny shiver + "prrr…"  */
.tree .sleeper-head,
.tree .sleeper-look {
  transform-box: fill-box;
  transform-origin: 100% 92%;          /* = the neck: the head turns from here */
}
.tree .sleeper-head { transition: transform 1.1s cubic-bezier(.35, .1, .25, 1); }
.tree .sleeper.is-awake .sleeper-head {
  transform: rotate(22deg) translate(-1px, -4px);   /* ✏️ TWEAK: how high the head lifts */
  transition-duration: .9s;
}
.tree .sleeper-eye-open   { fill: var(--ivory); opacity: 0; transition: opacity .3s ease; }
.tree .sleeper-eye-closed { transition: opacity .3s ease; }
.tree .sleeper.is-awake .sleeper-eye-open   { opacity: .9; transition-delay: .45s; }
.tree .sleeper.is-awake .sleeper-eye-closed { opacity: 0;  transition-delay: .45s; }
.tree .sleeper.is-awake .sleeper-z { animation: none; opacity: 0; }   /* no z's while awake */

.tree .sleeper.is-looking .sleeper-look { animation: cat-look 2.9s ease-in-out both; }
@keyframes cat-look {                  /* ✏️ TWEAK: the glance — a curious head tilt each way */
  0%   { transform: rotate(0deg); }
  18%  { transform: rotate(-9deg); }   /* glances down at its paws */
  38%  { transform: rotate(-9deg); }
  58%  { transform: rotate(7deg) scaleX(.96); }   /* then up, with a cute head tilt */
  80%  { transform: rotate(7deg) scaleX(.96); }
  100% { transform: rotate(0deg); }
}
.tree .sleeper-ear { transform-box: fill-box; transform-origin: 50% 100%; }
.tree .sleeper.is-looking .sleeper-ear--0 { animation: ear-twitch .5s ease-in-out .6s 2; }
.tree .sleeper.is-looking .sleeper-ear--1 { animation: ear-twitch .45s ease-in-out 1.7s 1; }
@keyframes ear-twitch {
  0%, 100% { transform: rotate(0deg); }
  40%      { transform: rotate(-14deg); }
}

.tree .sleeper-purr { transform-box: fill-box; transform-origin: 50% 100%; }
.tree .sleeper.is-purring .sleeper-purr { animation: purr-shiver .09s linear 0s 28 alternate; }
@keyframes purr-shiver {               /* ✏️ TWEAK: .35px = how much it vibrates while purring */
  from { transform: translateX(-.35px); }
  to   { transform: translateX(.35px); }
}
.tree .sleeper-purr-word {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 10px;                     /* ✏️ TWEAK: size of "prrr…" */
  fill: var(--gold-soft);
  opacity: 0;
  animation: purr-word 2.6s ease-out forwards;
}
@keyframes purr-word {
  0%   { opacity: 0;  transform: translate(0, 2px); }
  20%  { opacity: .9; }
  100% { opacity: 0;  transform: translate(-6px, -14px); }
}

/* --- the scroll, ink pot & quill beside the tree (see buildScroll in script.js) --- */
.tree .scroll-prop { cursor: pointer; outline: none; }
.tree .prop-hit {
  fill: transparent;
  stroke: transparent;
  stroke-width: max(1px, calc(var(--u) * 1px));
  stroke-dasharray: 4 6;
  pointer-events: all;
  transition: stroke .4s ease;
}
.tree .scroll-prop.is-focused .prop-hit { stroke: rgba(201, 164, 92, .45); }  /* keyboard focus */
.tree .prop-line {
  fill: none;
  stroke: var(--ivory-soft);
  stroke-width: max(.9px, calc(var(--u) * .9px));
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke .5s ease;
}
.tree .prop-fine     { stroke: rgba(233, 226, 207, .34); stroke-width: max(.6px, calc(var(--u) * .6px)); }
.tree .prop-paper       { fill: #2b291e; }   /* ✏️ TWEAK: parchment colours (scroll size is in script.js → SCROLL_PROP) */        /* aged parchment, kept dark & quiet */
.tree .prop-paper-light { fill: #34311f; }
.tree .prop-script { fill: none; stroke: rgba(233, 226, 207, .26); stroke-width: max(.5px, calc(var(--u) * .5px)); stroke-linecap: round; }
.tree .prop-ink    { fill: #040706; }
.tree .prop-glass  { fill: #0c1712; }
.tree .prop-glint  { fill: none; stroke: rgba(233, 226, 207, .45); stroke-width: max(.8px, calc(var(--u) * .8px)); stroke-linecap: round; }
.tree .prop-ribbon { fill: #4a3a22; stroke: rgba(201, 164, 92, .7); stroke-width: max(.6px, calc(var(--u) * .6px)); stroke-linejoin: round; }
.tree .prop-seal   { fill: #8a6a32; stroke: var(--gold-soft); stroke-width: max(.6px, calc(var(--u) * .6px)); }
.tree .prop-brass  { fill: none; stroke: var(--gold); stroke-width: max(1px, calc(var(--u) * 1.1px)); stroke-linecap: round; }
.tree .prop-shadow { fill: rgba(0, 0, 0, .38); }
.tree .prop-vane   { fill: rgba(233, 226, 207, .07); }
.tree .prop-barb {
  fill: none;
  stroke: rgba(233, 226, 207, .5);
  stroke-width: max(.45px, calc(var(--u) * .45px));
  stroke-linecap: round;
  transition: stroke .5s ease;
}
.tree .prop-barb--down { stroke: rgba(233, 226, 207, .3); }
.tree .prop-shaft  { stroke: var(--ivory); stroke-width: max(1.1px, calc(var(--u) * 1.2px)); }
.tree .prop-name {
  font-family: var(--font-body);
  font-style: italic;
  font-size: calc(var(--label-size, 26px) * .68);
  letter-spacing: .1em;
  fill: var(--ivory-soft);
  transition: fill .4s ease;
}
.tree .prop-hint {
  font-family: var(--font-body);
  font-style: italic;
  font-size: calc(var(--label-size, 26px) * .52);
  letter-spacing: .12em;
  fill: var(--gold-soft);
  opacity: 0;
  transition: opacity .4s ease;
}
/* hover / focus: the ink lines warm to gold and the hint appears */
.tree .scroll-prop.is-active .prop-line,
.tree .scroll-prop.is-active .prop-barb { stroke: var(--gold-soft); }
.tree .scroll-prop.is-active .prop-name { fill: var(--gold-soft); }
.tree .scroll-prop.is-active .prop-hint { opacity: .95; }
/* --- the buried hand (see buildBuriedHand / BURIED_HAND in script.js) --- */
.tree .hand-prop { cursor: pointer; outline: none; }
.tree .hand-prop.is-focused .prop-hit { stroke: rgba(201, 164, 92, .45); }      /* keyboard focus */
.tree .hand-bone {
  fill: rgba(233, 226, 207, .06);
  stroke: rgba(233, 226, 207, .55);              /* ✏️ TWEAK: how visible the bones are (quiet on purpose) */
  stroke-width: max(.6px, calc(var(--u) * .65px));
  stroke-linejoin: round;
  transition: stroke .6s ease, filter .6s ease;
}
.tree .hand-joint { fill: none; stroke: rgba(233, 226, 207, .45); stroke-width: max(.5px, calc(var(--u) * .55px)); transition: stroke .6s ease; }
.tree .hand-soil  { fill: #0b1410; }              /* the earth in front (matches the page's --bg) */
.tree .hand-clod  { fill: rgba(233, 226, 207, .14); }
.tree .hand-prop .hand-bones { transition: filter .6s ease; }
/* hover / focus: the bones catch the light, faintly */
.tree .hand-prop.is-active .hand-bone,
.tree .hand-prop.is-active .hand-joint { stroke: var(--ivory); }
.tree .hand-prop.is-active .hand-bones { filter: drop-shadow(0 0 3px rgba(233, 226, 207, .45)); }
.tree .hand-prop.is-active .prop-hint { opacity: .9; }
/* awake (every required world visited — shared/puzzle.js): a restrained red glow that breathes */
.tree .hand-prop.is-awake .hand-bone, .tree .hand-prop.is-awake .hand-joint { stroke: #e9a3a0; }
.tree .hand-prop.is-awake .hand-bones { animation: hand-ember 3.6s ease-in-out infinite; }
@keyframes hand-ember {
  0%, 100% { filter: drop-shadow(0 0 2px rgba(200, 50, 50, .35)); }
  50%      { filter: drop-shadow(0 0 6px rgba(220, 60, 60, .6)); }   /* ✏️ TWEAK: how red it glows */
}
.hand-dialog__message { font-style: italic; font-size: 1.08rem; line-height: 1.7; letter-spacing: .01em; color: var(--ivory); }
.hand-dialog__door { margin-bottom: .6rem; border-color: #e9a3a0; box-shadow: 0 0 22px rgba(220, 60, 60, .35); }   /* the hidden door (awake hand) */
@media (prefers-reduced-motion: reduce) { .tree .hand-prop.is-awake .hand-bones { animation: none; filter: drop-shadow(0 0 4px rgba(220, 60, 60, .5)); } }

/* the quill stirs very slightly in the breeze, pivoting where it sits in the ink */
.tree .scroll-quill {
  transform-box: fill-box;
  transform-origin: 96% 100%;
  animation: quill-sway 7s ease-in-out infinite alternate;
}
@keyframes quill-sway {
  from { transform: rotate(-1.2deg); }   /* ✏️ TWEAK: how much the quill sways */
  to   { transform: rotate(1.5deg); }
}


/* --- the night sky behind the tree (settings: SKY in script.js) --- */
.night-sky {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.sky-star {                            /* tiny twinkling stars */
  position: absolute;
  border-radius: 50%;
  background: #f4f1e6;
  opacity: .2;
  animation: sky-twinkle var(--tw, 4s) ease-in-out var(--tw-delay, 0s) infinite alternate;
}
@keyframes sky-twinkle { to { opacity: var(--tw-max, .8); } }
/* a shooting star: drifts in slowly, then streaks away fast, its tail
   growing longer the faster it goes */
.sky-shooter {
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: #fff;
  will-change: translate, opacity;     /* PERFORMANCE: let the graphics card move it */
  box-shadow: 0 0 6px 2px rgba(255, 255, 255, .7), 0 0 14px 4px rgba(201, 164, 92, .35);
  rotate: var(--angle, 18deg);
  /* ✏️ TWEAK: the curve below = slow at first, then very quick */
  animation: sky-shoot var(--dur, 3s) cubic-bezier(.78, 0, .96, .5) forwards;
}
.sky-shooter::before {                 /* the tail (stretched with a cheap scale, not a width change) */
  content: "";
  position: absolute;
  right: 1px;
  top: 50%;
  height: 1.5px;
  width: 260px;                        /* ✏️ TWEAK: longest tail */
  margin-top: -.75px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .25) 40%, rgba(255, 255, 255, .95));
  transform-origin: right center;
  will-change: transform;
  animation: sky-tail var(--dur, 3s) cubic-bezier(.78, 0, .96, .5) forwards;
}
@keyframes sky-shoot {
  0%   { translate: 0 0;      opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { translate: 125vw 0;  opacity: 0; }   /* across the whole screen */
}
@keyframes sky-tail {
  0%   { transform: scaleX(.015); }
  60%  { transform: scaleX(.15); }
  100% { transform: scaleX(1); }
}

/* --- the meadow: wild grass & wild flowers (settings: MEADOW in script.js) ---
   (the wind waves across it are done by the animation loop in script.js) */
.tree .meadow { pointer-events: none; }
.tree .meadow-grass {
  fill: none;
  stroke: #5f9a5a;                     /* ✏️ TWEAK: wild grass colour */
  stroke-width: max(.8px, calc(var(--u) * .8px));
  stroke-linecap: round;
  opacity: .8;
}
.tree .meadow-grass.is-light { stroke: #8cc27d; opacity: .65; }
/* the bigger outlined grass clumps (line-art style) */
.tree .meadow-blade {
  fill: rgba(22, 52, 30, .78);         /* ✏️ TWEAK: inside of the outlined blades (dark, so they read as line art) */
  stroke: #79b46c;                     /* ✏️ TWEAK: outline colour of the blades */
  stroke-width: max(.9px, calc(var(--u) * .9px));
  stroke-linejoin: round;
}
.tree .meadow-blade-vein { fill: none; stroke: rgba(121, 180, 108, .55); stroke-width: max(.6px, calc(var(--u) * .6px)); stroke-linecap: round; }
.tree .meadow-blade-foot { fill: none; stroke: #79b46c; stroke-width: max(.9px, calc(var(--u) * .9px)); stroke-linecap: round; }
.tree .meadow-stem { fill: none; stroke: #6fa265; stroke-width: max(.7px, calc(var(--u) * .7px)); stroke-linecap: round; }
.tree .meadow-leaf { fill: rgba(108, 176, 96, .45); stroke: #6fa265; stroke-width: max(.5px, calc(var(--u) * .5px)); }
.tree .meadow-petal { fill: var(--petal); fill-opacity: .55; stroke: var(--petal); stroke-width: max(.5px, calc(var(--u) * .5px)); }
.tree .meadow-heart { fill: #e8c262; }
.tree .meadow-bell  { fill: var(--petal); fill-opacity: .6; stroke: var(--petal); stroke-width: max(.5px, calc(var(--u) * .5px)); }
.tree .meadow-bud   { fill: var(--petal); fill-opacity: .8; }


/* ---------------------------------------------------------------------------
   6. GROW-IN ANIMATION
   script.js gives each piece a start time (--d) and duration (--dur), then
   adds the class "is-growing" to the <svg>, which starts everything.
   Lines use pathLength="1", so "dashoffset 1 -> 0" draws them from start
   to finish.
   --------------------------------------------------------------------------- */
.tree .draw {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  opacity: 0;
}
.tree .grow-fade { opacity: 0; }
.tree .leaf-grow { transform: scale(0); opacity: 0; }
.tree .label     { opacity: 0; }
.tree .cat       { opacity: 0; }

/* ✏️ TWEAK: the START TIMES of the grow-in live in TIMING in script.js */
.tree.is-growing .draw      { animation: draw      var(--dur, 1s) var(--ease-grow) var(--d, 0s) both; }
.tree.is-growing .grow-fade { animation: fade-in   var(--dur, 1s) ease             var(--d, 0s) both; }
.tree.is-growing .leaf-grow { animation: leaf-pop  var(--dur, .7s) cubic-bezier(.3, 1.4, .5, 1) var(--d, 0s) both; }
.tree.is-growing .label     { animation: label-in  1.1s var(--ease-soft) var(--d, 0s) both; }
.tree.is-growing .cat       { animation: fade-in   1.4s ease var(--d, 0s) both; }

@keyframes draw {
  0%   { stroke-dashoffset: 1; opacity: 0; }
  2%   { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 1; }
}
@keyframes fade-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes leaf-pop {
  from { transform: scale(0);  opacity: 0; }
  to   { transform: scale(1);  opacity: 1; }
}
@keyframes label-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* after growing finishes script.js swaps "is-growing" for "is-grown" so the
   final state stays put without keeping hundreds of animations alive */
.tree.is-grown .draw      { stroke-dasharray: none; stroke-dashoffset: 0; opacity: 1; }
.tree.is-grown .grow-fade,
.tree.is-grown .label,
.tree.is-grown .cat       { opacity: 1; }
.tree.is-grown .leaf-grow { transform: none; opacity: 1; }


/* ---------------------------------------------------------------------------
   7. BUTTONS, CAPTIONS, DIALOG
   --------------------------------------------------------------------------- */
.tree-bar {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  padding: clamp(.75rem, 2vh, 1.25rem) clamp(1rem, 3vw, 2rem);
  pointer-events: none;                /* only the button itself is clickable */
}
.tree-bar > * { pointer-events: auto; }
.tree-bar__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.25rem;
  letter-spacing: .4em;
  text-indent: .4em;
  color: var(--ivory-soft);
  outline: none;
}

.ghost-button {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: .5em;
  padding: .55em 1.05em;
  font: inherit;
  font-size: .95rem;
  letter-spacing: .04em;
  color: var(--ivory);
  background: rgba(11, 20, 16, .55);
  border: 1px solid rgba(233, 226, 207, .28);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color .3s ease, color .3s ease, background .3s ease;
}
a.ghost-button { text-decoration: none; }     /* the same look when used as a link */

/* the ♪ music on/off button (top-right of the tree screen) */
.music-button {
  justify-self: end;
  gap: .45em;
  padding: .5em .85em;
}
.music-button[hidden] { display: none; }
.music-button__icon { font-size: 1.05rem; line-height: 1; }
.music-button__bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; }
.music-button__bars i {
  display: block;
  width: 2px;
  height: 3px;
  background: currentColor;
  opacity: .6;
  border-radius: 1px;
}
/* while music is on, the little bars bob gently */
.music-button.is-on .music-button__bars i { animation: music-bar 1.2s ease-in-out infinite alternate; }
.music-button.is-on .music-button__bars i:nth-child(2) { animation-delay: -.4s; animation-duration: .9s; }
.music-button.is-on .music-button__bars i:nth-child(3) { animation-delay: -.8s; animation-duration: 1.4s; }
@keyframes music-bar { from { height: 3px; } to { height: 12px; } }
.music-button:not(.is-on) { opacity: .65; }
.ghost-button:hover { border-color: var(--gold); color: var(--gold-soft); }
.ghost-button:focus-visible {
  outline: 2px solid var(--gold-soft);
  outline-offset: 3px;
}
.ghost-button--solid { background: rgba(201, 164, 92, .12); border-color: rgba(201, 164, 92, .5); }

/* --- THE MUSIC PLAYER (top left of the tree page; settings: PLAYLIST in script.js) --- */
.player {
  position: absolute;
  top: calc(clamp(.75rem, 2vh, 1.25rem) + 3.1rem);
  left: clamp(1rem, 3vw, 2rem);
  z-index: 3;                                 /* above the full-screen visualisers too */
  width: min(330px, calc(100% - 2rem));       /* ✏️ TWEAK: player width */
  padding: .55rem .7rem .5rem;
  color: var(--ivory);
  background: rgba(11, 20, 16, .66);
  border: 1px solid rgba(233, 226, 207, .2);
  border-radius: 14px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .35);
}
.player[hidden] { display: none; }
.player__row { display: flex; align-items: center; gap: .2rem; }
.player__btn {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  color: var(--ivory-soft);
  background: none;
  border: 1px solid transparent;
  border-radius: 50%;
  cursor: pointer;
  transition: color .25s ease, border-color .25s ease, background .25s ease;
}
.player__btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.player__btn svg .is-fill { fill: currentColor; stroke: none; }
.player__btn:hover { color: var(--gold-soft); border-color: rgba(201, 164, 92, .35); }
.player__btn:focus-visible { outline: 2px solid var(--gold-soft); outline-offset: 1px; }
.player__btn--main {
  width: 36px;
  height: 36px;
  color: var(--gold-soft);
  border-color: rgba(201, 164, 92, .55);
  background: rgba(201, 164, 92, .1);
}
.player__btn--main svg { width: 17px; height: 17px; }
.player__icon-pause { display: none; }
.player.is-playing .player__icon-play { display: none; }
.player.is-playing .player__icon-pause { display: block; }
.player.is-playing .player__btn--main { box-shadow: 0 0 14px rgba(201, 164, 92, .35); }
.player__info { flex: 1; min-width: 0; padding: 0 .35rem; }
.player__title, .player__artist { margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player__title { font-family: var(--font-display); font-size: 1rem; letter-spacing: .02em; line-height: 1.2; }
.player__artist { font-size: .74rem; font-style: italic; color: var(--ivory-soft); }
.player__seek { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: .5rem; margin-top: .3rem; }
.player__time { font-size: .72rem; font-variant-numeric: tabular-nums; color: var(--ivory-soft); min-width: 2.4em; }
.player__total { text-align: right; }
/* the seek bar: the played part glows gold, the knob carries a little light */
.player__bar {
  --p: 0%;
  width: 100%;
  height: 16px;
  margin: 0;
  background: none;
  -webkit-appearance: none;
          appearance: none;
  cursor: pointer;
}
.player__bar::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--sage-deep), var(--gold) var(--p), rgba(233, 226, 207, .18) var(--p));
}
.player__bar::-moz-range-track { height: 3px; border-radius: 3px; background: rgba(233, 226, 207, .18); }
.player__bar::-moz-range-progress { height: 3px; border-radius: 3px; background: var(--gold); }
.player__bar::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 11px;
  height: 11px;
  margin-top: -4px;
  border-radius: 50%;
  background: var(--gold-soft);
  box-shadow: 0 0 0 3px rgba(201, 164, 92, .18), 0 0 10px rgba(226, 199, 132, .8);
}
.player__bar::-moz-range-thumb { width: 11px; height: 11px; border: 0; border-radius: 50%; background: var(--gold-soft); box-shadow: 0 0 10px rgba(226, 199, 132, .8); }
.player__bar:focus-visible { outline: 1px solid var(--gold-soft); outline-offset: 2px; }
/* the volume: 🔈 (mute) and a slim slider in the same style */
.player__vol { display: flex; align-items: center; gap: .35rem; margin-top: .15rem; }
.player__btn--small { width: 24px; height: 24px; }
.player__btn--small svg { width: 14px; height: 14px; }
.player__slash { display: none; }
.player__btn.is-muted .player__waves { display: none; }
.player__btn.is-muted .player__slash { display: block; }
.player__volume {
  --p: 100%;
  flex: 1;
  height: 14px;
  margin: 0 .2rem 0 0;
  background: none;
  -webkit-appearance: none;
          appearance: none;
  cursor: pointer;
}
.player__volume::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--ivory-soft) var(--p), rgba(233, 226, 207, .16) var(--p));
}
.player__volume::-moz-range-track { height: 2px; background: rgba(233, 226, 207, .16); }
.player__volume::-moz-range-progress { height: 2px; background: var(--ivory-soft); }
.player__volume::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 9px;
  height: 9px;
  margin-top: -3.5px;
  border-radius: 50%;
  background: var(--ivory);
}
.player__volume::-moz-range-thumb { width: 9px; height: 9px; border: 0; border-radius: 50%; background: var(--ivory); }
.player__volume:focus-visible { outline: 1px solid var(--gold-soft); outline-offset: 2px; }

/* opened by double-click: a kind note on how to run Nekoland (index.html) */
.needs-server {
  position: fixed;
  left: 50%;
  bottom: 1.2rem;
  z-index: 50;
  transform: translateX(-50%);
  width: min(92vw, 30rem);
  padding: .9rem 1.2rem;
  text-align: center;
  font-size: .92rem;
  line-height: 1.5;
  color: var(--ivory);
  background: #0f1a15;
  border: 1px solid rgba(201, 164, 92, .5);
  border-radius: 12px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, .5);
}
.needs-server[hidden] { display: none; }
.needs-server p { margin: .2rem 0; }
.needs-server__title { font-family: var(--font-display); font-size: 1.15rem; color: var(--gold-soft); }
.needs-server code { font-family: Consolas, "Courier New", monospace; color: var(--gold-soft); }
/* ☰ the list of songs */
.player__list {
  list-style: none;
  margin: .5rem 0 0;
  padding: .35rem 0 0;
  max-height: min(44vh, 320px);
  overflow-y: auto;
  border-top: 1px dashed rgba(233, 226, 207, .16);
  scrollbar-width: thin;                      /* a slim gold scrollbar, not the bright default */
  scrollbar-color: rgba(201, 164, 92, .45) transparent;
}
.player__list[hidden] { display: none; }
/* narrower windows: a slimmer player, so it stays in the corner of the sky */
@media (max-width: 1100px) {
  .player { width: min(262px, calc(100% - 2rem)); }
  .player__artist { display: none; }
  .player__title { font-size: .9rem; }
}
.player__song {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  align-items: center;
  width: 100%;
  padding: .38rem .4rem;
  font: inherit;
  font-size: .86rem;
  text-align: left;
  color: var(--ivory-soft);
  background: none;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
}
.player__song:hover, .player__song:focus-visible { background: rgba(201, 164, 92, .1); color: var(--ivory); outline: none; }
.player__song-n { font-family: var(--font-display); color: var(--gold); opacity: .75; }
.player__song-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player__song.is-current { color: var(--gold-soft); }
.player__song.is-current .player__song-n::before { content: "♪"; }
.player__song.is-current .player__song-n { font-size: 0; }
.player__song.is-current .player__song-n::before { font-size: .9rem; }

.tree-caption,
.plate-caption {
  position: absolute;
  bottom: clamp(.6rem, 2vh, 1.2rem);
  margin: 0;
  font-style: italic;
  font-size: .9rem;
  letter-spacing: .05em;
  color: var(--ivory-soft);
  pointer-events: none;
}
.tree-caption  { left: 50%; transform: translateX(-50%); text-align: center; width: max-content; max-width: calc(100% - 2rem); }
.plate-caption { right: clamp(1rem, 3vw, 2rem); color: var(--ivory-faint); }

/* the placeholder room pop-up */
.room-dialog {
  width: min(92vw, 30rem);
  padding: 0;
  color: var(--ivory);
  background: #0f1a15;
  border: 1px solid rgba(201, 164, 92, .4);
  border-radius: 14px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
}
.room-dialog::backdrop { background: rgba(5, 10, 7, .7); }
.room-dialog[open] { animation: dialog-in .45s var(--ease-soft); }
@keyframes dialog-in {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
.room-dialog__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .7rem;
  padding: 2rem 1.75rem 1.75rem;
  text-align: center;
}
.room-dialog__ornament { width: 120px; height: 24px; fill: none; stroke: var(--gold); stroke-width: 1; }
.room-dialog__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2rem;
  letter-spacing: .08em;
}
.room-dialog__text { margin: 0; color: var(--ivory-soft); line-height: 1.55; }
.room-dialog__note {
  margin: .4rem 0 .6rem;
  padding: .8rem 1rem;
  font-size: .88rem;
  line-height: 1.55;
  color: var(--ivory-soft);
  border: 1px dashed rgba(233, 226, 207, .22);
  border-radius: 8px;
}
.room-dialog__note code {
  font-family: Consolas, "Courier New", monospace;
  font-size: .85em;
  color: var(--gold-soft);
}

.noscript {
  position: fixed;
  inset: auto 1rem 1rem 1rem;
  padding: 1rem;
  text-align: center;
  background: var(--bg-raised);
  border: 1px solid var(--ivory-faint);
}


/* ---------------------------------------------------------------------------
   7b. THE SCROLL PAGE (scroll.html) — empty for now, ready to be filled.
   Unlike the tree, this page is allowed to scroll if its content grows.
   --------------------------------------------------------------------------- */
html.page-scroll,
html.page-scroll body { overflow: auto; }
.scroll-page {
  position: relative;
  min-height: 100vh;
  padding: 6rem 1.5rem 3rem;
}
.scroll-page .tree-bar { position: fixed; }


/* ---------------------------------------------------------------------------
   8. SMALL SCREENS (phones)
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .tree-bar { grid-template-columns: auto 1fr auto; gap: .5rem; }
  .tree-bar__title { justify-self: center; font-size: 1rem; letter-spacing: .25em; text-indent: .25em; }
  .tree-bar__spacer { display: none; }
  .ghost-button { font-size: .85rem; padding: .5em .9em; }
  .plate-caption { display: none; }
  .tree-caption { font-size: .8rem; }
  .player { top: calc(clamp(.75rem, 2vh, 1.25rem) + 2.8rem); padding: .4rem .55rem; }
  .player__artist { display: none; }
}


/* ---------------------------------------------------------------------------
   9. REDUCED MOTION
   If someone has asked their computer for less motion (Windows: Settings >
   Accessibility > Visual effects > Animation effects OFF), we switch off the
   floating, fluttering and drawing, and just fade things in gently.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .door, .door__light, .leaf__window, .door__vine-leaf, .mural, .word-scroll.is-wrong, .door.is-rattling .door__leaf,
  .backdrop__motes span, .door-hint__dot, .music-button__bars i,
  .tree .label.is-active .label-cue,
  .sky-star, .sky-shooter, .sky-shooter::before,
  .tree .scroll-quill, .tree .cat-poke.is-poked,
  .tree .sleeper-body, .tree .sleeper-z,
  .tree .sleeper-look, .tree .sleeper-ear, .tree .sleeper-purr { animation: none !important; }
  .tree .sleeper-head { transform: none !important; }   /* still opens its eyes & purrs, without moving */
  .tree .cat-word { animation: fade-in .3s ease both !important; }

  .door__stage, .door__leaf { transition-duration: .01s !important; }
  .scene--door { --awaken: .05s !important; }          /* the door wakes at once, without the flowing light */
  .mural__flow { display: none; }
  .scene--door.is-unlocked .mural { opacity: .95; }
  .scene--door.is-unlocked .door__light { opacity: .95; }
  .elv-ivory, .elv-gold { transition-delay: 0s !important; }
  .door__leaf-breeze { animation: none !important; }
  .door__leaf-lean   { transform: none !important; transition: none !important; }
  .scene--door.is-opening .door__leaf  { transform: none; opacity: 0; transition: opacity .4s ease !important; }
  .scene--door.is-entering .door__stage { transform: none; }
  .scene--door.is-returning .door__stage { animation: none; }

  .tree .draw { stroke-dasharray: none; stroke-dashoffset: 0; }
  .tree.is-growing .draw,
  .tree.is-growing .grow-fade,
  .tree.is-growing .leaf-grow,
  .tree.is-growing .label,
  .tree.is-growing .cat { animation: fade-in .6s ease 0s both !important; transform: none; }
}

/* text only screen readers hear */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
