/* ALECARBAZZY — design system
   Art direction: ART_DIRECTION.md.

   The palette is MEASURED out of the 2026 shoot, not chosen from a mood board.
   Every value below traces to a pixel in assets/img/hero.webp. See ART_DIRECTION.md
   for the sampling method and the contrast ratios.

   THE EMBER RULE: --ember appears ONLY on things a visitor can act on. Primary
   button, focus ring, active nav underline, play badge, required marker. If ember
   shows up on something you cannot click, that is a bug, not a style. It is the
   burnt orange of the basketball rim and the duck boots, and it is the only warm
   thing in a cold photograph, which is exactly why it works as the one signal. */

/* ---------- tokens ---------- */
:root {
  /* base, kept from the first build: the photograph reads as near-black anyway */
  --ink: #0b0b0c;
  /* raised surfaces carry the court's green cast rather than a neutral lift */
  --ink-raised: #13181a;
  --ink-sunk: #08090a;

  /* overcast sky, hue 170 at 87% lightness. 14.8:1 on --ink. */
  --bone: #dce0df;
  /* Alphas chosen so both clear WCAG AA against BOTH --ink and the darker
     --ink-sunk used in the footer. Measured, not guessed: 8.30:1 and 4.99:1. */
  --bone-dim: rgba(220, 224, 223, 0.74);
  --bone-faint: rgba(220, 224, 223, 0.56);

  /* the wet court itself, #5F8275 straight off the photograph. 4.6:1 on ink.
     Structural marks and non-interactive emphasis only. */
  --sea: #5f8275;

  /* the rim and the duck boots: hue 13, the only warm cluster in the frame.
     Lifted from the measured #AF6657 to 5.1:1 so it passes AA as text and as
     a button fill, which #AF6657 only just did at 4.55:1. */
  --ember: #c4684f;
  --ember-lift: #d88268;

  --line: rgba(220, 224, 223, 0.13);
  --line-strong: rgba(220, 224, 223, 0.28);
  /* The boundary of an actual control. --line-strong measured 2.07:1 against the
     input fill, and for a ghost button the border IS the affordance. 0.46 alpha
     measures 3.4:1 on --ink-sunk and 3.2:1 on --ink-raised. */
  --line-control: rgba(220, 224, 223, 0.46);

  --display: "Bricolage Grotesque", "Trebuchet MS", sans-serif;
  --text: "Chivo", "Helvetica Neue", Arial, sans-serif;

  /* strict 1.25 ramp: 12 / 15 / 18.75 / 23.4, then display breaks away.
     Every font-size on the site comes from this list. Stray one-off sizes are
     what flattens a hierarchy, so there are none. */
  --t-xs: 0.75rem;
  --t-sm: 0.9375rem;
  --t-base: 1.1719rem;
  --t-md: 1.4648rem;
  --t-lg: clamp(1.83rem, 3.6vw, 2.29rem);
  --t-xl: clamp(2.29rem, 5.6vw, 3.05rem);
  --t-2xl: clamp(2.86rem, 7.8vw, 4.3rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 460ms;
  --shell: 1440px;
  --gutter: clamp(1.25rem, 4vw, 3rem);

  /* named layers, so nothing ever reaches for 9999 */
  --z-sticky: 100;
  --z-nav: 110;
  --z-skip: 200;

  /* 68px of masthead plus its 1px border. The hero subtracts this, and the
     mobile nav is offset by it, so the number lives in exactly one place. */
  --head-h: 69px;
}

/* ---------- reset ---------- */
*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--text);
  font-size: var(--t-base);
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; height: auto; }
a { color: inherit; }
button { font: inherit; color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 1em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--ember); color: var(--ink); }

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ember);
  color: var(--ink);
  padding: 0.75rem 1.25rem;
  z-index: var(--z-skip);
  font-weight: 700;
}
.skip:focus { left: 0; }

/* ---------- layout ---------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: clamp(4rem, 11vw, 8.5rem); }
.section--tight { padding-block: clamp(3rem, 7vw, 5rem); }
.section + .section { border-top: 1px solid var(--line); }

/* Section headings are real display type with a rule, not a tiny tracked kicker
   above every block. The kicker is the single most repeated tell in generated
   layouts and it was doing no work here that a heading does not do better. */
