/*
  Shared CSS for the four full-screen Bells instruments:
  C Major, Chromatic, Chords, Color Bars.
  Bells are PNG only — no boxes around them.
*/

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background:
    linear-gradient(180deg, #eaf6ff 0%, #f6fbff 60%, #f4fff2 100%);
  color: #1b233e;
  font-family: "Nunito", "Avenir Next Rounded", "Trebuchet MS", Verdana, sans-serif;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
}

button { font: inherit; cursor: pointer; }

.home-pill {
  position: fixed;
  z-index: 30;
  top: calc(12px + env(safe-area-inset-top));
  left: calc(12px + env(safe-area-inset-left));
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #512d1b;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 6px 14px rgba(14, 59, 93, 0.18);
  text-decoration: none;
}

.home-pill svg {
  width: 22px;
  height: 22px;
  stroke: currentColor;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.instrument-stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  padding: calc(8px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom));
  display: grid;
  place-items: end center;
  overflow: hidden;
}

.bells-board {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: end;
  justify-content: center;
  gap: 2px;
  position: relative; /* anchor for .bell-glow-host */
  /* glide engine sets touch-action: none on this element */
}

/* One shared host for all bell glows. Sits behind every bell PNG in the board
   so a neighboring bell's glow can't paint over an adjacent bell. */
.bell-glow-host {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  z-index: 0;
}

/* ---- shared bell button: no box, png only ---- */
.bell {
  border: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  position: relative;
  /* Sit above the shared .bell-glow-host so neighbors' glows can't paint
     on top of this bell's PNG. */
  z-index: 1;
}

/* ---- kamehameha-style energy wave on bell trigger ----
   Spark layers are children of .bell-glow-host (NOT individual bells).
   JS sets left/top/width/height to match each triggered bell's bounding box,
   so the wave + embers emanate from the right position while painting BEHIND
   all bells in the board. */
.bell-spark-layer {
  position: absolute;
  pointer-events: none;
  overflow: visible;
  /* left/top set inline at the bell's center; width/height set inline to the
     bell's box; translate(-50%, -50%) recenters on that point. */
  transform: translate(-50%, -50%);
}

/* Expanding circular glow that bursts outward from the bell's center.
   Radiates in all directions, fading at the edges. Reads as "color is
   radiating from the bell" — fills the surrounding area with the note's color. */
