:root {
  color-scheme: dark;
  --black: #020503;
  --panel: #060b08;
  --line: #173523;
  --line-strong: #2cff82;
  --text: #e9fff0;
  --muted: #75a886;
  --green: #00ff66;
}

* {
  box-sizing: border-box;
}

html {
  background: var(--black);
  min-height: 100%;
  scrollbar-color: var(--green) var(--black);
}

body {
  margin: 0;
  min-height: 100%;
  background:
    linear-gradient(180deg, rgba(0, 255, 102, 0.055), transparent 260px),
    var(--black);
  color: var(--text);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  letter-spacing: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

.shell {
  width: min(1280px, calc(100% - 32px));
  margin: 0 auto;
  padding: 32px 0 56px;
}

/* The document's h1 (site name) is intentionally not shown: the design has no
   visible headline. It is clipped to 1px so crawlers and screen readers still get
   a single clear page title, and it is the same treatment as .sr-only. */
h1 {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.media-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
  gap: 1px;
  align-items: start;
  margin-top: 28px;
  background: var(--line);
  border: 1px solid var(--line-strong);
}

.player {
  position: relative;
  overflow: hidden;
  width: 100%;
  aspect-ratio: 16 / 9;
  min-height: 240px;
  background: #000;
}

.player iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* The rail is exactly as tall as the player beside it, so both columns end on the
   same line. That used to be measured in JavaScript after first paint, which
   shifted every section below it by ~100px once the page was pre-rendered and
   there was real content to move (CLS 0.02). It is pure arithmetic now: the rail
   column is 340px plus the 1px gap, so the player - and therefore the rail - is
   (100cqw - 341px) at 16:9. No script, no reflow. */
.media-grid {
  container-type: inline-size;
}

.video-rail {
  display: grid;
  align-content: start;
  height: calc((100cqw - 341px) * 9 / 16);
  max-height: min(70vh, 720px);
  overflow: auto;
  background: var(--panel);
}

.video-card {
  display: grid;
  grid-template-columns: 112px minmax(0, 1fr);
  align-items: stretch;
  gap: 12px;
  min-height: 82px;
  padding: 10px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.video-card img {
  width: 112px;
  height: 62px;
  object-fit: cover;
  border: 1px solid rgba(44, 255, 130, 0.5);
  filter: grayscale(1) contrast(1.15);
}

.video-card span {
  align-self: center;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 650;
  line-height: 1.25;
  text-transform: uppercase;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

.video-card:hover,
.video-card:focus-visible,
.video-card:active,
.video-card.is-active {
  background: #092013;
  outline: 0;
}

.video-card:hover span,
.video-card:focus-visible span,
.video-card:active span,
.video-card.is-active span {
  color: var(--green);
}

.video-card:hover img,
.video-card:focus-visible img,
.video-card:active img,
.video-card.is-active img {
  filter: grayscale(0) contrast(1.05);
}

.playlists {
  display: grid;
  /* One row per release. The card carries its own service buttons, so it spans the
     block and the buttons sit at its right edge; a second column here would leave an
     empty cell painting this block's own background next to a lone card. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
  margin-top: 28px;
  background: var(--line);
  border: 1px solid var(--line);
}

.socials {
  margin-top: 1px;
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--panel);
}

.social-heading {
  padding: 22px 18px 18px;
  color: var(--green);
  font-size: clamp(1.1rem, 2.4vw, 1.75rem);
  font-weight: 900;
  letter-spacing: 0.04em;
  text-align: center;
  text-transform: uppercase;
}

.social-list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
}

.social-card {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  min-height: 62px;
  padding: 14px;
  background: var(--panel);
  color: var(--text);
  font-size: 0.82rem;
  font-weight: 800;
  text-transform: uppercase;
  overflow: hidden;
}

.social-card span:last-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.social-card:hover,
.social-card:focus-visible,
.social-card:active {
  background: var(--green);
  color: #001f0c;
  outline: 0;
}

.social-card:hover .service-icon,
.social-card:focus-visible .service-icon,
.social-card:active .service-icon {
  border-color: #001f0c;
}

.social-card:hover .brand-logo,
.social-card:focus-visible .brand-logo,
.social-card:active .brand-logo {
  color: #001f0c;
}

.link,
.text-link,
.link-card {
  position: relative;
  min-width: 0;
}

.link-card {
  display: grid;
  /* Content-sized tracks, centred as one group: the artwork, the release's title and badge
     and its music buttons read as a single element in the middle of the row. The middle
     track is minmax(0, auto), not 1fr - 1fr swallows the slack and pins the buttons to the
     far edge - and it can still shrink to nothing, so a long title wraps rather than
     overflowing the card. */
  grid-template-columns: 88px minmax(0, auto) auto;
  justify-content: center;
  align-items: center;
  gap: 14px;
  min-height: 112px;
  padding: 12px 14px 12px 12px;
  background: var(--panel);
  color: var(--text);
  transition: background 160ms ease, color 160ms ease;
}

.art {
  width: 88px;
  height: 88px;
  overflow: hidden;
  border: 1px solid rgba(44, 255, 130, 0.45);
  background: #000;
}

.art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: contrast(1.08) saturate(0.9);
  transition: filter 160ms ease, transform 160ms ease;
}

.art-fallback {
  display: grid;
  width: 100%;
  height: 100%;
  place-items: center;
  color: var(--green);
}

.art-fallback .brand-logo {
  width: 30px;
  height: 30px;
}

.link-copy {
  display: grid;
  gap: 10px;
  min-width: 0;
}

.link-title {
  color: var(--text);
  font-size: 0.92rem;
  font-weight: 750;
  line-height: 1.2;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

.badge {
  width: fit-content;
  border: 1px solid var(--line-strong);
  padding: 4px 7px;
  color: var(--green);
  font-size: 0.64rem;
  font-weight: 800;
  letter-spacing: 0.08em;
}

/* The card's action column: a release's music buttons. Nothing needs justifying here - the
   row is exactly as wide as its buttons, because the card's tracks size to their content and
   centre as a group - and the card's own gap is what sets them apart from the playlist text. */
.playlist-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The playlist card's music buttons are the page's main call to action, so they are the larger
   pair: a 48px box with a 26px glyph inside a 56px target, stepping down with the layout. The
   sizes live here rather than on .service-icon because the artist directory's Apple button and
   the social cards keep the shared 32px box, which is what makes a Spotify and an Apple button
   look like the same button wherever they appear side by side. */
.playlist-actions .service-action {
  min-width: 56px;
  min-height: 56px;
}

.playlist-actions .service-icon {
  width: 48px;
  height: 48px;
}

.playlist-actions .brand-logo {
  width: 26px;
  height: 26px;
}

/* The clickable wrapper around a service icon, shared by the artist directory's
   Apple button and the playlist cards' music buttons. The wrapper is the 44px touch
   target while the visible box stays the 32px .service-icon, so every music button
   on the page is the same size, has the same border and reacts the same way.
   Only the glyph inside differs. */
.service-action {
  display: grid;
  place-items: center;
  min-width: 44px;
  min-height: 44px;
}

.service-action:hover .service-icon,
.service-action:focus-visible .service-icon,
.service-action:active .service-icon {
  border-color: #001f0c;
  background: var(--green);
  color: #001f0c;
}

.service-icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 1px solid rgba(44, 255, 130, 0.65);
  color: var(--green);
  font-size: 1rem;
  font-weight: 900;
  line-height: 1;
}

.brand-logo {
  display: block;
  width: 17px;
  height: 17px;
  background: currentColor;
  mask: var(--brand-url) center / contain no-repeat;
  -webkit-mask: var(--brand-url) center / contain no-repeat;
}

/* The playlist card is a container, not a link: its music buttons are the links, and
   each one carries its own hover (see .service-action). A card-wide hover state would
   advertise a click target that no longer exists. */

.posts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  margin-top: 28px;
  background: var(--line);
  border: 1px solid var(--line);
}

.artists {
  /* One defined height for the whole directory: it sets the row height, and
     the player column stretches to inherit it, so both halves end at the
     same y. Bump this and both sides follow. */
  --artist-row-height: 560px;

  display: grid;
  grid-template-columns: minmax(320px, 420px) minmax(0, 1fr);
  /* No gap: the 1px divider is drawn by the panel border-right instead. The
     background is --panel, not --line, so no leftover cell can ever render
     as a block of divider green. */
  gap: 0;
  /* Columns fill the row rather than hugging their own content. */
  align-items: stretch;
  min-height: var(--artist-row-height);
  margin-top: 28px;
  background: var(--panel);
  border: 1px solid var(--line);
}

.artist-list {
  min-width: 0;
}

/* Left column is just the Spotify player, stretched to the full row height
   so it fills the panel top to bottom. */
.artist-panel {
  min-width: 0;
  padding: 12px;
  background: var(--panel);
  border-right: 1px solid var(--line);
}

/* Clips Spotify rounded card against a dark backing so no light pixel can
   survive at a corner. The 13px outer radius minus the 1px frame clips at
   exactly 12px, the radius Spotify own embed markup recommends, so the clip
   lands on the wedge boundary rather than inside it. */
.embed-frame {
  height: 100%;
  overflow: hidden;
  border: 1px solid rgba(44, 255, 130, 0.45);
  border-radius: 13px;
  background: #000;
}

.embed-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 12px;
  background: #000;
  /* The embed's own document reports color-scheme:normal with transparent
     backgrounds, so any part of it that paints nothing would otherwise composite
     over the iframe's default WHITE canvas. Declaring the scheme here makes that
     canvas dark, so no light pixel can appear around or below the Spotify card. */
  color-scheme: dark;
}

