/* ===================================================================
   /LINKS — the evergreen page

   This is the address in the bio, so it is opened on a phone, from a social
   app, by someone who came to tap one thing and leave. It is written phone
   first: everything below is the phone layout, and the desktop rules are the
   exception at the bottom of the file, not the other way round.

   It behaves like the link page it is — one column, full-width targets, thumb
   reach, the list grows downward when streaming links arrive. It does not look
   like one: no cards, no centred stack of rounded rectangles, no template. The
   sheet is the Afterglow poster the homepage resolves into, and the links are
   fluorescent stickers pasted onto it.

   The background is fixed and the content scrolls over it, so adding links
   never breaks the layout — the page just gets longer, which is what a link
   page is supposed to do.
   =================================================================== */

/* --- fonts ---------------------------------------------------------
   Self-hosted, no runtime CDN, per the approved technical direction. Both
   faces are SIL Open Font License 1.1 and the licence text sits beside the
   files in assets/fonts/.

   These replace the Haettenschweiler/Impact stand-in the layout was originally
   sized against. League Gothic is a genuinely different face — narrower, with
   its own cap height — so anything tuned by eye against the stand-in has to be
   looked at again rather than assumed to still hold.

   The display face blocks rather than swaps: it carries the wordmark and the
   confession at half-screen size, and a flash of Impact at that scale is more
   disruptive than a beat of nothing. The mono is small and incidental, so it
   swaps. */

@font-face {
  font-family: 'League Gothic';
  src: url(/assets/fonts/league-gothic-latin-400-normal.woff2) format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url(/assets/fonts/ibm-plex-mono-latin-400-normal.woff2) format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --mustard:  #F3A31E;
  --hotpink:  #ED3D78;
  --coral:    #FF7138;
  --softpink: #F5C4CA;
  --twilight: #174C49;
  --cyan:     #38D9D0;
  --pinkreg:  #FF4F85;
  --midnight: #091D2B;
  --ink-warm: #17140F;
  --paper:    #FFF0CD;
  --denim:    #2E4A6B;
  --lilac:    #B9A3D9;

  --dot: 5px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  background: var(--midnight);
  color: var(--paper);
  font-family: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', monospace;
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
}

/* --- the sheet ---------------------------------------------------- */
/* Fixed behind everything. The content scrolls; the poster does not. */

.sheet {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background-color: #0B2733;
  isolation: isolate;
  z-index: 0;
}

/* The night the homepage ends on. A single low glow off-centre, so the sheet
   has a light source rather than an even wash. Placed low, because on a phone
   the bottom half of the screen is where the links are. */
.afterglow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 90% 45% at 30% 82%, rgba(56,217,208,.20) 0%, transparent 62%),
    radial-gradient(ellipse 80% 40% at 78% 12%, rgba(255,79,133,.14) 0%, transparent 60%),
    linear-gradient(178deg, #0A2130 0%, #091D2B 58%, #07141D 100%);
}

.fibre {
  position: absolute;
  inset: 0;
  background-image: url(/assets/tex-fibre.webp);
  background-size: 512px 512px;
  opacity: .18;
  mix-blend-mode: overlay;
  pointer-events: none;
}

/* Printed, not a UI grid. Quieter here than on the homepage — this page is
   read, not scrolled through. */
.grid {
  position: absolute;
  inset: -20%;
  background-image:
    repeating-linear-gradient(0deg,  var(--cyan) 0 1px, transparent 1px calc(var(--dot) * 3)),
    repeating-linear-gradient(90deg, var(--cyan) 0 1px, transparent 1px calc(var(--dot) * 3));
  opacity: .09;
  mix-blend-mode: screen;
}

/* --- floral planes ------------------------------------------------ */
/* PLACEHOLDER ART, same generator as the homepage. On a phone they are
   wallpaper: they respond to nothing, because there is no pointer to respond
   to and nothing here should move while somebody is aiming at a link. */

.plane { position: absolute; inset: -25%; pointer-events: none; }
/* The plates are <img> now, not <svg>. The old selector would have left every
   plate in normal flow, stacked down the page. */
