/* @format */

/* ==========================================================================
   /wishlist — a sunny day.

   A wish's size is what it costs YOU, the reader. With the cards gone, that
   scale is carried purely by type size: a tiny wish is small text, a large
   one is big text. Nothing on the page asks twice.

   Build notes:
   - No framework, no build step, no image files. The clouds are SVG
     fractal-noise (feTurbulence) rasterised as a tiling background and
     scrolled at three speeds. Drawn circle-clouds read as cartoon; banded
     noise is what actually looks like weather.
   - Motion is transform-only on fixed layers, so it composites on the GPU
     and never triggers layout. prefers-reduced-motion parks everything.
   - Text sits directly on the sky, so every label carries a soft white
     text-shadow. That is what keeps ink legible when a cloud drifts behind
     it, and it is why the copy is dark rather than white — a line of text
     can end up over pale cloud or open blue, and only dark survives both.
   ========================================================================== */

@font-face {
  font-family: "Europa";
  /* Absolute paths: this file lives in /styles/, the fonts do not. */
  src: url("/fonts/europa.woff2") format("woff2"),
    url("/fonts/europa.woff") format("woff"),
    url("/fonts/europa.ttf") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

:root {
  --ink: #123a54; /* deep sky-blue-black, for the wishes themselves */
  --ink-soft: #4d7c9b; /* hostnames, costs, signature */
  --halo: rgba(255, 255, 255, 0.75); /* the glow that lifts ink off cloud */
  --measure: 52rem;
}

* {
  box-sizing: border-box;
}

html {
  /* Flat mid-sky underneath everything. The gradient itself lives on .sky,
     which is position:fixed and therefore always exactly one viewport tall.
     This colour is the safety net behind it (overscroll, rubber-banding). */
  background-color: #6fb0dd;
}

body.wishlist-page {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
    sans-serif;
  font-size: 17px;
  line-height: 1.5;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* --------------------------------------------------------------------------
   THE SKY
   One fixed, inert layer behind everything. aria-hidden in the markup.

   The gradient is four stops rather than two because a real sky is not a
   linear ramp: it holds a deep blue through the upper third, then falls off
   quickly to a pale, hazy band at the horizon. Two stops looks like a
   gradient; four looks like air.
   -------------------------------------------------------------------------- */

.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    #2b7bc0 0%,
    #4e9ad4 28%,
    #86bfe3 62%,
    #c3dfee 88%,
    #dcebf3 100%
  );
}

/* Atmospheric haze: real distant air is brighter and less saturated near the
   horizon. This sits above the cloud layers and washes them out toward the
   bottom, which is what sells depth more than the clouds themselves do. */
.haze {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0) 45%,
    rgba(255, 255, 255, 0.22) 78%,
    rgba(255, 255, 255, 0.45) 100%
  );
}

/* Each layer is one tiled noise-cloud SVG. The element is wider than the
   viewport by two tiles so that translating it by exactly one tile width
   loops seamlessly — the repeat pattern lands back on itself.

   The SVG: feTurbulence fractalNoise generates cloud-like noise;
   feColorMatrix then forces RGB to pure white and remaps the red channel
   into alpha with a steep slope (2.6R - 1.25), which clips the low half to
   fully transparent and leaves soft-edged white masses. baseFrequency is
   anisotropic (x much lower than y) so the masses stretch horizontally the
   way wind-sheared cloud does. stitchTiles keeps the tiling seamless. */
/* --tile is the width of one repeat of this layer's background. The layer
   animates right by exactly that distance and then snaps back, which is what
   makes the loop invisible.

   The overhang either side MUST exceed --tile. It is sized from --tile rather
   than hardcoded because an earlier fixed 1200px overhang was smaller than
   the mid (1400px) and near (2100px) tiles: at the end of each cycle those
   layers' own left edge slid into the viewport and swept a hard vertical
   boundary across the page. The +160px is slack against subpixel rounding at
   the moment the animation wraps. */
.clouds {
  position: absolute;
  top: 0;
  left: calc(-1 * var(--tile) - 160px);
  width: calc(100% + var(--tile) * 2 + 320px);
  height: 100%;
  background-repeat: repeat;
  will-change: transform;
  animation: drift var(--speed) linear infinite;
}

/* One keyframe for every layer: the distance is read from --tile per layer. */
@keyframes drift {
  to {
    transform: translate3d(var(--tile), 0, 0);
  }
}

.clouds--far {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='500'%3E%3Cfilter id='a' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.0055 0.019' numOctaves='6' seed='11' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 2.4 0 0 0 -1.3'/%3E%3C/filter%3E%3Crect width='900' height='500' filter='url(%23a)'/%3E%3C/svg%3E");
  --tile: 900px;
  --speed: 300s;
  background-size: 900px 320px;
  background-position: 0 -40px;
  opacity: 0.38;
}

.clouds--mid {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='500'%3E%3Cfilter id='b' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.004 0.013' numOctaves='6' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 2.7 0 0 0 -1.35'/%3E%3C/filter%3E%3Crect width='900' height='500' filter='url(%23b)'/%3E%3C/svg%3E");
  --tile: 1400px;
  --speed: 195s;
  background-size: 1400px 520px;
  background-position: 0 60px;
  opacity: 0.55;
}

.clouds--near {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='500'%3E%3Cfilter id='c' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.003 0.0085' numOctaves='7' seed='19' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 3 0 0 0 -1.55'/%3E%3C/filter%3E%3Crect width='900' height='500' filter='url(%23c)'/%3E%3C/svg%3E");
  --tile: 2100px;
  --speed: 120s;
  background-size: 2100px 780px;
  background-position: 0 140px;
  opacity: 0.72;
}

