/* ===================================================================
   KNMS DYNAMIC SCENE — layered city/village parallax with day/night
   -------------------------------------------------------------------
   A composite backdrop: a distant city skyline on the left of the frame
   hands over, across a haze band and a flyover, to a village with a
   gopuram and paddy fields on the right. Each band is its own element
   moving at its own rate under scroll and pointer, which is where the
   depth comes from.

   HOW THE COLOUR WORKS — read this before editing anything below.
   The silhouette layers are NOT coloured pictures. Each one is a CSS
   gradient wearing an SVG *mask* from assets/img/scene/. That indirection
   is the whole design: it means one set of artwork can be violet at
   midnight, rose at sunrise and hazy blue at noon, and can transition
   between those smoothly, which baked-in SVG colours could never do.

   The transition itself needs @property. Plain custom properties are
   not interpolable, so `transition: background 1s` on a gradient does
   nothing; registering each colour with @property makes it animatable
   and the gradient is rebuilt each frame. Browsers without @property
   (pre-2023) still get every phase — they just snap between them
   instead of easing, which is a fair degradation.

   Loaded on any page that renders a scene. Everything is namespaced
   `kn-scene` / `kn-layer` / `--sc-*`.
   =================================================================== */

/* --- Registered, interpolable phase tokens --------------------------
   Initial values are the DUSK palette, which is what the site looked
   like before this file existed — so a scene that never gets a phase
   attribute still lands somewhere deliberate. */