.artist-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  max-height: var(--artist-row-height);
  overflow: auto;
  background: var(--line);
  scrollbar-color: var(--green) var(--panel);
}

/* Each artist is a row of two links: the card that swaps the embed player, and the
   act's Apple Music page. They are siblings rather than nested, because a link inside a
   link is invalid HTML, and the row leaves the card's own two-column layout alone. The
   row paints the panel colour so the list's 1px hairline grid keeps showing through. */
.artist-row {
  display: flex;
  align-items: stretch;
  background: var(--panel);
}

.artist-row .artist-card {
  flex: 1 1 auto;
  min-width: 0;
}

/* The cell that separates the Apple action from the card and paints the panel colour
   behind it. The 44px target, the centring, the icon box and the hover all come from
   .service-action - the same wrapper the playlist cards' music buttons use - which is
   what makes this button look like the Spotify one beside it. `color` is deliberately
   not set here: .service-icon draws the glyph in --green. */
.artist-apple {
  flex: 0 0 auto;
  border-left: 1px solid var(--line);
  background: var(--panel);
}

.artist-card {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 32px;
  align-items: center;
  gap: 10px;
  min-height: 72px;
  padding: 14px;
  border: 0;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 850;
  line-height: 1.15;
  text-align: left;
  text-transform: uppercase;
  cursor: pointer;
}

