/* ============================================================================
   NEKOLAND — visualiser.css   (looks for the ♫ visualiser modes, visualiser.js)
   ----------------------------------------------------------------------------
   ✏️ Search for "TWEAK" (Ctrl+F) to find every spot that is safe to change.

   Sections:
     1. The ♫ button and its menu
     2. Mode 1 — the Tree Visualiser (what changes on the tree)
     3. The full-screen stage (modes 2 & 3)
     4. Mode 2 — the Atelier (procedural wallpaper)
     5. Mode 3 — the Gallery
     6. Phones
     7. Reduced motion

   PERFORMANCE: everything that moves here only changes transform or
   opacity (cheap for laptops without a graphics card). The pictures,
   lattices and masks are drawn once and then just slid or faded.
   ============================================================================ */


/* ---------------------------------------------------------------------------
   1. THE ♫ BUTTON — a small cousin of the ♪ music button — and its menu
   --------------------------------------------------------------------------- */
.tree-bar__tools {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.vis-menu-wrap { position: relative; }
.vis-button {
  gap: .35em;
  padding: .42em .62em;                       /* ✏️ TWEAK: size of the ♫ button */
  font-size: .8rem;
  opacity: .8;
}
.vis-button:hover, .vis-button.is-on, .vis-button[aria-expanded="true"] { opacity: 1; }
.vis-button.is-on { border-color: rgba(201, 164, 92, .6); color: var(--gold-soft); }
.vis-button__icon {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
}
.vis-button__bars { height: 9px; }
.vis-button.is-on .vis-button__icon { animation: vis-icon-turn 9s linear infinite; }
.vis-button.is-on .vis-button__bars i { animation: vis-bar 1.1s ease-in-out infinite alternate; }
.vis-button.is-on .vis-button__bars i:nth-child(2) { animation-delay: -.5s; animation-duration: .8s; }
.vis-button.is-on .vis-button__bars i:nth-child(3) { animation-delay: -.2s; animation-duration: 1.3s; }
@keyframes vis-bar { from { height: 2px; } to { height: 9px; } }
@keyframes vis-icon-turn { to { transform: rotate(360deg); } }

.vis-menu {
  position: absolute;
  top: calc(100% + .6rem);
  right: 0;
  z-index: 5;
  width: min(18rem, calc(100vw - 2rem));
  padding: .9rem .6rem .7rem;
  color: var(--ivory);
  background: #0f1a15;
  border: 1px solid rgba(201, 164, 92, .4);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
  animation: dialog-in .35s var(--ease-soft);
}
.vis-menu[hidden] { display: none; }
.vis-menu__head { display: grid; justify-items: center; gap: .3rem; margin-bottom: .45rem; }
.vis-menu__eyebrow {
  margin: 0;
  font-style: italic;
  font-size: .82rem;
  letter-spacing: .14em;
  color: var(--ivory-soft);
}
.vis-menu__ornament { width: 110px; height: 13px; fill: none; stroke: var(--gold); stroke-width: 1; }
.vis-menu__item {
  display: grid;
  grid-template-columns: 2.1rem 1fr;
  align-items: center;
  width: 100%;
  padding: .5rem .6rem;
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: 0;
  border-radius: 9px;
  cursor: pointer;
  transition: background .25s ease;
}
.vis-menu__item:hover, .vis-menu__item:focus-visible { background: rgba(201, 164, 92, .1); outline: none; }
.vis-menu__item:focus-visible { box-shadow: inset 0 0 0 1px rgba(226, 199, 132, .55); }
.vis-menu__num {
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: .06em;
  color: var(--gold);
  opacity: .8;
}
.vis-menu__text { display: grid; gap: .05rem; }
.vis-menu__name { font-family: var(--font-display); font-size: 1.08rem; letter-spacing: .03em; }
.vis-menu__note { font-style: italic; font-size: .8rem; color: var(--ivory-soft); }
/* the mode that's playing now: gold, with a little glowing dot */
.vis-menu__item[aria-checked="true"] .vis-menu__name { color: var(--gold-soft); }
.vis-menu__item[aria-checked="true"] .vis-menu__name::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: .5em;
  vertical-align: middle;
  border-radius: 50%;
  background: var(--gold-soft);
  box-shadow: 0 0 8px rgba(226, 199, 132, .9);
}
.vis-menu__item--exit { margin-top: .35rem; border-top: 1px dashed rgba(233, 226, 207, .16); border-radius: 0 0 9px 9px; }
.vis-menu__status {
  margin: .55rem .4rem 0;
  font-size: .74rem;
  font-style: italic;
  line-height: 1.4;
  text-align: center;
  color: var(--ivory-faint);
}


