/* ═══════════════════════════════════════════════════════════════════════
   Dyoce — site layer over the Nocturne design system.

   Two layouts share one document:

   1. FLAT  (the default in this file) — a plain, scrolling page. This is the
      phone and tablet experience, and what you get with JavaScript off or
      before the engine boots. Not a fallback: it is the layout most visitors
      will actually see, and it is designed for touch.
   2. TRACK (.mode-track, set by js/dyoce.js) — the multitrack editor: three
      parallax lanes scrubbed by wheel, drag, arrow keys or the transport.
      Needs 900 x 640 of viewport, so it is a desktop and landscape-tablet
      layout only.

   All colour, type, spacing and radius values come from css/nocturne.css.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── shared ─────────────────────────────────────────────────────────── */

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

.spacer { flex: 1; }

a { color: var(--color-accent-300); }
a:hover { color: var(--color-accent-200); }

/* A short solid accent mark — the Nocturne signature, kept solid while
   freestanding rules fade at their ends. */
.accent-mark { width: 22px; height: 2px; background: var(--color-accent); }

.brand {
  font: var(--font-heading-weight) 17px var(--font-heading);
  letter-spacing: 0.24em; color: var(--color-text); text-decoration: none;
}
.brand:hover { color: var(--color-accent-200); }

/* Images resolve to their caption when the file is not there yet — drop
   artwork into images/ and it takes over with no markup change. */
img.is-missing { display: none; }

.cover { position: relative; background: var(--color-neutral-900); overflow: hidden; }
.cover-img { width: 100%; height: 100%; object-fit: cover; }
.cover-ph {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding: var(--space-3); margin: 0; text-align: center;
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-neutral-600);
}
.cover-img:not(.is-missing) + .cover-ph { display: none; }

/* Typographic covers. The remixes carry the original artists' official
   single art on SoundCloud, which isn't ours to republish — so those slots
   are set in Nocturne type instead of borrowed artwork. */
.cover-type {
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: var(--space-1); padding: var(--space-4);
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--color-accent-900) 70%, var(--color-neutral-900)),
      var(--color-neutral-900) 62%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent) 14%, transparent);
}
.cover-type::before {
  content: ''; width: 18px; height: 2px; background: var(--color-accent);
  margin-bottom: auto;
}
.cover-orig {
  font-size: 9px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--color-neutral-500);
}
.cover-name {
  font: var(--font-heading-weight) 17px/1.15 var(--font-heading);
  letter-spacing: -0.015em; color: var(--color-neutral-200);
  overflow-wrap: anywhere;
}

.work-orig {
  font-size: 10px; letter-spacing: 0.04em;
  color: var(--color-neutral-600); margin: var(--space-1) 0 0;
}
.work-name { text-decoration: none; color: var(--color-text); }
.work-name:hover { color: var(--color-accent-200); }
.work-name .ext { font-size: 0.8em; margin-left: 4px; color: var(--color-neutral-600); }
.work-name:hover .ext { color: inherit; }

.work-title { display: flex; align-items: baseline; gap: 7px; margin-top: var(--space-3); }
.work-n { font-size: 10px; color: var(--color-neutral-600); font-variant-numeric: tabular-nums; }
.work-name { font: var(--font-heading-weight) 13px var(--font-heading); letter-spacing: -0.01em; }
.work-meta { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-1); }
.work-yr { font-size: 10.5px; color: var(--color-neutral-500); font-variant-numeric: tabular-nums; }
.work-meta .tag { font-size: 9px; padding: 1px 7px; }

.show { display: grid; gap: 2px; }
.show-date {
  font-size: 10.5px; letter-spacing: 0.1em;
  color: var(--color-neutral-500); font-variant-numeric: tabular-nums;
}
.show-venue { font: var(--font-heading-weight) 14.5px var(--font-heading); }
.show-city { font-size: 12px; color: var(--color-neutral-400); }
.show-flag { font-size: 9px; padding: 1px 7px; width: fit-content; margin-top: var(--space-1); }

.statement { margin: 0; }
.statement p {
  font: 400 16.5px/1.55 var(--font-body);
  color: var(--color-neutral-200); margin: var(--space-4) 0 0;
}
.statement em { font-style: normal; color: var(--color-accent-300); }