.artist-name {
  grid-column: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.artist-card .service-icon {
  grid-column: 2;
  justify-self: end;
}

.artist-card:hover,
.artist-card:focus-visible,
.artist-card:active,
.artist-card.is-active {
  background: var(--green);
  color: #001f0c;
  outline: 0;
}

.artist-card:hover .service-icon,
.artist-card:focus-visible .service-icon,
.artist-card:active .service-icon,
.artist-card.is-active .service-icon {
  border-color: #001f0c;
  color: #001f0c;
}

/* Selection is a state, not a hover: the card whose artist is in the player keeps the
   green fill and gains a marker rail no hover state has, so "this one is in the player"
   is readable at a glance and while the pointer is somewhere else. Clicking the marked
   card again clears it and puts DIFTUG Radio back in the player. The rail is the same
   dark green the label uses on the green fill, and 4px keeps it inside the card's own
   padding rather than over the list hairline. */
.artist-card.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: #001f0c;
}

/* Full-width row under both columns (grid-column spans the row), linking to the
   static /artists directory. 48px keeps it a comfortable thumb target. */
.artist-all {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 12px;
  border-top: 1px solid var(--line);
  background: var(--panel);
  color: var(--green);
  font-size: 0.72rem;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

.artist-all:hover,
.artist-all:focus-visible,
.artist-all:active {
  background: var(--green);
  color: #001f0c;
  outline: 0;
}

.signup-panel {
  margin-top: 18px;
  display: flex;
  justify-content: center;
}

.signup-form {
  /* One shared height keeps the email input and its + button exactly level. */
  --field-height: 46px;
  --field-button-width: 46px;

  width: min(420px, 100%);
  display: grid;
  gap: 8px;
}

.email-title {
  color: var(--green);
  font-size: 0.82rem;
  font-weight: 900;
  letter-spacing: 0.06em;
  text-align: center;
  text-transform: uppercase;
}

/* One unified control. The outer border lives on the label, so the input and
   the + can never show mismatched edges of their own. */
.email-entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--field-button-width);
  align-items: stretch;
  border: 1px solid rgba(44, 255, 130, 0.65);
  background: #000;
}

