/* ==========================================================================
   Nectaring, aero.css
   The home page, rebuilt in the Frutiger Aero / Windows Vista register.

   Rules this file keeps from the original build, because they were earned:
     - Zero external requests. Fonts are self hosted, every graphic is CSS.
     - WCAG AA in both light and dark. Glass is only ever as transparent as
       the text on top of it can afford.
     - prefers-reduced-motion stops every moving part.
     - No JavaScript is required to read anything.

   Open source used here: Open Sans (SIL OFL 1.1), by Steve Matteson, who
   also designed Segoe UI. It is the closest legitimately free face to the
   Vista voice. Everything else, the glass, the gloss, the bubbles, the
   aurora, is plain CSS.

   SECTIONS
     1.  Fonts
     2.  Tokens, light and dark
     3.  Reset and base
     4.  The sky: gradient, aurora, bubbles
     5.  Taskbar and ticker
     6.  Windows: frame, title bar, gloss
     7.  Wordmark and hero
     8.  The new single
     9.  Listen: the back catalogue
     10. About
     11. Commonplace book
     12. Contact
     13. Buttons and links
     14. Footer
     15. Motion
     16. Small screens
     17. Reduced motion, forced colors, print
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. FONTS

   Two faces, both SIL OFL, both self hosted, no external request.

     UnifrakturMaguntia   the wordmark, and only ever the wordmark.
               Blackletter is unreadable as running text and magnificent
               as a logotype.

     Grenze Gotisch       the chrome: headings, song titles, title bars,
               buttons, the badge. Gothic but still legible at 16px.

     Nunito               running text and anything small. Rounded
               humanist, so it sits beside the gothic without arguing
               with it, and boring enough to read a bio in.

     Nunito italic is declared but nothing on this page uses it at the
     moment. That costs nothing: a browser only downloads a face it
     actually needs, so an unused @font-face is a declaration, not a
     download. It stays because italic copy is likely to come back.

     Chicle was here and is retired. Its file is still in fonts/ and is
     never fetched; delete it whenever.
   -------------------------------------------------------------------------- */

@font-face {
  /* The gothic. Used on chrome and labels, never on running text. */
  font-family: "Grenze Gotisch";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/grenze-gotisch-latin-600-normal.woff2") format("woff2");
}
@font-face {
  /* Blackletter, and it earns exactly one job: the illuminated initial. */
  font-family: "UnifrakturMaguntia";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/unifrakturmaguntia-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/nunito-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/nunito-latin-400-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/nunito-latin-600-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/nunito-latin-700-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../fonts/nunito-latin-800-normal.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
   2. TOKENS

   The sky is the authentic 2007 palette: deep aqua at the top of the
   window, warming down through teal to a lime horizon. Pink is the accent
   that runs through all of it, and it is Jackson's, not Microsoft's.

   Two pinks, deliberately. --pink is the loud one and is only ever used as
   a FILL or for large display type. --pink-ink is the one that carries
   small text, because the loud one does not clear 4.5:1 on glass.
   -------------------------------------------------------------------------- */

:root {
  color-scheme: light dark;

  --sky-1: #0a4f74;
  --sky-2: #0f83ac;
  --sky-3: #2fb6c2;
  --sky-4: #79d6b4;
  --sky-5: #c6e98d;

  --aurora-a: rgba(255, 90, 175, 0.34);
  --aurora-b: rgba(120, 235, 255, 0.50);
  --aurora-c: rgba(198, 233, 141, 0.45);

  /* The glass now carries a pink cast. Every pane on the page is very
     slightly rose, which is a quieter way to add pink than painting more
     pink things on top of it. */
  --glass:        rgba(255, 244, 250, 0.76);
  --glass-strong: rgba(255, 248, 252, 0.90);
  --glass-quiet:  rgba(255, 244, 250, 0.54);
  --glass-edge:   rgba(255, 255, 255, 0.92);
  --glass-line:   rgba(10, 79, 116, 0.16);

  --ink:      #2a0f2b;
  --ink-soft: #5c2748;

  --pink:      #ff2d95;
  --pink-ink:  #ad005b;
  --pink-deep: #7d0042;
  --pink-bright: #ff2d95;   /* the token sparkle.js reads */
  --pink-wash: rgba(255, 45, 149, 0.20);

  --shadow: 0 18px 46px rgba(4, 42, 60, 0.30);
  --shadow-soft: 0 8px 22px rgba(4, 42, 60, 0.20);

  --radius: 14px;
  --radius-sm: 9px;

  /* The wordmark only. Blackletter is unreadable as running text and
     magnificent as a logotype, which is exactly the job it has here. */
  --display: "UnifrakturMaguntia", "Grenze Gotisch", serif;
  --gothic: "Grenze Gotisch", "Palatino Linotype", Palatino, serif;
  --black-letter: "UnifrakturMaguntia", "Grenze Gotisch", serif;
  --body: "Nunito", "Segoe UI", system-ui, -apple-system, sans-serif;

  /* Gold leaf. Three stops, because real gilding has a dark line where the
     leaf turns away from the light and that is what stops it reading as
     yellow plastic. */
  --gold-1: #fff3c4;
  --gold-2: #e8b23a;
  --gold-3: #8a5a12;
  --gold-leaf: linear-gradient(180deg, var(--gold-1) 0%, #f7d97a 26%, var(--gold-2) 52%, var(--gold-3) 74%, #f2cf6a 100%);
  --gold-ink: #6b4405;

  --measure: 64ch;
  --pad: clamp(1rem, 4vw, 2.25rem);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sky-1: #04121f;
    --sky-2: #072a44;
    --sky-3: #0b4a5c;
    --sky-4: #10614f;
    --sky-5: #1d6f3f;

    --gold-1: #ffeeb0; --gold-2: #d9a334; --gold-3: #6d4610;
    --gold-ink: #f2cf6a;
    --aurora-a: rgba(255, 45, 149, 0.38);
    --aurora-b: rgba(60, 190, 230, 0.32);
    --aurora-c: rgba(150, 220, 120, 0.24);

    --glass:        rgba(28, 12, 38, 0.74);
    --glass-strong: rgba(28, 12, 38, 0.88);
    --glass-quiet:  rgba(28, 12, 38, 0.56);
    --glass-edge:   rgba(150, 225, 245, 0.34);
    --glass-line:   rgba(150, 225, 245, 0.18);

    --ink:      #fdeaf6;
    --ink-soft: #d4a9c8;

    --pink:      #ff4ea5;
    --pink-ink:  #ff8fc6;
    --pink-deep: #ffb9dc;
    --pink-bright: #ff4ea5;
    --pink-wash: rgba(255, 78, 165, 0.18);

    --shadow: 0 18px 46px rgba(0, 0, 0, 0.55);
    --shadow-soft: 0 8px 22px rgba(0, 0, 0, 0.40);
  }
}

/* --------------------------------------------------------------------------
   3. RESET AND BASE
   -------------------------------------------------------------------------- */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--body);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.075rem);
  line-height: 1.62;
  color: var(--ink);
  background: var(--sky-1);
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }

h1, h2, h3 {
  font-family: var(--gothic);
  font-weight: 600;
  line-height: 1.2;
  margin: 0;
  letter-spacing: 0;
  text-wrap: balance;
}

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