.kit { display: grid; gap: var(--space-2); }
.kit h6 { margin: 0; color: var(--color-neutral-400); }
.kit > a {
  font: var(--font-heading-weight) 12px var(--font-body);
  letter-spacing: 0.12em; text-decoration: none; width: fit-content;
}
.kit-note { font-size: 11.5px; color: var(--color-neutral-500); margin: var(--space-2) 0 0; }

.signup-form { display: flex; gap: var(--space-3); flex-wrap: wrap; }
.signup-form .input { width: 190px; }
.signup-note { font-size: 11.5px; color: var(--color-neutral-500); margin: var(--space-2) 0 0; }
.signup.is-joined .signup-form { display: none; }
.signup.is-joined .signup-note {
  font: var(--font-heading-weight) 13px var(--font-body);
  color: var(--color-accent-300);
}

.runout-link {
  font: var(--font-heading-weight) 11px var(--font-body);
  letter-spacing: 0.14em; color: var(--color-neutral-400); text-decoration: none;
}
.runout-link:hover { color: var(--color-accent-300); }

.runout-card h3 { margin: var(--space-4) 0 var(--space-1); }
.runout-card .lede { color: var(--color-neutral-400); font-size: 13px; margin: 0 0 var(--space-4); }
.runout-card .btn { width: fit-content; }

.social {
  font: var(--font-heading-weight) 10.5px var(--font-body);
  letter-spacing: 0.14em; text-decoration: none;
}

/* Lane transport toggles — solo / mute, borrowed straight from a mixer. */
.tgl {
  width: 26px; height: 20px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font: 600 10px var(--font-body); border-radius: var(--radius-sm); cursor: pointer;
  color: var(--color-neutral-600); background: transparent;
  border: 1px solid var(--color-neutral-800);
}
.tgl:hover { color: var(--color-neutral-300); }
.tgl-solo[aria-pressed="true"] {
  color: var(--color-accent-200); border-color: var(--color-accent); background: var(--color-accent-900);
}
.tgl-mute[aria-pressed="true"] {
  color: var(--color-neutral-200); border-color: var(--color-neutral-500); background: var(--color-neutral-900);
}

/* ── FLAT layout — the default, and the phone / tablet experience ────── */

/* iOS inflates text in some orientations unless told not to. */
html { -webkit-text-size-adjust: 100%; }
body {
  background: var(--color-bg);
  -webkit-tap-highlight-color: color-mix(in srgb, var(--color-accent) 22%, transparent);
}

/* The ground glow is a fixed pseudo-element rather than
   `background-attachment: fixed`, which iOS Safari renders unreliably and
   repaints expensively while scrolling. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(1200px 800px at 28% -12%,
    color-mix(in srgb, var(--color-section-glow) 22%, var(--color-bg)),
    var(--color-bg) 62%);
}

.app {
  position: relative; z-index: 1;
  max-width: 920px; margin: 0 auto;
  /* env() keeps content clear of notches and the home indicator; the max()
     means the safe area only ever adds to the design's own padding. */
  padding-left: max(var(--space-6), env(safe-area-inset-left));
  padding-right: max(var(--space-6), env(safe-area-inset-right));
  padding-bottom: max(var(--space-8), env(safe-area-inset-bottom));
}

.topbar {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: var(--space-6);
  padding: var(--space-4) 0;
  padding-top: max(var(--space-4), env(safe-area-inset-top));
  background: color-mix(in srgb, var(--color-bg) 88%, transparent);
  backdrop-filter: blur(8px);
}
.readout, .pos, .year, .transport, .ruler,
.runout, .playhead, .playhead-wash, .lane-toggles, .lead, .hint { display: none; }
.topbar-book { margin-left: auto; font-size: 12px; }

.lane { padding: var(--space-8) 0; position: relative; }
.lane::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-neutral-800) 8%,
    var(--color-neutral-800) 92%, transparent);
}
.lane:last-of-type::after { display: none; }

.lane-head { display: flex; align-items: baseline; gap: var(--space-4); margin-bottom: var(--space-6); }
.lane-name {
  font: var(--font-heading-weight) 10.5px var(--font-body);
  letter-spacing: 0.14em; color: var(--color-neutral-300); margin: 0;
}
.lane-count { font-size: 10px; letter-spacing: 0.1em; color: var(--color-neutral-600); margin: 0; }