.sect__h {
  font-size: var(--t-lg);
  margin: 0 0 0.75rem;
  display: flex;
  align-items: baseline;
  gap: clamp(1rem, 3vw, 2rem);
}
.sect__h::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
  translate: 0 -0.35em;
}
.sect__lede { margin-bottom: clamp(2rem, 5vw, 3.5rem); }

.lede {
  font-size: var(--t-md);
  line-height: 1.45;
  color: var(--bone-dim);
  max-width: 46ch;
}

/* ---------- header ---------- */
.masthead {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
@supports not (backdrop-filter: blur(1px)) {
  .masthead { background: var(--ink); }
}

.masthead__in {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 68px;
}

.wordmark {
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--t-sm);
  letter-spacing: 0.16em;
  text-decoration: none;
  white-space: nowrap;
  margin-right: auto;
}

.nav { display: flex; align-items: center; gap: clamp(0.85rem, 2vw, 1.75rem); }

.nav a {
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--bone-dim);
  padding-block: 0.4rem;
  position: relative;
  transition: color var(--dur) var(--ease);
}
.nav a:hover { color: var(--bone); }

.nav a[aria-current="page"] { color: var(--bone); }
.nav a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--ember);
}

.lang {
  display: flex;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
}
/* NOT overflow: hidden. It clipped the focus ring on the ES/EN buttons down to an
   18x2px sliver that read as a divider. The buttons carry the rounding instead. */
.lang button:first-child { border-radius: 999px 0 0 999px; }
.lang button:last-child { border-radius: 0 999px 999px 0; }
.lang button {
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0.3rem 0.7rem;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.lang button[aria-pressed="true"] { background: var(--bone); color: var(--ink); }

.burger {
  display: none;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  width: 42px; height: 38px;
  cursor: pointer;
  padding: 0;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex-direction: column;
}
.burger span {
  display: block;
  width: 18px; height: 1.5px;
  background: var(--bone);
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* Six nav items plus the language toggle stop fitting before 860px now that
   Tienda is back, so the breakpoint moved out with them. */
@media (max-width: 940px) {
  .burger { display: flex; }
  .nav {
    position: fixed;
    inset: var(--head-h) 0 auto 0;
    z-index: var(--z-nav);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--ink);
    border-bottom: 1px solid var(--line);
    padding: 0.5rem var(--gutter) 1.5rem;
    transform: translateY(-120%);
    /* visibility, not just transform. A translated element is still focusable, so
       the six nav links sat in the tab order rendered 373px above the viewport,
       with a focus ring nobody could see and no way to scroll to them. Delaying
       the visibility change keeps the slide animation intact. */
    visibility: hidden;
    transition: transform var(--dur) var(--ease), visibility 0s linear var(--dur);
    max-height: calc(100svh - var(--head-h));
    overflow-y: auto;
  }
  .nav[data-open="true"] {
    transform: translateY(0);
    visibility: visible;
    transition: transform var(--dur) var(--ease), visibility 0s;
  }
  .nav a {
    padding-block: 1rem;
    border-bottom: 1px solid var(--line);
    font-size: var(--t-sm);
  }
  .nav a[aria-current="page"]::after { width: 22px; right: auto; bottom: 0.6rem; }
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 0.95rem 1.9rem;
  font-family: var(--text);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 2px;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }

/* the only filled element on the site — see the ember rule */
.btn--primary { background: var(--ember); color: var(--ink); }
.btn--primary:hover { background: var(--ember-lift); }

.btn--ghost { border-color: var(--line-control); color: var(--bone); background: none; }
.btn--ghost:hover { border-color: var(--bone); }

.btn--block { width: 100%; }

/* At 390px the two hero CTAs wrapped and pushed the secondary one under the
   fold. Tightening the inline padding fits both on one line. */
@media (max-width: 480px) {
  .btn { padding-inline: 1.25rem; letter-spacing: 0.08em; }
  .hero__cta { gap: 0.5rem; }
}

/* ---------- hero ---------- */
.hero {
  position: relative;
  /* The masthead is sticky, so it takes 69px of flow above the hero. A flat
     100svh therefore pushed the hero 69px past the fold and cut the primary
     call to action off the bottom of a 390x844 phone, measured, not guessed. */
  min-height: min(calc(100svh - var(--head-h)), 880px);
  display: grid;
  align-content: end;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
}

/* The photograph he picked himself: 6000x4000, dead centre composition, the hoop
   directly above his head. It is NOT desaturated here. The court's sea green and
   the rim's burnt orange are the palette, so draining them would throw away the
   thing the palette is derived from. Phones get the 3:4 crop of the same file.
   A url() inside a custom property set in an inline style resolves against the
   STYLESHEET in Chrome, which once produced assets/assets/img/... and a 404.
   Image URLs live in here, never in the markup. */
.hero__bg {
  position: absolute;
  inset: -6% 0 -6% 0;
  background: var(--ink-sunk) url("img/hero@portrait.webp") center 30% / cover no-repeat;
  filter: brightness(0.74) contrast(1.05) saturate(0.92);
  will-change: transform;
}
@media (min-width: 861px) {
  .hero__bg { background-image: url("img/hero@1200.webp"); background-position: center 38%; }
}
@media (min-width: 1500px) {
  .hero__bg { background-image: url("img/hero.webp"); }
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11,11,12,0.55) 0%, rgba(11,11,12,0.03) 26%,
              rgba(11,11,12,0.38) 56%, rgba(11,11,12,0.93) 100%);
  pointer-events: none;
}