::selection { background: var(--pink); color: #fff; }

:focus-visible {
  outline: 3px solid var(--pink);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  background: #fff;
  color: var(--pink-deep);
  padding: 0.7rem 1.1rem;
  border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 700;
}
.skip-link:focus { left: 0; }

.container {
  width: min(1080px, 100% - 2rem);
  margin-inline: auto;
}

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

/* --------------------------------------------------------------------------
   4. THE SKY

   Four stacked fixed layers instead of one flat gradient, because a gradient
   is what made the old version read as basic:

     .sky       a rendered sky photograph, clouded, with a sun bloom
     .caustics  underwater light, screened over the sky and slowly drifting
     .bokeh     out-of-focus bubbles, far back, drifting the other way
     .bubbles   sharp bubbles in front, built by aero.js

   The two bubble fields moving at different speeds is what gives the page
   depth: near ones cross the screen, far ones barely move.

   All four are decorative, aria-hidden, and none of them takes a pointer
   event. Every texture was generated here with numpy, so there is nothing
   to license and nothing to attribute.
   -------------------------------------------------------------------------- */

.sky {
  position: fixed;
  inset: 0;
  z-index: -4;
  pointer-events: none;
  /* The gradient stays underneath as the paint-before-load colour and as the
     fallback if the image ever fails. */
  background:
    image-set(url("../img/sky-light.webp") type("image/webp")) center / cover no-repeat,
    linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 34%, var(--sky-3) 62%, var(--sky-4) 84%, var(--sky-5) 100%);
  background-attachment: fixed;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sky {
    background:
      image-set(url("../img/sky-dark.webp") type("image/webp")) center / cover no-repeat,
      linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 34%, var(--sky-3) 62%, var(--sky-4) 84%, var(--sky-5) 100%);
    background-attachment: fixed;
  }
}

/* Caustics. Screen blend means the black of the tile does nothing and only
   the filaments of light land, which is why the file carries no alpha. */
.caustics {
  position: fixed;
  inset: -12%;
  z-index: -3;
  pointer-events: none;
  background: url("../img/caustics.webp") 0 0 / 520px 520px repeat;
  mix-blend-mode: screen;
  opacity: 0.24;
  animation: caustic-drift 48s linear infinite;
  will-change: background-position;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .caustics { opacity: 0.20; }
}

@keyframes caustic-drift {
  from { background-position: 0 0, 0 0; }
  to   { background-position: 620px 380px, -420px 260px; }
}

/* A second caustic pass at a different scale and direction. Two crossing
   layers is what stops it looking like a repeating tile. */
.caustics::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../img/caustics.webp") 0 0 / 940px 940px repeat;
  opacity: 0.55;
  animation: caustic-drift-2 71s linear infinite reverse;
}

@keyframes caustic-drift-2 {
  from { background-position: 0 0; }
  to   { background-position: -940px 520px; }
}

/* The far bubble field. */
.bokeh {
  position: fixed;
  inset: -8%;
  z-index: -2;
  pointer-events: none;
  background: url("../img/bokeh.webp") center / cover no-repeat;
  opacity: 0.85;
  animation: bokeh-drift 90s ease-in-out infinite alternate;
  will-change: transform;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bokeh { opacity: 0.55; }
}

@keyframes bokeh-drift {
  from { transform: translate3d(0, 0, 0) scale(1.02); }
  to   { transform: translate3d(-3vw, -5vh, 0) scale(1.10); }
}

/* One pink bloom, low and slow, so the sky is never purely Microsoft. */
.aurora {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  overflow: hidden;
  filter: blur(110px);
  opacity: 0.62;
}

.aurora span {
  position: absolute;
  display: block;
  border-radius: 50%;
  mix-blend-mode: screen;
  will-change: transform;
}

.aurora .a1 {
  width: 62vw; height: 62vw;
  left: -16vw; top: -12vw;
  background: radial-gradient(circle, var(--aurora-a) 0%, transparent 68%);
  animation: drift-a 38s ease-in-out infinite alternate;
}
.aurora .a2 {
  width: 44vw; height: 44vw;
  right: -12vw; top: 26vh;
  background: radial-gradient(circle, var(--aurora-b) 0%, transparent 70%);
  animation: drift-b 47s ease-in-out infinite alternate;
}
.aurora .a3 {
  width: 56vw; height: 56vw;
  left: 26vw; bottom: -26vw;
  background: radial-gradient(circle, var(--aurora-c) 0%, transparent 70%);
  animation: drift-c 55s ease-in-out infinite alternate;
}

@keyframes drift-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(9vw, 11vh, 0) scale(1.18); }
}
@keyframes drift-b {
  from { transform: translate3d(0, 0, 0) scale(1.1); }
  to   { transform: translate3d(-11vw, 15vh, 0) scale(0.9); }
}
@keyframes drift-c {
  from { transform: translate3d(0, 0, 0) scale(0.95); }
  to   { transform: translate3d(-7vw, -13vh, 0) scale(1.22); }
}

/* The near bubble field, built by aero.js so the count follows the screen. */
.bubbles {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}

.bubble {
  position: absolute;
  bottom: -18vh;
  border-radius: 50%;
  background:
    radial-gradient(circle at 30% 26%,
      rgba(255, 255, 255, 0.98) 0%,
      rgba(255, 255, 255, 0.50) 15%,
      rgba(255, 255, 255, 0.07) 40%,
      rgba(180, 240, 255, 0.05) 62%,
      rgba(255, 255, 255, 0.30) 86%,
      rgba(255, 255, 255, 0.70) 100%);
  box-shadow:
    inset 0 0 18px rgba(255, 255, 255, 0.65),
    inset -4px -6px 12px rgba(120, 220, 255, 0.35),
    0 0 16px rgba(255, 255, 255, 0.32);
  opacity: 0;
  will-change: transform, opacity;
  animation-name: rise;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.bubble.pinkish {
  background:
    radial-gradient(circle at 30% 26%,
      rgba(255, 255, 255, 0.98) 0%,
      rgba(255, 200, 230, 0.58) 17%,
      rgba(255, 45, 149, 0.12) 44%,
      rgba(255, 45, 149, 0.06) 64%,
      rgba(255, 130, 195, 0.36) 86%,
      rgba(255, 190, 225, 0.78) 100%);
  box-shadow:
    inset 0 0 18px rgba(255, 215, 238, 0.7),
    inset -4px -6px 12px rgba(255, 100, 180, 0.35),
    0 0 20px rgba(255, 45, 149, 0.35);
}

@keyframes rise {
  0%   { transform: translate3d(0, 0, 0) scale(0.7); opacity: 0; }
  8%   { opacity: 0.9; }
  92%  { opacity: 0.7; }
  100% { transform: translate3d(var(--drift, 4vw), -128vh, 0) scale(1); opacity: 0; }
}

/* --------------------------------------------------------------------------
   5. TASKBAR AND TICKER
   -------------------------------------------------------------------------- */

.taskbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.55rem clamp(0.9rem, 3vw, 1.6rem);
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.72) 0%,
    rgba(255, 255, 255, 0.44) 49%,
    rgba(255, 255, 255, 0.20) 51%,
    rgba(255, 255, 255, 0.42) 100%);
  backdrop-filter: blur(18px) saturate(1.5);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  border-bottom: 1px solid var(--glass-edge);
  box-shadow: 0 4px 18px rgba(4, 42, 60, 0.22);
}