.signup-form input {
  width: 100%;
  min-width: 0;
  height: var(--field-height);
  min-height: var(--field-height);
  padding: 0 12px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  outline: 0;
}

.signup-form input:focus {
  box-shadow: inset 0 0 0 1px var(--green);
}

/* The + is the submit action, not a character floating at the end of the
   input. Every default a browser button carries (padding, margin, border,
   background, native appearance, its own font) is cleared; what is left is a
   fixed-width slot, one divider line, and a flex-centred glyph. */
.signup-form button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--field-button-width);
  height: var(--field-height);
  min-height: var(--field-height);
  margin: 0;
  padding: 0;
  border: 0;
  /* the single clean divider between input and button */
  border-left: 1px solid rgba(44, 255, 130, 0.65);
  border-radius: 0;
  background: transparent;
  color: var(--green);
  font: inherit;
  font-size: 1.0625rem;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

.signup-form button:hover,
.signup-form button:focus-visible {
  background: var(--green);
  color: #001f0c;
  outline: 0;
}

.signup-form output {
  min-height: 18px;
  color: var(--muted);
  font-size: 0.78rem;
  text-align: center;
  text-transform: uppercase;
}

.post {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1;
  background: #010201;
}

.post img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.15);
  transition: filter 160ms ease, transform 160ms ease;
}

.post:hover img,
.post:focus-visible img,
.post:active img {
  filter: grayscale(0) contrast(1.05);
  transform: scale(1.015);
}

.empty {
  display: none;
}