.hero__in {
  position: relative;
  z-index: 2;
  padding-bottom: clamp(3rem, 8vw, 6rem);
  padding-top: clamp(6rem, 20vw, 10rem);
}

/* The one place display type breaks the ramp on purpose. A recording artist's
   name at viewport scale over their own photograph is the genre's own grammar,
   and it is the single move that stops this reading as a template with a picture
   in it. Measured at 223px on a 1440 viewport, 41px at 390. */
.hero__name {
  font-size: clamp(2.9rem, 14.2vw, 13.5rem);
  line-height: 0.82;
  letter-spacing: -0.045em;
  margin: 0 0 1.75rem;
  text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 2px 40px rgba(11, 11, 12, 0.55);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 2rem;
  max-width: 62ch;
}
.hero__tag {
  font-size: var(--t-md);
  line-height: 1.35;
  color: var(--bone);
  margin: 0;
  flex: 1 1 22ch;
}
.hero__cta { display: flex; flex-wrap: wrap; gap: 0.75rem; }

.hero__scroll {
  position: absolute;
  right: var(--gutter);
  bottom: clamp(3rem, 8vw, 6rem);
  z-index: 3;
  font-size: var(--t-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bone-dim);
  text-shadow: 0 1px 12px rgba(11, 11, 12, 0.85);
  writing-mode: vertical-rl;
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.hero__scroll::after {
  content: "";
  width: 1px; height: 46px;
  background: linear-gradient(var(--bone-faint), transparent);
}
@media (max-width: 940px) { .hero__scroll { display: none; } }

/* ---------- page headers ---------- */
.pagehead {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: clamp(2rem, 6vw, 4.5rem);
  align-items: center;
}
.pagehead--plain { grid-template-columns: 1fr; }
@media (max-width: 900px) { .pagehead { grid-template-columns: 1fr; } }

.pagehead__h {
  font-size: var(--t-2xl);
  margin-bottom: 1.25rem;
  text-transform: uppercase;
  letter-spacing: -0.035em;
}

/* The photographs are full length and mostly empty court. Laying type over one
   wastes it, so the press frames sit BESIDE the heading instead of behind it. */
.pagehead__art {
  border: 1px solid var(--line);
  background: var(--ink-raised);
  aspect-ratio: 3 / 4;
  overflow: hidden;
}
.pagehead__art img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* biased up: these are full length portraits and the face is the top third */
  object-position: center 22%;
}

/* ---------- the Spotify facade ----------
   Nothing loads from open.spotify.com until a visitor presses play. The first
   build's embedded players were the entire reason its Best Practices score was
   77: Spotify sets third party cookies the moment the iframe mounts. This is the
   same trade the YouTube facade already made.
   The id always travels on the card it belongs to, so a heading and its player
   can never describe different releases. */
.splay {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  padding: 0;
  border: 1px solid var(--line);
  background: var(--ink-raised);
  cursor: pointer;
  overflow: hidden;
}
.splay img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 700ms var(--ease), filter 700ms var(--ease);
}
.splay:hover img, .splay:focus-visible img { transform: scale(1.035); filter: brightness(1.06); }