/* In dark, the Press Kit pill kept its light-mode glass, which put pale pink
   on a pale ground at 2.2:1. It gets dark glass so the pink can stay pink. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .taskbar nav a {
    background: linear-gradient(180deg, rgba(18, 58, 78, 0.92), rgba(10, 38, 52, 0.62));
    border-color: rgba(150, 225, 245, 0.32);
  }
  :root:not([data-theme="light"]) .taskbar nav a:hover {
    background: linear-gradient(180deg, var(--pink) 0%, #c9006b 100%);
    color: #fff;
  }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .taskbar {
    background: linear-gradient(
      180deg,
      rgba(12, 40, 55, 0.80) 0%,
      rgba(12, 40, 55, 0.58) 49%,
      rgba(12, 40, 55, 0.42) 51%,
      rgba(12, 40, 55, 0.62) 100%);
  }
}

.taskbar .brand {
  font-family: var(--gothic);
  font-weight: 600;
  font-size: 1.24rem;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.taskbar .brand::before {
  content: "";
  width: 15px; height: 15px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 30%,
      #fff 0%, rgba(255,255,255,0.7) 22%,
      var(--pink) 60%, #8d0049 100%);
  box-shadow: 0 0 10px var(--pink-wash);
  flex: none;
}

.taskbar nav a {
  color: var(--pink-ink);
  font-weight: 800;
  text-decoration: none;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  border: 1px solid var(--glass-line);
  background: linear-gradient(180deg, rgba(255,255,255,0.85), rgba(255,255,255,0.35));
  transition: transform 160ms ease, box-shadow 160ms ease, color 160ms ease;
}
.taskbar nav a:hover {
  color: #fff;
  background: linear-gradient(180deg, var(--pink) 0%, #d1006e 100%);
  box-shadow: 0 4px 14px rgba(255, 45, 149, 0.45);
  transform: translateY(-1px);
}

/* The ticker. Early internet, and it earns its place: it is where "out now"
   lives while you are anywhere on the page. */
.ticker {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, #f0187f 0%, #99004f 100%);
  color: #fff;
  font-weight: 800;
  font-size: 0.82rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  padding: 0.44rem 0;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), 0 3px 12px rgba(255,45,149,0.35);
}

.ticker span {
  display: inline-block;
  white-space: nowrap;
  padding-left: 100%;
  animation: ticker 26s linear infinite;
}

@keyframes ticker {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-100%, 0, 0); }
}

/* --------------------------------------------------------------------------
   6. WINDOWS

   The unit of the whole page. A pane of Aero glass with a glossy title bar,
   three decorative chrome buttons, and a shine that sweeps once when it
   arrives and again on hover.
   -------------------------------------------------------------------------- */

.win {
  position: relative;
  isolation: isolate;
  margin: 0 0 clamp(1.6rem, 5vw, 2.8rem);
  border-radius: var(--radius);
  /* Two edges, not one: a bright outer rim and a darker inner line. That is
     what makes a pane read as having thickness instead of being a rectangle
     with a border. */
  border: 1px solid var(--glass-edge);
  outline: 1px solid rgba(10, 79, 116, 0.10);
  outline-offset: -2px;
  background:
    /* the wet sheen across the top third */
    radial-gradient(140% 68% at 50% -18%,
      rgba(255, 255, 255, 0.88) 0%,
      rgba(255, 255, 255, 0.34) 42%,
      rgba(255, 255, 255, 0.00) 68%),
    linear-gradient(180deg, var(--glass-strong) 0%, var(--glass) 62%, var(--glass-strong) 100%);
  backdrop-filter: blur(22px) saturate(1.7);
  -webkit-backdrop-filter: blur(22px) saturate(1.7);
  box-shadow:
    var(--shadow),
    0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.98),
    inset 0 -14px 26px rgba(255, 255, 255, 0.30),
    inset 0 -1px 0 rgba(255, 255, 255, 0.55);
  overflow: hidden;
}

/* Dew on the glass, plus a caustic pass across it. Both sit above the pane's
   own fill and below its content, both are decorative, and neither ever
   takes a click. */
.win::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    url("../img/butterflies.webp") 0 0 / 150px 150px repeat,
    url("../img/droplets.webp") 0 0 / 280px 280px repeat,
    url("../img/caustics.webp") 0 0 / 520px 520px repeat;
  background-blend-mode: normal, normal, screen;
  opacity: 0.20;          /* enough to feel wet and figured, not enough to fight the copy */
  mix-blend-mode: soft-light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .win::before { opacity: 0.22; }
}

/* One long specular streak down the pane, the way light sits on wet glass. */
.win::after {
  content: "";
  position: absolute;
  z-index: 0;
  top: -30%;
  bottom: -30%;
  left: -14%;
  width: 26%;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.30) 45%,
    rgba(255, 255, 255, 0.55) 50%,
    rgba(255, 255, 255, 0.28) 55%,
    transparent 100%);
  transform: skewX(-16deg) rotate(2deg);
  opacity: 0.65;
}

/* Content sits above both decorative layers. */
.win-bar, .win-body { position: relative; z-index: 1; }

.win-bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.62rem 0.85rem;
  border-bottom: 1px solid var(--glass-line);
  background: linear-gradient(
    180deg,
    rgba(252, 255, 255, 0.96) 0%,
    rgba(214, 243, 253, 0.78) 46%,
    rgba(150, 214, 236, 0.42) 50%,
    rgba(196, 236, 250, 0.62) 82%,
    rgba(236, 251, 255, 0.86) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -1px 0 rgba(255, 255, 255, 0.75),
    0 1px 0 rgba(10, 79, 116, 0.10);
  overflow: hidden;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .win-bar {
    background: linear-gradient(
      180deg,
      rgba(20, 62, 82, 0.92) 0%,
      rgba(20, 62, 82, 0.62) 48%,
      rgba(20, 62, 82, 0.38) 50%,
      rgba(20, 62, 82, 0.62) 100%);
  }
}