.lane-track { display: grid; gap: var(--space-6); list-style: none; margin: 0; padding: 0; }
[data-lane="0"] { grid-template-columns: repeat(auto-fill, minmax(154px, 1fr)); }
[data-lane="0"] .cover { width: 100%; aspect-ratio: 1; border-radius: var(--radius-sm); }
[data-lane="0"] .cover-name { font-size: 22px; }
[data-lane="0"] .work { padding: var(--space-3); border: 1px solid var(--color-neutral-800); border-radius: var(--radius-md); background: var(--color-surface); }
[data-lane="0"] .runout-card { grid-column: 1 / -1; }
[data-lane="1"] .clip, [data-lane="2"] .clip { max-width: 62ch; }

.botbar {
  display: flex; align-items: center; gap: var(--space-6); flex-wrap: wrap;
  padding: var(--space-6) 0 0; margin-top: var(--space-8); position: relative;
}

/* ── touch ──────────────────────────────────────────────────────────────
   Everything a finger has to hit gets a 44px box. The text stays the size
   the design system asks for; only the hit area grows.
   ──────────────────────────────────────────────────────────────────── */

.kit > a, .runout-link, .social {
  display: inline-flex; align-items: center; min-height: 44px;
}
.botbar .btn, .topbar-book { min-height: 44px; }
.btn { white-space: nowrap; }

/* The release title stretches its hit area over the whole card, so tapping
   anywhere on a cover opens that track. Disabled in the mixer, where a click
   on the card means "park the playhead here" instead. */
[data-lane="0"] .work { position: relative; }
[data-lane="0"] .work-name::after { content: ''; position: absolute; inset: 0; }

/* Anything under 16px makes iOS Safari zoom the page when the field takes
   focus, and it never zooms back out. */
.signup-form .input { font-size: 16px; }
.signup-form .btn { min-height: 44px; }

/* The design system is dense by intent; on a phone its smallest steps fall
   below comfortable reading, so lift only those. */
@media (max-width: 560px) {
  .lane-name, .lane-count { font-size: 11.5px; }
  .work-n, .work-yr, .work-orig, .cover-ph { font-size: 11px; }
  .kit-note, .signup-note { font-size: 12.5px; }
  .lane { padding: var(--space-6) 0; }
}
.botbar::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-neutral-800) 8%,
    var(--color-neutral-800) 92%, transparent);
}
.btn-book { font-size: 12px; }

/* ── TRACK layout — the multitrack editor ───────────────────────────── */

.mode-track {
  --topbar-h: 54px;
  --botbar-h: 46px;
  --ruler-h: 22px;
  --label-w: 132px;
  --track-w: 7600px;
  height: 100%;
}
.mode-track body {
  height: 100%; overflow: hidden; overscroll-behavior: none;
  user-select: none;
}

.mode-track .app {
  height: 100vh; height: 100dvh; max-width: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; overflow: hidden;
}

/* transport bar */
.mode-track .topbar {
  height: var(--topbar-h); flex: none; position: relative; top: auto; z-index: 6;
  display: flex; align-items: center; gap: var(--space-6);
  padding: 0 max(var(--space-6), env(safe-area-inset-right)) 0 max(var(--space-6), env(safe-area-inset-left));
  background: none; backdrop-filter: none;
}
.mode-track .topbar::after {
  content: ''; position: absolute; left: 48px; right: 48px; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-neutral-800) 12%,
    var(--color-neutral-800) 88%, transparent);
}
.mode-track .readout {
  display: flex; align-items: center; gap: var(--space-3);
  border: 1px solid var(--color-neutral-800); border-radius: 999px;
  padding: 4px 12px; min-width: 210px;
  font: var(--font-heading-weight) 10.5px var(--font-body);
  letter-spacing: 0.1em; color: var(--color-accent-300);
}
.readout-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-accent); flex: none; }
.is-playing .readout-dot { animation: dyocePulse 1.2s infinite; }
@keyframes dyocePulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
.readout-text { font-variant-numeric: tabular-nums; white-space: nowrap; }

.mode-track .pos {
  display: block; font-size: 10.5px; letter-spacing: 0.12em;
  color: var(--color-neutral-600); font-variant-numeric: tabular-nums;
}
.mode-track .year {
  display: block; font: var(--font-heading-weight) 20px var(--font-heading);
  letter-spacing: 0.1em; color: var(--color-accent); font-variant-numeric: tabular-nums;
}
.mode-track .transport { display: flex; gap: var(--space-2); }
.mode-track .topbar-book { display: none; }

.mode-track .transport .btn-icon { font-size: 11px; }