.plane img { position: absolute; display: block; }
/* Pushed past photographic saturation so the florals read as printed ink rather
   than as photographs — same reasoning as the sheet. */
.plane--far  { filter: blur(1.6px) saturate(1.12) contrast(1.08) brightness(.86); opacity: .76; }
.plane--mid  { filter: saturate(1.24) contrast(1.1);  opacity: .92; }
.plane--fore { filter: saturate(1.36) contrast(1.16); opacity: .9;  }

.distress {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: overlay;
  opacity: .16;
  background-image: url(/assets/tex-distress.webp);
  background-size: 1024px 1024px;
}

/* --- the column --------------------------------------------------- */
/* One column, and it owns the page. Top-aligned so the wordmark sits under the
   notch, links pushed into the lower half where a thumb actually reaches. */

.page {
  position: relative;
  z-index: 10;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding:
    calc(env(safe-area-inset-top) + var(--dot) * 7)
    calc(env(safe-area-inset-right) + var(--dot) * 4)
    calc(env(safe-area-inset-bottom) + var(--dot) * 6)
    calc(env(safe-area-inset-left) + var(--dot) * 4);
}

/* --- wordmark ----------------------------------------------------- */

.wordmark {
  display: grid;
  font-family: 'League Gothic', 'Haettenschweiler', 'Impact', system-ui, sans-serif;
  text-transform: uppercase;
  line-height: .78;
  font-size: clamp(30px, 9vw, 54px);
  pointer-events: none;
}
.wordmark .ink { display: block; grid-area: 1 / 1; }
/* One impression, and it is crisp.

   The homepage prints this in three — two offset inks under a crisp key — and
   that belongs there: it is a press sheet and the plates pull apart under the
   pointer. Here it was fringing the letterforms even in perfect register,
   because a screen-blended colour copy under an antialiased edge tints exactly
   the pixels that define the shape. At this size that reads as blur, and there
   is no press on this page to justify paying for it. */
.wordmark .ink--key  { color: var(--paper); }
/* One knob: the lockup scales from the container's font-size and both lines
   are set in em off it. */
.wordmark .l1 { display: block; font-size: 1em; letter-spacing: -.005em; }
.wordmark .l2 {
  display: block;
  font-size: 1.71em;
  letter-spacing: -.02em;
  margin-top: -.06em;
}
.wordmark .ink--key .l2 { color: var(--cyan); }
.wordmark .ink--key .l2 .hinge { color: var(--paper); }
.wordmark .hinge { font-size: .74em; letter-spacing: .01em; padding-inline: 0 .1em; }

/* --- the release -------------------------------------------------- */
/* The reason somebody followed the link. It sits directly under the wordmark
   and above every list, and the song's name is the loudest thing on the page
   after the artist's.

   Set in the display face, in one crisp impression like GOODNIGHT on the
   homepage — the offset ink copies are what the wordmark uses, and a second
   misregistered element this close to it would read as a mistake. */

.release {
  margin-top: calc(var(--dot) * 7);
}

/* --- the cover ----------------------------------------------------
   A square, because a record is a square. It is the one true rectangle on a
   sheet that otherwise refuses them, which is why it can carry the artwork
   without the page turning into a grid of cards.

   Pasted on, not framed: cut edge, hard cyan offset where the second pass
   missed, halftone over the top. The artwork drops straight in — set
   RELEASE.cover and the placeholder plate is replaced with no other change. */

.cover {
  position: relative;
  aspect-ratio: 1;
  width: 100%;
  margin: 0 0 calc(var(--dot) * 3.5);
  rotate: -1.2deg;
  clip-path: polygon(0.6% 3%, 99.4% 0%, 99% 97%, 0.4% 100%);
  box-shadow: 6px 6px 0 var(--cyan);
  background: #071620;
  overflow: hidden;
}
.cover__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Until the artwork exists: the record's own sheet, in miniature. Blossoms
   from the same generator as the background, so an empty cover still belongs
   to the release rather than reading as a broken image. */