.win-title {
  font-family: var(--display);
  font-weight: 400;
  font-size: 1.08rem;
  letter-spacing: 0.01em;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.win-title .orb {
  width: 12px; height: 12px;
  border-radius: 50%;
  flex: none;
  background: radial-gradient(circle at 32% 28%, #fff 0%, rgba(255,255,255,0.6) 26%, var(--pink) 62%, #8d0049 100%);
  box-shadow: 0 0 8px var(--pink-wash);
}

/* Decorative only. They do nothing, they are hidden from assistive tech,
   and they are not focusable, because a control that lies is worse than
   no control. */
.win-chrome {
  display: flex;
  gap: 5px;
  flex: none;
}

.win-chrome i {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  line-height: 1;
  color: rgba(11, 43, 54, 0.55);
  font-style: normal;
  width: 30px; height: 19px;
  border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, 0.9);
  background:
    radial-gradient(120% 90% at 50% -30%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.0) 62%),
    linear-gradient(180deg, rgba(246,253,255,0.96) 0%, rgba(200,235,248,0.55) 49%, rgba(160,214,235,0.30) 51%, rgba(216,242,252,0.72) 100%);
  box-shadow:
    0 1px 3px rgba(4, 42, 60, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -1px 0 rgba(255, 255, 255, 0.6);
}
.win-chrome i:nth-child(1)::before { content: "\2013"; }
.win-chrome i:nth-child(2)::before { content: "\25A1"; }
.win-chrome i:nth-child(3)::before { content: "\2715"; color: #fff; }

.win-chrome i:last-child {
  background:
    radial-gradient(120% 90% at 50% -30%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 60%),
    linear-gradient(180deg, #ff8ec6 0%, var(--pink) 49%, #c9006b 51%, #ff62ae 100%);
  border-color: rgba(255, 255, 255, 0.75);
  box-shadow:
    0 2px 6px rgba(255, 45, 149, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

/* The shine. One sweep on arrival, another on hover. */
.win-bar::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: -60%;
  width: 45%;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.55) 38%,
    rgba(255, 255, 255, 0.95) 50%,
    rgba(255, 255, 255, 0.55) 62%,
    transparent 100%);
  transform: skewX(-18deg);
  pointer-events: none;
}

.win.in-view .win-bar::after { animation: sweep 1500ms ease-out 250ms 1; }
.win:hover .win-bar::after   { animation: sweep 900ms ease-out 1; }

@keyframes sweep {
  from { left: -60%; }
  to   { left: 130%; }
}

.win-body { padding: var(--pad); }

/* --------------------------------------------------------------------------
   7. WORDMARK AND HERO
   -------------------------------------------------------------------------- */

.hero {
  text-align: center;
  padding: clamp(2.6rem, 9vw, 5rem) 0 clamp(1.6rem, 5vw, 2.6rem);
}

/* The logotype. Three stacked copies of the word, which is how you get a
   glossy Vista-era mark out of live text instead of a flat PNG:

     ::before   sits behind, carries the halo, the pink bloom and the drop
                shadow.
     .wm-fill   the real, selectable, screen-readable text, filled with a
                vertical gradient through background-clip so it is lit from
                above rather than being flat white.
     ::after    sits in front and paints the hard highlight across the top
                half only, clipped to the same letterforms.

   The two decorative copies were <span>s until they were not. aria-hidden
   keeps them from a screen reader but does nothing about text extraction,
   so the h1 of the site read "NectaringNectaringNectaring" to anything
   harvesting the page, which in the single most heavily weighted element on
   the site looks like keyword stuffing. Generated content is painted, not
   part of the document text, so the word now appears exactly once.

   The word comes from data-word on the h1, so it is stated once in the
   markup too.

   The whole thing then gets a wet-floor reflection.
   -------------------------------------------------------------------------- */

.wordmark {
  position: relative;
  display: inline-block;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2.6rem, 10.5vw, 5.8rem);
  line-height: 1.14;
  letter-spacing: 0.012em;
  margin: 0;
  -webkit-box-reflect: below -4px linear-gradient(transparent 78%, rgba(0, 0, 0, 0.30));
}

.wordmark span { display: block; }

.wordmark::before,
.wordmark::after {
  content: attr(data-word);
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
}

.wordmark::before {
  z-index: -1;
  color: #fff;
  text-shadow:
    0 1px 0 rgba(255, 255, 255, 0.95),
    0 2px 2px rgba(140, 225, 250, 0.85),
    0 0 18px rgba(255, 255, 255, 0.55),
    0 0 36px rgba(255, 45, 149, 0.60),
    0 0 84px rgba(255, 45, 149, 0.32),
    0 14px 34px rgba(4, 42, 60, 0.6);
}

.wm-fill {
  background: linear-gradient(
    180deg,
    #ffffff 0%,
    #f4fdff 32%,
    #cceffb 48%,
    #ffffff 51%,
    #e9f9ff 72%,
    #b7e5f6 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.wordmark::after {
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.92) 0%,
    rgba(255, 255, 255, 0.34) 44%,
    rgba(255, 255, 255, 0.00) 50%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* In dark the sky is nearly black, so the mark needs less halo and more
   edge, or it blooms into a smudge. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wordmark::before {
    text-shadow:
      0 1px 0 rgba(255, 255, 255, 0.8),
      0 0 14px rgba(180, 240, 255, 0.5),
      0 0 40px rgba(255, 45, 149, 0.55),
      0 0 90px rgba(255, 45, 149, 0.28),
      0 14px 34px rgba(0, 0, 0, 0.75);
  }
  :root:not([data-theme="light"]) .wm-fill {
    background: linear-gradient(180deg, #ffffff 0%, #eafbff 34%, #a8e0f2 49%, #ffffff 52%, #d7f2fc 74%, #8fd2ea 100%);
    -webkit-background-clip: text;
    background-clip: text;
  }
}

.tagline {
  font-family: var(--body);
  font-weight: 600;
  /* Clear of the wordmark's reflection, which paints outside layout. */
  margin: 2.6rem auto 0;
  max-width: min(46ch, 100%);
  color: #fff;
  font-size: clamp(1.02rem, 0.95rem + 0.5vw, 1.28rem);
  text-shadow: 0 1px 6px rgba(20, 4, 30, 0.8);

  /* Block, but only as wide as its own text: inline-block put the plaque
     on the same line as the wordmark and shoved the logotype off centre. */
  display: block;
  width: fit-content;
  padding: 0.62rem 1.35rem;
  background: linear-gradient(180deg, rgba(46, 12, 48, 0.62) 0%, rgba(24, 6, 34, 0.72) 100%);
  backdrop-filter: blur(10px) saturate(1.3);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  border-top: 2px solid transparent;
  border-bottom: 2px solid transparent;
  border-image: var(--gold-leaf) 1;
  box-shadow: 0 10px 30px rgba(20, 4, 30, 0.42);
}

/* --------------------------------------------------------------------------
   8. THE NEW SINGLE
   The largest object on the page, and the first thing under the wordmark.
   -------------------------------------------------------------------------- */

.win-single {
  border-color: rgba(255, 255, 255, 0.95);
  box-shadow:
    0 22px 60px rgba(4, 42, 60, 0.38),
    0 0 0 1px var(--pink-wash),
    0 0 60px rgba(255, 45, 149, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.95);
}

.single-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: clamp(1.2rem, 4vw, 2.4rem);
  align-items: center;
}

.single-art {
  position: relative;
  display: block;
  border-radius: var(--radius-sm);
  margin-bottom: 2.4rem;   /* room for the reflection */
  transition: transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.single-art img {
  width: 100%;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 14px 34px rgba(4, 42, 60, 0.35);
  -webkit-box-reflect: below 4px linear-gradient(transparent 90%, rgba(0, 0, 0, 0.26));
}

.single-art::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-sm);
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(255, 255, 255, 0.42) 46%,
    rgba(255, 255, 255, 0.72) 50%,
    rgba(255, 255, 255, 0.42) 54%,
    transparent 70%);
  background-size: 260% 100%;
  background-position: 130% 0;
  opacity: 0;
  transition: opacity 220ms ease, background-position 900ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.single-art:hover { transform: translateY(-4px) scale(1.015); }
.single-art:hover::after,
.single-art:focus-visible::after { opacity: 1; background-position: -40% 0; }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  background: linear-gradient(180deg, #f7409a 0%, #e2117a 45%, #96004f 100%);
  color: #fff;
  font-weight: 700;
  font-size: 0.76rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.75);
  box-shadow: 0 4px 14px rgba(255, 45, 149, 0.45), inset 0 1px 0 rgba(255,255,255,0.6);
}

.badge::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 8px #fff;
  animation: blip 1.8s ease-in-out infinite;
}

@keyframes blip {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.7); }
}

.single-title {
  font-family: var(--gothic);
  font-size: clamp(1.6rem, 1.1rem + 2.2vw, 2.6rem);
  line-height: 1.22;
  margin: 0.75rem 0 0.3rem;
  color: var(--ink);
  text-wrap: balance;
}

.single-meta {
  color: var(--ink-soft);
  font-size: 0.94rem;
  margin: 0 0 1.1rem;
}

.single-blurb {
  max-width: 42ch;
  margin-bottom: 1.3rem;
}

/* --------------------------------------------------------------------------
   9. LISTEN, the back catalogue
   -------------------------------------------------------------------------- */

.catalogue {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: clamp(1rem, 3vw, 1.8rem);
}

.catalogue li { text-align: center; }

.catalogue a {
  display: block;
  border-radius: var(--radius-sm);
  text-decoration: none;
  /* The reflection paints outside the layout box, so the caption needs
     room or the two collide. */
  margin-bottom: 2.2rem;
  transition: transform 300ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.catalogue img {
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 10px 26px rgba(4, 42, 60, 0.32);
  -webkit-box-reflect: below 3px linear-gradient(transparent 92%, rgba(0, 0, 0, 0.24));
  transition: box-shadow 300ms ease, filter 300ms ease;
}

.catalogue a:hover { transform: translateY(-6px); }
.catalogue a:hover img {
  box-shadow: 0 18px 34px rgba(4, 42, 60, 0.4), 0 0 28px rgba(255, 45, 149, 0.5);
  filter: saturate(1.15);
}

.catalogue h3 {
  font-family: var(--gothic);
  font-size: 1.22rem;
  line-height: 1.25;
  margin: 0 0 0.15rem;
  color: var(--ink);
}

.catalogue .year {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   10. ABOUT
   -------------------------------------------------------------------------- */

.about-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.7fr);
  gap: clamp(1.2rem, 4vw, 2.4rem);
  align-items: center;
}

