/* footer.css — the closing scene.
 *
 * The spec faults the conventional footer (fault 45, HIGH) and asks for a final
 * scene instead: clouds, stars, a giant contact statement, a paper plane,
 * social links as physical objects, an illustrated horizon, a day/night
 * transition, and tiny hidden details.
 *
 * Everything here is scoped under `.finale`, the element footer.js renders
 * into. The element is expected to be `<footer class="finale" data-finale>`
 * sitting at the end of the document flow, after the desktop.
 *
 * Two rules govern the whole file:
 *   1. No-JS is never a blank band. footer.css carries the noscript fallback,
 *      so the contact routes survive scripting being off.
 *   2. Nothing may push the page sideways. The drifting clouds, the plane and
 *      the oversized headline are all clipped by the scene, never by the
 *      viewport, so the 390px floor holds.
 *
 * Colour: the day and night skies are two stacked gradient plates that
 * cross-fade, which is what makes the transition possible — a single
 * background-image cannot interpolate between gradients.
 */

/* ══ the scene ═════════════════════════════════════════════════════════════ */

.finale {
  /* Day is the default paint so the noscript fallback reads on a pale sky. */
  --finale-fg: #17151F;
  --finale-fg-soft: rgba(23, 21, 31, .66);
  --finale-line: #17151F;
  --finale-card: rgba(255, 252, 244, .94);
  --finale-cloud: rgba(255, 255, 255, .9);
  --finale-hill-far: #D9E1C4;
  --finale-hill-near: #C6D5A4;
  --finale-field: #AFC08C;
  --finale-base: linear-gradient(#A9C7EE 0%, #CFC4EE 34%, #EBD7F2 62%, #FBD9E2 82%, #FFE8C2 100%);
  --finale-solid: #FFE8C2;

  position: relative;
  isolation: isolate;
  min-height: clamp(560px, 88vh, 920px);
  padding-block: clamp(3rem, 11vh, 7rem) 0;
  background: var(--finale-base);
  border-top: 2px solid var(--ink, #17151F);
  color: var(--finale-fg);
  font-family: var(--sans, ui-sans-serif, system-ui, "Segoe UI", Helvetica, Arial, sans-serif);

  /* Clouds and the plane travel past the edges; clip them inside the scene so
     they can never become horizontal page scroll. `clip` (not `hidden`) so the
     scene is not turned into a scroll container and focus rings stay intact. */
  overflow-x: clip;
  overflow-y: visible;
}

@supports not (overflow: clip) {
  .finale { overflow-x: hidden; overflow-y: hidden; }
}

.finale *,
.finale *::before,
.finale *::after { box-sizing: border-box; }

.finale[data-sky="night"] {
  --finale-fg: #FFF6E2;
  --finale-fg-soft: rgba(255, 246, 226, .68);
  --finale-line: rgba(255, 246, 226, .36);
  --finale-card: rgba(26, 20, 56, .74);
  --finale-cloud: rgba(255, 246, 226, .13);
  --finale-hill-far: #3B2E66;
  --finale-hill-near: #2A2050;
  --finale-field: #1A1338;
  --finale-base: linear-gradient(#141033 0%, #241C55 30%, #4A3B84 58%, #8E5E8C 80%, #D98C86 100%);
  --finale-solid: #241C55;
}

/* ── the two sky plates ─────────────────────────────────────────────────── */

.finale__sky { position: absolute; inset: 0; z-index: 0; overflow: clip; pointer-events: none; }

.finale__plate {
  position: absolute;
  inset: 0;
  background: var(--finale-base);
  transition: opacity 1.5s ease;
}

.finale__plate--night { opacity: 0; }

.finale[data-sky="night"] .finale__plate--day { opacity: 0; }
.finale[data-sky="night"] .finale__plate--night { opacity: 1; }

/* ── stars ──────────────────────────────────────────────────────────────── */

/* Stars are four-point sparkles, not dots — the single motif that makes a sky
   read as painted rather than as a starfield widget. The shape is a clip-path
   so sixty of them cost no extra markup, and the twinkle runs on ::before so
   it never fights the day/night fade on the wrapper. */
.finale__stars { position: absolute; inset: 0 0 30% 0; }

.finale__star {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s);
  height: var(--s);
  rotate: var(--r, 0deg);
  opacity: 0;
  transition: opacity 1.1s ease;
}

.finale__star::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--finale-star, #FFF6E2);
  clip-path: polygon(50% 0, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0 50%, 42% 42%);
  animation: finale-twinkle var(--tw, 4s) ease-in-out var(--d, 0s) infinite;
}

.finale[data-sky="night"] .finale__star { opacity: var(--o, .85); }

/* the hidden detail: four of the stars sit in a paper-plane shape, and they are
   the only ones that keep their point after the sky settles */
.finale__star--plane { --finale-star: #FFFDF4; --s: 13px; opacity: 1; }
.finale__star--plane::before { animation: none; }

/* The grain. Every other layer here is flat fill; this is what stops flat fill
   from reading as flat fill. It sits over the whole scene rather than over the
   sky, because the horizon is the part that most needs the tooth.
   Deliberately no blend mode: compositing a full-band blend over animating
   content is the one way this scene could cost frames during a scroll. */
.finale__grain {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: .07;
  background-size: 180px 180px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

.finale__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

@keyframes finale-twinkle {
  0%, 100% { opacity: .18; }
  50% { opacity: 1; }
}

/* ── sun / moon ─────────────────────────────────────────────────────────── */

.finale__orb {
  position: absolute;
  top: clamp(1.5rem, 7vh, 4rem);
  right: clamp(1rem, 8vw, 7rem);
  width: clamp(54px, 9vw, 104px);
  aspect-ratio: 1;
  transition: opacity 1.5s ease;
}

.finale__orb svg { position: absolute; inset: 0; width: 100%; height: 100%; }

.finale__orb--sun { opacity: 1; }
.finale__orb--moon { opacity: 0; }

.finale[data-sky="night"] .finale__orb--sun { opacity: 0; }
.finale[data-sky="night"] .finale__orb--moon { opacity: 1; }

.finale__orb--sun svg { animation: finale-turn 90s linear infinite; }
@keyframes finale-turn { to { rotate: 360deg; } }

/* ── clouds ─────────────────────────────────────────────────────────────── */

/* Three pill-built clouds, and one oversized one sitting on the horizon —
   the brief asks for a giant cloud, not a scatter of small ones. */
.finale__clouds { position: absolute; inset: 0; }

.finale-cloud {
  position: absolute;
  display: block;
  width: 210px;
  height: 52px;
  border-radius: 999px;
  background: var(--finale-cloud);
  animation: finale-drift 150s linear infinite;
}

.finale-cloud::before,
.finale-cloud::after {
  content: "";
  position: absolute;
  bottom: 0;
  border-radius: 999px;
  background: inherit;
}

.finale-cloud::before { width: 104px; height: 72px; left: 24px; }
.finale-cloud::after { width: 74px; height: 56px; right: 22px; }

.finale-cloud--1 { top: 9%;  left: -70px;  scale: .8;  animation-duration: 168s; }
.finale-cloud--2 { top: 22%; right: -90px; scale: 1.15; animation-duration: 214s; animation-direction: reverse; }
.finale-cloud--3 { top: 34%; left: -40px;  scale: .55; animation-duration: 132s; }

.finale-cloud--giant {
  /* the land is the containing block, so 100% puts the cloud's base exactly on
     the horizon instead of guessing at a viewport offset */
  bottom: 100%;
  left: -14%;
  width: 128%;
  max-width: none;
  height: clamp(54px, 9vw, 96px);
  opacity: .85;
  animation-duration: 300s;
}

.finale-cloud--giant::before { width: 34%; height: 250%; left: 8%; }
.finale-cloud--giant::after { width: 26%; height: 190%; right: 10%; }

@keyframes finale-drift {
  from { translate: -34vw 0; }
  to { translate: 134vw 0; }
}

/* ── paper plane ────────────────────────────────────────────────────────── */

.finale__plane {
  position: absolute;
  top: 26%;
  left: 0;
  width: 46px;
  height: 27px;
  animation: finale-fly 64s linear infinite;
}

.finale__plane svg { width: 100%; height: 100%; display: block; }
.finale__plane svg path:first-child { fill: #FFFDF6; stroke: #17151F; stroke-width: 2; stroke-linejoin: round; }
.finale__plane svg path + path { fill: none; stroke: rgba(23, 21, 31, .55); stroke-width: 1.6; stroke-linecap: round; }

/* the contrail: three dots that fade in behind the plane */
.finale__plane::before {
  content: "";
  position: absolute;
  top: 52%;
  right: 96%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(255, 253, 246, .8);
  box-shadow: -14px 5px 0 rgba(255, 253, 246, .5), -26px 9px 0 rgba(255, 253, 246, .28);
}

@keyframes finale-fly {
  0% { translate: -12vw 0; rotate: -4deg; }
  50% { translate: 48vw 4vh; rotate: 3deg; }
  100% { translate: 108vw -3vh; rotate: -2deg; }
}

/* ══ the statement ════════════════════════════════════════════════════════ */

.finale__body {
  position: relative;
  z-index: 2;
  max-width: min(1080px, 100%);
  min-height: clamp(280px, 52vh, 560px);
  margin-inline: auto;
  padding-inline: clamp(1.1rem, 5vw, 3rem);
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(.55rem, 1.6vh, 1.1rem);
  text-align: center;
}

.finale__body > * {
  transition: opacity .7s ease, translate .7s cubic-bezier(.2, .9, .25, 1);
}

/* Staged arrival. The attribute is only ever set by script, so without JS
   nothing matches and the scene is simply already there. */
.finale[data-in="false"] .finale__body > * { opacity: 0; translate: 0 16px; }
.finale[data-in="false"] .finale__body > *:nth-child(2) { transition-delay: .08s; }
.finale[data-in="false"] .finale__body > *:nth-child(3) { transition-delay: .16s; }
.finale[data-in="false"] .finale__body > *:nth-child(4) { transition-delay: .24s; }
.finale[data-in="false"] .finale__body > *:nth-child(5) { transition-delay: .32s; }

.finale__eyebrow {
  margin: 0;
  font-family: var(--mono, ui-monospace, Consolas, monospace);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--finale-fg-soft);
}

.finale__title {
  margin: 0;
  max-width: 22ch;
  font-family: var(--serif, Georgia, "Times New Roman", serif);
  font-size: clamp(1.45rem, 4.9vw, 3.4rem);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -.02em;
  text-wrap: balance;
}

/* the giant contact statement: the address itself is the headline */
.finale__mail {
  position: relative;
  display: inline-block;
  max-width: 100%;
  font-family: var(--serif, Georgia, "Times New Roman", serif);
  font-size: clamp(1.3rem, 6.8vw, 4.6rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.025em;
  color: inherit;
  text-decoration: none;
  overflow-wrap: anywhere;
}

.finale__wave {
  position: absolute;
  left: 0;
  bottom: -.18em;
  display: block;
  width: 100%;
  height: .34em;
  color: currentColor;
  opacity: .38;
  transition: opacity .25s ease;
}

.finale__wave path {
  fill: none;
  stroke: currentColor;
  stroke-width: 3.5;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.finale__mail:hover .finale__wave,
.finale__mail:focus-visible .finale__wave { opacity: 1; }

.finale__mail:focus-visible { outline-offset: 6px; }

.finale__lede {
  margin: 0;
  max-width: 54ch;
  font-size: clamp(.86rem, 2.6vw, 1rem);
  line-height: 1.6;
  color: var(--finale-fg-soft);
  text-wrap: pretty;
}

/* ── social links as physical objects ───────────────────────────────────── */

.finale__routes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .7rem;
  margin: clamp(.4rem, 2vh, 1rem) 0 0;
  padding: 0;
  list-style: none;
}

.finale__route { display: flex; }

.object {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: .1rem .7rem;
  padding: .6rem .95rem;
  border: 2px solid var(--finale-line);
  border-radius: 14px;
  background: var(--finale-card);
  color: inherit;
  text-decoration: none;
  rotate: -.8deg;
  box-shadow: 0 5px 0 -1px rgba(23, 21, 31, .2);
  transition: translate .25s ease, rotate .25s ease, box-shadow .25s ease;
}

.finale__route:nth-child(even) .object { rotate: .9deg; }

.object:hover,
.object:focus-visible {
  translate: 0 -3px;
  rotate: 0deg;
  box-shadow: 0 8px 0 -1px rgba(23, 21, 31, .26);
}

.object:focus-visible { outline: 3px solid currentColor; outline-offset: 3px; }

.object__art {
  grid-row: 1 / span 2;
  width: 32px;
  height: 32px;
  display: block;
}

.object__name {
  font-family: var(--serif, Georgia, serif);
  font-size: .98rem;
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.15;
}

.object__url {
  font-family: var(--mono, ui-monospace, Consolas, monospace);
  font-size: .68rem;
  line-height: 1.3;
  color: var(--finale-fg-soft);
  overflow-wrap: anywhere;
}

/* ══ the horizon ══════════════════════════════════════════════════════════
 *
 * The ground is in normal flow, not absolutely positioned. That sounds like a
 * detail and is the whole reason this composition works: the horizon is the
 * land's top edge, the grass sits exactly on it, and the colophon band below
 * is the same earth colour, so the ground reads as one continuous mass rather
 * than a landscape with a toolbar laid across it.
 */

.finale__land {
  position: relative;
  z-index: 1;
  height: clamp(84px, 15vh, 160px);
  margin-top: clamp(1.4rem, 5vh, 3.4rem);
  pointer-events: none;
}

/* Light pooling behind the hills. The sky is a linear ramp, which is correct
   for a sky and wrong for a landscape — a horizon is where the light is. */
.finale__land::before {
  content: "";
  position: absolute;
  left: -10%;
  right: -10%;
  bottom: 18%;
  height: 78%;
  background: radial-gradient(58% 92% at 50% 100%, var(--finale-halo), transparent 72%);
  transition: opacity 1.5s ease;
}

.finale__ridge {
  position: absolute;
  left: -10%;
  width: 120%;
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  transition: background-color 1.5s ease;
}

/* the far hill rises highest and sits furthest back */
.finale__ridge--far {
  bottom: 44%;
  height: 118%;
  left: -16%;
  width: 132%;
  background: var(--finale-hill-far);
}

.finale__ridge--near {
  bottom: 26%;
  height: 92%;
  border-radius: 44% 56% 0 0 / 100% 100% 0 0;
  background: var(--finale-hill-near);
}

/* the earth itself: flat-topped on purpose, so the grass line is the horizon */
.finale__field {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 62%;
  background: var(--finale-field);
  transition: background-color 1.5s ease;
}

.finale__fringe {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 62%;
  width: 100%;
  height: clamp(18px, 3.4vh, 34px);
  color: var(--finale-field);
  transition: color 1.5s ease;
}

.finale__fringe path { fill: currentColor; }

/* ── tiny hidden details ────────────────────────────────────────────────── */

/* a snail, working its way along the grass line */
.finale__snail {
  position: absolute;
  left: 12%;
  bottom: 60%;
  width: 30px;
  animation: finale-crawl 46s ease-in-out infinite;
}

.finale__snail path { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.finale__snail circle { fill: none; stroke: currentColor; stroke-width: 2; }
.finale__snail circle + path { stroke-width: 1.4; }

@keyframes finale-crawl {
  0%, 100% { translate: 0 0; }
  50% { translate: 30px -1px; }
}

/* a firefly, only at night */
.finale__firefly {
  position: absolute;
  left: 63%;
  bottom: 80%;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #FFF3A6;
  box-shadow: 0 0 12px 3px rgba(255, 243, 166, .75);
  opacity: 0;
  animation: finale-blink 5.5s ease-in-out infinite;
}

.finale[data-sky="day"] .finale__firefly { animation: none; opacity: 0; }

@keyframes finale-blink {
  0%, 100% { opacity: 0; transform: scale(.6); }
  12% { opacity: 1; transform: scale(1); }
  26% { opacity: .1; transform: scale(.7); }
  38% { opacity: 1; transform: scale(1); }
  60% { opacity: 0; transform: scale(.6); }
}

/* the signpost on the far side of the field */
.finale__sign {
  position: absolute;
  right: 16%;
  bottom: 58%;
  width: 62px;
  height: 46px;
}

.finale__sign rect { fill: currentColor; }
.finale__sign text {
  font-family: var(--mono, ui-monospace, Consolas, monospace);
  font-size: 11px;
  font-weight: 700;
  fill: var(--finale-solid);
}

/* ══ the colophon ═════════════════════════════════════════════════════════ */

.finale__bar {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .55rem 1.2rem;
  margin-top: 0;
  padding: 1rem clamp(1.1rem, 5vw, 3rem) 1.2rem;
  font-size: .74rem;
  color: var(--finale-fg-soft);
  background: var(--finale-field);
  transition: background-color 1.5s ease;
}

.finale__bar p { margin: 0; }

.finale__bar-mid { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; }

.finale__toggle,
.finale__top {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .48rem .95rem;
  border: 2px solid var(--finale-line);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: 700;
  letter-spacing: .01em;
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease;
}

.finale__toggle:hover,
.finale__top:hover { background: var(--finale-fg); color: var(--finale-solid); }

.finale__toggle:focus-visible,
.finale__top:focus-visible { outline: 3px solid currentColor; outline-offset: 3px; }

.finale__toggle-glyph {
  display: grid;
  place-items: center;
  width: 14px;
  height: 14px;
  font-size: .72rem;
  line-height: 1;
}

.finale[data-sky="day"] .finale__toggle-glyph::before { content: "☾"; }
.finale[data-sky="night"] .finale__toggle-glyph::before { content: "☀"; }

/* ══ no-JS ════════════════════════════════════════════════════════════════ */

/* Sits in the same optical place the scripted body would, so the two states
   share a centre line and the band never reads as a broken header. */
.finale__noscript {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: clamp(280px, 52vh, 560px);
  max-width: min(48rem, 100%);
  margin-inline: auto;
  padding: clamp(2.5rem, 8vh, 5rem) clamp(1.1rem, 5vw, 3rem);
  text-align: center;
}

.finale__noscript h2 {
  margin: 0 0 1rem;
  font-family: var(--serif, Georgia, serif);
  font-size: clamp(1.6rem, 6vw, 3.2rem);
  letter-spacing: -.02em;
}

.finale__noscript p { margin: .35rem 0; overflow-wrap: anywhere; }

.finale__noscript a {
  color: inherit;
  font-weight: 700;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

/* ══ performance ══════════════════════════════════════════════════════════ */

/* Off-screen, nothing in the scene animates. footer.js flips data-live. */
.finale[data-live="off"] *,
.finale[data-live="off"] *::before { animation-play-state: paused; }

/* ══ reduced motion ═══════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  .finale *,
  .finale *::before,
  .finale *::after {
    animation: none !important;
    transition-duration: .001ms !important;
  }

  .finale__plate { opacity: 0; }
  .finale__plate--day { opacity: 1; }
  .finale[data-sky="night"] .finale__plate--day { opacity: 0; }
  .finale[data-sky="night"] .finale__plate--night { opacity: 1; }

  .finale[data-in="false"] .finale__body > * { opacity: 1; translate: none; }

  /* the plane and the clouds park instead of travelling off the scene */
  .finale__plane { top: 11%; translate: 16vw 0; }
  .finale-cloud--1 { translate: 22vw 0; }
  .finale-cloud--2 { translate: -18vw 0; }
  .finale-cloud--3 { translate: 54vw 0; }

  .finale__orb--sun svg { rotate: 0deg; }
  .finale__star::before { opacity: .85; }
}