/* ---------------------------------------------------------------------------
   2. MODE 1 — THE TREE VISUALISER
   --------------------------------------------------------------------------- */
.vis-ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  animation: vis-in 1.2s ease both;
}
@keyframes vis-in { from { opacity: 0; } to { opacity: 1; } }

/* the cats on the branches slip away (the sleeping cats stay asleep) */
.tree.is-grown .cat { transition: opacity .6s ease; }
.scene--tree.vis-tree .tree .cat { opacity: 0 !important; pointer-events: none; }
.scene--tree.vis-tree .tree .cat-hit { pointer-events: none; }
/* the stars go out */
.night-sky { transition: opacity .8s ease, visibility 0s; }
.scene--tree.vis-on .night-sky { opacity: 0; visibility: hidden; transition: opacity .8s ease, visibility 0s linear .8s; }
.scene--tree.vis-on .sky-star { animation-play-state: paused; }
/* the faint rings round the tree glow a little brighter */
.scene--tree.vis-tree .tree .deco-ring { stroke: rgba(201, 164, 92, .24); transition: stroke 1s ease; }


/* ---------------------------------------------------------------------------
   3. THE FULL-SCREEN STAGE (modes 2 & 3). The top bar stays above it.
   --------------------------------------------------------------------------- */
.vis-stage {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  background: #04060a;
  animation: vis-in .9s ease both;
}
/* hide (and pause) the tree underneath while a full-screen mode covers it */
.scene--tree.vis-full .tree-world,
.scene--tree.vis-full > .tree-caption,
.scene--tree.vis-full > .plate-caption { visibility: hidden; }
.scene--tree.vis-full .tree *,
.scene--tree.vis-full .tree *::before,
.scene--tree.vis-full .tree *::after { animation-play-state: paused !important; }
.scene--tree.vis-full .tree-bar__title {             /* readable over any wallpaper */
  padding: .2em .9em;
  background: rgba(8, 10, 14, .45);
  border-radius: 999px;
  text-shadow: 0 0 10px rgba(0, 0, 0, .6);
}


/* ---------------------------------------------------------------------------
   4. MODE 2 — THE ATELIER (procedural wallpaper)
   The colours come from the weave's palette: --base, --accent, --second, --light
   --------------------------------------------------------------------------- */
.atl { position: absolute; inset: 0; background: #05060a; }
.atl-weave {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--base);
  opacity: 0;
  transition: opacity var(--fade, 2.6s) ease;
}
.atl-weave.is-on, .atl-weave.is-leaving { opacity: 1; }   /* the new one fades in OVER the old one */

/* the wall: a painting drifting slowly, or a tile pattern */
.atl-wall { position: absolute; inset: -4%; }
.atl-painting {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  animation: atl-drift 70s ease-in-out infinite alternate;   /* ✏️ TWEAK: 70s = how slowly the wall drifts */
}
@keyframes atl-drift {
  from { transform: scale(1.04); }
  to   { transform: scale(1.13) translate(var(--drift, 1%), calc(var(--drift, 1%) * -.5)); }
}
.atl-tile {
  position: absolute;
  inset: -12%;
  background-repeat: repeat;
  opacity: .42;                               /* ✏️ TWEAK: how strong a tile wall is */
  animation: atl-tile 120s linear infinite alternate;
}
@keyframes atl-tile { to { transform: translate(4%, -3%); } }
/* the palette wash and a deep vignette, so everything sits in one light */
.atl-grade {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 70% 62% at 50% 46%, color-mix(in srgb, var(--base) 18%, transparent), color-mix(in srgb, var(--base) 90%, transparent) 100%),
    linear-gradient(180deg, color-mix(in srgb, var(--base) 35%, transparent), color-mix(in srgb, var(--base) 55%, transparent));
}