.splay__badge {
  position: absolute;
  right: 8%;
  bottom: 8%;
  width: clamp(22px, 17%, 62px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--ember);
  display: grid;
  place-items: center;
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease);
}
.splay:hover .splay__badge { transform: scale(1.09); background: var(--ember-lift); }
.splay__badge::after {
  content: "";
  width: 28%; height: 36%;
  background: var(--ink);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  margin-left: 8%;
}

/* once the real player is in, the tile stops pretending to be a button */
.splay[data-live="true"] {
  aspect-ratio: auto;
  border: 0;
  background: none;
  cursor: default;
  overflow: visible;
}
.splay[data-live] .splay__badge { display: none; }
.splay[data-live] { cursor: default; }
.splay iframe { display: block; width: 100%; border: 0; border-radius: 12px; }

/* the compact player that drops under a ranked row */
.sp-inline { grid-column: 1 / -1; margin-top: 0.9rem; }
.sp-inline iframe { display: block; width: 100%; height: 152px; border: 0; border-radius: 12px; }

/* ---------- start here ---------- */
.starter {
  display: grid;
  grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
  gap: clamp(1.75rem, 5vw, 4.5rem);
  align-items: center;
  padding-bottom: clamp(2.5rem, 6vw, 4rem);
  border-bottom: 1px solid var(--line);
}
@media (max-width: 780px) { .starter { grid-template-columns: 1fr; } }

.starter__n {
  font-family: var(--display);
  font-size: var(--t-md);
  color: var(--sea);
  margin: 0 0 0.4rem;
  letter-spacing: 0.1em;
}
.starter__title { font-size: var(--t-xl); margin: 0 0 1rem; }

.ranks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}
.rank {
  display: grid;
  grid-template-columns: 2.5rem 72px minmax(0, 1fr) auto;
  gap: 0 clamp(1rem, 2.5vw, 1.75rem);
  align-items: center;
  padding-block: clamp(1rem, 2.5vw, 1.4rem);
  border-bottom: 1px solid var(--line);
}
.rank .plinks { justify-content: flex-end; }
.rank__n {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-md);
  color: var(--sea);
  font-variant-numeric: tabular-nums;
}
.rank .splay { width: 72px; }
.rank .splay__badge { width: 24px; right: 6%; bottom: 6%; }
.rank__title { font-size: var(--t-md); margin: 0; }
@media (max-width: 560px) {
  .rank {
    grid-template-columns: 1.75rem 56px minmax(0, 1fr);
    row-gap: 0.35rem;
  }
  .rank .splay { width: 56px; }
  .rank .splay__badge { width: 20px; }
  /* the links drop under the title once the row runs out of width */
  .rank .plinks { grid-column: 3 / -1; justify-content: flex-start; }
}

/* ---------- platform links ---------- */
.plinks { display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; margin: 0; }
/* Lighthouse's target-size audit passes these on the spacing exception, but at
   12px they measure 19px tall and this audience is on phones. The padding takes
   every one of them past the 24px WCAG 2.2 floor on its own merits. */
.plink {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ember);
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 24px;
  padding-block: 0.32rem;
}
.plink::after { content: "\2197"; transition: transform var(--dur) var(--ease); }
.plink:hover::after { transform: translate(2px, -2px); }

/* ---------- latest / feature ---------- */
.feature {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 5vw, 4.5rem);
  align-items: center;
}
@media (max-width: 780px) { .feature { grid-template-columns: 1fr; } }
.feature__title { font-size: var(--t-xl); margin: 0 0 0.65rem; }

/* ---------- albums and EPs ---------- */
.majors {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
}
.major {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}
@media (max-width: 700px) { .major { grid-template-columns: 1fr; } }
.major__title { font-size: var(--t-lg); margin: 0 0 0.5rem; }

/* ---------- singles grid ---------- */
.yr {
  font-family: var(--text);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--bone-faint);
  margin: clamp(2.5rem, 6vw, 4rem) 0 1.5rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.yr:first-of-type { margin-top: 0; }

.releases {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(44%, 190px), 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
  list-style: none;
  padding: 0;
  margin: 0;
}

.release { display: flex; flex-direction: column; gap: 0.55rem; }

.release__art {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--ink-raised);
  border: 1px solid var(--line);
}
.release__art img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 700ms var(--ease), filter 700ms var(--ease);
}
.release:hover .release__art img,
.release:focus-within .release__art img {
  transform: scale(1.035);
  filter: brightness(1.06);
}