/* stage */
.mode-track .stage {
  position: relative; flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  cursor: grab; touch-action: none;
}
.mode-track .stage.is-dragging { cursor: grabbing; }

.mode-track .runout {
  display: block; position: absolute; top: 0; bottom: 0; right: 0; width: 58%;
  opacity: 0; transition: opacity .3s; pointer-events: none;
  background: radial-gradient(120% 140% at 100% 50%,
    var(--color-section-glow), var(--color-section) 42%, transparent 74%);
}

.mode-track .ruler {
  display: block; height: var(--ruler-h); flex: none;
  margin-left: var(--label-w); position: relative; overflow: hidden;
}
.mode-track .ruler-track {
  position: absolute; top: 0; bottom: 0; left: 0; width: var(--track-w);
}
.ruler-year { position: absolute; top: 4px; display: flex; gap: 6px; align-items: center; }
.ruler-year i { width: 1px; height: 9px; background: var(--color-neutral-700); }
.ruler-year span {
  font-size: 10px; letter-spacing: 0.1em;
  color: var(--color-neutral-600); font-variant-numeric: tabular-nums;
}
.ruler-tick { position: absolute; top: 10px; width: 1px; height: 4px; background: var(--color-neutral-800); }

/* lanes */
.mode-track .lane {
  flex: var(--lane-flex) 1 0; display: flex; min-height: 0;
  position: relative; padding: 0;
  /* Above the playhead (z 2) so a centred release card covers the head line
     instead of the line cutting down through its cover, title and year. Only
     the one card under the head ever overlaps the line; the text lanes are
     transparent, so it still reads through them and in the gaps between cards. */
  z-index: 3;
}
.mode-track .lane::after {
  content: ''; left: 48px; right: 48px; display: block;
  background: linear-gradient(90deg, transparent, var(--color-neutral-800) 15%,
    var(--color-neutral-800) 85%, transparent);
}
.mode-track .lane:last-of-type::after { display: none; }

.mode-track .lane-head {
  width: var(--label-w); flex: none; display: flex; flex-direction: column;
  gap: 5px; justify-content: center; align-items: stretch; margin: 0;
  padding: var(--space-4) var(--space-6);
  border-right: 1px solid var(--color-neutral-900);
  background: var(--color-bg); position: relative; z-index: 2;
}
.mode-track .lane-toggles { display: flex; gap: 5px; margin-top: var(--space-1); }

.mode-track .lane-view {
  flex: 1; position: relative; overflow: hidden;
  transition: opacity .35s;
}
.mode-track .lane-tinted .lane-view {
  background: color-mix(in srgb, var(--color-neutral-900) 30%, transparent);
}

.mode-track .lane-track {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: var(--track-w); display: block; gap: 0;
  will-change: transform;
}

.mode-track .clip {
  position: absolute; top: 50%; left: 0;
  transform: translate(-50%, -50%);
  max-width: none;
}
.mode-track .clip[data-anchor="left"] { transform: translate(0, -50%); white-space: nowrap; }

.mode-track .lead {
  display: block; font-size: 10px; letter-spacing: 0.16em;
  color: var(--color-neutral-600);
}
.mode-track [data-lane="2"] .lead:not([data-anchor]) { font-size: 11px; letter-spacing: 0.14em; color: var(--color-neutral-500); }

/* release clips */
.mode-track .work {
  width: 154px; padding: var(--space-4);
  border: 1px solid var(--color-neutral-800); border-radius: var(--radius-md);
  background: var(--color-surface);
  transform: translate(-50%, -50%) scale(var(--cs, 1));
  transition: transform .25s, border-color .25s, box-shadow .25s;
}
.mode-track .work:hover { border-color: var(--color-neutral-600); }
.mode-track .work.is-active {
  border-color: var(--color-accent);
  box-shadow: 0 0 22px color-mix(in srgb, var(--color-accent) 25%, transparent);
  transform: translate(-50%, calc(-50% - 5px)) scale(var(--cs, 1));
}
.mode-track .work .cover { width: 132px; height: 132px; border-radius: var(--radius-sm); }
.mode-track .work-name { white-space: nowrap; }