/* the laser-cut lattices (white stencils, coloured by the palette) */
.atl-stencil {
  -webkit-mask: var(--mask) center / contain no-repeat;
          mask: var(--mask) center / contain no-repeat;
  background: var(--accent);
}
.atl-halo {
  position: absolute;
  left: 50%;
  top: 52%;
  width: min(122vmin, 1150px);
  aspect-ratio: 1;
  translate: -50% -50%;
  background: var(--light);
  opacity: .12;
  animation: atl-turn 260s linear infinite;   /* ✏️ TWEAK: 260s = one slow turn of the great lattice */
}
@keyframes atl-turn { to { transform: rotate(360deg); } }
.atl--moon .atl-halo { left: var(--moon-x, 50%); width: min(96vmin, 900px); }
.atl--moon:has(.atl-window.is-left) .atl-halo { --moon-x: 32%; }
.atl--moon:has(.atl-window.is-right) .atl-halo { --moon-x: 68%; }

/* the tall lattice strips at the sides */
.atl-strip {
  position: absolute;
  top: 52%;
  height: min(70vh, 680px);
  aspect-ratio: 378 / 1656;
  translate: 0 -50%;
  background: var(--second);
  opacity: .5;
}
.atl-strip--left  { left: 6vw; }
.atl-strip--right { right: 6vw; }

/* triptych: two side windows, seen through carved screens */
.atl-sidewin, .atl-sidewin__frame {
  position: absolute;
  top: 50%;
  height: min(46vh, 430px);
  aspect-ratio: 100 / 160;
  translate: 0 -46%;
}
.atl-sidewin {
  overflow: hidden;
  -webkit-mask: var(--shape) center / 100% 100% no-repeat;
          mask: var(--shape) center / 100% 100% no-repeat;
  background: var(--base) center / 260px auto repeat;
}
.atl-sidewin .atl-through { width: 100%; height: 100%; object-fit: cover; display: block; opacity: .85; }
.atl-sidewin--left, .atl-sidewin__frame--left   { left: 9vw; }
.atl-sidewin--right, .atl-sidewin__frame--right { right: 9vw; }
.atl-sidewin__frame { -webkit-mask-size: 100% 100%; mask-size: 100% 100%; background: var(--second); opacity: .55; }

/* THE WINDOW: lattice frame behind, the picture seen through the glass */
.atl-window {
  position: absolute;
  left: 50%;
  top: 52%;                                   /* a little low, so the rays never cover the title */
  aspect-ratio: var(--ratio, 1);
  transform: translate(-50%, -50%) scale(var(--breath, 1));   /* --breath: set by the music */
  will-change: transform;
}
.atl-window--arch   { height: min(60vh, 600px); }     /* ✏️ TWEAK: window sizes */
.atl-window--circle { height: min(52vmin, 520px); }
.atl-window--tall   { height: min(54vh, 520px); }
.atl-window.is-left  { left: 32%; }
.atl-window.is-right { left: 68%; }
.atl-frame { position: absolute; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; }
.atl-window--arch .atl-frame   { inset: -7% -12% -3%; }
.atl-window--circle .atl-frame { inset: -17%; }
.atl-window--tall .atl-frame   { inset: -9% -12%; }
.atl-glass {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--base);
  -webkit-mask: var(--shape) center / 100% 100% no-repeat;
          mask: var(--shape) center / 100% 100% no-repeat;
}
.atl-glass .atl-through {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  animation: atl-glass-drift 40s ease-in-out infinite alternate;
}
@keyframes atl-glass-drift { from { transform: scale(1.02); } to { transform: scale(1.12); } }
.atl-glass.is-planet .atl-through { object-fit: cover; transform-origin: 50% 47%; }
/* a little sheen on the glass, and a soft shadow at its edge */
.atl-glass__shine {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(135deg, rgba(255, 255, 255, .14), transparent 38%),
    radial-gradient(ellipse at 50% 50%, transparent 58%, rgba(0, 0, 0, .45) 100%);
  pointer-events: none;
}

/* a Nekoland cat, sitting in the window */
.atl-cat {
  position: absolute;
  bottom: 0;
  left: 70%;
  height: 19%;                                /* ✏️ TWEAK: size of the window cat */
  aspect-ratio: .7;
  -webkit-mask: var(--mask) bottom center / contain no-repeat;
          mask: var(--mask) bottom center / contain no-repeat;
  background: rgba(6, 6, 12, .92);
}
.atl-cat.is-flipped { left: 12%; transform: scaleX(-1); }