.cover__placeholder {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 60% at 32% 78%, rgba(56,217,208,.22) 0%, transparent 62%),
    radial-gradient(ellipse 70% 55% at 76% 20%, rgba(255,79,133,.18) 0%, transparent 60%),
    linear-gradient(168deg, #123240 0%, #0A2130 55%, #07141D 100%);
}
.cover__placeholder svg { position: absolute; overflow: visible; }

/* Halftone and a registration edge over whatever is underneath, so the artwork
   and the placeholder are printed by the same press. */
.cover__ink {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background-image: radial-gradient(var(--ink-warm) 30%, transparent 31%);
  background-size: 3px 3px;
  opacity: .13;
  mix-blend-mode: multiply;
  box-shadow: inset 0 0 0 1px rgba(255,79,133,.35);
}

/* Overlaid caption: the title printed onto the bottom of the cover instead of
   under it. The scrim is what keeps it legible over artwork nobody has seen
   yet — without it this only works against a dark cover. */
.release--overlay .cover { margin-bottom: 0; }
.release--overlay .caption {
  position: absolute;
  z-index: 4;
  left: 0; right: 0; bottom: 0;
  padding: calc(var(--dot) * 8) calc(var(--dot) * 3.5) calc(var(--dot) * 3);
  background: linear-gradient(to top, rgba(7,20,29,.92) 12%, rgba(7,20,29,0) 100%);
}

.release .eyebrow {
  font-size: calc(var(--dot) * 2.1);
  letter-spacing: .32em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-bottom: calc(var(--dot) * 1.4);
}
.release .song {
  font-family: 'League Gothic', 'Haettenschweiler', 'Impact', system-ui, sans-serif;
  font-size: clamp(46px, 14vw, 84px);
  line-height: .82;
  letter-spacing: -.02em;
  text-transform: uppercase;
  color: var(--paper);
  font-weight: 400;
}

/* The state of the release, printed as a stamp rather than set as a line of
   text: it is the one thing on the page that changes, so it should look
   applied to the sheet, not typeset into it. */
.cta {
  display: inline-block;
  margin-top: calc(var(--dot) * 2.4);
  padding: calc(var(--dot) * 1.6) calc(var(--dot) * 3.4);
  font-size: calc(var(--dot) * 2.2);
  letter-spacing: .3em;
  text-transform: uppercase;
  text-decoration: none;
  rotate: -1.6deg;
  clip-path: polygon(1.5% 6%, 99% 0%, 98% 94%, 0.5% 100%);
}
/* Waiting: a stamp, not a button. Nothing to press yet, so it does not look
   pressable — no fill, no shadow, hairline only. */
.cta--waiting {
  color: var(--cyan);
  box-shadow: inset 0 0 0 1px var(--cyan);
  background: rgba(56,217,208,.07);
}
/* Live or on pre-order it becomes the loudest sticker on the sheet: mustard,
   the one warm ink, used exactly once. */
.cta:is(a) {
  background: var(--mustard);
  color: var(--ink-warm);
  box-shadow: 4px 4px 0 var(--pinkreg);
}
.cta:is(a):active { translate: 2px 2px; box-shadow: 1px 1px 0 var(--pinkreg); }
.cta:focus-visible { outline: 2px solid var(--paper); outline-offset: 5px; }

/* --- the lists ---------------------------------------------------- */
/* Socials first — the links that work today — then the stores waiting on the
   release. `margin-top: auto` on the first group pushes both into the thumb
   half on a tall screen, and simply follows the release once there are enough
   links to fill the page. */

.group {
  display: flex;
  flex-direction: column;
  gap: calc(var(--dot) * 3);
}
.group + .group { margin-top: calc(var(--dot) * 4); }
#social { margin-top: auto; padding-top: calc(var(--dot) * 7); }

/* Neon sticker. A pasted-on scrap: flat fluorescent ink, a hard cyan offset
   where the second pass missed, and a cut edge that is not a rectangle. The
   glow is the neon — kept behind the sticker so the label stays flat and
   readable rather than blooming.

   Full width on a phone because that is the honest target size. The asymmetry
   comes from the tilt and from the cut, not from making them small and
   scattering them where a thumb cannot go. */
