/* isolaazzurra820.com
 *
 * One page, three album cards that turn over. No build step and no framework:
 * the whole interactive surface is a flip and a scroll.
 *
 * Every colour comes from `presets/03_nirvana_retro.toml` in the isola3
 * renderer that made the videos, so the site and the records are the same
 * world rather than two guesses at it.
 *
 * The page commits to one look instead of carrying a light theme as well: the
 * records are a night drive, and a pale version of that is a different thing.
 * Colours are still declared explicitly, never inherited from the browser.
 */

/* Orbitron for the display, Chakra Petch for everything read in quantity.
 *
 * Press Start 2P was here and is gone: it is *arcade* 8-bit, not synthwave,
 * and the page was saying the same thing twice — the pixel type is already
 * painted into the covers, which are the artwork. Freed of repeating it, the
 * page can set synthwave type while the sleeves supply the 8-bit.
 *
 * Both self-hosted with their OFL licences beside them, as the old font was:
 * no third-party request on the critical path and no flash of fallback type.
 */
@font-face {
  font-family: "Orbitron";
  src: url("assets/Orbitron.ttf") format("truetype");
  font-weight: 400 900;
  font-display: swap;
}
@font-face {
  font-family: "Chakra Petch";
  src: url("assets/ChakraPetch.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Chakra Petch";
  src: url("assets/ChakraPetch-SemiBold.ttf") format("truetype");
  font-weight: 600;
  font-display: swap;
}

:root {
  --night: #0a0014;
  --deep: #1b0a2e;
  --violet: #4a0f5a;
  --magenta: #ff2d95;
  --sun: #ff66c4;
  --ink: #eef3f4;
  --muted: #9d8fae;
  --rule: #3a2450;
  color-scheme: dark;
}

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

body {
  margin: 0;
  background: var(--night);
  color: var(--ink);
  font-family: "Chakra Petch", ui-sans-serif, system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; image-rendering: pixelated; }

/* The CRT cue that earns the most for the least. Fixed, so it does not scroll
   with the content, and inert to the pointer. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.22) 0 1px,
    transparent 1px 3px
  );
}

.wordmark,
.eyebrow,
.card-title,
.chip {
  font-family: "Orbitron", ui-sans-serif, sans-serif;
  font-weight: 700;
}

/* ── header ─────────────────────────────────────────────────────────── */

header {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: clamp(32px, 5vw, 56px) 20px 0;
}

/* Names set letter by letter — the wordmark and every album title.
   The spacing is a non-breaking space between letters rather than
   `letter-spacing`, so what a screen reader says and what a selection copies
   is still the word. Each word is one flex item and so cannot break inside
   itself: a long title wraps at a separator or not at all.

   Two ways this went wrong before. Plain spaces let a narrow screen break
   anywhere and left `a_a` dangling at the end of a line; `nowrap` on the whole
   thing then stopped the wrap altogether and pushed the mark off both edges of
   a phone. Words as flex items is what does both jobs at once. */
.spaced { display: flex; flex-wrap: wrap; }
.spaced .w,
.spaced .sep { white-space: nowrap; }
.spaced .sep { color: var(--magenta); }

.wordmark {
  justify-content: center;
  margin: 0;
  font-size: clamp(17px, 4.6vw, 34px);
  letter-spacing: 0.05em;
  line-height: 1.6;
  text-wrap: balance;
}

.tagline {
  margin: 18px auto 0;
  color: var(--muted);
  font-size: 15px;
  letter-spacing: 0.02em;
}
.tagline span {
  display: block;
  margin-top: 4px;
  color: var(--sun);
}

/* ── the horizon behind the cards ───────────────────────────────────── */

.horizon {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 160%;
  min-width: 1100px;
  transform: translateX(-50%);
  z-index: 0;
  opacity: 0.34;
  /* Behind the cards and well down: at full strength the road ran straight
     through the titles underneath them and the sun sat hidden behind a cover.
     It is ground, not a picture competing with the covers. */
  mask-image: linear-gradient(to bottom, #000 40%, transparent 92%);
  -webkit-mask-image: linear-gradient(to bottom, #000 40%, transparent 92%);
}

.stage {
  position: relative;
  padding: clamp(24px, 4vw, 44px) 0 clamp(28px, 5vw, 56px);
  overflow: hidden;
}

/* ── carousel ───────────────────────────────────────────────────────── */

.carousel {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 clamp(8px, 3vw, 24px);
}

/* Native scroll, not a drag script. A drag carousel fights tap-to-flip: a
   swipe that ends on a card fires a click and the card turns while you are
   still scrolling. The browser suppresses that click after a real drag. */
.track {
  display: flex;
  gap: clamp(16px, 4vw, 40px);
  flex: 1;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding: 10px calc(50% - min(42vw, 210px));
  scrollbar-width: none;
}
.track::-webkit-scrollbar { display: none; }

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

.album {
  flex: 0 0 min(84vw, 420px);
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Gone on a phone: there you swipe, and 44px a side taken out of a 420px
   screen made the card wider than the space left for it, so the cover was
   clipped at both edges. */
@media (max-width: 720px) {
  .arrow { display: none; }
}

.arrow {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: rgba(10, 0, 20, 0.7);
  color: var(--ink);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.arrow:hover { border-color: var(--magenta); color: var(--sun); }
.arrow[disabled] { opacity: 0.25; cursor: default; }

.dots {
  position: relative;
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 26px;
}
.dot {
  width: 10px;
  height: 10px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 0;
  background: transparent;
  cursor: pointer;
}
.dot[aria-current="true"] { background: var(--magenta); border-color: var(--magenta); }

/* ── the card, and its turn ─────────────────────────────────────────── */

.flipper {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: var(--deep);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.flipper:focus-visible {
  outline: 2px solid var(--sun);
  outline-offset: 3px;
}

/* Without JavaScript both faces simply stack: the cover, then the tracklist
   underneath. The list is in the markup either way, so it is crawlable and the
   page is not empty when a script fails. */

/* `display: block` is not decoration. The faces are spans — a button may not
   contain a div — and on an inline box `height: 100%` does nothing at all, so
   the cards collapsed to nothing and the covers never appeared. */
.faces { display: block; width: 100%; height: 100%; }
.face { display: block; position: relative; }
.face.back { padding: 20px; }

html.js .flipper {
  perspective: 1200px;
  background: transparent;
  border: 0;
}
html.js .faces {
  position: relative;
  transform-style: preserve-3d;
  transition: transform 620ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
html.js .flipper[aria-expanded="true"] .faces { transform: rotateY(180deg); }
html.js .face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border: 1px solid var(--rule);
  border-radius: 2px;
  overflow: hidden;
}
html.js .face.back {
  transform: rotateY(180deg);
  background: var(--deep);
  display: flex;
  flex-direction: column;
}

@media (prefers-reduced-motion: reduce) {
  html.js .faces { transition: none; }
  /* A cross-fade rather than a turn, for anyone who asked not to be spun. */
  html.js .flipper[aria-expanded="true"] .faces { transform: none; }
  html.js .flipper[aria-expanded="true"] .face.front { opacity: 0; }
  html.js .face.back { transform: none; opacity: 0; }
  html.js .flipper[aria-expanded="true"] .face.back { opacity: 1; }
  html.js .face { transition: opacity 200ms linear; }
}

.face.front img { width: 100%; height: 100%; object-fit: cover; }

.back h2 {
  margin: 0 0 2px;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--sun);
}
.back .year { margin: 0 0 14px; font-size: 11px; color: var(--muted); }

.tracks {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: track;
  font-size: clamp(11px, 2.6vw, 13px);
  line-height: 1.65;
  flex: 1;
  /* 22 tracks do not fit one column of a square at a readable size — about
     330px of text in a 340px card — so the long list runs in two. */
  columns: 1;
}
.tracks.two-up { columns: 2; column-gap: 18px; }

.tracks li {
  counter-increment: track;
  display: flex;
  gap: 7px;
  break-inside: avoid;
}
.tracks li::before {
  content: counter(track, decimal-leading-zero);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* Intro, intermezzo and outro carry no label — the colour says it, the same
   way it does on the Instagram tiles. */
.tracks li.instrumental,
.tracks li.instrumental::before { color: var(--sun); }

/* ── under the card ─────────────────────────────────────────────────── */

.meta { display: flex; flex-direction: column; gap: 10px; }

/* Spaced, like the wordmark and like the covers set them. They were not, and
   the reason recorded here was that `l e _ c a s e _ e _ i l _ s i l e n z i o`
   measured 533px against a 420px card and wrapped through the middle of a
   word. That was true of a run of text; it is not true of five flex items,
   which wrap between words and never inside one. */
.card-title {
  margin: 0;
  font-size: clamp(12px, 2.4vw, 16px);
  line-height: 1.5;
  letter-spacing: 0.04em;
}
.card-year { margin: 0; font-size: 13px; color: var(--muted); }

.listen,
.chip {
  align-self: flex-start;
  padding: 10px 16px;
  border: 1px solid var(--magenta);
  border-radius: 2px;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
}
.listen { color: var(--ink); background: rgba(255, 45, 149, 0.12); }
.listen:hover { background: var(--magenta); color: var(--night); }
.listen:focus-visible { outline: 2px solid var(--sun); outline-offset: 2px; }

.chip {
  color: var(--muted);
  border-color: var(--rule);
  background: transparent;
  font-size: 10px;
}

/* ── footer ─────────────────────────────────────────────────────────── */

footer {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: clamp(40px, 7vw, 80px) 20px clamp(36px, 6vw, 64px);
  border-top: 1px solid var(--rule);
  margin-top: clamp(30px, 6vw, 60px);
}

footer a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--magenta);
  padding-bottom: 2px;
}
footer a:hover { color: var(--sun); }
footer p { margin: 18px 0 0; font-size: 12px; color: var(--muted); }

/* Stacked and centred, which is how Paolo asked for them. A row would fit
   three words on a phone, but only just, and the fourth platform that gets
   added later would break it. */
.links {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 38px, not the 18 a footer paragraph gets: the links are 14px apart, so at 18
   the mark read as a fourth item in the list instead of the start of the
   sign-off. A line of air makes the footer two groups. */
.colophon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin-top: 38px;
}

/* The tab's own mark, closing the page. `pixelated` is not a preference here:
   the browser's default smoothing turns a 48px drawing into a blur the moment
   the device pixel ratio is not 1, which is most phones. Shown at exactly the
   size it was drawn, so 2x screens double it and nothing is resampled. */
.mark {
  width: 48px;
  height: 48px;
  image-rendering: pixelated;
}
