/* /tube — procedural music videos. YouTube-2008 layout grammar (player +
   right rail + comments below) reinterpreted in the site's mono CRT/VT323
   palette. The black-and-white wireframes inside the canvas carry the low-
   poly "retro CGI" tone; the surrounding chrome stays consistent with
   /space and the home page. */

.tube-page > main { padding-top: 2.6em; }

.tube {
  max-width: 1040px;
  margin: 0.6em auto 4em;
  padding: 0 0.8em;
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 0.8em;
  align-items: start;
}
@media (max-width: 820px) {
  .tube { grid-template-columns: 1fr; }
}
/* `display: grid` ties on specificity with the UA `[hidden] { display: none }`
   rule and wins by source order, so without this override the player <main>
   stays painted under the homepage widget when tube-home.js sets `hidden`.
   Mirrors the .tube__overlay[hidden] override below. */
.tube[hidden] { display: none; }

.tube__player-col { display: flex; flex-direction: column; gap: 0.8em; min-width: 0; }
.tube__sidebar    { display: flex; flex-direction: column; gap: 0.8em; min-width: 0; }

/* ----------------------------------------------------------------------- */
/* shared panel grammar (mirrors space.css). Kept local to /tube so the
   home page is unaffected. */

.panel {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
}
.panel__h {
  background: #000;
  color: #fff;
  font-size: 0.95em;
  letter-spacing: 0.06em;
  padding: 0.15em 0.5em;
  display: flex;
  align-items: center;
  gap: 0.4em;
  text-transform: uppercase;
}
.panel__h small {
  margin-left: auto;
  font-size: 0.8em;
  opacity: 0.75;
}

/* ----------------------------------------------------------------------- */
/* video stage: canvas + click-to-play overlay */

.tube__stage {
  position: relative;
  background: #000;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  /* 16:9 — keeps a YouTube-shaped frame at every width. */
  aspect-ratio: 16 / 9;
  overflow: hidden;
  line-height: 0;
}
.tube__canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* Tube canvas is intentionally low-res then upscaled — keeps the low-poly
     wireframes pixel-crisp instead of antialiased into mush. */
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
.tube__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.35);
  border: 0;
  cursor: pointer;
  color: #fff;
  font: inherit;
  transition: opacity 0.18s;
}
.tube__overlay svg {
  width: 22%;
  height: auto;
  fill: currentColor;
  filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.6));
}
.tube__overlay:hover svg { transform: scale(1.05); }
.tube__overlay[hidden] { display: none; }
.tube-page.is-playing .tube__overlay { opacity: 0; pointer-events: none; }

/* Loading state: the play triangle is replaced by a spinner from the moment
   the user clicks (or autoplay-on-arrival fires) until the worker finishes
   the synthesis pass. Without this, the play button sits visible during the
   ~500ms build, inviting an extra click. */
.tube__overlay.is-loading { cursor: wait; pointer-events: none; }
.tube__overlay.is-loading svg { display: none; }
.tube__overlay.is-loading::before {
  content: '';
  width: 18%;
  aspect-ratio: 1;
  border: 3px solid rgba(255, 255, 255, 0.25);
  border-top-color: #fff;
  border-radius: 50%;
  animation: tube-overlay-spin 0.9s linear infinite;
}
@keyframes tube-overlay-spin { to { transform: rotate(360deg); } }

/* ----------------------------------------------------------------------- */
/* embedded radio engine — needed for window.MORF_RADIO but not the focal
   UI on /tube. The big play overlay above the canvas drives playback;
   this aside is a tiny strip beneath the player for next/seek/download. */

.radio.tube__radio {
  position: static;
  width: auto;
  margin: 0;
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
}
.radio.tube__radio .radio__bar { display: none; }
.radio.tube__radio .radio__scope-wrap { display: none; }
.radio.tube__radio .radio__body { padding: 0.4em 0.6em; gap: 0.4em; }

/* ----------------------------------------------------------------------- */
/* video meta block: title / byline / stats / rating / description / tags */