.sticker {
  --tilt: 0deg;
  position: relative;
  display: flex;
  align-items: center;
  gap: calc(var(--dot) * 3);
  min-height: calc(var(--dot) * 12);   /* ~60px: a real tap target */
  padding: calc(var(--dot) * 2.6) calc(var(--dot) * 4);
  background: var(--hotpink);
  color: var(--paper);
  text-decoration: none;
  /* Four corners each pulled off true by a different amount, so it reads as
     scissors rather than as a rounded component default. */
  clip-path: polygon(0.8% 5%, 99.4% 0%, 98.8% 95%, 0.4% 100%);
  box-shadow: 4px 4px 0 var(--cyan);
  rotate: var(--tilt);
  -webkit-tap-highlight-color: transparent;
  transition: rotate .18s ease, translate .18s ease, box-shadow .18s ease;
}

/* The neon, behind the ink. */
.sticker::before {
  content: '';
  position: absolute;
  inset: -6px;
  z-index: -1;
  background: var(--pinkreg);
  filter: blur(14px);
  opacity: .3;
}

/* Halftone over the ink, so the sticker is printed rather than filled. */
.sticker::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(var(--ink-warm) 32%, transparent 33%);
  background-size: 3px 3px;
  opacity: .16;
  mix-blend-mode: multiply;
}

.sticker svg {
  width: calc(var(--dot) * 5);
  height: calc(var(--dot) * 5);
  flex: none;
}

.sticker .label {
  display: block;
  text-transform: uppercase;
  font-size: calc(var(--dot) * 3.2);
  letter-spacing: .2em;
  line-height: 1.1;
}
/* The handle, so somebody can tell it is the right account before they leave
   the page they are on. */
.sticker .handle {
  display: block;
  font-size: calc(var(--dot) * 2.2);
  letter-spacing: .06em;
  opacity: .72;
  margin-top: .35em;
}

/* Touch has no hover, so the press is the feedback. */
.sticker:active {
  translate: 2px 2px;
  box-shadow: 1px 1px 0 var(--cyan);
}
/* Keyboard focus has to be visible on top of a busy sheet. */
.sticker:focus-visible { outline: 2px solid var(--paper); outline-offset: 5px; }

/* A store that has no link yet: the plate is cut and the halftone is there,
   but it has not been through the fluorescent pass. It reads as part of the
   same print run rather than as a disabled button, and it is unmistakably not
   yet pressable. */
/* Held back from the live stickers, but only in ink weight — never in
   legibility. The first pass was faint enough to be hard to read on a bright
   desktop screen, which made "not yet" look like "broken". */
.sticker--ghost {
  background: rgba(56,217,208,.14);
  box-shadow: inset 0 0 0 1.5px rgba(56,217,208,.9);
  color: var(--paper);
}
.sticker--ghost::before { display: none; }        /* no neon on an unlit plate */
.sticker--ghost::after  { opacity: .07; }
.sticker--ghost:active  { translate: 0; box-shadow: inset 0 0 0 1.5px rgba(56,217,208,.9); }
.sticker--ghost .handle { color: var(--cyan); opacity: 1; letter-spacing: .18em; }

/* --- return to the homepage --------------------------------------- */
/* On a phone this is a normal sticker at the end of the list, in the second
   ink. A tab rotated onto the screen edge is a bad thumb target. */

.goodnight {
  align-self: flex-start;
  margin-top: calc(var(--dot) * 5);
  padding: calc(var(--dot) * 2) calc(var(--dot) * 4);
  background: var(--cyan);
  color: var(--ink-warm);
  font-size: calc(var(--dot) * 2.4);
  letter-spacing: .3em;
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: -3px 3px 0 var(--pinkreg);
  rotate: -1.4deg;
  clip-path: polygon(1.5% 4%, 99% 0%, 98% 96%, 0% 100%);
  -webkit-tap-highlight-color: transparent;
}
.goodnight:active { translate: -2px 2px; box-shadow: -1px 1px 0 var(--pinkreg); }
.goodnight:focus-visible { outline: 2px solid var(--paper); outline-offset: 4px; }

/* --- trim, proof and colour bars ---------------------------------- */
/* The press furniture the homepage carries. On a phone the sheet is small and
   the furniture is the first thing that would crowd it, so only the colour
   bars and the proof number survive; crop marks and the registration readout
   come back on a wide screen. */