.about-copy { max-width: var(--measure); }

/* See section 21 for the portrait: it became a reliquary. */

/* --------------------------------------------------------------------------
   11. COMMONPLACE BOOK
   -------------------------------------------------------------------------- */

.quote-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.quote-list li { margin: 0 0 1.4rem; }
.quote-list.js li { display: none; }
.quote-list.js li.active { display: block; animation: quote-in 520ms ease-out; }

@keyframes quote-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.quote-list blockquote {
  margin: 0 0 0.55rem;
  padding: 0 0 0 1rem;
  border-left: 3px solid var(--pink);
  max-width: var(--measure);
}

.quote-list blockquote p {
  font-size: 1.06rem;
  font-style: italic;
  color: var(--ink);
}

.quote-attr {
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.quote-more { margin: 1.1rem 0 0; }

/* --------------------------------------------------------------------------
   12. CONTACT
   -------------------------------------------------------------------------- */

.contact-rows {
  display: grid;
  gap: 0.55rem;
  margin: 0;
}

.contact-rows div {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.8rem;
  align-items: baseline;
}

.contact-rows dt {
  font-weight: 600;
  color: var(--ink-soft);
  min-width: 6.5rem;
}

.contact-rows dd { margin: 0; }

/* --------------------------------------------------------------------------
   13. BUTTONS AND LINKS
   -------------------------------------------------------------------------- */

a { color: var(--pink-ink); }
a:hover { color: var(--pink-deep); }

.win-body a:not(.btn):not(.catalogue a) {
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
}

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.35rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.8);
  font-weight: 700;
  font-size: 0.98rem;
  text-decoration: none;
  color: var(--ink);
  background:
    radial-gradient(120% 96% at 50% -34%, rgba(255,255,255,0.98) 0%, rgba(255,255,255,0) 60%),
    linear-gradient(
      180deg,
      rgba(252, 255, 255, 0.96) 0%,
      rgba(216, 243, 253, 0.66) 48%,
      rgba(168, 219, 240, 0.36) 50%,
      rgba(226, 247, 255, 0.72) 100%);
  box-shadow:
    var(--shadow-soft),
    inset 0 1px 0 rgba(255, 255, 255, 0.98),
    inset 0 -2px 5px rgba(255, 255, 255, 0.7);
  overflow: hidden;
  transition: transform 180ms ease, box-shadow 180ms ease;
  cursor: pointer;
}

.btn::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: -70%;
  width: 50%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.85), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
}

.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.btn:hover::after { animation: sweep 800ms ease-out 1; }
.btn:active { transform: translateY(0); }

.btn-go {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.65);
  background:
    radial-gradient(120% 96% at 50% -34%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 58%),
    linear-gradient(180deg, #ff6fb2 0%, #f0187f 47%, #a30055 50%, #e42d90 100%);
  box-shadow:
    0 10px 26px rgba(255, 45, 149, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.85),
    inset 0 -2px 6px rgba(255, 255, 255, 0.35);
}
.btn-go:hover { color: #fff; box-shadow: 0 12px 30px rgba(255, 45, 149, 0.62); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn {
    color: var(--ink);
    border-color: rgba(150, 225, 245, 0.35);
    background: linear-gradient(
      180deg,
      rgba(24, 68, 88, 0.95) 0%,
      rgba(18, 54, 72, 0.7) 48%,
      rgba(12, 40, 56, 0.55) 50%,
      rgba(20, 60, 80, 0.75) 100%);
  }
  :root:not([data-theme="light"]) .btn-go {
    color: #fff;
    background: linear-gradient(180deg, #ff77bd 0%, var(--pink) 47%, #c9006c 50%, #ff4fa3 100%);
  }
}

/* --------------------------------------------------------------------------
   14. FOOTER
   -------------------------------------------------------------------------- */

.site-footer {
  margin-top: clamp(2rem, 6vw, 3.5rem);
  padding: 1.4rem 0 2.2rem;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.42) 0%,
    rgba(255, 255, 255, 0.68) 100%);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-top: 1px solid var(--glass-edge);
  text-align: center;
  font-size: 0.92rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .site-footer {
    background: linear-gradient(180deg, rgba(9, 33, 45, 0.55) 0%, rgba(9, 33, 45, 0.85) 100%);
  }
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
  justify-content: center;
  margin: 0 0 0.6rem;
}

.footer-links a { font-weight: 600; }

/* --------------------------------------------------------------------------
   15. MOTION: arrival
   -------------------------------------------------------------------------- */

.js-reveal .win,
.js-reveal .catalogue li {
  opacity: 0;
  transform: translateY(24px) scale(0.985);
}