/* --------------------------------------------------------------------------
   PAGE SHELL
   -------------------------------------------------------------------------- */

.shell {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 2rem 1.25rem 5rem;
}

/* The nav sits at the very top, where the gradient is at its darkest and a
   cloud can pass right behind it. Small dark text lost that fight, so these
   get more size, more weight and a stronger halo than body copy does. */
.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.4rem;
  justify-content: center;
  margin-bottom: 4rem;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
}

.nav a {
  color: #0d3049;
  font-weight: 600;
  text-decoration: none;
  padding: 0.25rem 0.1rem;
  border-bottom: 2px solid transparent;
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.95),
    0 0 12px rgba(255, 255, 255, 0.9);
}

.nav a:hover,
.nav a:focus-visible {
  border-bottom-color: #0d3049;
}

.masthead {
  text-align: center;
  margin-bottom: 4.5rem;
}

.masthead h1 {
  font-family: "Europa", -apple-system, Helvetica, Arial, sans-serif;
  font-weight: normal;
  font-size: clamp(3.25rem, 13vw, 6rem);
  line-height: 1;
  letter-spacing: 0.05em;
  margin: 0;
  color: #ffffff;
  /* A hard-edged sky-blue shadow rather than a blur: the type should look
     cut out of the sky, not floating above it. */
  text-shadow: 0 3px 0 rgba(24, 78, 118, 0.3);
}

/* --------------------------------------------------------------------------
   A SIZE BAND

   The heading is centred with a rule running out to both margins. The two
   .rule spans are flex:1, so they always split the leftover width evenly
   however long the label is.
   -------------------------------------------------------------------------- */

.band {
  margin-bottom: 4.5rem;
}

.band-head {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-bottom: 2rem;
}

.band-head .rule {
  flex: 1;
  height: 1px;
  background: rgba(18, 58, 84, 0.28);
}

/* Deep ink, NOT white. A band heading can be scrolled to any point in the
   fixed gradient and can overlap a passing cloud, so it has no reliable
   backdrop. White reads at the top of the page and vanishes at the bottom. */
.band-head h2 {
  font-family: "Europa", -apple-system, Helvetica, Arial, sans-serif;
  font-weight: normal;
  font-size: 1.6rem;
  letter-spacing: 0.09em;
  margin: 0;
  white-space: nowrap;
  color: var(--ink);
  text-shadow: 0 1px 0 var(--halo);
}

/* --------------------------------------------------------------------------
   THE WISHES
   One per row, centred, sitting straight on the sky.
   -------------------------------------------------------------------------- */

.wishes {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
}

.wishes li + li {
  margin-top: var(--row-gap, 1.5rem);
}

.wish {
  --wish-size: 1.6rem;

  display: inline-block;
  max-width: 100%;
  font-size: var(--wish-size);
  line-height: 1.3;
  color: var(--ink);
  text-decoration: none;
  /* The halo is doing real work here: with no card behind the type, this is
     the only thing separating ink from a cloud edge passing underneath. */
  text-shadow: 0 1px 2px var(--halo), 0 0 10px var(--halo);
  transition: color 0.2s ease;
}

.wish-text {
  display: block;
  border-bottom: 1px solid rgba(18, 58, 84, 0.3);
  padding-bottom: 0.1em;
}

/* Sized in rem, not em. At 0.62em these scaled with the tier, so the tiny
   band's hosts came out around 10px on a busy sky — unreadable. A fixed rem
   size keeps every host line at one legible size regardless of tier, and it
   reads better as a system: the wish grows, its address does not. */
.wish-host {
  display: block;
  margin-top: 0.4em;
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  color: #2f5f7e;
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.95),
    0 0 12px rgba(255, 255, 255, 0.9);
}

.wish:hover .wish-text,
.wish:focus-visible .wish-text {
  border-bottom-color: var(--ink);
}

.wish:hover,
.wish:focus-visible {
  color: #06283d;
}

.wish:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 6px;
  border-radius: 2px;
}

/* --- the tiers ---------------------------------------------------------- */
/* Every wish is set at one size. An earlier version scaled type by tier so
   that a bigger ask was literally bigger; that is dropped deliberately. The
   band headings carry the hierarchy now, and uniform type keeps the column
   of wishes reading as one calm list rather than a crescendo.
   The .wishes--* classes stay on the lists as hooks for per-tier spacing. */
.wishes--tiny,
.wishes--small,
.wishes--medium,
.wishes--large {
  --row-gap: 1.9rem;
}

/* --------------------------------------------------------------------------
   FOOTER
   -------------------------------------------------------------------------- */

.ground {
  text-align: center;
  margin-top: 6rem;
  color: var(--ink);
  text-shadow: 0 1px 0 var(--halo);
}

.ground p {
  margin: 0 0 0.75rem;
}

.ground .sign {
  font-family: "Europa", -apple-system, Helvetica, Arial, sans-serif;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   RESPONSIVE
   -------------------------------------------------------------------------- */

@media (max-width: 750px) {
  body.wishlist-page {
    font-size: 16px;
  }

  .shell {
    padding: 1.5rem 1.15rem 4rem;
  }

  .masthead {
    margin-bottom: 3rem;
  }

  .nav {
    margin-bottom: 2.5rem;
  }

  .band {
    margin-bottom: 3.5rem;
  }

  /* One-word labels stay on the line even on a phone; just tighten the gap
     so the rules keep a usable length either side. */
  .band-head {
    gap: 0.9rem;
  }

  .wish {
    --wish-size: 1.25rem;
  }
}

/* --------------------------------------------------------------------------
   REDUCED MOTION
   The clouds are decorative; a stopped sky is still a sky.
   -------------------------------------------------------------------------- */

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

  .wish {
    transition: none;
  }
}