.trim {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 40;
  font-size: calc(var(--dot) * 1.8);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-warm);
  mix-blend-mode: difference;
}
.trim .proof {
  position: absolute;
  left: calc(env(safe-area-inset-left) + var(--dot) * 2);
  bottom: calc(env(safe-area-inset-bottom) + var(--dot) * 1.5);
}
.trim .reg  { display: none; }
.trim .crop { display: none; }
.trim .bars {
  position: absolute;
  right: calc(env(safe-area-inset-right) + var(--dot) * 2);
  top: calc(env(safe-area-inset-top) + var(--dot) * 2);
  display: flex;
  gap: 2px;
}
.trim .bars i { width: calc(var(--dot) * 2.2); height: calc(var(--dot) * 1.3); display: block; }

.crop { position: absolute; width: calc(var(--dot) * 4); height: calc(var(--dot) * 4); }
.crop::before, .crop::after { content: ''; position: absolute; background: var(--ink-warm); }
.crop::before { width: 100%; height: 1px; top: 50%; }
.crop::after  { height: 100%; width: 1px; left: 50%; }
.crop.tl { top: var(--dot); left: var(--dot); }
.crop.tr { top: var(--dot); right: var(--dot); }
.crop.bl { bottom: var(--dot); left: var(--dot); }
.crop.br { bottom: var(--dot); right: var(--dot); }



@media (prefers-reduced-motion: reduce) {
  .sticker { transition: none; }
}

/* ===================================================================
   WIDE SCREENS

   Same page, not a different one. The phone layout IS the design — one
   column, full-width stickers, the same order and the same proportions — so a
   desktop does not get a rearrangement, it just stops the column from
   stretching to the width of a monitor.

   The column stays hard left rather than centred, which is where the homepage
   sets its wordmark and its lyric, and which keeps the sheet asymmetric: the
   poster is the space the column does not fill.
   =================================================================== */

@media (min-width: 760px) {
  .page {
    /* A column that reads at arm's length, roughly a phone's worth of measure,
       centred on the sheet with the poster showing past it on both sides.

       It was hard left, on the reasoning that the homepage sets its wordmark
       and lyric against the left edge. That is right for a sheet you scroll
       through and wrong for a page you are meant to stand in front of and
       tap: off to one side it read as though the layout had failed rather
       than as composition. The asymmetry on this page comes from the tilts
       and the cut edges, which is enough of it. */
    max-width: 480px;
    margin-inline: auto;
    min-height: 100vh;
    padding-left: calc(var(--dot) * 4);
    padding-right: calc(var(--dot) * 4);
    padding-top: calc(var(--dot) * 9);
  }

  /* The viewport-relative type sizes were sized against a phone. Past the cap
     they have to be pinned to the column instead, or the wordmark grows to the
     width of the screen while the links stay put. */
  /* THE WORDMARK SIZE KNOB, wide screens. Everything in the lockup scales from
     this one value: BLOOM at 1.71x, GUNWA at 1x, the hinge at .74x of BLOOM. */
  .wordmark { font-size: 42px; }

  /* A pointer exists, so the stickers can answer it. Same sticker, same size —
     it straightens against its own tilt and lifts off the sheet. */
  .sticker::before { transition: opacity .18s ease; }
  .sticker:hover,
  .sticker:focus-visible {
    rotate: 0deg;
    translate: 0 -2px;
    box-shadow: 7px 7px 0 var(--cyan);
  }
  .sticker:hover::before,
  .sticker:focus-visible::before { opacity: .55; }
  .goodnight:hover { translate: 0 -2px; box-shadow: -5px 5px 0 var(--pinkreg); }

  /* The rest of the press furniture fits once there is a margin to put it in. */
  .trim .reg  { display: block; position: absolute; right: calc(var(--dot) * 2); bottom: calc(var(--dot) * 2); }
  .trim .crop { display: block; }

}

/* No flower cursor. It was a drawn mark over a hidden system cursor and it read
   as cheesy; the homepage carries the same note. The pointer still misregisters
   the plates, which is the part that was doing work.                        */