@property --sc-sky-1     { syntax: "<color>"; inherits: true; initial-value: #2a0f63; }
@property --sc-sky-2     { syntax: "<color>"; inherits: true; initial-value: #5a2192; }
@property --sc-sky-3     { syntax: "<color>"; inherits: true; initial-value: #a83f8f; }
@property --sc-sky-4     { syntax: "<color>"; inherits: true; initial-value: #ef7a34; }
@property --sc-sky-5     { syntax: "<color>"; inherits: true; initial-value: #ffc555; }

@property --sc-far-top   { syntax: "<color>"; inherits: true; initial-value: #5b3780; }
@property --sc-far-bot   { syntax: "<color>"; inherits: true; initial-value: #3a1f5e; }
@property --sc-near-top  { syntax: "<color>"; inherits: true; initial-value: #3b2159; }
@property --sc-near-bot  { syntax: "<color>"; inherits: true; initial-value: #24123c; }
@property --sc-vill-top  { syntax: "<color>"; inherits: true; initial-value: #2c1846; }
@property --sc-vill-bot  { syntax: "<color>"; inherits: true; initial-value: #180c2c; }
@property --sc-field-top { syntax: "<color>"; inherits: true; initial-value: #1d1030; }
@property --sc-field-bot { syntax: "<color>"; inherits: true; initial-value: #0d0620; }

@property --sc-cloud     { syntax: "<color>"; inherits: true; initial-value: #ffb27a; }
@property --sc-haze      { syntax: "<color>"; inherits: true; initial-value: #ff9a3c; }
@property --sc-sun       { syntax: "<color>"; inherits: true; initial-value: #ffd98a; }
@property --sc-light     { syntax: "<color>"; inherits: true; initial-value: #ffcf7a; }
@property --sc-star      { syntax: "<color>"; inherits: true; initial-value: #dfe6ff; }

/* Numeric tokens. Registered for the same reason: a phase change should
   fade the stars in, not switch them on. */
@property --sc-star-o    { syntax: "<number>"; inherits: true; initial-value: 0.25; }
@property --sc-light-o   { syntax: "<number>"; inherits: true; initial-value: 0.55; }
@property --sc-moon-o    { syntax: "<number>"; inherits: true; initial-value: 0.2;  }
@property --sc-sun-o     { syntax: "<number>"; inherits: true; initial-value: 1;    }
@property --sc-mist-o    { syntax: "<number>"; inherits: true; initial-value: 0.18; }
@property --sc-fire-o    { syntax: "<number>"; inherits: true; initial-value: 0;    }
@property --sc-haze-o    { syntax: "<number>"; inherits: true; initial-value: 0.5;  }
@property --sc-bird-o    { syntax: "<number>"; inherits: true; initial-value: 0.3;  }
/* Occupants of the sky. Each is strongest in the phase it belongs to and
   fades to nothing in the phases it does not — sun rays at the low-sun hours,
   a kite by day, an aircraft after dark, cloud shadows only when there is sun
   overhead to cast them. */
@property --sc-ray-o     { syntax: "<number>"; inherits: true; initial-value: 0.4;  }
@property --sc-kite-o    { syntax: "<number>"; inherits: true; initial-value: 0.35; }
@property --sc-plane-o   { syntax: "<number>"; inherits: true; initial-value: 0.5;  }
@property --sc-shadow-o  { syntax: "<number>"; inherits: true; initial-value: 0.08; }
/* Sun/moon travel along an arc; x is the horizontal station, y the height
   above the horizon. Both are percentages of the scene box. */
@property --sc-sun-x     { syntax: "<percentage>"; inherits: true; initial-value: 74%; }
@property --sc-sun-y     { syntax: "<percentage>"; inherits: true; initial-value: 62%; }
@property --sc-moon-x    { syntax: "<percentage>"; inherits: true; initial-value: 22%; }
@property --sc-moon-y    { syntax: "<percentage>"; inherits: true; initial-value: 30%; }

/* ===================================================================
   1. Scene container
   =================================================================== */

.kn-scene {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    /* Its own stacking context: the layers use z-index freely and must not
       interleave with whatever the host page stacks around them. */
    isolation: isolate;
    /* Parallax offsets, written by scene.js once per frame on THIS element
       only. Every layer derives its own shift from these two numbers and its
       own --d, so a scroll costs one style write rather than one per layer. */
    --kn-sy: 0px;
    --kn-mx: 0px;
    /* How far a layer at depth 1.0 would travel. Individual layers scale this
       by --d, so the near fields move most and the far skyline barely at all. */
    --sc-bleed: 96px;

    transition:
        --sc-sky-1 1600ms ease, --sc-sky-2 1600ms ease, --sc-sky-3 1600ms ease,
        --sc-sky-4 1600ms ease, --sc-sky-5 1600ms ease,
        --sc-far-top 1600ms ease, --sc-far-bot 1600ms ease,
        --sc-near-top 1600ms ease, --sc-near-bot 1600ms ease,
        --sc-vill-top 1600ms ease, --sc-vill-bot 1600ms ease,
        --sc-field-top 1600ms ease, --sc-field-bot 1600ms ease,
        --sc-cloud 1600ms ease, --sc-haze 1600ms ease, --sc-sun 1600ms ease,
        --sc-light 1600ms ease, --sc-star 1600ms ease,
        --sc-star-o 1600ms ease, --sc-light-o 1800ms ease, --sc-moon-o 1600ms ease,
        --sc-sun-o 1600ms ease, --sc-mist-o 1600ms ease, --sc-fire-o 1800ms ease,
        --sc-haze-o 1600ms ease, --sc-bird-o 1600ms ease,
        --sc-sun-x 2400ms ease, --sc-sun-y 2400ms ease,
        --sc-moon-x 2400ms ease, --sc-moon-y 2400ms ease;
}

/* ===================================================================
   2. Phase palettes
   -------------------------------------------------------------------
   Four phases, set as data-phase by scene.php on first paint (server
   clock) and corrected by scene.js to the visitor's own clock. Server
   first so there is no flash of the wrong time of day.

   The silhouettes get LIGHTER and LOWER in contrast towards dawn/day —
   that is aerial perspective, and it is what makes the city read as
   genuinely distant rather than as a dark sticker on a bright sky.
   =================================================================== */

/* ---- DAWN — the village sunrise. Sun low and right, over the fields,
        mist in the hollows, city still holding a few lights. --------- */
.kn-scene[data-phase="dawn"] {
    /* Warmer and lighter than the first pass, which sat so close to the dusk
       palette that the two phases were hard to tell apart. Sunrise differs
       from sunset by being CLEANER — less red, more gold, and the zenith
       already going blue rather than still violet. */
    --sc-sky-1: #23407e;  --sc-sky-2: #5a63a6;  --sc-sky-3: #b183ac;
    --sc-sky-4: #f7ac70;  --sc-sky-5: #ffe0ad;
    --sc-far-top: #7d7ba6; --sc-far-bot: #5b567f;
    --sc-near-top: #5d5480; --sc-near-bot: #3e3660;
    --sc-vill-top: #4a3d63; --sc-vill-bot: #2b2344;
    --sc-field-top: #3a3050; --sc-field-bot: #1e1834;
    --sc-cloud: #ffc9a8;  --sc-haze: #ffbf86;  --sc-sun: #fff0c4;
    --sc-light: #ffd9a0;  --sc-star: #cdd6ff;
    --sc-star-o: 0.12; --sc-light-o: 0.3; --sc-moon-o: 0.1; --sc-sun-o: 1;
    --sc-mist-o: 0.5; --sc-fire-o: 0; --sc-haze-o: 0.6; --sc-bird-o: 0.85;
    /* Rays are at their strongest here: low sun plus the mist it is shining
       through is exactly the condition that makes beams visible at all. */
    --sc-ray-o: 0.55; --sc-kite-o: 0.45; --sc-plane-o: 0.3; --sc-shadow-o: 0.1;
    /* Half-risen behind the mid-rise band, in the gap between the city and
       the village — the one place in the frame where the skyline is low
       enough for the disc to clear it. Parked over the village proper it
       hid behind the gopuram; parked over the city it never rose at all. */
    --sc-sun-x: 56%; --sc-sun-y: 40%;
    --sc-moon-x: 14%; --sc-moon-y: 22%;
}

/* ---- DAY — flat overhead light, heat haze, everything blue-grey. --- */
.kn-scene[data-phase="day"] {
    --sc-sky-1: #1d5fc4;  --sc-sky-2: #3f8ada;  --sc-sky-3: #74b3ee;
    --sc-sky-4: #a9d2f6;  --sc-sky-5: #dceaf8;
    /* The spread from far to near is much wider than in the dark phases.
       Daylight is when aerial perspective is strongest: the distant city
       genuinely is pale blue-grey and the near field genuinely is dark, and
       compressing that range was what made the first daytime pass read as
       fog rather than as noon. */
    --sc-far-top: #7e93b8; --sc-far-bot: #5f7398;
    --sc-near-top: #4e6188; --sc-near-bot: #334366;
    --sc-vill-top: #44507a; --sc-vill-bot: #26325a;
    --sc-field-top: #2f3a60; --sc-field-bot: #182040;
    --sc-cloud: #ffffff;  --sc-haze: #cfe3f5;  --sc-sun: #fffdf0;
    --sc-light: #ffe9b8;  --sc-star: #ffffff;
    --sc-star-o: 0; --sc-light-o: 0; --sc-moon-o: 0; --sc-sun-o: 1;
    /* Haze and mist pulled right back. Screen-blended warmth over an already
       pale sky has nowhere to go but white, and at the old values it bleached
       the whole frame. */
    --sc-mist-o: 0.03; --sc-fire-o: 0; --sc-haze-o: 0.13; --sc-bird-o: 0.55;
    /* Day was the flattest phase — no lights, no fireflies, no mist and a
       high sun that casts no beams. The kite and the cloud shadows are what
       give it something to watch. */
    --sc-ray-o: 0.14; --sc-kite-o: 1; --sc-plane-o: 0.35; --sc-shadow-o: 0.3;
    --sc-sun-x: 50%; --sc-sun-y: 16%;
    --sc-moon-x: 8%;  --sc-moon-y: 12%;
}

/* ---- DUSK — the city's golden hour falling into violet. ------------ */
.kn-scene[data-phase="dusk"] {
    --sc-sky-1: #2a0f63;  --sc-sky-2: #5a2192;  --sc-sky-3: #a83f8f;
    --sc-sky-4: #ef7a34;  --sc-sky-5: #ffc555;
    --sc-far-top: #5b3780; --sc-far-bot: #3a1f5e;
    --sc-near-top: #3b2159; --sc-near-bot: #24123c;
    --sc-vill-top: #2c1846; --sc-vill-bot: #180c2c;
    --sc-field-top: #1d1030; --sc-field-bot: #0d0620;
    --sc-cloud: #ffb27a;  --sc-haze: #ff9a3c;  --sc-sun: #ffd98a;
    --sc-light: #ffcf7a;  --sc-star: #dfe6ff;
    --sc-star-o: 0.3; --sc-light-o: 0.5; --sc-moon-o: 0.25; --sc-sun-o: 0.95;
    --sc-mist-o: 0.16; --sc-fire-o: 0.25; --sc-haze-o: 0.62; --sc-bird-o: 0.5;
    --sc-ray-o: 0.42; --sc-kite-o: 0.3; --sc-plane-o: 0.6; --sc-shadow-o: 0.08;
    /* Setting behind the towers, and meant to stay there. The disc is
       occluded by the skyline at this station — what the visitor sees is the
       corona and the horizon glow rimming the buildings, which is what a
       backlit city at sunset actually looks like. */
    --sc-sun-x: 20%; --sc-sun-y: 58%;
    --sc-moon-x: 80%; --sc-moon-y: 26%;
}

/* ---- NIGHT — the city's hour. Windows blazing, village on oil lamps,
        fireflies over the fields, the sky finally dark enough for stars. */
.kn-scene[data-phase="night"] {
    --sc-sky-1: #05071c;  --sc-sky-2: #0c1236;  --sc-sky-3: #172050;
    --sc-sky-4: #232f66;  --sc-sky-5: #3a3f74;
    --sc-far-top: #1a2149; --sc-far-bot: #0e1230;
    --sc-near-top: #10142f; --sc-near-bot: #070a1e;
    --sc-vill-top: #0c0f28; --sc-vill-bot: #05061a;
    --sc-field-top: #080a1e; --sc-field-bot: #030410;
    --sc-cloud: #4a5490;  --sc-haze: #3d63b0;  --sc-sun: #ffe6b0;
    --sc-light: #ffc861;  --sc-star: #eaf0ff;
    --sc-star-o: 1; --sc-light-o: 0.92; --sc-moon-o: 1; --sc-sun-o: 0;
    --sc-mist-o: 0.1; --sc-fire-o: 1; --sc-haze-o: 0.4; --sc-bird-o: 0;
    /* No sun, so no rays and no cloud shadows; nobody flies a kite after
       dark. The aircraft is at its most visible instead — a blinking beacon
       crossing a black sky is the whole point of it. */
    --sc-ray-o: 0; --sc-kite-o: 0; --sc-plane-o: 1; --sc-shadow-o: 0;
    --sc-sun-x: 4%;  --sc-sun-y: 96%;
    --sc-moon-x: 68%; --sc-moon-y: 20%;
}

/* ===================================================================
   3. Sky, celestial bodies, horizon glow
   =================================================================== */

.kn-scene__sky {
    position: absolute;
    inset: 0;
    z-index: 0;
    background: linear-gradient(
        180deg,
        var(--sc-sky-1) 0%,
        var(--sc-sky-2) 26%,
        var(--sc-sky-3) 48%,
        var(--sc-sky-4) 66%,
        var(--sc-sky-5) 78%,
        var(--sc-sky-1) 100%
    );
}

/* Stars. Masked so their colour follows the phase — a star field baked
   white stays white against a dawn sky and reads as dirt on the screen. */
.kn-scene__stars {
    position: absolute;
    inset: -4% -2% 20%;
    z-index: 1;
    background: var(--sc-star);
    opacity: var(--sc-star-o);
    -webkit-mask: url("../img/scene/stars.svg") center bottom / 100% 100% no-repeat;
            mask: url("../img/scene/stars.svg") center bottom / 100% 100% no-repeat;
    /* The whole field creeps, the way the sky actually turns. Far too slow to
       watch — the point is that a visitor who leaves the page open does not
       come back to an identical sky. The travel is held well inside the 2%
       horizontal bleed on this element so no edge can be pulled into frame. */
    animation: kn-starfield 260s ease-in-out infinite;
}
@keyframes kn-starfield {
    0%, 100% { transform: translate3d(0, 0, 0); }
    50%      { transform: translate3d(-1.1%, 0.5%, 0); }
}
/* Two blink rhythms, each carrying its OWN set of stars.
   -------------------------------------------------------------------
   The masks here are stars-blink-a/b.svg, not stars.svg, and that is the
   whole point. Every one of these layers paints the same flat colour inside
   the same parent, and painting an opaque colour over the identical opaque
   colour changes nothing — so when all three shared stars.svg the animation
   ran flawlessly and produced no visible twinkle whatever. A star can only be
   seen to blink if it is absent from the steady field, which is why the three
   files are disjoint.

   The mask URL is written out rather than inherited: `mask: inherit` on a
   pseudo-element is honoured unevenly across engines, and a pseudo that
   silently loses its mask paints as a full-bleed rectangle of star colour
   over the sky — a spectacular failure for a one-word saving. */
.kn-scene__stars::before,
.kn-scene__stars::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--sc-star);
    -webkit-mask-repeat: no-repeat;   mask-repeat: no-repeat;
    -webkit-mask-position: center bottom; mask-position: center bottom;
    -webkit-mask-size: 100% 100%;     mask-size: 100% 100%;
}
.kn-scene__stars::before {
    -webkit-mask-image: url("../img/scene/stars-blink-a.svg");
            mask-image: url("../img/scene/stars-blink-a.svg");
    animation: kn-twinkle 4.2s ease-in-out infinite;
}
.kn-scene__stars::after {
    -webkit-mask-image: url("../img/scene/stars-blink-b.svg");
            mask-image: url("../img/scene/stars-blink-b.svg");
    /* Longer period and a negative delay, so the two sets are already out of
       step on the first frame and never come back into phase. */
    animation: kn-twinkle 6.7s ease-in-out infinite;
    animation-delay: -2.3s;
}

/* Down to almost nothing and back to full. A shallow ramp is what made the
   first version read as a slow brightness wobble rather than as stars
   actually blinking. */
@keyframes kn-twinkle {
    0%, 100% { opacity: 0.05; }
    45%      { opacity: 1; }
    60%      { opacity: 0.72; }
}

/* The sun: a hot core inside a broad bloom. Two elements' worth of gradient
   in one box, so it stays a single compositable layer. */
.kn-scene__sun,
.kn-scene__moon {
    position: absolute;
    z-index: 2;
    width: 22vmax;
    height: 22vmax;
    max-width: 340px;
    max-height: 340px;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
}
.kn-scene__sun {
    left: var(--sc-sun-x);
    top: var(--sc-sun-y);
    opacity: var(--sc-sun-o);
    background:
        radial-gradient(circle, var(--sc-sun) 0%, var(--sc-sun) 16%,
                        color-mix(in srgb, var(--sc-sun) 70%, transparent) 24%,
                        transparent 62%);
    filter: blur(0.4px);
    animation: kn-sun-breathe 9s ease-in-out infinite;
}
/* The corona is a separate, much larger and much softer pool. Trying to do
   both from one gradient forces a compromise: either the disc has no edge or
   the glow has a visible rim. */
.kn-scene__sun::after {
    content: '';
    position: absolute;
    inset: -120%;
    border-radius: 50%;
    background: radial-gradient(circle,
        color-mix(in srgb, var(--sc-sun) 45%, transparent) 0%,
        color-mix(in srgb, var(--sc-haze) 22%, transparent) 34%,
        transparent 70%);
    animation: kn-sun-breathe 13s ease-in-out infinite reverse;
}
.kn-scene__moon {
    left: var(--sc-moon-x);
    top: var(--sc-moon-y);
    opacity: var(--sc-moon-o);
    width: 12vmax; height: 12vmax; max-width: 168px; max-height: 168px;
    /* The disc holds its colour to 38% and falls away over four percent, so
       it has an actual edge. Faded out gradually — as it was — the moon read
       as a smudge on the lens rather than as a body, and at the low opacity
       it carries at dusk that is all anyone saw. */
    background:
        radial-gradient(circle at 42% 40%, #f8faff 0%, #e6ebff 38%,
                        rgba(206, 218, 255, 0.5) 42%, rgba(180, 196, 255, 0.16) 52%,
                        transparent 72%);
}
/* Craters — three faint dents, offset from centre so the moon has a face. */
.kn-scene__moon::before {
    content: '';
    position: absolute;
    inset: 34%;
    border-radius: 50%;
    background:
        radial-gradient(circle at 30% 34%, rgba(150, 165, 215, 0.5) 0 14%, transparent 16%),
        radial-gradient(circle at 62% 56%, rgba(150, 165, 215, 0.4) 0 11%, transparent 13%),
        radial-gradient(circle at 44% 72%, rgba(150, 165, 215, 0.3) 0 8%,  transparent 10%);
}

@keyframes kn-sun-breathe {
    0%, 100% { transform: translate(-50%, -50%) scale(1); }
    50%      { transform: translate(-50%, -50%) scale(1.07); }
}

/* Horizon glow — the light the sun actually throws along the skyline, which
   is what marries the sky to the silhouettes. Without it the buildings look
   pasted onto the gradient. */
.kn-scene__glow {
    position: absolute;
    left: -10%; right: -10%;
    /* Anchored so the ellipse's centre lands ON the horizon (the silhouette
       baseline sits around 71% down the box). Pooled at the very bottom, as
       it first was, the glow lit the foreground fields instead of the sky the
       buildings stand against — which is the one place it has to work. */
    bottom: 24%;
    height: 46%;
    z-index: 2;
    opacity: var(--sc-haze-o);
    background: radial-gradient(
        ellipse 60% 100% at var(--sc-sun-x) 100%,
        color-mix(in srgb, var(--sc-haze) 62%, transparent) 0%,
        color-mix(in srgb, var(--sc-haze) 24%, transparent) 38%,
        transparent 72%);
    mix-blend-mode: screen;
}

/* ===================================================================
   4. Parallax layers
   -------------------------------------------------------------------
   Every silhouette band is the same recipe: a phase-coloured gradient
   masked by an SVG from assets/img/scene/.

   --d is the depth. It multiplies BOTH the scroll offset and the pointer
   offset, so one number controls how near a band feels. The values
   ascend strictly back-to-front; keep them that way or the bands will
   cross each other and the illusion inverts.
   =================================================================== */

.kn-layer {
    position: absolute;
    /* Bled past all four edges so parallax travel can never drag an edge into
       frame. The bleed is generous because the near layers move most. */
    left: calc(var(--sc-bleed) * -1);
    right: calc(var(--sc-bleed) * -1);
    top: calc(var(--sc-bleed) * -1);
    bottom: calc(var(--sc-bleed) * -1);
    pointer-events: none;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center bottom;
            mask-position: center bottom;
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    /* Scroll moves layers vertically, the pointer nudges them horizontally.
       Both are clamped upstream in scene.js, so this can stay a plain
       multiply with no min()/max() gymnastics. */
    transform: translate3d(
        calc(var(--kn-mx) * var(--d)),
        calc(var(--kn-sy) * var(--d)),
        0);
    will-change: transform;
}

.kn-layer--city-far  { --d: 0.14; z-index: 3;  background: linear-gradient(180deg, var(--sc-far-top),   var(--sc-far-bot)   62%); -webkit-mask-image: url("../img/scene/city-far.svg");  mask-image: url("../img/scene/city-far.svg"); }
.kn-layer--city-near { --d: 0.28; z-index: 6;  background: linear-gradient(180deg, var(--sc-near-top),  var(--sc-near-bot)  66%); -webkit-mask-image: url("../img/scene/city-near.svg"); mask-image: url("../img/scene/city-near.svg"); }
.kn-layer--village   { --d: 0.44; z-index: 9;  background: linear-gradient(180deg, var(--sc-vill-top),  var(--sc-vill-bot)  70%); -webkit-mask-image: url("../img/scene/village.svg");   mask-image: url("../img/scene/village.svg"); }
.kn-layer--fields    { --d: 0.72; z-index: 12; background: linear-gradient(180deg, var(--sc-field-top), var(--sc-field-bot) 74%); -webkit-mask-image: url("../img/scene/fields.svg");    mask-image: url("../img/scene/fields.svg"); }

/* --- Lit windows -----------------------------------------------------
   Each lights band shares its silhouette's --d exactly. If the two ever
   drift apart the windows slide off their buildings, which is the single
   most obvious way this whole effect can break — so they are declared
   next to each other and should stay that way. */

.kn-lights {
    /* --sc-light-k is the per-depth brightness. It exists because the flicker
       keyframes below have to set `opacity` explicitly, which would otherwise
       overwrite anything set here — so the depth factor has to travel INTO
       the keyframes as a variable rather than being applied on top of them. */
    --sc-light-k: 1;
    opacity: calc(var(--sc-light-o) * var(--sc-light-k));
    background: var(--sc-light);
    /* The bloom. drop-shadow on a masked element blooms the mask's shape
       rather than its box, so hundreds of windows glow individually without
       hundreds of elements. Kept tight — a wide radius here turns the whole
       skyline into a smear and costs real paint time. */
    filter: drop-shadow(0 0 2px color-mix(in srgb, var(--sc-light) 70%, transparent))
            drop-shadow(0 0 6px color-mix(in srgb, var(--sc-light) 32%, transparent));
    mix-blend-mode: screen;
}
/* Distance dims light as surely as it desaturates colour. Without this the
   far towers burn as brightly as the near ones and the two bands flatten
   into a single wall of windows. */
.kn-layer--lights-far  { --d: 0.14; z-index: 4;  --sc-light-k: 0.6; }
.kn-layer--lights-near { --d: 0.28; z-index: 7;  --sc-light-k: 0.92; }
.kn-layer--lights-vill { --d: 0.44; z-index: 10; --sc-light-k: 1; }

.kn-layer--lights-far.is-a  { -webkit-mask-image: url("../img/scene/lights-far-a.svg");  mask-image: url("../img/scene/lights-far-a.svg"); }
.kn-layer--lights-far.is-b  { -webkit-mask-image: url("../img/scene/lights-far-b.svg");  mask-image: url("../img/scene/lights-far-b.svg"); }
.kn-layer--lights-near.is-a { -webkit-mask-image: url("../img/scene/lights-near-a.svg"); mask-image: url("../img/scene/lights-near-a.svg"); }
.kn-layer--lights-near.is-b { -webkit-mask-image: url("../img/scene/lights-near-b.svg"); mask-image: url("../img/scene/lights-near-b.svg"); }
.kn-layer--lights-vill      { -webkit-mask-image: url("../img/scene/village-lights.svg"); mask-image: url("../img/scene/village-lights.svg"); }

/* Windows switching on and off, done by cross-fading the two frames rather
   than animating individual windows. The two animations are the same length
   and exactly out of phase, so total light stays roughly constant and the
   city never appears to dim as a whole. */
.kn-layer--lights-far.is-a,
.kn-layer--lights-near.is-a  { animation: kn-lights-a 11s steps(1, end) infinite; }
.kn-layer--lights-far.is-b,
.kn-layer--lights-near.is-b  { animation: kn-lights-b 11s steps(1, end) infinite; }
/* Offset the near pair so the two depths don't switch on the same beat. */
.kn-layer--lights-near.is-a,
.kn-layer--lights-near.is-b  { animation-delay: -4.4s; }

@keyframes kn-lights-a {
    0%, 46%   { opacity: calc(var(--sc-light-o) * var(--sc-light-k)); }
    54%, 100% { opacity: 0; }
}
@keyframes kn-lights-b {
    0%, 46%   { opacity: 0; }
    54%, 100% { opacity: calc(var(--sc-light-o) * var(--sc-light-k)); }
}

/* The village lamps don't switch — they breathe, the way a flame does. */
.kn-layer--lights-vill { animation: kn-flame 5.5s ease-in-out infinite; }
@keyframes kn-flame {
    0%, 100% { opacity: calc(var(--sc-light-o) * var(--sc-light-k) * 0.82); }
    38%      { opacity: calc(var(--sc-light-o) * var(--sc-light-k)); }
    62%      { opacity: calc(var(--sc-light-o) * var(--sc-light-k) * 0.9); }
}

/* ===================================================================
   5. Atmosphere — haze band, mist, clouds
   =================================================================== */

/* The haze band is what actually blends the two halves of the picture.
   It sits between the city and the village in the stack, so the city
   recedes behind it and the village stands in front — the same trick a
   landscape painter uses to put a valley between two ridges. */
.kn-scene__haze {
    position: absolute;
    left: 0; right: 0;
    bottom: 12%;
    height: 34%;
    z-index: 5;
    opacity: var(--sc-haze-o);
    background: linear-gradient(
        180deg,
        transparent 0%,
        color-mix(in srgb, var(--sc-haze) 16%, transparent) 42%,
        color-mix(in srgb, var(--sc-haze) 34%, transparent) 78%,
        color-mix(in srgb, var(--sc-haze) 20%, transparent) 100%);
    mix-blend-mode: screen;
}

/* Ground mist, dawn's signature. Two bands drifting in opposite directions
   at different rates; a single band reads as a smudge on the glass. */
.kn-scene__mist {
    position: absolute;
    left: -30%; right: -30%;
    bottom: 0;
    height: 26%;
    z-index: 11;
    opacity: var(--sc-mist-o);
    pointer-events: none;
}
.kn-scene__mist::before,
.kn-scene__mist::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse 40% 60% at 18% 60%, rgba(255, 244, 232, 0.9), transparent 70%),
        radial-gradient(ellipse 34% 54% at 46% 76%, rgba(255, 238, 224, 0.75), transparent 70%),
        radial-gradient(ellipse 44% 62% at 78% 62%, rgba(255, 246, 236, 0.85), transparent 70%);
    filter: blur(14px);
}
.kn-scene__mist::before { animation: kn-mist-a 46s linear infinite; }
.kn-scene__mist::after  { animation: kn-mist-b 63s linear infinite; opacity: 0.7; }

@keyframes kn-mist-a {
    from { transform: translate3d(-8%, 0, 0)  scaleX(1); }
    50%  { transform: translate3d(6%, -6%, 0) scaleX(1.08); }
    to   { transform: translate3d(-8%, 0, 0)  scaleX(1); }
}
@keyframes kn-mist-b {
    from { transform: translate3d(7%, -3%, 0)  scaleX(1.06); }
    50%  { transform: translate3d(-6%, 3%, 0)  scaleX(1); }
    to   { transform: translate3d(7%, -3%, 0)  scaleX(1.06); }
}

/* --- Clouds ----------------------------------------------------------
   The strip is 200% wide and carries the tile twice, so translating it by
   exactly -50% lands the second copy where the first began — a seamless
   loop with no JavaScript and no reset flash. clouds.svg keeps its left
   and right edges clear specifically so this join is invisible. */
.kn-clouds {
    position: absolute;
    left: 0;
    width: 200%;
    z-index: 2;
    background: var(--sc-cloud);
    -webkit-mask: url("../img/scene/clouds.svg") 0 0 / 50% 100% repeat-x;
            mask: url("../img/scene/clouds.svg") 0 0 / 50% 100% repeat-x;
    pointer-events: none;
}
/* Pace. One full cycle carries a bank exactly one screen width, so the
   duration in seconds IS the seconds-per-screen — the only number that
   matters here, and the one to change if the drift ever needs retuning.

   At the original 190s the far bank crossed at about 7px/s on a 1440px
   screen, which is below the threshold where movement is noticed at all:
   the sky read as a still image. These values put it near 16px/s — slow
   enough to stay calm behind body copy, quick enough that a visitor
   watching the hero can see the weather move. */
.kn-clouds--far {
    top: -2%;
    height: 46%;
    opacity: 0.42;
    filter: blur(2px);
    animation: kn-cloud-drift 118s linear infinite;
}
.kn-clouds--near {
    top: 4%;
    height: 56%;
    opacity: 0.24;
    filter: blur(5px);
    /* Same direction as the far bank, and faster. It used to run in reverse
       to keep the two banks visually distinct, but one sky blowing two ways
       reads as an error once the motion is fast enough to notice — and with
       both going the same way the speed difference does the separating on its
       own, as parallax rather than as contradiction. */
    animation: kn-cloud-drift 74s linear infinite;
}
@keyframes kn-cloud-drift {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}

/* --- Crepuscular rays -------------------------------------------------
   Beams fanning out from wherever the sun currently is. It reads as light
   through haze, and it is the cheapest way to make the low-sun phases feel
   like weather rather than like a gradient.

   Positioned from --sc-sun-x / --sc-sun-y, so it follows the sun through
   every phase with no second set of coordinates to keep in step — the one
   thing that would otherwise drift out of sync the moment a palette moved. */
.kn-scene__rays {
    position: absolute;
    left: var(--sc-sun-x);
    top: var(--sc-sun-y);
    width: 150vmax;
    height: 150vmax;
    z-index: 2;
    opacity: var(--sc-ray-o);
    mix-blend-mode: screen;
    pointer-events: none;
    background: repeating-conic-gradient(from 0deg at 50% 50%,
        color-mix(in srgb, var(--sc-sun) 26%, transparent) 0deg 2.5deg,
        transparent 2.5deg 11deg);
    /* Faded to nothing well before the edge: a hard circular cut-off would
       read as a lens artefact rather than as light. */
    -webkit-mask: radial-gradient(circle, #000 0%, rgba(0,0,0,0.5) 26%, transparent 58%);
            mask: radial-gradient(circle, #000 0%, rgba(0,0,0,0.5) 26%, transparent 58%);
    filter: blur(7px);
    animation: kn-rays 140s linear infinite;
}
/* Very slow rotation. Beams that hold still look printed on; at this rate the
   movement is not consciously visible but the frame never looks static. */
@keyframes kn-rays {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* --- Cloud shadows ----------------------------------------------------
   Soft dark pools drifting over the whole landscape. Deliberately driven by
   the SAME keyframes and duration as the near cloud bank, so the shadows
   travel with the clouds casting them instead of sliding independently. */
.kn-scene__cloudshadow {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 200%;
    z-index: 13;
    opacity: var(--sc-shadow-o);
    pointer-events: none;
    background:
        radial-gradient(ellipse 9% 34% at 8%  62%, rgba(8, 12, 38, 0.9), transparent 70%),
        radial-gradient(ellipse 7% 28% at 22% 74%, rgba(8, 12, 38, 0.8), transparent 70%),
        radial-gradient(ellipse 10% 36% at 34% 66%, rgba(8, 12, 38, 0.85), transparent 70%),
        radial-gradient(ellipse 8% 30% at 47% 78%, rgba(8, 12, 38, 0.75), transparent 70%),
        radial-gradient(ellipse 9% 34% at 58% 62%, rgba(8, 12, 38, 0.9), transparent 70%),
        radial-gradient(ellipse 7% 28% at 72% 74%, rgba(8, 12, 38, 0.8), transparent 70%),
        radial-gradient(ellipse 10% 36% at 84% 66%, rgba(8, 12, 38, 0.85), transparent 70%),
        radial-gradient(ellipse 8% 30% at 97% 78%, rgba(8, 12, 38, 0.75), transparent 70%);
    filter: blur(22px);
    animation: kn-cloud-drift 74s linear infinite;
}

/* ===================================================================
   6. Living things — birds, fireflies, a kite and an aircraft
   =================================================================== */

/* --- Kite -------------------------------------------------------------
   Daytime's one piece of foreground life. Flown from the village side of
   the frame, in the band of open sky above the huts.

   The only coloured artwork in the scene (see kite.svg), so it is a
   background-image rather than a mask. */
.kn-scene__kite {
    position: absolute;
    left: 66%;
    top: 8%;
    width: 42px;
    height: 92px;
    z-index: 8;
    opacity: var(--sc-kite-o);
    background: url("../img/scene/kite.svg") top center / contain no-repeat;
    /* Pivot at the top, where the line would be: a kite swings from its
       tether, it does not rotate about its own middle. */
    transform-origin: 50% 0%;
    animation: kn-kite 16s ease-in-out infinite;
    pointer-events: none;
}
@keyframes kn-kite {
    /* Uneven timing on purpose — a kite is pushed about by gusts, and evenly
       spaced keyframes give it the pendulum rhythm of a metronome instead. */
    0%, 100% { transform: translate3d(0, 0, 0) rotate(-7deg); }
    22%      { transform: translate3d(-22px, -18px, 0) rotate(6deg); }
    41%      { transform: translate3d(-6px, -4px, 0) rotate(-2deg); }
    68%      { transform: translate3d(18px, -22px, 0) rotate(9deg); }
    85%      { transform: translate3d(8px, -6px, 0) rotate(2deg); }
}

/* --- Aircraft ---------------------------------------------------------
   A contrail with a blinking red beacon at its nose, crossing high above
   the skyline. Mostly off-frame: the keyframes hold it off the left edge
   for the first two thirds of the cycle, so it is an occasional event
   rather than a shuttle. */
.kn-scene__plane {
    position: absolute;
    top: 11%;
    left: 0;
    width: 100%;
    height: 0;
    z-index: 2;
    opacity: var(--sc-plane-o);
    pointer-events: none;
}
.kn-scene__plane::before,
.kn-scene__plane::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    animation: kn-plane 96s linear infinite;
}
/* The trail, brightest at the nose and dissolving behind. */
.kn-scene__plane::before {
    width: 58px;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--sc-star) 30%, transparent) 55%,
        color-mix(in srgb, var(--sc-star) 85%, transparent) 100%);
}
/* The beacon, offset to sit at the leading end of that trail. It carries the
   same travel animation plus its own blink, so the two never separate. */
.kn-scene__plane::after {
    width: 4px;
    height: 4px;
    margin: -1px 0 0 56px;
    border-radius: 50%;
    background: #ff6a52;
    box-shadow: 0 0 7px 2px rgba(255, 106, 82, 0.75);
    animation: kn-plane 96s linear infinite, kn-beacon 1.8s steps(1, end) infinite;
}
@keyframes kn-plane {
    0%, 64% { transform: translate3d(-12vw, 0, 0); }
    100%    { transform: translate3d(114vw, -4vh, 0); }
}
@keyframes kn-beacon {
    0%, 24%   { opacity: 1; }
    25%, 100% { opacity: 0.12; }
}

/* A flock crossing the frame. Long duration with a long "off-screen" tail
   in the keyframes, so most of the time there is no flock at all: birds
   that cross every eight seconds read as a screensaver. */
.kn-scene__birds {
    position: absolute;
    inset: 0;
    z-index: 8;
    opacity: var(--sc-bird-o);
    pointer-events: none;
    overflow: hidden;
}
.kn-scene__birds i {
    position: absolute;
    display: block;
    width: 150px;
    height: 60px;
    background: color-mix(in srgb, var(--sc-vill-bot) 78%, transparent);
    -webkit-mask: url("../img/scene/birds.svg") center / contain no-repeat;
            mask: url("../img/scene/birds.svg") center / contain no-repeat;
    animation: kn-fly 52s linear infinite;
}
.kn-scene__birds i:nth-child(1) { top: 22%; transform: scale(1);    animation-duration: 58s; animation-delay: -4s;  }
.kn-scene__birds i:nth-child(2) { top: 33%; transform: scale(0.62); animation-duration: 74s; animation-delay: -32s; opacity: 0.75; }
.kn-scene__birds i:nth-child(3) { top: 15%; transform: scale(0.42); animation-duration: 96s; animation-delay: -61s; opacity: 0.5;  }

@keyframes kn-fly {
    /* Held off-frame for the first two thirds, then one crossing with a
       gentle rise — birds climbing away from the fields at first light. */
    0%, 62%  { transform: translate3d(-20vw, 0, 0); }
    82%      { transform: translate3d(52vw, -3vh, 0); }
    100%     { transform: translate3d(124vw, -8vh, 0); }
}

/* Fireflies over the fields, night only. Positioned inline by scene.php so
   the scatter isn't a repeating CSS pattern. */
.kn-scene__fireflies {
    position: absolute;
    inset: 0;
    z-index: 13;
    opacity: var(--sc-fire-o);
    pointer-events: none;
    overflow: hidden;
}
.kn-scene__fireflies i {
    position: absolute;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: #ffe89a;
    box-shadow: 0 0 6px 2px rgba(255, 226, 140, 0.65);
    animation: kn-firefly 9s ease-in-out infinite;
}
@keyframes kn-firefly {
    /* Fireflies don't blink on a timer — they drift dark and pulse at the
       top of the arc. The opacity and the movement peak together. */
    0%   { transform: translate3d(0, 0, 0) scale(0.6);            opacity: 0; }
    18%  { opacity: 0.9; }
    50%  { transform: translate3d(26px, -34px, 0) scale(1);       opacity: 1; }
    82%  { opacity: 0.35; }
    100% { transform: translate3d(52px, -8px, 0) scale(0.6);      opacity: 0; }
}

/* A shooting star, night only. Almost always off-screen: the 3% active
   window in the keyframes is what keeps it a surprise rather than a tic. */
.kn-scene__shoot {
    position: absolute;
    top: 12%;
    left: 8%;
    width: 160px;
    height: 2px;
    z-index: 2;
    opacity: 0;
    background: linear-gradient(90deg, transparent, #ffffff 60%, #cfe0ff);
    border-radius: 2px;
    filter: drop-shadow(0 0 6px rgba(190, 215, 255, 0.9));
    transform: rotate(14deg);
    animation: kn-shoot 27s ease-in infinite;
}
.kn-scene__shoot:nth-of-type(2) {
    top: 26%; left: 52%; width: 110px;
    animation-duration: 41s; animation-delay: -18s;
}
@keyframes kn-shoot {
    0%,  96%  { opacity: 0; transform: translate3d(0, 0, 0) rotate(14deg) scaleX(0.2); }
    97%       { opacity: 1; }
    99.4%     { opacity: 0; transform: translate3d(38vw, 16vh, 0) rotate(14deg) scaleX(1); }
    100%      { opacity: 0; transform: translate3d(0, 0, 0) rotate(14deg) scaleX(0.2); }
}

/* ===================================================================
   7. Finishing passes
   =================================================================== */

/* Ground shadow anchoring the fields to the bottom edge.
   It resolves into the PAGE background rather than into the scene's own
   ground colour, which is what lets the band end partway down the hero with
   no seam: the field darkens, then becomes the page, and there is no line to
   see. Get this wrong and a hard horizontal edge appears across the hero at
   whatever height the band happens to stop. */
.kn-scene__floor {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 30%;
    z-index: 14;
    background: linear-gradient(180deg,
        transparent 0%,
        color-mix(in srgb, var(--sc-field-bot) 82%, transparent) 58%,
        var(--knms-bg, #150a2e) 100%);
    pointer-events: none;
}

/* Corner vignette so hero copy and panels have something to sit against. */
.kn-scene__vignette {
    position: absolute;
    inset: 0;
    z-index: 15;
    pointer-events: none;
    background:
        radial-gradient(ellipse 78% 68% at 50% 46%, transparent 42%, rgba(8, 4, 24, 0.42) 82%, rgba(6, 3, 20, 0.74) 100%),
        linear-gradient(180deg, rgba(10, 5, 30, 0.34) 0%, transparent 24%, transparent 62%, rgba(8, 4, 24, 0.42) 100%);
}

/* ===================================================================
   8. Theme variants
   -------------------------------------------------------------------
   Same layers, different emphasis. Hiding a band is not enough on its
   own — whatever remains has to be re-weighted, or the composition ends
   up with a hole where the hidden band was.
   =================================================================== */

/* Skyline — the city alone. The village goes, and the far hill and
   treeline already drawn into city-far.svg become the horizon. */
.kn-scene[data-theme="skyline"] .kn-layer--village,
.kn-scene[data-theme="skyline"] .kn-layer--lights-vill { display: none; }
.kn-scene[data-theme="skyline"] .kn-layer--city-near   { --d: 0.34; }
.kn-scene[data-theme="skyline"] .kn-scene__haze        { bottom: 16%; height: 26%; }

/* Heritage — the village, with the city pushed back to a rumour on the
   horizon. The near blocks go entirely and the far skyline is dimmed and
   desaturated rather than merely faded, so it reads as distance and not
   as a half-loaded image. */
.kn-scene[data-theme="heritage"] .kn-layer--city-near,
.kn-scene[data-theme="heritage"] .kn-layer--lights-near { display: none; }
.kn-scene[data-theme="heritage"] .kn-layer--city-far    { opacity: 0.5; filter: blur(1.4px) saturate(0.65); }
.kn-scene[data-theme="heritage"] .kn-layer--lights-far  { opacity: calc(var(--sc-light-o) * 0.45); }
.kn-scene[data-theme="heritage"] .kn-layer--village     { --d: 0.36; }
.kn-scene[data-theme="heritage"] .kn-scene__mist        { opacity: calc(var(--sc-mist-o) * 1.5); }

/* ===================================================================
   9. Intensity, placement and motion policy
   =================================================================== */

/* "Subtle" keeps the whole picture and drops only the movement, so the
   scene still tells the same story on a low-powered machine. */
.kn-scene[data-motion="still"] *,
.kn-scene[data-motion="still"]::before,
.kn-scene[data-motion="still"]::after { animation: none !important; }
.kn-scene[data-motion="still"] .kn-layer { transform: none !important; }
/* With the crossfade stopped, frame A alone would be the only lit windows.
   Pin both frames half-lit instead so a still city still looks inhabited. */
.kn-scene[data-motion="still"] .kn-lights { opacity: calc(var(--sc-light-o) * 0.6); }

/* --- Hero placement --------------------------------------------------
   The scene is a BAND at the top of the hero, not a fill.

   This is the single most important rule in the file, and it is not a
   stylistic choice. The homepage hero is tall — it carries the portrait,
   the headline, two rail panels and a bottom row — so on a 1440px screen
   it runs well past 1300px high. Stretched to fill that, a 16:9 artwork
   mapped with `slice` scales to the HEIGHT and crops the width to
   roughly the middle third: the city and the village stop appearing in
   the same frame, which is the whole point of the picture. On a phone it
   was worse still — a ~1.7x zoom onto one block of mid-rise flats.

   Capping the height keeps the band near the artwork's own proportions,
   so the full composition stays visible, and the sky simply continues
   below it into the page colour (see .kn-scene__floor). */
.kn-hero .kn-scene {
    z-index: 0;
    bottom: auto;
    height: clamp(360px, 76vh, 760px);
}
/* The hero's own children need to sit above the scene. These are the two
   that were previously stacked against .kn-hero__sky. */
.kn-hero .kn-hero__inner,
.kn-hero .kn-hero__bottom { position: relative; z-index: 4; }

/* --- Site-wide ambient placement -------------------------------------
   A fixed, heavily muted copy behind every page. It sits at z-index -2
   because body::before (-1) and body::after (-1) are both already spoken
   for in app.css — see the note in includes/header.php. The body's own
   background colour paints on the canvas behind all of them, so -2 is
   still visible. */
.kn-ambient {
    position: fixed;
    inset: 0;
    z-index: -2;
    pointer-events: none;
    overflow: hidden;
}
.kn-ambient .kn-scene {
    /* Pushed down so the scenery sits along the bottom of the viewport and
       the page's own content reads against plain sky. */
    top: 18%;
    opacity: 0.5;
}
.kn-ambient .kn-scene__sky { opacity: 0.55; }
.kn-ambient .kn-scene__vignette { display: none; }
/* Ambient is background, not subject. Anything that reads as an EVENT is cut
   — a passing aircraft or a bobbing kite behind body copy pulls the eye off
   the words, which is the opposite of what a backdrop is for. What stays is
   atmosphere: sky, silhouettes, lights and the sun's rays. */
.kn-ambient .kn-scene__birds,
.kn-ambient .kn-scene__shoot,
.kn-ambient .kn-scene__fireflies,
.kn-ambient .kn-scene__plane,
.kn-ambient .kn-scene__kite,
.kn-ambient .kn-scene__cloudshadow,
.kn-ambient .kn-scene__mist { display: none; }
/* Softened further behind a page: at hero strength the beams compete with
   text for attention. */
.kn-ambient .kn-scene__rays { opacity: calc(var(--sc-ray-o) * 0.45); }
.kn-ambient .kn-lights { filter: none; opacity: calc(var(--sc-light-o) * 0.7); }
/* Fade the whole thing out under the top of the page so text never has to
   compete with a skyline. */
.kn-ambient::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg,
        var(--knms-bg, #150a2e) 0%,
        color-mix(in srgb, var(--knms-bg, #150a2e) 72%, transparent) 26%,
        color-mix(in srgb, var(--knms-bg, #150a2e) 30%, transparent) 62%,
        transparent 100%);
}

/* ===================================================================
   10. Visitor time-of-day control
   -------------------------------------------------------------------
   Rendered `hidden` by scene.php and revealed by scene.js, so it never
   appears as a dead button. Suppressed entirely when the CMS has locked
   the phase — see scene_config().
   =================================================================== */

/* An author `display` beats the user-agent's `[hidden] { display: none }`, so
   without this the control would paint before scene.js ever ran — which is
   exactly the dead button the `hidden` attribute is there to prevent. */
.kn-timeswitch[hidden] { display: none; }

.kn-timeswitch {
    position: absolute;
    top: 1rem;
    right: 1rem;
    z-index: 6;                       /* above the scene, below hero copy */
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.42rem 0.85rem 0.42rem 0.6rem;
    font: 600 0.78rem/1 'Inter', system-ui, sans-serif;
    letter-spacing: 0.02em;
    color: rgba(255, 255, 255, 0.92);
    background: rgba(18, 10, 42, 0.42);
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 999px;
    cursor: pointer;
    backdrop-filter: blur(8px) saturate(1.2);
    -webkit-backdrop-filter: blur(8px) saturate(1.2);
    transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.kn-timeswitch:hover {
    background: rgba(30, 18, 66, 0.62);
    border-color: rgba(255, 255, 255, 0.4);
    transform: translateY(-1px);
}
.kn-timeswitch:focus-visible {
    outline: 2px solid var(--knms-accent, #ffc94a);
    outline-offset: 3px;
}
/* The dot is the state, read at a glance and without the label. */
.kn-timeswitch__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: linear-gradient(140deg, #ffd98a, #ff8a3c);
    box-shadow: 0 0 8px rgba(255, 190, 90, 0.8);
    transition: background 0.4s ease, box-shadow 0.4s ease;
}
.kn-timeswitch[data-value="auto"]  .kn-timeswitch__dot { background: linear-gradient(140deg, #8be9ff, #7a5cff); box-shadow: 0 0 8px rgba(140, 190, 255, 0.75); }
.kn-timeswitch[data-value="dawn"]  .kn-timeswitch__dot { background: linear-gradient(140deg, #ffd9a0, #ff9a6b); box-shadow: 0 0 8px rgba(255, 190, 140, 0.8); }
.kn-timeswitch[data-value="day"]   .kn-timeswitch__dot { background: linear-gradient(140deg, #fffbe8, #ffd34d); box-shadow: 0 0 9px rgba(255, 225, 120, 0.9); }
.kn-timeswitch[data-value="dusk"]  .kn-timeswitch__dot { background: linear-gradient(140deg, #ff9a3c, #a83f8f); box-shadow: 0 0 8px rgba(255, 140, 90, 0.8); }
.kn-timeswitch[data-value="night"] .kn-timeswitch__dot { background: linear-gradient(140deg, #dfe6ff, #3a4a9c); box-shadow: 0 0 8px rgba(190, 205, 255, 0.7); }

@media (max-width: 600px) {
    /* Small screens give the hero no spare width beside the headline, and the
       label is the first thing that can go — the dot still carries the state. */
    .kn-timeswitch { top: 0.6rem; right: 0.6rem; padding: 0.4rem 0.55rem; }
    .kn-timeswitch__label { display: none; }
}

/* ===================================================================
   11. Responsive and accessibility
   =================================================================== */

@media (max-width: 900px) {
    .kn-scene { --sc-bleed: 60px; }
    /* A phone frame is taller than it is wide, so `100% 100%` — which lets
       the artwork scale to cover and crop the sides — throws away everything
       but a single block of flats. `auto` height instead: the artwork keeps
       its proportions, spans the full width as a panoramic strip along the
       bottom of the band, and the sky fills the space above it. Both halves
       of the composition survive, which is the requirement.

       The bottom bleed also goes: with the strip anchored to the box bottom,
       60px of overhang would push the foreground fields out of frame. Losing
       it costs nothing, because downward parallax is covered by the top
       bleed, which stays. */
    .kn-layer {
        bottom: 0;
        -webkit-mask-size: 118% auto;
                mask-size: 118% auto;
        -webkit-mask-position: 58% bottom;
                mask-position: 58% bottom;
    }
    .kn-scene__stars,
    .kn-scene__stars::before,
    .kn-scene__stars::after {
        -webkit-mask-size: 140% 100%;
                mask-size: 140% 100%;
    }
    .kn-scene__sun, .kn-scene__moon { width: 30vmax; height: 30vmax; }
    .kn-scene__birds i { width: 96px; height: 40px; }
    /* The kite moves further into the frame: at 66% it sat under the right
       edge on a narrow screen, where the panorama is cropped tighter. */
    .kn-scene__kite { left: 58%; width: 32px; height: 70px; }
    .kn-scene__plane::before { width: 42px; }
    .kn-scene__plane::after  { margin-left: 40px; }
}

@media (max-width: 600px) {
    /* Fewer moving parts on a phone: the near cloud bank and the second
       shooting star cost paint time for detail nobody can resolve. */
    .kn-clouds--near,
    .kn-scene__shoot:nth-of-type(2) { display: none; }
    .kn-scene__fireflies i:nth-child(n + 7) { display: none; }
    /* Eight blurred gradients over a full-height layer is the most expensive
       thing in the scene, and on a phone the shadows are too small to read
       as cloud anyway. */
    .kn-scene__cloudshadow { display: none; }
}

/* Motion is decoration here, never information — so under a reduced-motion
   preference the scene simply stops. The picture, the phase colours and the
   day/night difference all survive; only the movement goes. */
@media (prefers-reduced-motion: reduce) {
    .kn-scene *,
    .kn-scene *::before,
    .kn-scene *::after { animation: none !important; }
    .kn-scene .kn-layer { transform: none !important; }
    .kn-scene { transition: none; }
    .kn-lights { opacity: calc(var(--sc-light-o) * 0.6); }
}

/* No color-mix() means the atmosphere layers would compute to nothing and
   the picture would lose its light. Fall back to flat translucent warms —
   less exact, but present. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
    .kn-scene__glow { background: radial-gradient(ellipse 60% 100% at var(--sc-sun-x) 100%, rgba(255, 176, 92, 0.5), transparent 70%); }
    .kn-scene__haze { background: linear-gradient(180deg, transparent, rgba(255, 176, 92, 0.28) 78%, rgba(255, 176, 92, 0.16)); }
    .kn-lights      { filter: drop-shadow(0 0 4px rgba(255, 205, 120, 0.8)); }
    .kn-scene__floor{ background: linear-gradient(180deg, transparent, rgba(8, 4, 22, 0.9) 78%); }
    .kn-scene__birds i { background: rgba(20, 10, 40, 0.8); }
}