.js-reveal .win.in-view,
.js-reveal .catalogue li.in-view {
  opacity: 1;
  transform: none;
  transition: opacity 700ms ease, transform 700ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.js-reveal .catalogue li:nth-child(2).in-view { transition-delay: 90ms; }
.js-reveal .catalogue li:nth-child(3).in-view { transition-delay: 180ms; }

/* --------------------------------------------------------------------------
   16. SMALL SCREENS
   -------------------------------------------------------------------------- */

@media (max-width: 780px) {
  .single-grid,
  .about-grid {
    grid-template-columns: 1fr;
  }
  /* justify-self as well as auto margins: it is the grid-native way to say
     this, and it cannot be undone by a later margin shorthand. */
  .single-art { max-width: 340px; margin-inline: auto; justify-self: center; }
  .about-portrait { max-width: 300px; margin-inline: auto; justify-self: center; }
  .single-blurb { max-width: none; }
}

@media (max-width: 480px) {
  .taskbar { padding-inline: 0.8rem; }
  .taskbar .brand { font-size: 0.98rem; }
  .ticker { font-size: 0.74rem; }
  .btn { width: 100%; justify-content: center; }
  /* The reflections cost paint on a phone and give the least back there. */
  .catalogue img,
  .single-art img { -webkit-box-reflect: none; }
}

/* --------------------------------------------------------------------------
   17. REDUCED MOTION, FORCED COLORS, PRINT
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .aurora span,
  .bubble,
  .caustics,
  .caustics::after,
  .bokeh,
  .ticker span,
  .badge::before,
  .win-bar::after,
  .btn::after {
    animation: none !important;
  }

  /* The bubbles stay, they just stop. A still sky is still the sky. */
  .bubble { opacity: 0.5; transform: none; }
  .ticker span { padding-left: 0; }

  .js-reveal .win,
  .js-reveal .catalogue li {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .single-art:hover,
  .catalogue a:hover,
  .btn:hover { transform: none; }
}

@media (forced-colors: active) {
  .win, .taskbar, .btn { border: 1px solid CanvasText; }
  .bubble, .aurora, .sky, .caustics, .bokeh { display: none; }
  .win::before, .win::after { display: none; }
}

@media print {
  .sky, .aurora, .bubbles, .caustics, .bokeh, .ticker, .taskbar { display: none; }
  .win::before, .win::after { display: none; }
  .win { box-shadow: none; border: 1px solid #999; background: #fff; }
  body { color: #000; background: #fff; }
}

/* --------------------------------------------------------------------------
   18. THE PAYOFF, RE-DRESSED

   The costume's last window used to drop back into the archival register:
   aged paper and IM Fell, the site's own voice returning after the noise.
   The site's own voice is now Aero, so the payoff follows it there. Same
   idea, different home. These rules only load on this page, so index.html's
   older look is untouched if it is still around.
   -------------------------------------------------------------------------- */

.nx-payoff {
  background: linear-gradient(180deg, var(--glass-strong) 0%, var(--glass) 100%) !important;
  backdrop-filter: blur(22px) saturate(1.6);
  -webkit-backdrop-filter: blur(22px) saturate(1.6);
  border: 1px solid var(--glass-edge) !important;
  border-radius: var(--radius) !important;
  color: var(--ink) !important;
  font-family: var(--body) !important;
  box-shadow:
    0 22px 60px rgba(4, 42, 60, 0.42),
    0 0 60px rgba(255, 45, 149, 0.26),
    inset 0 1px 0 rgba(255, 255, 255, 0.95) !important;
}

.nx-payoff .nx-bar {
  background: linear-gradient(
    180deg,
    rgba(236, 250, 255, 0.94) 0%,
    rgba(206, 240, 250, 0.66) 48%,
    rgba(180, 228, 242, 0.34) 50%,
    rgba(214, 243, 252, 0.62) 100%) !important;
  border-bottom: 1px solid var(--glass-line) !important;
  color: var(--ink) !important;
  font-family: var(--body) !important;
  font-variant: normal !important;
  font-weight: 700 !important;
  letter-spacing: 0.01em !important;
}

.nx-payoff .nx-body { font-size: 0.98rem !important; }
.nx-payoff .nx-body p {
  font-family: var(--body) !important;
  color: var(--ink) !important;
}

.nx-payoff-title {
  font-family: var(--body) !important;
  font-style: normal !important;
  font-weight: 700 !important;
  color: var(--ink) !important;
  letter-spacing: -0.01em;
}

.nx-payoff-art {
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255, 255, 255, 0.9) !important;
  box-shadow: 0 12px 30px rgba(4, 42, 60, 0.35);
}

/* The buttons become the page's own glass pills, and the first one is pink,
   because on this page the pink one is the one you are meant to press. */
.nx-payoff .nx-btn {
  font-family: var(--body) !important;
  font-weight: 700 !important;
  border-radius: 999px !important;
  padding: 0.62rem 1.2rem !important;
  border: 1px solid rgba(255, 255, 255, 0.8) !important;
  color: var(--ink) !important;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(255, 255, 255, 0.62) 48%,
    rgba(255, 255, 255, 0.30) 50%,
    rgba(255, 255, 255, 0.62) 100%) !important;
  box-shadow: var(--shadow-soft), inset 0 1px 0 rgba(255, 255, 255, 0.9) !important;
}

.nx-payoff .nx-row .nx-btn:first-child {
  color: #fff !important;
  border-color: rgba(255, 255, 255, 0.65) !important;
  background: linear-gradient(180deg, #ff77bd 0%, var(--pink) 47%, #d40073 50%, #ff4fa3 100%) !important;
  box-shadow: 0 8px 22px rgba(255, 45, 149, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.65) !important;
}

.nx-payoff .nx-btn:hover {
  border-color: rgba(255, 255, 255, 0.95) !important;
  transform: translateY(-2px);
}

/* ==========================================================================
   19. THE MEDIEVAL LAYER

   "Medieval Frutiger Aero insanity." The hinge between the two is stained
   glass: it is the same material Aero spent 2007 imitating, only lit from
   behind and held together with lead. So the medieval vocabulary here is
   glazier's, not scribe's, with gold leaf as the one warm colour allowed
   into a page that is otherwise pink and aqua.

     - a rose window behind the wordmark, turning once every four minutes
     - gold-leaf leading around every pane, with a jewel at each corner
     - a gothic face on the chrome, a blackletter initial on the bio
     - quatrefoil dividers where the old site had fleurons
   ========================================================================== */

/* ---- the rose window ---------------------------------------------------- */

.hero { position: relative; }

.rose {
  position: absolute;
  left: 50%;
  top: 48%;
  width: min(92vw, 40rem);   /* 112vw overflowed the page on a phone */
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  z-index: -1;
  pointer-events: none;
  background: url("../img/rose-window.webp") center / contain no-repeat;
  opacity: 0.62;
  filter: drop-shadow(0 0 46px rgba(255, 45, 149, 0.5))
          drop-shadow(0 0 90px rgba(120, 235, 255, 0.28));
  animation: rose-turn 240s linear infinite;
  will-change: transform;
}

@keyframes rose-turn {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rose { opacity: 0.72; }
}

/* ---- gold leading around every pane ------------------------------------- */

.win {
  /* The gold sits in the border-image so it follows the corner radius. */
  border: 3px solid transparent;
  border-image: var(--gold-leaf) 1;
  border-radius: 0;                 /* border-image and radius do not mix */
  box-shadow:
    var(--shadow),
    0 0 0 1px rgba(64, 40, 6, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.75),
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -14px 26px rgba(255, 255, 255, 0.26);
}

/* Nothing sits on top of the panes. The arcade, then the crown, then this:
   both tried to put architecture above the title bar and both read as an
   ornament stuck on a box. The gothic is carried by the frame, the lintel
   and the ground pattern instead, which is where it was working anyway. */

/* ---- the title bar becomes a gothic lintel ------------------------------ */

.win-bar {
  border-bottom: 2px solid transparent;
  border-image: var(--gold-leaf) 1;
  border-top: 0; border-left: 0; border-right: 0;
}

.win-title {
  font-family: var(--gothic);
  font-weight: 600;
  font-size: 1.22rem;
  letter-spacing: 0.012em;
}

/* A hairline of gold cusping under the lintel, echoing the arcade above. */
.win-bar::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 7px;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 0,
      transparent 0 4.6px,
      var(--gold-2) 4.6px 5.8px,
      transparent 5.8px) 0 0 / 12px 7px repeat-x;
  opacity: 0.5;
}

/* ---- the illuminated initial -------------------------------------------- */

.illuminated::first-letter {
  float: left;
  font-family: var(--black-letter);
  font-size: 4.1em;
  line-height: 0.78;
  padding: 0.10em 0.14em 0.02em 0;
  margin: 0.02em 0.12em 0 0;
  color: var(--gold-ink);
  background: var(--gold-leaf);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}

/* The gilded box the initial sits in. Drawn on the paragraph rather than the
   letter, because ::first-letter cannot carry a box of its own. */
.illuminated {
  position: relative;
}

/* ---- quatrefoil dividers ------------------------------------------------ */

.divider {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: 1.7rem 0 1.3rem;
  color: var(--gold-2);
}
.divider::before,
.divider::after {
  content: "";
  flex: 1 1 auto;
  height: 2px;
  background: var(--gold-leaf);
  opacity: 0.75;
}
.divider svg { flex: none; width: 26px; height: 26px; display: block; }

/* ---- gothic labels ------------------------------------------------------ */

.ticker {
  font-family: var(--gothic);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: none;
  border-top: 2px solid transparent;
  border-bottom: 2px solid transparent;
  border-image: var(--gold-leaf) 1;
}

.taskbar {
  border-bottom: 3px solid transparent;
  border-image: var(--gold-leaf) 1;
}

.badge {
  font-family: var(--gothic);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.10em;
  border-radius: 0;
  border: 2px solid transparent;
  border-image: var(--gold-leaf) 1;
}

.btn {
  font-family: var(--gothic);
  font-weight: 600;
  font-size: 1.06rem;
  letter-spacing: 0.015em;
  border-radius: 0;
  border: 2px solid transparent;
  border-image: var(--gold-leaf) 1;
}

/* Cover art gets a leaded frame too, so the artwork reads as glazed. */
.single-art img,
.catalogue img {
  border-radius: 0;
  border: 3px solid transparent;
  border-image: var(--gold-leaf) 1;
}

.quote-list blockquote {
  border-left: 3px solid transparent;
  border-image: var(--gold-leaf) 1;
}

/* ---- reduced motion, forced colours, print ------------------------------ */

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

@media (forced-colors: active) {
  .rose, .win-jewels, .win-bar::before { display: none; }
}

@media print {
  .rose, .win-jewels { display: none; }
}