.release__title {
  font-size: var(--t-base);
  letter-spacing: -0.015em;
  margin: 0;
  line-height: 1.15;
}
.release__meta {
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-faint);
  margin: 0 0 0.35rem;
}
.release__note {
  font-size: var(--t-xs);
  line-height: 1.5;
  color: var(--bone-dim);
  margin: 0 0 0.35rem;
}
.release .plinks { margin-top: auto; padding-top: 0.2rem; gap: 0.3rem 1rem; }

/* ---------- playlists ---------- */
.plists {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr));
  gap: clamp(1.5rem, 4vw, 3rem);
}
.plist__h { font-size: var(--t-md); margin: 0 0 1rem; }
.embed { border: 0; width: 100%; height: 152px; border-radius: 12px; background: var(--ink-raised); }
.embed--tall { height: 352px; }

/* ---------- video ---------- */
.videos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: clamp(1.5rem, 4vw, 2.5rem);
  list-style: none; padding: 0; margin: 0;
}
/* A shelf of vertical clips wants narrow columns, not 340px ones: a 9:16 tile at
   340px stands 600px tall and a single row fills the whole screen. */
.videos--short {
  grid-template-columns: repeat(auto-fill, minmax(min(46%, 210px), 1fr));
  gap: clamp(1rem, 2.5vw, 1.5rem);
}

/* click-to-load facade: no YouTube iframe until the visitor asks for one, and the
   poster frame is self hosted so the page makes no request to i.ytimg.com either */
.ytlite {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  border: 1px solid var(--line);
  background: var(--ink-raised);
  cursor: pointer;
  padding: 0;
  display: block;
  overflow: hidden;
}
.ytlite img { width: 100%; height: 100%; object-fit: cover; }
.ytlite::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(11, 11, 12, 0.32);
  transition: background var(--dur) var(--ease);
}
.ytlite:hover::before { background: rgba(11, 11, 12, 0.12); }
.ytlite__play {
  position: absolute;
  inset: 50% auto auto 50%;
  translate: -50% -50%;
  width: 68px; height: 68px;
  border-radius: 50%;
  background: var(--ember);
  display: grid;
  place-items: center;
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease);
}
.ytlite:hover .ytlite__play { transform: scale(1.08); background: var(--ember-lift); }
.ytlite__play::after {
  content: "";
  width: 17px; height: 22px;
  background: var(--ink);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  margin-left: 4px;
}
.ytlite iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.ytlite--short { aspect-ratio: 9 / 16; }
.ytlite--short .ytlite__play { width: 52px; height: 52px; }
.ytlite--short .ytlite__play::after { width: 13px; height: 17px; }

/* social clips, rendered only when data/videos.json carries any */
.sclips {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 1rem;
}
.sclip {
  display: grid;
  gap: 0.4rem;
  padding: 1.25rem;
  border: 1px solid var(--line);
  background: var(--ink-raised);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease);
}
.sclip:hover { border-color: var(--line-strong); }
.sclip__net {
  font-size: var(--t-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ember);
  font-weight: 700;
}

/* ---------- bio ---------- */
/* NOT max-width on the shell. .bio IS the .shell element, so constraining it here
   centred the whole block in the viewport while every other section starts at the
   left gutter. The measure is set on the paragraphs instead. */
.bio__route, .bio__p, .bio__note { max-width: 56ch; }
.bio__route {
  font-size: var(--t-xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sea);
  margin: 0 0 2rem;
}
/* His own words, from his own Spotify artist page, verbatim including the spacing.
   Setting them larger than body copy is the only editing they get. */
.bio__p {
  font-size: var(--t-md);
  line-height: 1.5;
  color: var(--bone);
  margin: 0 0 1.1rem;
  max-width: 54ch;
}
.bio__note {
  margin-top: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-size: var(--t-sm);
  color: var(--bone-dim);
  max-width: 54ch;
}

/* ---------- notices ---------- */
.notice {
  border: 1px solid var(--line-strong);
  background: var(--ink-raised);
  padding: clamp(1.25rem, 3vw, 1.75rem);
  color: var(--bone-dim);
  font-size: var(--t-sm);
}