@media (max-width: 900px) {
  /* Between 761px and 900px the artist section is still side-by-side, which
     leaves each directory cell too narrow for one line at 0.78rem. Letting the
     name wrap beats truncating it: at 768px thirty of the sixty-two names were
     cut off behind an ellipsis, including "Satanic Steakhouse" and
     "ShaggyTheAirhead". The 72px card height still holds two lines, and
     overflow-wrap lets an unbroken handle like "ZAKdaTERROR" split instead of
     overflowing the cell. */
  .artist-name {
    display: -webkit-box;
    overflow: hidden;
    overflow-wrap: anywhere;
    white-space: normal;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
}

@media (max-width: 760px) {
  .shell {
    width: min(100% - 16px, 1120px);
    /* Clears the home indicator / gesture bar on phones. */
    padding: 12px 0 calc(36px + env(safe-area-inset-bottom, 0px));
  }

  /* .playlists is missing on purpose: it is already a single column at every width. */
  .media-grid,
  .posts,
  .artists {
    grid-template-columns: 1fr;
  }

  .media-grid {
    margin-top: 8px;
  }

  .player {
    min-height: 0;
  }

  .video-rail {
    height: auto;
    max-height: 340px;
    overflow: auto;
  }

  .video-card {
    grid-template-columns: 104px minmax(0, 1fr);
    min-height: 76px;
    padding: 8px;
  }

  .video-card img {
    width: 104px;
    height: 58px;
  }

  .video-card span {
    font-size: 0.72rem;
    -webkit-line-clamp: 2;
  }

  .playlists {
    margin-top: 16px;
  }

  .artists {
    min-height: 0;
    /* The gap between the email signup form and the Spotify directory. #posts sits
       between them in the DOM, but it renders as `posts empty` -> display:none
       while Instagram is unavailable, so this margin is the whole gap on a phone.
       10px keeps the two blocks close without touching the signup form above. */
    margin-top: 10px;
  }

  /* Spotify's artist embed is measured, not guessed, and at this width it renders
     one of two layouts with a hard switch between 320px and 360px of frame height:

       - below ~360px: a compact branch whose player box is only 152px tall while
         its list runs to 242px, so a taller frame leaves a filler band under the
         player and a shorter one cuts the list off. The old 200px value sat in
         exactly that dead zone: 48px of blank under the 152px player, with the
         list clipped mid-row.
       - at 360px and above: the real layout - a 200px header, an 8px gap, then
         52px track rows, capped at 10 rows (content ends at 725px).

     416px is the first size that fits that layout exactly: 200 + 8 + 4 x 52. The
     frame ends on the fourth row's edge, so there is no filler band and no
     half-cut row, and the artist list starts immediately beneath it. The desktop
     rule above is deliberately untouched. tests/mobile.js re-measures Spotify's
     live layout at this exact geometry, so a change on their side fails the suite
     instead of shipping another band. */
  .embed-frame {
    height: 416px;
  }

  /* A little tighter than desktop so the card uses more of the screen width. The
     green border and radius live on .embed-frame, so they are unaffected. */
  .artist-panel {
    padding: 10px;
  }

  .artist-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-height: 420px;
  }

  /* Touch scrolling stays inside the directory / rail instead of chaining to
     the page behind it, and gets momentum scrolling on older iOS. */
  .video-rail,
  .artist-list {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }

  .social-heading {
    padding: 18px 12px 14px;
    font-size: 1.35rem;
  }

  .social-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .social-card {
    min-height: 58px;
    padding: 12px;
    font-size: 0.76rem;
  }

  .signup-form {
    grid-template-columns: 1fr;
    width: min(420px, 100% - 24px);
  }

  .link-card {
    grid-template-columns: 72px minmax(0, auto) auto;
    min-height: 96px;
    gap: 12px;
  }

  .art {
    width: 72px;
    height: 72px;
  }

  .link-title {
    font-size: 0.82rem;
  }

  /* The playlist's larger buttons step back with the rest of the card. */
  .playlist-actions .service-action {
    min-width: 50px;
    min-height: 50px;
  }

  .playlist-actions .service-icon {
    width: 44px;
    height: 44px;
  }

  .playlist-actions .brand-logo {
    width: 23px;
    height: 23px;
  }
}

@media (max-width: 430px) {
  .shell {
    width: 100%;
    /* Keeps text out of the rounded display corners in landscape. */
    padding-inline: max(8px, env(safe-area-inset-left, 0px)) max(8px, env(safe-area-inset-right, 0px));
  }

  /* Narrowest phones: another 2px back so the Spotify card is as wide as it can be. */
  .artist-panel {
    padding: 8px;
  }

  .video-card {
    grid-template-columns: 92px minmax(0, 1fr);
  }

  .video-card img {
    width: 92px;
    height: 52px;
  }

  .social-list {
    grid-template-columns: 1fr;
  }

  .artist-list {
    grid-template-columns: 1fr;
    max-height: 380px;
  }

  .artist-card {
    min-height: 64px;
    padding: 12px;
  }

  .link-card {
    grid-template-columns: 64px minmax(0, auto) auto;
    min-height: 88px;
    padding: 10px;
  }

  .art {
    width: 64px;
    height: 64px;
  }

  /* Narrowest phones: the smallest the large pair goes - still bigger than the 32px box the
     rest of the page uses, and sized so the badge and title still fit beside it at 320px. */
  .playlist-actions .service-action {
    min-width: 46px;
    min-height: 46px;
  }

  .playlist-actions .service-icon {
    width: 40px;
    height: 40px;
  }

  .playlist-actions .brand-logo {
    width: 21px;
    height: 21px;
  }

  .email-title {
    font-size: 0.76rem;
  }
}