/* ==========================================================================
   20. NEON

   Fifteen percent hotter. The rule I kept to: neon is added as LIGHT around
   things (glow, bloom, rim) and never as a change to the colour of the text
   itself, because every glyph on this page has already been measured against
   its background and I did not want to spend that. Glow sits outside the
   letterforms; the letterforms stay where the contrast test left them.
   ========================================================================== */

:root {
  --neon-pink: 255, 45, 149;
  --neon-cyan: 77, 243, 255;
  --neon-gold: 255, 205, 90;
}

/* Panes get a coloured halo outside the gold, so the frame reads as lit
   rather than painted. */
.win {
  box-shadow:
    var(--shadow),
    0 0 0 1px rgba(64, 40, 6, 0.55),
    0 0 26px rgba(var(--neon-pink), 0.34),
    0 0 62px rgba(var(--neon-pink), 0.18),
    inset 0 0 0 1px rgba(255, 255, 255, 0.75),
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -14px 26px rgba(255, 255, 255, 0.26);
}

.win-single {
  box-shadow:
    var(--shadow),
    0 0 0 1px rgba(64, 40, 6, 0.6),
    0 0 34px rgba(var(--neon-pink), 0.52),
    0 0 90px rgba(var(--neon-pink), 0.26),
    0 0 130px rgba(var(--neon-cyan), 0.16),
    inset 0 0 0 1px rgba(255, 255, 255, 0.8),
    inset 0 1px 0 rgba(255, 255, 255, 0.95);
}

/* Headings pick up a glow. Cyan on one side of the hue, pink on the other,
   which is what makes it read as tube light rather than as a drop shadow. */
.single-title,
.catalogue h3,
.win-title {
  text-shadow:
    0 0 9px rgba(var(--neon-pink), 0.42),
    0 0 22px rgba(var(--neon-pink), 0.22),
    0 0 34px rgba(var(--neon-cyan), 0.14);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .single-title,
  :root:not([data-theme="light"]) .catalogue h3,
  :root:not([data-theme="light"]) .win-title {
    text-shadow:
      0 0 10px rgba(var(--neon-pink), 0.60),
      0 0 26px rgba(var(--neon-pink), 0.34),
      0 0 44px rgba(var(--neon-cyan), 0.22);
  }
}

/* The taskbar and the ticker become lit strips. */
.taskbar {
  box-shadow:
    0 4px 18px rgba(4, 42, 60, 0.22),
    0 0 24px rgba(var(--neon-pink), 0.28);
}

.ticker {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 0 22px rgba(var(--neon-pink), 0.65),
    0 0 54px rgba(var(--neon-pink), 0.32);
}

.ticker span { text-shadow: 0 0 10px rgba(255, 255, 255, 0.55); }