.panel--vidmeta { padding: 0.6em 0.8em 0.8em; }
.tube__title {
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  text-align: left;
  font-size: 1.4em;
  letter-spacing: 0.03em;
  white-space: normal;
  overflow: visible;
  line-height: 1.15;
}
.tube__title::before, .tube__title::after { content: none; }
.tube__byline {
  margin: 0.2em 0 0.6em;
  font-size: 0.95em;
  opacity: 0.8;
}
.tube__stats {
  margin: 0.1em 0;
  font-size: 0.85em;
  opacity: 0.7;
  letter-spacing: 0.04em;
}
.tube__rating {
  margin: 0.2em 0 0.6em;
  font-size: 1.1em;
  letter-spacing: 0.1em;
}
.tube__rating .star.half { opacity: 0.4; }
.tube__desc {
  margin: 0.5em 0 0.6em;
  font-size: 0.95em;
  white-space: pre-wrap;
}
.tube__tags {
  list-style: none;
  margin: 0.3em 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em;
}
.tube__tags > li::before { content: none; }
.tube__tags > li {
  border: 1.5px solid #000;
  padding: 0.05em 0.5em;
  font-size: 0.8em;
  letter-spacing: 0.05em;
}

/* ----------------------------------------------------------------------- */
/* comments */

.panel--cmts > .cmts {
  list-style: none;
  margin: 0;
  padding: 0.4em 0.6em 0.6em;
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  font-size: 0.9em;
}
.panel--cmts > .cmts > li::before { content: none; }
.cmt__head {
  display: flex;
  gap: 0.5em;
  align-items: baseline;
  font-size: 0.85em;
  opacity: 0.85;
}
.cmt__nick { font-weight: bold; letter-spacing: 0.02em; }
.cmt__time { opacity: 0.6; }
.cmt__body { margin: 0.1em 0 0; }

/* ----------------------------------------------------------------------- */
/* related videos sidebar */

.related {
  list-style: none;
  margin: 0;
  padding: 0.4em 0.5em;
  display: flex;
  flex-direction: column;
  gap: 0.4em;
}
.related > li { padding: 0; }
.related > li::before { content: none; }
.related__item {
  display: grid;
  grid-template-columns: 80px minmax(0, 1fr);
  gap: 0.5em;
  text-decoration: none;
  color: inherit;
  padding: 0.25em;
  border: 1.5px solid transparent;
  align-items: center;
}
.related__item > * { min-width: 0; }
/* Hover inverts the tile to match the site convention (top-bar links,
   trending tiles, body anchors all flip black→white on hover). The base
   rule from style.css already does this for plain <a>; we just need to
   stop fighting it with our own background override. */
.related__item:hover, .related__item:focus {
  background: #000;
  color: #fff;
  outline: none;
}
.related__item:hover .related__thumb,
.related__item:focus .related__thumb { border-color: #fff; }
.related__thumb {
  position: relative;
  width: 80px;
  height: 45px;
  border: 1.5px solid #000;
  background: #000;
  color: #00c800;
  font-size: 0.7em;
  letter-spacing: 0.1em;
  line-height: 1;
  overflow: hidden;
}
.related__thumb svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
/* Center-darkening vignette so the play arrow stays readable regardless of
   how dense the underlying Truchet weave is. */
.related__thumb::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.15) 75%);
  pointer-events: none;
}
.related__thumb-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.95em;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.85);
}
/* Title (track title) is the prominent line; the artist drops to a muted
   byline beneath it, with views as the smallest line. Same hierarchy as
   the home-page // TUBE // tiles + the /tube homepage tiles. */
