/* Per-language poster art.
   Everything here is drawn in CSS — gradients, and geometric motifs built from
   repeating-conic / repeating-linear layers. Nothing is a film poster or a
   photograph: those are somebody's copyright, and a nostalgia site that gets
   popular on borrowed posters gets a takedown the week it works.
   Each station gets its own colour memory instead. */

.poster {
  position: fixed;
  inset: 0;
  z-index: -1;
  opacity: 0.5;
  transition: opacity 0.6s ease;
  background-color: #171310;
  background-blend-mode: soft-light, normal;
}

/* A grain layer over everything, so the flat gradients read as printed paper
   rather than a CSS demo. */
.poster::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    0deg,
    rgba(255, 255, 255, 0.035) 0px,
    rgba(255, 255, 255, 0.035) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: overlay;
}

/* Bollywood marigold + film-reel amber */
.poster[data-station="hindi"] {
  background-image:
    radial-gradient(60% 45% at 20% 15%, rgba(232, 163, 61, 0.35), transparent 60%),
    radial-gradient(50% 40% at 85% 80%, rgba(190, 60, 40, 0.3), transparent 60%),
    linear-gradient(160deg, #2a1a10, #140f0c);
}

/* Vinyl + late-night blue */
.poster[data-station="english"] {
  background-image:
    radial-gradient(45% 45% at 75% 20%, rgba(90, 140, 200, 0.32), transparent 62%),
    radial-gradient(55% 40% at 15% 85%, rgba(40, 60, 110, 0.4), transparent 60%),
    linear-gradient(200deg, #131a26, #0f1116);
}

/* Kolam white on temple maroon */
.poster[data-station="tamil"] {
  background-image:
    repeating-conic-gradient(
      from 0deg at 18% 22%,
      rgba(255, 255, 255, 0.06) 0deg 6deg,
      transparent 6deg 24deg
    ),
    radial-gradient(60% 50% at 80% 70%, rgba(180, 50, 60, 0.35), transparent 60%),
    linear-gradient(150deg, #241012, #150c0d);
}

/* Kalamkari indigo + turmeric */
.poster[data-station="telugu"] {
  background-image:
    radial-gradient(50% 40% at 25% 25%, rgba(220, 170, 60, 0.28), transparent 60%),
    radial-gradient(55% 45% at 80% 75%, rgba(45, 70, 120, 0.4), transparent 62%),
    linear-gradient(170deg, #14161f, #0f0d10);
}

/* Warli white on earth red */
.poster[data-station="marathi"] {
  background-image:
    repeating-linear-gradient(
      45deg,
      rgba(255, 255, 255, 0.05) 0px,
      rgba(255, 255, 255, 0.05) 2px,
      transparent 2px,
      transparent 16px
    ),
    radial-gradient(55% 45% at 30% 20%, rgba(200, 90, 50, 0.3), transparent 62%),
    linear-gradient(155deg, #241610, #140e0b);
}

/* Phulkari — the loudest one on purpose */
.poster[data-station="punjabi"] {
  background-image:
    repeating-linear-gradient(
      60deg,
      rgba(255, 120, 40, 0.09) 0px,
      rgba(255, 120, 40, 0.09) 6px,
      transparent 6px,
      transparent 18px
    ),
    radial-gradient(50% 40% at 80% 25%, rgba(230, 150, 40, 0.32), transparent 60%),
    linear-gradient(140deg, #2a1c0e, #150f09);
}

/* Alpona white on terracotta */
.poster[data-station="bengali"] {
  background-image:
    radial-gradient(45% 40% at 22% 78%, rgba(230, 210, 180, 0.14), transparent 60%),
    radial-gradient(55% 45% at 78% 22%, rgba(190, 80, 45, 0.3), transparent 62%),
    linear-gradient(165deg, #221410, #130d0b);
}

/* Mysore sandal + gold */
.poster[data-station="kannada"] {
  background-image:
    radial-gradient(50% 42% at 30% 30%, rgba(215, 175, 90, 0.3), transparent 60%),
    radial-gradient(50% 40% at 78% 75%, rgba(120, 90, 40, 0.35), transparent 62%),
    linear-gradient(160deg, #201a11, #120f0c);
}

/* Backwater green */
.poster[data-station="malayalam"] {
  background-image:
    radial-gradient(55% 45% at 25% 25%, rgba(70, 150, 110, 0.3), transparent 60%),
    radial-gradient(50% 40% at 80% 80%, rgba(30, 80, 70, 0.4), transparent 62%),
    linear-gradient(175deg, #101a16, #0d100f);
}

/* Bandhani pink + mirror-work */
.poster[data-station="gujarati"] {
  background-image:
    repeating-conic-gradient(
      from 30deg at 70% 30%,
      rgba(255, 255, 255, 0.05) 0deg 4deg,
      transparent 4deg 20deg
    ),
    radial-gradient(50% 42% at 25% 70%, rgba(200, 70, 120, 0.3), transparent 60%),
    linear-gradient(150deg, #22121c, #140c11);
}

/* Ganga clay + brass */
.poster[data-station="bhojpuri"] {
  background-image:
    radial-gradient(55% 45% at 30% 20%, rgba(210, 150, 70, 0.28), transparent 60%),
    linear-gradient(165deg, #221a11, #130f0b);
}

/* Pattachitra ochre */
.poster[data-station="odia"] {
  background-image:
    radial-gradient(50% 42% at 75% 25%, rgba(215, 150, 60, 0.28), transparent 60%),
    radial-gradient(50% 40% at 20% 80%, rgba(140, 60, 40, 0.32), transparent 62%),
    linear-gradient(155deg, #20150f, #120d0a);
}

/* Bihu green + river mist */
.poster[data-station="assamese"] {
  background-image:
    radial-gradient(55% 45% at 25% 30%, rgba(90, 150, 90, 0.28), transparent 60%),
    radial-gradient(50% 40% at 80% 78%, rgba(40, 90, 80, 0.35), transparent 62%),
    linear-gradient(170deg, #121a14, #0e100e);
}

/* Desert dusk */
.poster[data-station="rajasthani"] {
  background-image:
    radial-gradient(60% 45% at 50% 12%, rgba(240, 170, 80, 0.3), transparent 62%),
    radial-gradient(60% 50% at 50% 95%, rgba(150, 60, 60, 0.32), transparent 60%),
    linear-gradient(180deg, #241609, #140d09);
}

/* Ghazal — smoke and one lamp */
.poster[data-station="urdu"] {
  background-image:
    radial-gradient(35% 30% at 50% 25%, rgba(230, 200, 140, 0.22), transparent 65%),
    linear-gradient(185deg, #161311, #0d0c0b);
}

/* ── per-station ambience ──
   Each language gets its own weather, not just its own colour. Same CSS-only
   rule as the posters: nothing downloaded, nothing licensed, and it costs
   about a kilobyte. All of it stops under prefers-reduced-motion. */

/* Monsoon rain — Hindi. Two sheets at different speeds so it reads as depth
   rather than as a moving texture. */
body[data-amb="rain"] .motes::before,
body[data-amb="rain"] .motes::after {
  content: "";
  position: absolute;
  inset: -20% -10%;
  background-image: repeating-linear-gradient(
    102deg,
    rgba(245, 234, 215, 0.16) 0px,
    rgba(245, 234, 215, 0.16) 1px,
    transparent 1px,
    transparent 22px
  );
  animation: rain 0.9s linear infinite;
  opacity: 0.25;
}

body[data-amb="rain"] .motes::after {
  animation-duration: 1.5s;
  opacity: 0.14;
  transform: scaleX(-1);
}

@keyframes rain {
  to {
    transform: translateY(22%) translateX(-6%);
  }
}

/* Temple lamp flicker — Tamil, Telugu, Kannada, Malayalam. */
body[data-amb="lamp"] .motes::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 8%;
  width: 60vmin;
  height: 60vmin;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 190, 90, 0.2), transparent 62%);
  animation: flicker 4.5s ease-in-out infinite;
}

@keyframes flicker {
  0%,
  100% {
    opacity: 0.55;
    transform: translateX(-50%) scale(1);
  }
  38% {
    opacity: 0.9;
    transform: translateX(-50%) scale(1.04);
  }
  62% {
    opacity: 0.62;
    transform: translateX(-50%) scale(0.98);
  }
}

/* Wheat-field sway — Punjabi, Bhojpuri, Rajasthani. A slow horizontal drift,
   like heat moving across a field. */
body[data-amb="field"] .motes::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    88deg,
    rgba(240, 169, 60, 0.06) 0px,
    rgba(240, 169, 60, 0.06) 3px,
    transparent 3px,
    transparent 34px
  );
  animation: sway 11s ease-in-out infinite alternate;
}

@keyframes sway {
  to {
    transform: translateX(26px) skewX(-1.5deg);
  }
}

/* River mist — Bengali, Assamese, Odia. */
body[data-amb="mist"] .motes::before,
body[data-amb="mist"] .motes::after {
  content: "";
  position: absolute;
  left: -20%;
  right: -20%;
  height: 42vh;
  background: radial-gradient(ellipse at 50% 60%, rgba(200, 220, 230, 0.09), transparent 68%);
  animation: drift 26s ease-in-out infinite alternate;
}

body[data-amb="mist"] .motes::before {
  bottom: -6%;
}

body[data-amb="mist"] .motes::after {
  bottom: 26%;
  animation-duration: 38s;
  animation-direction: alternate-reverse;
  opacity: 0.7;
}

@keyframes drift {
  to {
    transform: translateX(9%) translateY(-3%);
  }
}

/* Garba mirror-work — Gujarati. Slow rotating glints, never a strobe. */
body[data-amb="mirror"] .motes::before {
  content: "";
  position: absolute;
  inset: -30%;
  background: repeating-conic-gradient(
    from 0deg,
    rgba(255, 255, 255, 0.055) 0deg 3deg,
    transparent 3deg 26deg
  );
  animation: turn 90s linear infinite;
}

@keyframes turn {
  to {
    transform: rotate(360deg);
  }
}

/* Ghazal smoke — Urdu. One lamp, and something moving very slowly in front. */
body[data-amb="smoke"] .motes::before {
  content: "";
  position: absolute;
  inset: -10%;
  background:
    radial-gradient(ellipse at 30% 70%, rgba(245, 234, 215, 0.055), transparent 58%),
    radial-gradient(ellipse at 70% 40%, rgba(245, 234, 215, 0.04), transparent 62%);
  animation: curl 34s ease-in-out infinite alternate;
}

@keyframes curl {
  to {
    transform: translateY(-5%) translateX(4%) scale(1.08);
  }
}

/* Marathi keeps the plain motes: the Warli poster is already busy. */

/* Neon sign hum — English. A colour that belongs to a bar at 1am rather than
   to any Indian station, so the shift is felt as soon as you tune in. */
body[data-amb="neon"] .motes::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(40% 30% at 22% 26%, rgba(120, 160, 255, 0.12), transparent 62%),
    radial-gradient(38% 30% at 78% 72%, rgba(255, 90, 160, 0.1), transparent 62%);
  animation: hum 9s ease-in-out infinite alternate;
}

@keyframes hum {
  to {
    opacity: 0.6;
    transform: translateY(-2%) scale(1.03);
  }
}

/* This must stay BELOW every ambience rule. It sat above the neon block, and
   with equal specificity the later declaration won — so English alone kept
   animating for people who had asked for stillness. */
@media (prefers-reduced-motion: reduce) {
  body[data-amb] .motes::before,
  body[data-amb] .motes::after {
    animation: none;
  }
}

/* ── the hour of the day ──
   A wash over the station poster, not a replacement for it: soft-light keeps
   each language's colour memory underneath and only changes the light falling
   on it. Marigold Hindi at dawn is still marigold, just cooler and lower.
   Same rule as everything else here — drawn in CSS, nothing downloaded. */

.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Screen, not soft-light: the posters bottom out near #171310, and soft-light
     leaves dark pixels alone, so the whole layer was invisible on the very
     backgrounds it exists to tint. Screen lifts colour into the dark. */
  mix-blend-mode: screen;
  opacity: 0.5;
  /* Long, because the change should be noticed only if you look away and come
     back — the way actual daylight moves. */
  transition:
    background 2.4s ease,
    opacity 2.4s ease;
}

/* The one bright body in the sky, low at the edges of the day and high in the
   middle of it. Position and warmth are set per period below. */
.sky::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    var(--sun-size, 34% 26%) at var(--sun-x, 50%) var(--sun-y, 18%),
    var(--sun-tint, rgba(255, 214, 150, 0.5)),
    transparent 68%
  );
  transition:
    background 2.4s ease;
}

/* 5–7 · first light, still cold underneath, rose on top */
body[data-time="dawn"] .sky {
  --sun-x: 18%;
  --sun-y: 74%;
  --sun-size: 42% 30%;
  --sun-tint: rgba(255, 168, 140, 0.42);
  background: linear-gradient(180deg, rgba(90, 120, 175, 0.4), rgba(255, 180, 145, 0.22) 72%, transparent);
}

/* 7–11 · clean morning gold, the clearest hour */
body[data-time="morning"] .sky {
  --sun-x: 32%;
  --sun-y: 22%;
  --sun-size: 34% 26%;
  --sun-tint: rgba(255, 226, 170, 0.5);
  background: linear-gradient(180deg, rgba(190, 210, 240, 0.34), transparent 66%);
}

/* 11–16 · high, flat, bleached — the least atmospheric hour, deliberately */
body[data-time="afternoon"] .sky {
  --sun-x: 50%;
  --sun-y: 8%;
  --sun-size: 30% 24%;
  --sun-tint: rgba(255, 245, 220, 0.42);
  background: linear-gradient(180deg, rgba(225, 232, 240, 0.28), transparent 58%);
  opacity: 0.42;
}

/* 16–19 · the long light. The hour these songs were written for. */
body[data-time="dusk"] .sky {
  --sun-x: 84%;
  --sun-y: 78%;
  --sun-size: 48% 34%;
  --sun-tint: rgba(255, 146, 78, 0.5);
  background: linear-gradient(180deg, rgba(120, 90, 150, 0.42), rgba(255, 140, 90, 0.26) 78%, transparent);
}

/* 19–23 · lamps on, sky gone indigo */
body[data-time="night"] .sky {
  --sun-x: 70%;
  --sun-y: 16%;
  --sun-size: 22% 18%;
  --sun-tint: rgba(210, 225, 255, 0.3);
  background: linear-gradient(180deg, rgba(28, 38, 82, 0.62), rgba(40, 30, 60, 0.34) 70%, transparent);
}

/* 23–5 · nobody is awake. Coldest and darkest, with stars. */
body[data-time="latenight"] .sky {
  --sun-x: 24%;
  --sun-y: 14%;
  --sun-size: 16% 13%;
  --sun-tint: rgba(200, 220, 255, 0.26);
  background: linear-gradient(180deg, rgba(10, 16, 44, 0.72), rgba(16, 18, 40, 0.44) 74%, transparent);
  opacity: 0.58;
}

/* Stars only in the small hours, and only a few — a dense field would read as
   a screensaver. */
body[data-time="latenight"] .sky::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, rgba(255, 255, 255, 0.75), transparent),
    radial-gradient(1.5px 1.5px at 68% 12%, rgba(255, 255, 255, 0.6), transparent),
    radial-gradient(1px 1px at 34% 30%, rgba(255, 255, 255, 0.5), transparent),
    radial-gradient(1.5px 1.5px at 82% 34%, rgba(255, 255, 255, 0.65), transparent),
    radial-gradient(1px 1px at 52% 8%, rgba(255, 255, 255, 0.45), transparent),
    radial-gradient(1px 1px at 90% 20%, rgba(255, 255, 255, 0.5), transparent),
    radial-gradient(1.5px 1.5px at 6% 40%, rgba(255, 255, 255, 0.5), transparent);
  animation: twinkle 6s ease-in-out infinite alternate;
}

@keyframes twinkle {
  to {
    opacity: 0.45;
  }
}

@media (prefers-reduced-motion: reduce) {
  body[data-time="latenight"] .sky::after {
    animation: none;
  }
}

/* ── weather, and the edges of the room ──
   Two jobs in one element. ::before is slow cloud, drifting across the whole
   sky on a cycle long enough that you never catch it moving — you only notice
   the sky is different from when you last looked. ::after is the vignette,
   which has to be its own layer because darkening edges needs multiply and the
   sky above it is screening. */

.haze {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}

.haze::before {
  content: "";
  position: absolute;
  /* Wider than the screen so the drift never reveals an edge. */
  inset: -20% -30%;
  background:
    radial-gradient(38% 26% at 18% 22%, rgba(255, 240, 220, 0.1), transparent 70%),
    radial-gradient(46% 30% at 62% 14%, rgba(255, 236, 214, 0.08), transparent 72%),
    radial-gradient(34% 22% at 84% 40%, rgba(255, 244, 228, 0.07), transparent 70%),
    radial-gradient(50% 28% at 40% 66%, rgba(255, 238, 220, 0.06), transparent 74%);
  mix-blend-mode: screen;
  opacity: 0.5;
  animation: drift 190s linear infinite;
  transition: opacity 1.6s ease;
}

/* The room leans in a little once the music is actually on. */
body.playing .haze::before {
  opacity: 0.85;
}

@keyframes drift {
  to {
    transform: translateX(12%) translateY(-3%);
  }
}

/* Cloud takes the colour of the hour: warm and low at dusk, thin and cold in
   the small hours, almost gone at midday when the sky is flat. */
body[data-time="dawn"] .haze::before {
  filter: hue-rotate(-14deg) saturate(1.3);
}

body[data-time="afternoon"] .haze::before {
  opacity: 0.28;
}

body.playing[data-time="afternoon"] .haze::before {
  opacity: 0.45;
}

body[data-time="dusk"] .haze::before {
  filter: hue-rotate(-22deg) saturate(1.5);
}

body[data-time="night"] .haze::before,
body[data-time="latenight"] .haze::before {
  filter: hue-rotate(178deg) saturate(0.85);
  opacity: 0.34;
}

body.playing[data-time="night"] .haze::before,
body.playing[data-time="latenight"] .haze::before {
  opacity: 0.55;
}

/* Darkened corners. Keeps the eye on the record and hides the fact that a flat
   gradient has no horizon. */
.haze::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    120% 90% at 50% 42%,
    transparent 40%,
    rgba(0, 0, 0, 0.34) 78%,
    rgba(0, 0, 0, 0.62) 100%
  );
}

@media (prefers-reduced-motion: reduce) {
  .haze::before {
    animation: none;
  }
}

/* ── switched effects ──
   Everything below is off until a data attribute turns it on, so a station
   that wants none of it pays nothing. All CSS, same as the rest: no canvas, no
   library, no request. */

.fx {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}

.fx > * {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.2s ease;
}

/* A ceiling fan turning above the room. Four faint blades, one sweep every
   three seconds — the shadow you stop seeing in an Indian afternoon, which is
   exactly why it belongs under the music rather than in front of it. */
.fx-fan {
  background: repeating-conic-gradient(
    from 0deg at 50% 34%,
    rgba(0, 0, 0, 0.16) 0deg 10deg,
    transparent 10deg 90deg
  );
  animation: fan 3.1s linear infinite;
}

body.playing[data-time="afternoon"] .fx-fan,
body.playing[data-time="morning"] .fx-fan {
  opacity: 0.5;
}

@keyframes fan {
  to {
    transform: rotate(360deg);
  }
}

/* Monsoon lightning. Long gaps and a double flash, because a metronomic
   strobe reads as a broken screen and is unbearable for an hour. */
.fx-flash {
  background: linear-gradient(180deg, rgba(198, 220, 255, 0.5), rgba(198, 220, 255, 0.06) 46%, transparent 72%);
}

body.playing[data-amb="rain"] .fx-flash {
  animation: flash 17s ease-out infinite;
}

@keyframes flash {
  0%,
  92%,
  100% {
    opacity: 0;
  }
  93% {
    opacity: 0.5;
  }
  94% {
    opacity: 0.06;
  }
  95.5% {
    opacity: 0.34;
  }
  97% {
    opacity: 0;
  }
}

/* Fireflies over the fields — Punjabi, Bhojpuri, Rajasthani. */
body.playing[data-amb="field"] .fx-flies {
  opacity: 1;
}

.fx-flies span {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #ffe9a8;
  box-shadow: 0 0 6px 2px rgba(255, 226, 150, 0.5);
  opacity: 0;
  animation-name: firefly;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
}

@keyframes firefly {
  0%,
  100% {
    opacity: 0;
    transform: translate(0, 0);
  }
  20% {
    opacity: 0.85;
  }
  50% {
    opacity: 0.25;
    transform: translate(26px, -32px);
  }
  80% {
    opacity: 0.7;
    transform: translate(-14px, -54px);
  }
}

/* Projector grain. The gate weave of a print that has been run too many
   times — the reason these songs sound like a cinema and not a file. */
.fx-grain {
  background-image: repeating-linear-gradient(
      112deg,
      rgba(255, 255, 255, 0.045) 0px,
      rgba(255, 255, 255, 0.045) 1px,
      transparent 1px,
      transparent 4px
    ),
    repeating-linear-gradient(
      8deg,
      rgba(0, 0, 0, 0.05) 0px,
      rgba(0, 0, 0, 0.05) 1px,
      transparent 1px,
      transparent 6px
    );
  mix-blend-mode: overlay;
  animation: weave 5.5s steps(6, end) infinite;
}

body.playing .fx-grain {
  opacity: 0.55;
}

/* Heavier in the dark, when a room actually looks like a projection. */
body.playing[data-time="night"] .fx-grain,
body.playing[data-time="latenight"] .fx-grain {
  opacity: 0.85;
}

@keyframes weave {
  0% {
    transform: translate(0, 0);
  }
  25% {
    transform: translate(-1px, 1px);
  }
  50% {
    transform: translate(1px, -1px);
  }
  75% {
    transform: translate(-1px, -1px);
  }
  100% {
    transform: translate(0, 0);
  }
}

/* ── drift inside the hour ──
   Six periods are six looks, and after a while a listener who leaves the radio
   on all evening sees the same "dusk" for three hours. These two cycles are far
   longer than anyone watches for — ten minutes and half an hour — so nothing
   ever appears to move; the sky is just never quite where you left it.
   Only while playing: a paused page should sit still. */

body.playing .sky {
  animation: settle 600s ease-in-out infinite;
}

@keyframes settle {
  0%,
  100% {
    filter: hue-rotate(0deg) saturate(1);
  }
  50% {
    filter: hue-rotate(-7deg) saturate(1.09);
  }
}

/* The sun and moon do not hold still for four hours either. A few percent is
   enough — any more and it reads as an animation rather than as time. */
body.playing .sky::before {
  animation: sunwalk 1800s linear infinite alternate;
}

@keyframes sunwalk {
  to {
    transform: translate(3.5%, -1.8%) scale(1.06);
  }
}

/* Stillness means stillness: the fan, the lightning and the grain are the
   three most likely to bother someone who asked for it. */
@media (prefers-reduced-motion: reduce) {
  .fx-fan,
  .fx-flash,
  .fx-grain,
  .fx-flies span,
  body.playing .sky,
  body.playing .sky::before {
    animation: none;
  }

  .fx-flash {
    opacity: 0 !important;
  }
}