/* the garland of line-drawn flowers (one sheet of 6 x 3 flowers) */
.atl-garland {
  position: absolute;
  left: 50%;
  bottom: 3.5vh;
  display: flex;
  align-items: flex-end;
  gap: .4vmin;
  translate: -50% 0;
}
.atl-flower {
  display: block;
  width: calc(5.6vmin * var(--s, 1));          /* ✏️ TWEAK: 5.6vmin = flower size */
  aspect-ratio: 128.67 / 174.67;
  -webkit-mask: var(--mask) var(--fx) var(--fy) / 600% 300% no-repeat;
          mask: var(--mask) var(--fx) var(--fy) / 600% 300% no-repeat;
  background: var(--light);
  opacity: .82;
  transform-origin: 50% 100%;
  animation: atl-sway 5.5s ease-in-out calc(var(--i, 0) * -.7s) infinite alternate;
}
.atl-flower.is-mirrored { scale: -1 1; }
@keyframes atl-sway { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }

/* the little planet as a moon (its black sky melts into the wall) */
.atl-moon {
  position: absolute;
  top: 10vh;
  width: min(22vmin, 220px);
  mix-blend-mode: screen;
  opacity: .9;
  animation: atl-float 12s ease-in-out infinite alternate;
}
.atl-moon.is-left  { left: 5vw; }
.atl-moon.is-right { right: 5vw; }
@keyframes atl-float { to { transform: translateY(-10px) rotate(4deg); } }

/* opened by double-click (file://): Chrome refuses to load mask pictures there,
   so the same lattices, flowers and cat are shown as ordinary (ivory) pictures */
.atl--plain .atl-stencil {
  -webkit-mask: none;
          mask: none;
  background: var(--mask) center / contain no-repeat;
}
.atl--plain .atl-frame,
.atl--plain .atl-sidewin__frame { background-size: 100% 100%; opacity: .8; }
.atl--plain .atl-strip { opacity: .35; }
.atl--plain .atl-flower { -webkit-mask: none; mask: none; background: var(--mask) var(--fx) var(--fy) / 600% 300% no-repeat; }
.atl--plain .atl-cat { -webkit-mask: none; mask: none; background: var(--mask) bottom center / contain no-repeat; filter: brightness(.12); }

/* the rays (canvas) */
.atl-canvas { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; pointer-events: none; }

/* the controls at the bottom right of modes 2 & 3: the name of what you're
   looking at, and the button(s) to change it (nothing changes by itself) */
.vis-controls {
  position: absolute;
  right: clamp(1rem, 3vw, 2rem);
  bottom: clamp(.6rem, 2vh, 1.2rem);
  z-index: 4;
  display: flex;
  align-items: center;
  gap: .6rem;
  max-width: calc(100% - 2rem);
}
.vis-controls__label {
  margin: 0;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-style: italic;
  font-size: .9rem;
  letter-spacing: .05em;
  color: var(--ivory);
  /* a soft dark backing, so the name reads on pale walls too */
  padding: .38em .9em;
  background: rgba(8, 10, 14, .52);
  border-radius: 999px;
}
.vis-controls__label em { font-family: var(--font-display); font-size: 1.05rem; }
.vis-controls__btn { flex: none; font-size: .85rem; }
.gal-prev, .gal-next { width: 2.3rem; height: 2.3rem; padding: 0; justify-content: center; font-size: 1.35rem; line-height: 1; }


/* ---------------------------------------------------------------------------
   5. MODE 3 — THE GALLERY
   --------------------------------------------------------------------------- */
@property --shade { syntax: "<number>"; inherits: true; initial-value: .5; }
.gal {
  position: absolute;
  inset: 0;
  background: #000;
  --shade: .5;
  transition: --shade var(--gal-fade, 2.4s) ease;
}
.gal-slide {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0;
  background-repeat: repeat;
  /* the old picture waits until the new one is fully in (speed: GALLERY.fadeSeconds) */
  transition: opacity var(--gal-fade, 2.4s) ease var(--gal-fade, 2.4s);
}
.gal-slide.is-on { opacity: 1; z-index: 1; transition-delay: 0s; }
/* a picture of a different shape: a soft blurred glow of its own colours fills
   the screen behind it (a tiny 48 x 27 copy, stretched — no costly blur) */