/* ===========================================================================
   SHADOW RANK : SEASON 1 - top ad banner
   Cross-promo leaderboard for rank.entershadowland.com/s1, sitting above the
   shell. Rank's palette is scoped to .sr-ad on purpose: none of it may leak
   into the Underground black/green theme. Values are lifted from the season
   page so the ad reads first-party:
     .supply-strip    -> strip framing, 3px red edge, belt with edge fades
     .supply-eyebrow  -> mono eyebrow + blinking live dot
     .supply-title    -> Big Shoulders Display headline + small mono note
     .hero-stamp      -> rotated 1px-red "stamp" CTA
     #bgHatch/#bgScan -> hazard hatch + scanline texture
   =========================================================================== */
.sr-ad {
  --sr-border-strong: #2c2727;
  --sr-bone: #ece4d2;
  --sr-text-secondary: #b8aea0;
  --sr-red: #c8102e;
  --sr-red-bright: #ff3040;
  --sr-red-signal: #ff5060;
  --sr-mono: "JetBrains Mono", "IBM Plex Mono", monospace;
  --sr-display: "Big Shoulders Display", "Arial Narrow", sans-serif;

  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: stretch;
  /* Content self-centres on ultrawide displays while the strip itself stays
     full-bleed, so it still reads as a banner ad rather than a page block. */
  padding-inline: max(0px, calc((100% - 1400px) / 2));
  min-height: 84px;
  border-bottom: 1px solid var(--sr-border-strong);
  background: linear-gradient(180deg, rgba(12, 12, 13, 0.92), rgba(7, 7, 7, 0.85));
  color: var(--sr-bone);
  font-family: var(--sr-mono);
  text-decoration: none;
  overflow: hidden;
  isolation: isolate;
}

/* Hazard hatch (Rank's #bgHatch): diagonal red hairlines, screened on. */
.sr-ad-hatch {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    135deg,
    transparent 0 22px,
    rgba(200, 16, 46, 0.025) 22px 23px
  );
  mix-blend-mode: screen;
}

/* Scanlines (Rank's #bgScan). */
.sr-ad::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    0deg,
    rgba(0, 0, 0, 0.32) 0 1px,
    transparent 1px 3px
  );
  opacity: 0.22;
}

.sr-ad-rail,
.sr-ad-belt,
.sr-ad-end {
  position: relative;
  z-index: 2;
}

/* --- left rail: live eyebrow + display headline --- */
.sr-ad-rail {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  padding: 10px 18px 10px 22px;
  border-right: 1px solid var(--sr-border-strong);
  background: rgba(9, 9, 10, 0.92);
  transition: background 160ms ease;
}

/* 3px red edge, the same treatment as .supply-strip::before. */
.sr-ad-rail::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 3px;
  height: 60%;
  background: var(--sr-red);
}

.sr-ad-eyebrow {
  display: flex;
  align-items: center;
  gap: 7px;
  overflow: hidden;
  color: var(--sr-red-signal);
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
}

.sr-ad-dot {
  width: 5px;
  height: 5px;
  flex: none;
  background: var(--sr-red-bright);
  box-shadow: 0 0 8px rgba(255, 48, 64, 0.8);
  animation: srAdBlink 2.4s steps(1, end) infinite;
}

@keyframes srAdBlink {
  0%,
  70% {
    opacity: 1;
  }
  71%,
  76% {
    opacity: 0.25;
  }
  77%,
  100% {
    opacity: 1;
  }
}

.sr-ad-title {
  display: flex;
  align-items: baseline;
  gap: 7px;
  overflow: hidden;
  color: var(--sr-red-bright);
  font-family: var(--sr-display);
  font-size: 1.5rem;
  font-weight: 900;
  letter-spacing: 0.06em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

.sr-ad-title small {
  color: var(--sr-text-secondary);
  font-family: var(--sr-mono);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.2em;
}

/* --- looping belt of season elements --- */
.sr-ad-belt {
  display: flex;
  align-items: center;
  min-width: 0;
  overflow: hidden;
}

.sr-ad-belt::before,
.sr-ad-belt::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40px;
  z-index: 2;
  pointer-events: none;
}

.sr-ad-belt::before {
  left: 0;
  background: linear-gradient(90deg, rgba(9, 9, 10, 0.95), transparent);
}