.related__title {
  font-size: 0.9em;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.related__name {
  font-size: 0.78em;
  opacity: 0.75;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0.02em;
}
.related__sub {
  font-size: 0.7em;
  opacity: 0.6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ----------------------------------------------------------------------- */
/* tube homepage view — shown when /tube.html has no ?artist= or share-hash.
   Mirrors the // TUBE // widget chrome on the site home page (slashy bar,
   double-bordered card, 4-col 16:9 thumb grid) but sized up to 12 tiles.
   tube-home.js owns the swap with the player <main>; the player markup
   stays in the DOM (just hidden) so tube.js's top-level setup remains
   harmless. A second widget will sit below the recommended one later. */

.tube-home {
  max-width: 1040px;
  margin: 0.6em auto 4em;
  padding: 0 0.8em;
  display: flex;
  flex-direction: column;
  gap: 0.8em;
}
/* Same trick as .tube[hidden] above — without this the homepage stays
   painted in player mode (and on the player page you'd see an empty
   // RECOMMENDED // bar above the canvas). */
.tube-home[hidden] { display: none; }

.tube-home__rec {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  display: flex;
  flex-direction: column;
}
.tube-home__bar {
  background: #fff;
  color: #000;
  border-bottom: 2px solid #000;
  font-size: 0.95em;
  line-height: 1.6em;
  letter-spacing: 0.06em;
  white-space: nowrap;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0 0.5em;
}
/* Slashy bar trim — same repeating-line motif as the home-page widgets so
   /tube reads as one of the family rather than its own dialect. */
.tube-home__bar::before,
.tube-home__bar::after {
  content: "";
  flex: 1;
  min-width: 0;
  height: 0.45em;
  background: repeating-linear-gradient(
    to bottom,
    #000 0 1px,
    transparent 1px 3px
  );
}

.tube-home__list {
  list-style: none;
  margin: 0;
  padding: 0.8em 0.7em 1em;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1em 0.6em;
}
@media (max-width: 820px) {
  .tube-home__list { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 540px) {
  .tube-home__list { grid-template-columns: repeat(2, 1fr); }
}
.tube-home__list > li { min-width: 0; }
.tube-home__list > li::before { content: none; }
.tube-home__list a,
.tube-home__list a:visited {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: left;
  gap: 0.2em;
  padding: 0.25em 0.2em;
  font-size: 0.9em;
  text-decoration: none;
  color: inherit;
}
.tube-home__list a:hover,
.tube-home__list a:focus-visible {
  background: #000;
  color: #fff;
  outline: none;
}

.tube-home__thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 2px solid currentColor;
  background: #000;
  color: #00c800;
  font-size: 1.1em;
  line-height: 1;
  letter-spacing: 0.1em;
  box-shadow: 2px 2px 0 currentColor;
  overflow: hidden;
  margin-bottom: 0.3em;
}
.tube-home__thumb svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
/* Center-darkening vignette so the play arrow stays readable regardless of
   how dense the underlying Truchet weave is. Mirrors the same trick on the
   related-videos sidebar inside the player view. */
.tube-home__thumb::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.15) 75%);
  pointer-events: none;
}
.tube-home__thumb-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.85);
}
.tube-home__list a:hover .tube-home__thumb,
.tube-home__list a:focus-visible .tube-home__thumb {
  /* Keep the box-shadow visible against the inverted black hover bg. */
  background: #000;
  color: #00c800;
}

.tube-home__title {
  font-size: 0.95em;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tube-home__byline {
  font-size: 0.8em;
  opacity: 0.75;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tube-home__sub {
  font-size: 0.75em;
  opacity: 0.6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Favorites widget extras — empty-state hint and the PREV / page x/y / NEXT
   pager footer. Tile rendering reuses .tube-home__list / __thumb / __title
   / __byline / __sub from the recommended grid above; only the bar text
   ("// FAVORITES //") and the per-tile sub-line ("♥ saved 3d ago") differ.
   The empty-state hint replaces the list when MORF_LIBRARY is empty so the
   widget never collapses to an unlabelled bar. */
.tube-home__fav-empty {
  margin: 0;
  padding: 1em 1em;
  font-size: 0.85em;
  opacity: 0.7;
  text-align: center;
}

/* Pager footer. Same border-top + invert-on-hover idiom as the other
   "▸ OPEN FEED" footers across the site, but split into three slots: prev
   button, centered page-info status, next button. tube-home.js hides the
   whole row when one page fits everything (≤8 favorites). */
.tube-home__fav-pager {
  display: flex;
  align-items: stretch;
  border-top: 2px solid #000;
  font-size: 0.85em;
  letter-spacing: 0.08em;
}
.tube-home__fav-page {
  flex: 0 0 auto;
  padding: 0.4em 0.9em;
  background: #fff;
  color: #000;
  border: 0;
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.tube-home__fav-page:hover:not([disabled]),
.tube-home__fav-page:focus-visible:not([disabled]) {
  background: #000;
  color: #fff;
  outline: none;
}
/* Disabled = at the head/tail of the list. Keep it visible as a label
   (still says PREV / NEXT) but drop the affordance — no hover flip,
   no pointer cursor, faded text. */
.tube-home__fav-page[disabled] {
  opacity: 0.35;
  cursor: default;
}
.tube-home__fav-pageinfo {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.4em 0.5em;
  text-align: center;
  opacity: 0.75;
  /* Vertical separators between the three slots — hairlines so they read
     as a single bar rather than three pills. */
  border-left:  1.5px solid #000;
  border-right: 1.5px solid #000;
}