.gal-ambient {
  position: absolute;
  inset: -6%;
  width: 112%;
  height: 112%;
  opacity: 0;
}
.gal-slide.is-contained .gal-ambient { opacity: .75; }
.gal-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: gal-drift var(--kb-dur, 19s) ease-in-out infinite alternate both;   /* drifts there and back, for as long as it stays */
}
.gal-img:not([src]) { display: none; }
.gal-slide.is-contained .gal-img { object-fit: contain; }
@keyframes gal-drift {                        /* the slow "Ken Burns" drift */
  from { transform: scale(1.01) translate(0, 0); }
  to   { transform: scale(var(--kb-s, 1.08)) translate(var(--kb-x, 0), var(--kb-y, 0)); }
}
/* darkens the bottom (where the music is drawn) — more for bright pictures */
.gal-shade {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(0deg, rgba(4, 4, 10, var(--shade)) 0%, rgba(4, 4, 10, calc(var(--shade) * .55)) 24%, transparent 46%),
    radial-gradient(ellipse 85% 80% at 50% 45%, transparent 55%, rgba(0, 0, 0, .45) 100%),
    linear-gradient(180deg, rgba(0, 0, 0, .35), transparent 16%);
}
.gal-canvas { position: absolute; inset: 0; z-index: 3; width: 100%; height: 100%; pointer-events: none; }
.gal-caption {
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--ivory);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 1s ease, transform 1s ease;
}
.gal-caption::before { content: "✦ "; color: var(--gold-soft); font-style: normal; }
.gal-caption.is-on { opacity: .92; transform: none; }


/* ---------------------------------------------------------------------------
   5b. MODE 4 — THE CAT WALLPAPER (catmode.js). Each plate is two still
   pictures (the wall, and the cards) with the visualiser between them.
   --------------------------------------------------------------------------- */
.cats-mode { position: absolute; inset: 0; background: #0c0a10; overflow: hidden; }
.cats-plate {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--fade, 1.8s) ease;
}
.cats-plate.is-on, .cats-plate.is-leaving { opacity: 1; }   /* the new plate fades in OVER the old one */
.cats-wall, .cats-cards, .cats-vis { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.cats-vis { pointer-events: none; }
.cats-wall { animation: cats-drift 70s ease-in-out infinite alternate; }   /* ✏️ TWEAK: 70s = how slowly the wall drifts */
@keyframes cats-drift { from { transform: scale(1.005); } to { transform: scale(1.045) translate(-.6%, .4%); } }
.cats-cards { will-change: transform; }
.cats-style { text-transform: capitalize; }

/* ⤓ keep: shows it's busy while the picture is drawn */
.vis-keep.is-busy { opacity: .6; cursor: progress; }
/* the Tree Visualiser's own controls (its plate name is shown there instead) */
.scene--tree.vis-tree > .plate-caption { visibility: hidden; }
.vis-controls--tree { z-index: 3; }


/* ---------------------------------------------------------------------------
   6. PHONES (portrait): fewer ornaments, everything centred
   --------------------------------------------------------------------------- */
@media (max-aspect-ratio: 4/5) {
  .atl-strip, .atl-moon, .atl-sidewin, .atl-sidewin__frame { display: none; }
  .atl-window.is-left, .atl-window.is-right { left: 50%; }
  .atl--moon .atl-halo { left: 50%; }
  .atl-window--arch { height: min(56vh, 128vw); }
  .atl-window--circle { height: 82vw; }
  .atl-window--tall { height: min(48vh, 110vw); }
  .atl-flower { width: calc(9vw * var(--s, 1)); }
  .gal-caption { font-size: .95rem; }
}
@media (max-width: 1150px) {
  .atl-garland { bottom: calc(3.5vh + 3.2rem); }   /* clear of the controls at the bottom */
}
@media (max-width: 640px) {
  .tree-bar__tools { gap: .35rem; }
  .vis-button { padding: .4em .5em; }
  .atl-plate { font-size: .78rem; }
}


/* ---------------------------------------------------------------------------
   7. REDUCED MOTION — the pictures stay still (the music still shows)
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .vis-button.is-on .vis-button__icon, .vis-button.is-on .vis-button__bars i,
  .atl-painting, .atl-tile, .atl-halo, .atl-glass .atl-through, .atl-flower, .atl-moon,
  .gal-img, .vis-ring, .vis-stage, .cats-wall { animation: none !important; }
  .cats-plate { transition-duration: .01s !important; }
  .atl-weave, .gal-slide, .gal-caption { transition-duration: .01s !important; transition-delay: 0s !important; }
}