.sr-ad-belt::after {
  right: 0;
  background: linear-gradient(270deg, rgba(9, 9, 10, 0.95), transparent);
}

.sr-ad-track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: srAdLoop 38s linear infinite;
}

@keyframes srAdLoop {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

.sr-ad-item {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  color: var(--sr-text-secondary);
  font-family: var(--sr-display);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Diamond separator, echoing Rank's red square markers. */
.sr-ad-item::after {
  content: "";
  width: 3px;
  height: 3px;
  flex: none;
  background: var(--sr-red);
  transform: rotate(45deg);
}

/* Chip inside a belt item, mirrors .sitem-pts. */
.sr-ad-item b {
  padding: 1px 6px;
  border: 1px solid rgba(200, 16, 46, 0.35);
  background: rgba(200, 16, 46, 0.06);
  color: var(--sr-red-signal);
  font-family: var(--sr-mono);
  font-size: 0.52rem;
  font-weight: 700;
  letter-spacing: 0.14em;
}

/* --- right end: stamp CTA --- */
.sr-ad-end {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 22px 10px 18px;
  border-left: 1px solid var(--sr-border-strong);
  background: rgba(9, 9, 10, 0.92);
}

.sr-ad-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--sr-red);
  background: rgba(200, 16, 46, 0.06);
  color: var(--sr-red-signal);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  /* the rotated "stamp" treatment from .hero-stamp */
  transform: rotate(-1.5deg);
  transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
}

/* The whole strip is the hit target. */
.sr-ad:hover .sr-ad-rail,
.sr-ad:focus-visible .sr-ad-rail,
.sr-ad:active .sr-ad-rail,
.sr-ad:hover .sr-ad-end,
.sr-ad:focus-visible .sr-ad-end,
.sr-ad:active .sr-ad-end {
  background: rgba(200, 16, 46, 0.08);
}

.sr-ad:hover .sr-ad-cta,
.sr-ad:focus-visible .sr-ad-cta,
.sr-ad:active .sr-ad-cta {
  border-color: var(--sr-red-bright);
  background: var(--sr-red-bright);
  color: #0a0203;
}

.sr-ad:focus-visible {
  outline: 0;
  /* matches the inset ring used by .signup-form input:focus */
  box-shadow: inset 0 0 0 1px var(--sr-red-bright);
}

@media (prefers-reduced-motion: reduce) {
  .sr-ad-dot,
  .sr-ad-track {
    animation: none;
  }

  .sr-ad-belt {
    overflow-x: auto;
  }

  /* The card/post transitions are decorative only. */
  .link-card,
  .art img,
  .post img,
  .video-card img {
    transition: none;
  }
}

/* ===========================================================================
   ARTISTS PAGE (/artists)
   Static, server-rendered companion to the homepage directory: every artist in
   the DIFTUG playlists plus the metadata that can actually be sourced. Every
   rule is scoped under .a-page so nothing here can leak into index.html, and
   the page ships no JavaScript at all - it is one HTML file and this sheet.
   Artwork is loaded from Spotify's public image CDN, never from this deploy.
   =========================================================================== */
.a-page {
  --a-card-min: 300px;
  display: grid;
  gap: 1px;
  margin-top: 24px;
  background: var(--line);
  border: 1px solid var(--line);
}

.a-head {
  padding: 20px 16px;
  background: var(--panel);
}

.a-brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  color: var(--text);
}

.a-brand img {
  width: 44px;
  height: 44px;
  border: 1px solid rgba(44, 255, 130, 0.45);
  background: #000;
  object-fit: contain;
}

.a-brand span {
  font-size: 0.78rem;
  font-weight: 900;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.a-kicker {
  margin: 16px 0 0;
  color: var(--green);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.a-title {
  margin: 8px 0 0;
  color: var(--text);
  font-size: clamp(1.35rem, 4.2vw, 2.1rem);
  font-weight: 900;
  line-height: 1.1;
  text-transform: uppercase;
}

.a-lede {
  margin: 10px 0 0;
  max-width: 62ch;
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.5;
}

.a-lede a,
.a-foot a,
.a-link {
  color: var(--green);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.a-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--a-card-min), 1fr));
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--line);
}