/* ---------- forms ---------- */
.capture {
  background: var(--ink-raised);
  border: 1px solid var(--line);
  padding: clamp(1.5rem, 4.5vw, 3.25rem);
}
.capture__h { font-size: var(--t-lg); margin: 0 0 0.75rem; }

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.1rem 1.25rem;
}
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form-grid { grid-template-columns: 1fr; } }

.field { display: flex; flex-direction: column; gap: 0.45rem; }

.field label {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--bone-dim);
}
.field .req { color: var(--ember); }

.field input,
.field select,
.field textarea {
  font: inherit;
  font-size: var(--t-sm);
  color: var(--bone);
  background: var(--ink-sunk);
  border: 1px solid var(--line-control);
  border-radius: 2px;
  padding: 0.85rem 0.95rem;
  width: 100%;
  transition: border-color var(--dur) var(--ease);
}
.field textarea { min-height: 132px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--bone-faint); }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--bone-faint); }

.field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--bone-dim) 50%),
                    linear-gradient(135deg, var(--bone-dim) 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 2px), calc(100% - 14px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.5rem;
}
.field select option { background: var(--ink-raised); color: var(--bone); }

.field[data-invalid="true"] input,
.field[data-invalid="true"] select { border-color: #e08a68; }

.field__err {
  font-size: var(--t-xs);
  color: #e8a184;
  min-height: 0;
  line-height: 1.4;
}
.field__err:empty { display: none; }

.consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  align-items: start;
  padding: 1rem;
  border: 1px solid var(--line);
  background: var(--ink-sunk);
}
/* 24x24 is the WCAG 2.2 minimum, and this is the one control with a legal
   consequence attached to it. */
.consent input { width: 24px; height: 24px; margin-top: 1px; accent-color: var(--ember); flex: none; }
.consent span { font-size: var(--t-xs); line-height: 1.55; color: var(--bone-dim); }
.consent a { color: var(--bone); }

/* Off-canvas at -9999px forced body{overflow-x:hidden}, which clips every
   positioned layer on the page. Same result, no page-wide clip. */
.honey {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  opacity: 0;
  pointer-events: none;
}

.form-note {
  font-size: var(--t-xs);
  color: var(--bone-faint);
  line-height: 1.55;
}

/* Status is information, not an action, so it carries no ember. */
.form-status {
  margin-top: 1rem;
  padding: 0.95rem 1.1rem;
  border: 1px solid var(--line-strong);
  background: var(--ink-sunk);
  font-size: var(--t-sm);
  color: var(--bone-dim);
}
/* The region stays in the DOM and is emptied instead of hidden. A live region that
   is display:none at load and unhidden-and-written in the same tick is a pattern
   several screen reader and browser pairs simply never announce. */
.form-status[data-empty] { display: none; }

/* ---------- split (contact) ---------- */
.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}

/* ---------- privacy ---------- */
.priv__h { font-size: var(--t-md); margin: 2.5rem 0 0.75rem; }

/* ---------- footer ---------- */
.foot {
  border-top: 1px solid var(--line);
  background: var(--ink-sunk);
  padding-block: clamp(3rem, 7vw, 5rem) 2rem;
}
.foot__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 2.5rem;
  padding-bottom: 3rem;
  border-bottom: 1px solid var(--line);
}
.foot .foot__h {
  font-family: var(--text);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bone-faint);
  margin: 0 0 1.1rem;
}
.foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.1rem; }
.foot a {
  text-decoration: none;
  color: var(--bone-dim);
  font-size: var(--t-sm);
  transition: color var(--dur) var(--ease);
  /* 18px of line box is under the WCAG 2.2 24px target floor. The padding takes
     these past it without changing the footer's visual pitch, because the list
     gap came down by the same amount. */
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding-block: 0.25rem;
}
.foot a:hover { color: var(--bone); }

.foot__base {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  align-items: center;
  padding-top: 2rem;
  font-size: var(--t-xs);
  color: var(--bone-faint);
}
.foot__base a { font-size: var(--t-xs); }

/* ---------- motion ---------- */
/* Scoped to .js on purpose. If the reveal hid content by default, a failed or
   blocked script would leave every section below the fold invisible, which is
   exactly what a full page render caught. No JS now means no animation and a
   fully visible page, which is the correct failure direction. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  transition-delay: var(--d, 0ms);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
  .hero__bg { transform: none !important; }
}