.mode-track .show { width: 190px; }
.mode-track .statement { width: 360px; }
.mode-track .kit { width: 260px; }
.mode-track .signup { width: 320px; }
.mode-track .signup-form { user-select: text; flex-wrap: nowrap; }
.mode-track .signup-form .input { font-size: 14px; }
.mode-track .kit > a,
.mode-track .runout-link,
.mode-track .social { min-height: 0; }
.mode-track .botbar .btn,
.mode-track .signup-form .btn { min-height: 0; }
.mode-track .work-name::after { content: none; }
.mode-track .runout-card { width: 300px; }

/* playhead */
.mode-track .playhead-wash {
  display: block; position: absolute; top: 0; bottom: 0; width: 90px;
  z-index: 1; pointer-events: none;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--color-accent-900) 50%, transparent) 50%, transparent);
}
.mode-track .playhead {
  display: block; position: absolute; top: 0; bottom: 0; width: 2px;
  background: var(--color-accent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--color-accent) 50%, transparent);
  z-index: 2; pointer-events: none;
}
.playhead-tip {
  position: absolute; top: 0; left: -4px;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 7px solid var(--color-accent);
}

/* footer */
.mode-track .botbar {
  height: var(--botbar-h); flex: none; position: relative; z-index: 6;
  display: flex; align-items: center; gap: var(--space-8); flex-wrap: nowrap;
  padding: 0 max(var(--space-6), env(safe-area-inset-right)) 0 max(var(--space-6), env(safe-area-inset-left));
  margin: 0;
}
.mode-track .botbar::before { left: 48px; right: 48px; }
.mode-track .hint {
  display: block; font-size: 10px; letter-spacing: 0.12em;
  color: var(--color-neutral-600); margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .mode-track .work,
  .mode-track .lane-view,
  .mode-track .runout { transition: none; }
  .is-playing .readout-dot { animation: none; }
}

/* The mixer's own narrow end: at 900–1080px the footer hint crowds the links
   before anything else does, so it goes first. */
@media (max-width: 1080px) {
  .mode-track .hint { display: none; }
}

/* ── TRACK layout on phones ──────────────────────────────────────────────
   The mixer runs on touch below the desktop width. Everything the desktop
   spread out — the label spine, the transport bar, the cards, the rules —
   tightens so three lanes and the playhead keep their room on a phone. The
   engine already scrubs on pointer drag; this is only the fit.
   ──────────────────────────────────────────────────────────────────── */
@media (max-width: 899px) {
  .mode-track {
    --topbar-h: 48px;
    --botbar-h: 42px;
    --ruler-h: 20px;
    --label-w: 84px;
  }

  /* Transport bar: drop the wide now-playing pill and the POS counter — no
     room for them beside the brand, the year and the two transport buttons. */
  .mode-track .topbar { gap: var(--space-4); }
  .mode-track .readout,
  .mode-track .pos { display: none; }
  .mode-track .brand { font-size: 15px; letter-spacing: 0.16em; }
  .mode-track .year { font-size: 16px; }

  /* Bleed the hairline rules to the phone's own margin, not the desktop's. */
  .mode-track .topbar::after,
  .mode-track .lane::after,
  .mode-track .botbar::before { left: 16px; right: 16px; }

  /* Lane labels shrink to a spine — name over the solo/mute pair, no count. */
  .mode-track .lane-head { padding: var(--space-3); gap: 5px; }
  .mode-track .lane-name { font-size: 9px; letter-spacing: 0.04em; white-space: nowrap; }
  .mode-track .lane-count { display: none; }
  .mode-track .lane-toggles { gap: 4px; margin-top: 2px; }
  .mode-track .tgl { width: 22px; height: 18px; }

  /* Clips: cards a touch smaller so two never crowd the playhead; the text
     blocks cap to the viewport so nothing runs off the tape. */
  .mode-track .work { width: 140px; padding: var(--space-3); }
  .mode-track .work .cover { width: 116px; height: 116px; }
  .mode-track .work .cover-name { font-size: 17px; }
  .mode-track .statement { width: min(300px, 78vw); }
  .mode-track .kit { width: min(240px, 70vw); }
  .mode-track .signup { width: min(280px, 82vw); }
  .mode-track .runout-card { width: min(280px, 80vw); }

  /* Footer: keep the booking button and the swipe hint; the social links,
     already in the STATEMENT lane's kit, step aside. */
  .mode-track .botbar { gap: var(--space-4); }
  .mode-track .social { display: none; }
  .mode-track .hint {
    display: block; font-size: 9px; letter-spacing: 0.1em;
    color: var(--color-neutral-600); white-space: nowrap;
  }
}