.a-card {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr);
  align-content: start;
  gap: 12px;
  padding: 12px;
  background: var(--panel);
}

/* Fixed square so a late-loading or absent cover cannot shift the layout. */
.a-art {
  display: grid;
  width: 72px;
  height: 72px;
  place-items: center;
  overflow: hidden;
  border: 1px solid rgba(44, 255, 130, 0.45);
  background: #000;
}

.a-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: contrast(1.08) saturate(0.9);
}

.a-art .brand-logo {
  width: 26px;
  height: 26px;
  color: var(--muted);
}

.a-body {
  min-width: 0;
}

.a-name {
  margin: 0;
  color: var(--text);
  font-size: 0.92rem;
  font-weight: 900;
  line-height: 1.2;
  text-transform: uppercase;
  /* Names wrap here instead of truncating: this page is the full roster, so a
     long name must stay readable rather than hidden behind an ellipsis. */
  overflow-wrap: anywhere;
}

.a-links {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

.a-links a {
  display: inline-flex;
  align-items: center;
  /* Full touch height at every width: this pill row is the whole interaction on
     the page, and tablets are touch devices too. */
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid rgba(44, 255, 130, 0.45);
  color: var(--green);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

.a-links a:hover,
.a-links a:focus-visible,
.a-links a:active {
  border-color: var(--green);
  background: var(--green);
  color: #001f0c;
  outline: 0;
}

.a-foot {
  padding: 16px;
  background: var(--panel);
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

.a-foot p {
  margin: 0 0 8px;
}

.a-foot p:last-child {
  margin-bottom: 0;
}

@media (max-width: 430px) {
  /* The auto-fill grid has already collapsed to one column at this width. */
  .a-page {
    --a-card-min: 100%;
  }
}

/* ===========================================================================
   TOUCH HARDENING
   Behaviour-only rules for phones. Nothing here changes the desktop layout:
   they remove the browser's default touch artefacts (grey tap flash, the
   legacy 300ms tap delay, double-tap zoom on controls) so a tap on a static
   site feels as immediate as a click does with a mouse.
   =========================================================================== */
.link-card,
.service-action,
.social-card,
.video-card,
.artist-card,
.post,
.sr-ad,
.signup-form button {
  -webkit-tap-highlight-color: transparent;
  /* Kills the 300ms delay and double-tap-to-zoom on these controls without
     disabling pinch zoom for the page as a whole. */
  touch-action: manipulation;
}

@media (max-width: 900px) {
  .sr-ad {
    grid-template-columns: minmax(0, 1fr) auto;
    min-height: 76px;
  }

  .sr-ad-belt {
    display: none;
  }

  .sr-ad-title {
    font-size: 1.3rem;
  }

  .sr-ad-end {
    gap: 12px;
  }
}

@media (max-width: 620px) {
  .sr-ad {
    /* Two columns, not two rows. This used to collapse to one column, which
       pushed the CTA onto its own full-width row: the header grew by a whole
       row and left the top-right corner empty. The rail keeps the status line
       and the title, the CTA takes the space beside them. */
    grid-template-columns: minmax(0, 1fr) auto;
    min-height: 0;
  }

  .sr-ad-rail {
    /* The 10px right padding is the CTA's breathing room, so the two never
       touch even at 320px. The rail is minmax(0, 1fr) and the eyebrow/title are
       nowrap + overflow:hidden, so a longer season name clips rather than
       running under the button. */
    padding: 10px 10px 10px 16px;
    border-right: 0;
  }

  .sr-ad-end {
    /* No border-top and no full-width row: the CTA is part of the header now,
       vertically centred against the rail exactly as it is on desktop. */
    padding: 10px 14px 10px 0;
    border-left: 0;
    border-top: 0;
  }

  .sr-ad-eyebrow {
    font-size: 0.5rem;
    letter-spacing: 0.18em;
  }

  .sr-ad-title {
    font-size: 1.15rem;
  }

  .sr-ad-cta {
    transform: none;
  }
}