/* Buttons: the primary one is a tube, the secondary one catches its light. */
.btn-go {
  box-shadow:
    0 10px 26px rgba(var(--neon-pink), 0.55),
    0 0 26px rgba(var(--neon-pink), 0.60),
    0 0 60px rgba(var(--neon-pink), 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.85);
}
.btn-go:hover {
  box-shadow:
    0 14px 34px rgba(var(--neon-pink), 0.62),
    0 0 38px rgba(var(--neon-pink), 0.85),
    0 0 90px rgba(var(--neon-pink), 0.40),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.btn:not(.btn-go) {
  box-shadow:
    var(--shadow-soft),
    0 0 16px rgba(var(--neon-cyan), 0.30),
    inset 0 1px 0 rgba(255, 255, 255, 0.98);
}

.badge {
  box-shadow:
    0 4px 14px rgba(var(--neon-pink), 0.5),
    0 0 22px rgba(var(--neon-pink), 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

/* Cover art sits in a lit frame. */
.single-art img { box-shadow: 0 14px 34px rgba(4, 42, 60, 0.35), 0 0 30px rgba(var(--neon-pink), 0.40); }
.catalogue a:hover img {
  box-shadow: 0 18px 34px rgba(4, 42, 60, 0.4), 0 0 40px rgba(var(--neon-pink), 0.75), 0 0 80px rgba(var(--neon-cyan), 0.3);
}

/* The rose burns brighter and throws more light. */
.rose {
  opacity: 0.72;
  filter:
    saturate(1.2)
    drop-shadow(0 0 40px rgba(var(--neon-pink), 0.62))
    drop-shadow(0 0 100px rgba(var(--neon-cyan), 0.34));
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rose { opacity: 0.82; }
}

/* The wordmark gets a hotter halo, cyan under pink. */
.wordmark::before {
  text-shadow:
    0 1px 0 rgba(255, 255, 255, 0.95),
    0 2px 2px rgba(var(--neon-cyan), 0.75),
    0 0 16px rgba(255, 255, 255, 0.55),
    0 0 34px rgba(var(--neon-pink), 0.80),
    0 0 74px rgba(var(--neon-pink), 0.46),
    0 0 120px rgba(var(--neon-cyan), 0.26),
    0 14px 34px rgba(4, 42, 60, 0.6);
}

/* The bubbles run hotter too. */
.bubble { box-shadow:
  inset 0 0 18px rgba(255, 255, 255, 0.7),
  inset -4px -6px 12px rgba(var(--neon-cyan), 0.45),
  0 0 20px rgba(var(--neon-cyan), 0.34); }
.bubble.pinkish { box-shadow:
  inset 0 0 18px rgba(255, 215, 238, 0.75),
  inset -4px -6px 12px rgba(var(--neon-pink), 0.45),
  0 0 26px rgba(var(--neon-pink), 0.55); }


/* The hero has no tagline any more, so it needs its own bottom margin. */
.hero { padding-bottom: clamp(2.4rem, 8vw, 4.2rem); }

/* ==========================================================================
   21. THE RELIQUARY

   The portrait, in a gothic arch with a gold-leaf frame, drifting slowly
   through the spectrum with a highlight travelling across the glass and
   four sparks turning at the corners. A reliquary is the right reference:
   a precious object in a little gold building, which is what a portrait in
   an arch has always been.

   The arch is a percentage polygon rather than an SVG path, so it scales
   with the element instead of needing fixed coordinates. The same shape
   clips the frame and the image; the frame's padding is what shows through
   as gold.

   The image was also sitting left in its column. `margin-inline: auto` on a
   replaced element with `width: auto` does not reliably centre it, so the
   figure is a flex column with `align-items: center` and the centring is
   done by the layout instead of by margins.
   ========================================================================== */

.about-portrait {
  position: relative;
  /* `margin: 0` here, which is what a figure reset normally wants, silently
     undid the `margin-inline: auto` the small-screen block sets — this rule
     is further down the file, so it won. On a phone the figure is capped at
     300px inside a wider single-column track, and it sat against the left
     edge of it. Auto keeps the reset and the centring. */
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;      /* this centres the frame inside the figure */
  justify-content: center;
}

.reliquary {
  flex: none;
  --arch: polygon(
    0% 100%, 0% 42%,
    3% 34%, 8% 26%, 15% 18%, 24% 11%, 34% 5.5%, 44% 1.8%, 50% 0%,
    56% 1.8%, 66% 5.5%, 76% 11%, 85% 18%, 92% 26%, 97% 34%, 100% 42%,
    100% 100%
  );
  position: relative;
  padding: 9px;
  background: var(--gold-leaf);
  clip-path: var(--arch);
  filter:
    drop-shadow(0 10px 26px rgba(4, 42, 60, 0.45))
    drop-shadow(0 0 24px rgba(var(--neon-pink), 0.55))
    drop-shadow(0 0 54px rgba(var(--neon-cyan), 0.30));
  animation: reliquary-breathe 9s ease-in-out infinite alternate;
  will-change: filter;
}

@keyframes reliquary-breathe {
  from { filter: drop-shadow(0 10px 26px rgba(4, 42, 60, 0.45))
                 drop-shadow(0 0 20px rgba(var(--neon-pink), 0.45))
                 drop-shadow(0 0 44px rgba(var(--neon-cyan), 0.24)); }
  to   { filter: drop-shadow(0 10px 26px rgba(4, 42, 60, 0.45))
                 drop-shadow(0 0 34px rgba(var(--neon-pink), 0.80))
                 drop-shadow(0 0 78px rgba(var(--neon-cyan), 0.44)); }
}

/* A DEFINITE height, not max-height.

   With `width: auto` and `max-width: 100%`, the image's width depended on
   the frame, the frame was a shrink-to-fit flex item, and the frame's width
   depended on the image. That circle resolved to zero: the picture came out
   0x0, which then meant the lazy loader never had a visible box to trigger
   on, so the file was never even fetched. Pinning the height breaks the
   circle -- the width then follows from the aspect ratio. */
.reliquary img {
  display: block;
  height: clamp(210px, 30vw, 340px);
  width: auto;
  max-width: none;
  clip-path: var(--arch);
  animation: reliquary-hue 24s ease-in-out infinite;
  will-change: filter;
}

/* A full turn of the wheel, slowly. Saturation is lifted a little so the
   colours it lands on are as loud as the rest of the page. */
@keyframes reliquary-hue {
  /* The source is pink, around 330 degrees. Rotating POSITIVE walks it into
     orange and then green, which fights everything else here. Rotating
     NEGATIVE walks pink into magenta, violet, blue and cyan, which is the
     page's own range, so it reads as the picture catching different light
     rather than as a broken filter. Swept the range and stopped at -112:
     one more step, around -145, tips over into green. */
  0%   { filter: saturate(1.25) hue-rotate(12deg); }
  50%  { filter: saturate(1.5)  hue-rotate(-112deg); }
  100% { filter: saturate(1.25) hue-rotate(12deg); }
}

/* The glimmer: one hard highlight travelling across the glass on a long
   cycle, clipped to the arch so it never leaves the frame. */
.glimmer {
  position: absolute;
  inset: 9px;
  pointer-events: none;
  clip-path: var(--arch);
  background: linear-gradient(
    110deg,
    transparent 38%,
    rgba(255, 255, 255, 0.20) 46%,
    rgba(255, 255, 255, 0.85) 50%,
    rgba(255, 255, 255, 0.20) 54%,
    transparent 62%);
  background-size: 260% 100%;
  background-position: 160% 0;
  mix-blend-mode: overlay;
  animation: glimmer-sweep 6.5s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}

@keyframes glimmer-sweep {
  0%   { background-position: 170% 0; }
  55%  { background-position: -70% 0; }
  100% { background-position: -70% 0; }
}

/* Four sparks turning around the frame at different radii and speeds. */
.spark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, #fff 0%, rgba(255,255,255,.9) 30%, rgba(var(--neon-pink), .85) 60%, transparent 72%);
  box-shadow: 0 0 10px rgba(var(--neon-pink), 0.9), 0 0 22px rgba(var(--neon-cyan), 0.5);
}
.spark.s1 { animation: spark-orbit 11s linear infinite;              --r: 132px; }
.spark.s2 { animation: spark-orbit 15s linear infinite reverse;      --r: 168px; }
.spark.s3 { animation: spark-orbit 9s  linear infinite;              --r: 104px; animation-delay: -3s; }
.spark.s4 { animation: spark-orbit 19s linear infinite reverse;      --r: 190px; animation-delay: -7s; }

@keyframes spark-orbit {
  from { transform: rotate(0deg) translateX(var(--r, 140px)) rotate(0deg) scale(1); opacity: 0.35; }
  25%  { opacity: 1; }
  50%  { transform: rotate(180deg) translateX(var(--r, 140px)) rotate(-180deg) scale(1.35); opacity: 0.9; }
  75%  { opacity: 0.5; }
  to   { transform: rotate(360deg) translateX(var(--r, 140px)) rotate(-360deg) scale(1); opacity: 0.35; }
}

/* Everything above is decoration. Under reduced motion the reliquary stops
   entirely: no hue drift, no glimmer, no orbit. The frame and the arch
   stay, because they are structure rather than movement. */
@media (prefers-reduced-motion: reduce) {
  .reliquary,
  .reliquary img,
  .glimmer,
  .spark {
    animation: none !important;
  }
  .reliquary img { filter: saturate(1.15); }
  .glimmer { opacity: 0.35; background-position: 40% 0; }
  .spark { display: none; }
}

@media (max-width: 780px) {
  .spark.s2, .spark.s4 { display: none; }   /* less room to orbit in */
  .reliquary img { height: clamp(190px, 52vw, 280px); }
}

@media (forced-colors: active) {
  .glimmer, .spark { display: none; }
  .reliquary { filter: none; }
}

/* ==========================================================================
   22. CLASSES THAT REPLACED INLINE STYLE ATTRIBUTES

   The deployed _headers set `style-src 'self'` with no 'unsafe-inline'. A
   style="" attribute therefore works locally over file:// and is silently
   dropped by the browser in production. These two were the only ones on the
   page; they are classes now so the deployed page matches the local one.
   ========================================================================== */

.btn-row-spaced { margin-top: 1.6rem; }
.quote-sub { margin-bottom: 1.3rem; }

/* ==========================================================================
   23. CURSOR SPARKLES

   js/sparkle.js has been on this page since the first Aero draft and has
   been drawing nothing the whole time: its styles lived in css/style.css,
   which this page stopped loading when it stopped being the archival
   design. The script was faithfully creating a hundred and forty invisible
   elements. They live here now.

   Retuned for this page rather than copied across: two colours instead of
   one, alternating pink and cyan so the trail reads as neon rather than as
   a single-ink risograph pass, and a glow on each grain.

   Mouse only, nothing on touch, nothing under reduced motion, and the layer
   never intercepts a click — all of that is enforced in the script.
   ========================================================================== */

.sparkle-layer {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  overflow: hidden;
}

.sparkle {
  position: absolute;
  display: block;
  margin: -0.5em 0 0 -0.5em;        /* centre the grain on the cursor */
  background: var(--pink-bright);
  box-shadow: 0 0 8px rgba(var(--neon-pink), 0.9);
  /* A four-pointed star, the same shape the old site used, kept because it
     reads as a spark at 6px where a circle just reads as a dot. */
  clip-path: polygon(
    50% 0, 61% 39%, 100% 50%, 61% 61%,
    50% 100%, 39% 61%, 0 50%, 39% 39%
  );
  will-change: transform, opacity;
  animation-name: sparkle-settle;
  animation-timing-function: cubic-bezier(0.2, 0.6, 0.3, 1);
  animation-fill-mode: forwards;
}

/* Every third grain is cyan. The script emits them in order, so this gives
   a two-colour trail without the script needing to know about colour. */
.sparkle:nth-child(3n) {
  background: #7ef4ff;
  box-shadow: 0 0 8px rgba(var(--neon-cyan), 0.9);
}
.sparkle:nth-child(7n) {
  background: #fff4c4;
  box-shadow: 0 0 9px rgba(var(--neon-gold), 0.85);
}

/* It drifts a little, turns, and fades. The hold at 45% is deliberate: the
   first version faded from the moment it was born, so the trail was gone
   almost before you saw it. It stays bright for most of its life and then
   goes, which is what makes it read as a trail rather than a flicker. */
@keyframes sparkle-settle {
  0% {
    opacity: 1;
    transform: translate(0, 0) scale(0.7) rotate(0deg);
  }
  18% {
    opacity: 1;
    transform: translate(0, 0) scale(1.15) rotate(12deg);
  }
  45% {
    opacity: 0.92;
  }
  100% {
    opacity: 0;
    transform: translate(var(--sx, 0), var(--sy, 12px)) scale(0.18) rotate(var(--sr, 90deg));
  }
}