.bell-wave {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 150%;
  height: 150%;
  margin-left: -75%;
  margin-top: -75%;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    color-mix(in srgb, var(--note-color, #fff) 75%, white) 0%,
    color-mix(in srgb, var(--note-color, #fff) 55%, transparent) 22%,
    color-mix(in srgb, var(--note-color, #fff) 30%, transparent) 48%,
    color-mix(in srgb, var(--note-color, #fff) 14%, transparent) 70%,
    transparent 92%
  );
  /* No blur filter here on purpose. The gradient's own stops already feather
     the edge to nothing, so a 2px blur bought almost no softness — but it did
     force every live wave through a separate filter pass, on the layout where
     the most of them are alive at once. Removing it is invisible and it is the
     single biggest paint saving in the effect. */
  opacity: 0;
  pointer-events: none;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
  /* Two parallel animations so motion + opacity each have ONE smooth easing
     curve. No velocity discontinuities — feels chill and continuous. ~1.9s
     total so the glow lingers as the bell sound is still ringing out. */
  animation:
    bellWaveScale 1900ms cubic-bezier(.22, .61, .36, 1) forwards,
    bellWaveFade  1900ms cubic-bezier(.40, .00, .50, 1) forwards;
}

/* Single continuous ease-out scale — fast accel, smooth coast to rest */
@keyframes bellWaveScale {
  from { transform: scale(0.3); }
  to   { transform: scale(2.5); }
}

/* Smooth opacity: fast burst-in, then a long graceful fade-out so the
   glow lingers behind the bell long after the initial strike. */
@keyframes bellWaveFade {
  0%   { opacity: 0; }
  10%  { opacity: 0.9; }
  100% { opacity: 0; }
}

/* A few small particle "embers" that fly out with the wave for sparkle */
.bell-spark {
  position: absolute;
  left: 50%;
  top: 28%;
  width: 9px;
  height: 9px;
  margin-left: -4.5px;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    color-mix(in srgb, var(--note-color, #fff) 95%, white) 0%,
    color-mix(in srgb, var(--note-color, #fff) 75%, white) 45%,
    color-mix(in srgb, var(--note-color, #fff) 60%, transparent) 80%,
    transparent 100%
  );
  /* 0.3px of blur is below what the panel can resolve; it cost a filter pass
     per ember for nothing. */
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
  animation: bellEmberFly var(--spark-duration, 780ms) cubic-bezier(.22,.78,.34,1) forwards;
  animation-delay: var(--spark-delay, 0ms);
  --spark-x: 0px;
  --spark-y: -90px;
  --spark-scale: 1;
}

@keyframes bellEmberFly {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.4);
  }
  20% {
    opacity: 0.9;
    transform:
      translate(calc(var(--spark-x) * 0.28), calc(var(--spark-y) * 0.28))
      scale(var(--spark-scale));
  }
  60% {
    opacity: 0.7;
    transform:
      translate(calc(var(--spark-x) * 0.72), calc(var(--spark-y) * 0.72))
      scale(calc(var(--spark-scale) * 0.95));
  }
  100% {
    opacity: 0;
    transform:
      translate(var(--spark-x), var(--spark-y))
      scale(calc(var(--spark-scale) * 0.5));
  }
}

.bell img {
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
  filter: drop-shadow(0 4px 4px rgba(24, 47, 72, 0.18));
  user-select: none;
  -webkit-user-drag: none;
}

.bell.is-playing img {
  transform: translateY(2px) scale(1.02);
  filter: drop-shadow(0 2px 2px rgba(24, 47, 72, 0.16)) brightness(1.04);
}

.bell img {
  transition: transform 0.06s ease, filter 0.06s ease;
}

/* ---- C MAJOR: inverted arch ----
   Bells DO NOT overlap or touch — small visible gap between adjacent bells.
   Layout/proportions (RISE/SHIFT in c-major.html) are LOCKED — only --bell-w
   scales. Bell size is min(height-derived, width-derived) so the arch grows
   to fill landscape on iPhone/iPad without overflowing either axis.
   Width formula: visible span = 6.2*bw + 7*gap (the outermost C/c bells get
   pulled inward by SHIFT ±0.9, so the visible left/right edges sit at
   0.9*bw and (7.1*bw + 7g) within an 8-bell flex container). Reserving
   ~8px L/R + worst-case gap (7×16) gives bw ≤ (100vw - 120px) / 6.2. */
.cmajor-board {
  --bell-w: clamp(
    70px,
    min(27vh, calc((100vw - 120px) / 6.2)),
    220px
  );
  gap: clamp(4px, 0.9vw, 16px); /* clear visible gap between bells, scales with screen */
  padding-bottom: clamp(8px, 2vh, 24px);
}

/* Trim the stage's side padding so the arch can push to the screen edges */
.bell-instrument-cmajor .instrument-stage {
  padding-left: calc(4px + env(safe-area-inset-left));
  padding-right: calc(4px + env(safe-area-inset-right));
}

.cmajor-bell {
  position: relative;
  width: var(--bell-w);
  /* --rise and --shift are unitless multipliers (e.g. 1.5, 0.2). Multiplied
     by bell width to produce lengths. --shift is horizontal nudge: positive
     = right, negative = left. Used to pinch E/A inward toward F/G. */
  transform: translate(
    calc(var(--shift, 0) * var(--bell-w)),
    calc(var(--rise, 0) * var(--bell-w) * -1)
  );
}

/* The outermost bells (C, c) are visually shifted inward by 0.9*bell-w via
   SHIFT. Without this negative margin the flex container still reserves the
   full natural span (8*bell-w + 7*gap), overflowing the viewport on wide
   screens once bell-w grows. Negative margins absorb the gap that the shifts
   vacate, so the container's natural width equals the visible span
   (6.2*bell-w + 7*gap) — exactly what bell-w's width formula assumes.
   Note: :first-of-type/:last-of-type (not :first-child/:last-child) because
   the .bell-glow-host SPAN sits as the actual first child of the board. */
.cmajor-bell:first-of-type { margin-left: calc(var(--bell-w) * -0.9); }
.cmajor-bell:last-of-type  { margin-right: calc(var(--bell-w) * -0.9); }

/* Portrait phone — rotate overlay covers the board; just shrink in case */
@media (orientation: portrait) {
  .cmajor-board { --bell-w: clamp(56px, 12vw, 120px); }
}

/* ---- "rotate to play" overlay shown only in portrait on landscape-only instruments ---- */
.rotate-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(241, 246, 252, 0.96);
  padding: 24px;
}

.rotate-card {
  max-width: 280px;
  text-align: center;
  display: grid;
  justify-items: center;
  gap: 14px;
  color: #1b233e;
}

.rotate-icon {
  width: 88px;
  height: 88px;
  color: #099695;
  animation: rotateIconPulse 1.6s ease-in-out infinite;
}

.rotate-icon svg { width: 100%; height: 100%; }

.rotate-card p {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 900;
  line-height: 1.3;
}

@keyframes rotateIconPulse {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(90deg); }
}

/* Landscape-only instruments: show overlay in portrait */
.bell-instrument-page.landscape-only .rotate-overlay {
  display: none;
}

@media (orientation: portrait) {
  .bell-instrument-page.landscape-only .rotate-overlay {
    display: flex;
  }
}

/* Color Bars (later page) opts out of overlay even when portrait. */
