/* Starve: the Obi Deck world. A 90s OVA cassette cover: a red obi strip names the series, the
   release list is the cover's spec panel, and the player deck runs along the bottom. Dark and
   light are the same drawing on ink and on paper. */

@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo-latin-ext.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Martian Mono";
  src: url("/fonts/martian-mono-latin.woff2") format("woff2");
  font-weight: 100 800;
  font-stretch: 75% 112.5%;
  font-display: swap;
}

@font-face {
  font-family: "Akira Expanded";
  src: url("/fonts/akira-expanded.ttf") format("truetype");
  font-weight: 800;
  font-display: swap;
}

/* The obi ink is a typed colour so it can transition: when the theme swaps under the shutter,
   the panel's text and the red band's text fade between ink and white instead of jumping. */
@property --on-obi {
  syntax: "<color>";
  inherits: true;
  initial-value: #0c0c12;
}

/* ---- Tokens ----------------------------------------------------------------------------- */

:root {
  /* Dark: the cover on ink. */
  --ground: #0c0c12;
  --raised: #14141c;
  --sunk: #08080c;
  --line: #2a2a36;
  --line-strong: #454556;
  --text: #f0ece4;
  --text-2: #a3a2b3;
  --text-3: #85849a;
  --red: #ff3b2f;
  --on-red: #0c0c12;
  --red-text: #ff5a4f;
  --cyan: #2fd2c4;
  --stripe: #0c0c12;
  --on-obi: #0c0c12;
  --obi-tag-text: #ff3b2f;
  --grain-opacity: 0.06;

  --display: "Akira Expanded", "Archivo", system-ui, sans-serif;
  --display-stretch: 125%;
  --read: "Archivo", system-ui, sans-serif;
  --mono: "Martian Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  --bar-h: 3.5rem;
  --deck-h: 0px;
  --obi-w: 5.5rem;
  --panel-w: 22rem;
  --list-w: 30rem;
  --strip-w: 8.5rem;
  --gutter: 2rem;

  --quick: 150ms cubic-bezier(0.16, 1, 0.3, 1);
  --move: 240ms cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: dark;
}

/* Light follows the OS unless the theme switch chose dark; the switch can also force it. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    /* Light: the same drawing on bone paper. */
    --ground: #ebe8e0;
    --raised: #f5f3ee;
    --sunk: #dedad0;
    --line: #cdc8bc;
    --line-strong: #a39d90;
    --text: #0c0c12;
    --text-2: #4c4b5a;
    --text-3: #5e5c6a;
    --red: #ef3326;
    --on-red: #0c0c12;
    --red-text: #c0231a;
    --cyan: #0a6f67;
    --stripe: #ebe8e0;
    --on-obi: #ffffff;
    --obi-tag-text: #b31f16;
    --grain-opacity: 0.08;

    color-scheme: light;
  }
}

:root[data-theme="light"] {
  /* Light: the same drawing on bone paper. */
  --ground: #ebe8e0;
  --raised: #f5f3ee;
  --sunk: #dedad0;
  --line: #cdc8bc;
  --line-strong: #a39d90;
  --text: #0c0c12;
  --text-2: #4c4b5a;
  --text-3: #5e5c6a;
  --red: #ef3326;
  --on-red: #0c0c12;
  --red-text: #c0231a;
  --cyan: #0a6f67;
  --stripe: #ebe8e0;
  --on-obi: #ffffff;
  --obi-tag-text: #b31f16;
  --grain-opacity: 0.08;

  color-scheme: light;
}

/* ---- Base ------------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--line-strong) var(--ground);
  /* Room for the scrollbar is kept on every page, so switching between a short and a long
     page never changes the page width. */
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--ground);
  color: var(--text);
  font: 400 0.9375rem/1.55 var(--read);
  font-variant-numeric: tabular-nums;
  text-rendering: optimizeLegibility;
  padding-bottom: var(--deck-h);
}

/* Fine film grain over everything, never catching a pointer. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  opacity: var(--grain-opacity);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

::selection {
  background: var(--red);
  color: var(--on-red);
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-thickness: 2px;
}

:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}

.icon {
  width: 1rem;
  height: 1rem;
  fill: currentColor;
  flex: none;
}

/* ---- Controls --------------------------------------------------------------------------- */

button,
.read-toggle,
.load-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.25rem;
  padding: 0.5rem 0.875rem;
  border: 1px solid var(--text);
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: 500 0.6875rem/1 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--quick), color var(--quick), border-color var(--quick);
}

button:hover,
.read-toggle:hover,
.load-more:hover {
  background: var(--text);
  color: var(--ground);
}

button:disabled {
  opacity: 0.45;
  cursor: default;
}

button:disabled:hover {
  background: transparent;
  color: var(--text);
}

.htmx-request button,
button.htmx-request {
  opacity: 0.55;
  cursor: progress;
}

.field {
  width: 100%;
  min-height: 2.5rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: 0;
  background: var(--sunk);
  color: var(--text);
  caret-color: var(--red);
  font: 400 0.9375rem/1.3 var(--read);
}

.field::placeholder {
  color: var(--text-3);
}

.field:focus-visible {
  border-color: var(--cyan);
}

select.field {
  appearance: none;
  padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--text) 50%),
    linear-gradient(-45deg, transparent 50%, var(--text) 50%);
  background-position: calc(100% - 1rem) 50%, calc(100% - 0.65rem) 50%;
  background-size: 0.35rem 0.35rem;
  background-repeat: no-repeat;
}

.field-row:has(input[type="file"]) {
  flex-direction: column;
  align-items: flex-start;
}

.field-row input[type="file"].field {
  width: 100%;
}

input[type="file"].field {
  padding: 0.4rem;
  font: 400 0.8125rem/1.3 var(--mono);
}

input[type="file"].field::file-selector-button {
  margin-right: 0.75rem;
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--text);
  background: transparent;
  color: var(--text);
  font: 500 0.6875rem/1 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.field-label {
  display: block;
  margin-bottom: 0.5rem;
  font: 500 0.6875rem/1.2 var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-2);
}

.field-row {
  display: flex;
  gap: 0.5rem;
}

.field-row .field {
  flex: 1;
  min-width: 0;
}

.hint {
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--text-2);
}

/* ---- The top bar ------------------------------------------------------------------------ */

.bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 2rem;
  min-height: 3.5rem;
  padding: 0 var(--gutter);
  border-bottom: 1px solid var(--line);
  background: var(--ground);
}

.wordmark {
  margin: 0;
  font: 800 1.125rem/1 var(--display);
  font-stretch: var(--display-stretch);
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

.wordmark a {
  text-decoration: none;
}

.pages,
.groups {
  display: flex;
  align-items: stretch;
  align-self: stretch;
  gap: 1.5rem;
}

.groups {
  padding-left: 2rem;
  border-left: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}

.groups::-webkit-scrollbar {
  display: none;
}

.tab,
.group-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font: 500 0.75rem/1 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-2);
  text-decoration: none;
  transition: color var(--quick);
}

.tab:hover,
.group-link:hover {
  color: var(--text);
}

/* The current page or group gets the obi's red square. */
.tab[aria-current],
.group-link[aria-current] {
  color: var(--text);
}

.tab[aria-current]::before,
.group-link[aria-current]::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  background: var(--red);
}

/* Clicking the page you are on, already at the top: the marker blinks twice. */
.tab.here::before {
  animation: here 700ms steps(1, end);
}

@keyframes here {
  0%,
  50% {
    background: transparent;
  }

  25%,
  75% {
    background: var(--red);
  }
}

/* ---- The label: obi, panel and sheet ---------------------------------------------------- */

.label {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
}

.sleeve {
  position: sticky;
  top: var(--bar-h);
  display: grid;
  grid-template-columns: var(--obi-w) auto;
  grid-auto-rows: min-content;
  height: calc(100dvh - var(--bar-h) - var(--deck-h));
}

/* The obi strip: a red spine with the series set vertically, like a tape cover's band. */
.series-block {
  grid-column: 1;
  grid-row: 1 / span 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  height: calc(100dvh - var(--bar-h) - var(--deck-h));
  padding: 1.25rem 0;
  overflow: hidden;
  background: var(--red);
  color: var(--on-obi);
  transition: --on-obi 600ms cubic-bezier(0.65, 0, 0.35, 1);
}

.series {
  margin: 0;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  max-block-size: 70%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font: 800 2.75rem/1 var(--display);
  font-stretch: var(--display-stretch);
  text-transform: uppercase;
  text-wrap: nowrap;
}

.roster-name {
  font-size: 2.125rem;
}

.series-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  margin: 0;
  writing-mode: vertical-rl;
  font: 600 0.6875rem/1 var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.series-meta > * {
  display: block;
}

/* Caution chevrons close the obi, and double as the theme switch. */
.theme-switch {
  flex: none;
  width: 3rem;
  min-height: 1.5rem;
  height: 1.5rem;
  margin-top: auto;
  padding: 0;
  border: 0;
  background: repeating-linear-gradient(-45deg, var(--stripe) 0 25%, transparent 25% 50%) 0 0 / 1rem 1rem;
  cursor: pointer;
}

/* On hover the chevrons march sideways. The stripes sit on a 1rem square tile that repeats
   seamlessly at 45deg, so sliding the pattern exactly one tile loops without a join. */
.theme-switch:hover,
.theme-switch:focus-visible {
  background: repeating-linear-gradient(-45deg, var(--stripe) 0 25%, transparent 25% 50%) 0 0 / 1rem 1rem;
  animation: march 450ms linear infinite;
}

/* The theme change: theme.js sweeps this red obi shutter across the page and swaps the
   theme while it covers everything. Chevron edges lead and trail it. */
.shutter {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  background: var(--red);
  color: var(--on-obi);
  pointer-events: none;
  transform: translateX(-130%);
  will-change: transform;
  transition: --on-obi 600ms cubic-bezier(0.65, 0, 0.35, 1);
}

.shutter::before,
.shutter::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3rem;
  background: repeating-linear-gradient(-45deg, var(--red) 0 25%, transparent 25% 50%) 0 0 / 1.5rem 1.5rem;
}

/* The panel's spec sheet: hairline frame, crosses, corner captions, barcode and chevrons. */
.shutter-frame {
  position: absolute;
  inset: clamp(1rem, 3vw, 2.5rem);
  display: grid;
  place-items: center;
  border: 1px solid color-mix(in srgb, var(--on-obi) 55%, transparent);
}

.shutter-frame::before,
.shutter-frame::after {
  content: "";
  position: absolute;
  width: 1rem;
  height: 1rem;
  background: linear-gradient(var(--on-obi), var(--on-obi)) center / 1px 100% no-repeat,
    linear-gradient(var(--on-obi), var(--on-obi)) center / 100% 1px no-repeat;
}

.shutter-frame::before {
  top: -0.5rem;
  left: -0.5rem;
}

.shutter-frame::after {
  bottom: -0.5rem;
  right: -0.5rem;
}

.shutter-cap {
  font: 600 0.75rem/1 var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}

.shutter-frame > .shutter-cap,
.shutter-code {
  position: absolute;
}

.tl {
  top: 1.25rem;
  left: 1.25rem;
}

.tr {
  top: 1.25rem;
  right: 1.25rem;
}

.bl {
  bottom: 1.25rem;
  left: 1.25rem;
}

.shutter-code {
  bottom: 1.25rem;
  right: 1.25rem;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.375rem;
}

.shutter-bars {
  width: 7rem;
  height: 2rem;
  background: repeating-linear-gradient(90deg, var(--on-obi) 0 2px, transparent 2px 4px, var(--on-obi) 4px 7px,
    transparent 7px 9px, var(--on-obi) 9px 10px, transparent 10px 13px);
}

.shutter-core {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}

.shutter-label {
  font: 800 clamp(2.5rem, 9vw, 7rem)/1 var(--display);
  font-stretch: var(--display-stretch);
  text-transform: uppercase;
}

.shutter-rule {
  width: min(24rem, 60vw);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--on-obi) 15% 85%, transparent);
}

.shutter-chevrons {
  position: absolute;
  top: 50%;
  left: 1.25rem;
  width: 2rem;
  height: 6rem;
  margin-top: -3rem;
  background: repeating-linear-gradient(-45deg, var(--on-obi) 0 25%, transparent 25% 50%) 0 0 / 1rem 1rem;
  opacity: 0.85;
}

/* A faint scanline sweeps down the sheet while it covers the page. */
.shutter-scan {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.shutter-scan::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 18%;
  background: linear-gradient(transparent, color-mix(in srgb, var(--on-obi) 14%, transparent), transparent);
  animation: scan 1.6s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

@keyframes scan {
  from {
    transform: translateY(-100%);
  }

  to {
    transform: translateY(560%);
  }
}

@media (max-width: 60rem) {
  .tr,
  .shutter-chevrons {
    display: none;
  }

  .shutter-bars {
    width: 4.5rem;
    height: 1.5rem;
  }
}

.shutter::before {
  right: 100%;
}

.shutter::after {
  left: 100%;
}

@keyframes march {
  to {
    background-position: 1rem 0;
  }
}

.theme-switch:focus-visible {
  outline-color: var(--on-red);
}

.failing-count {
  padding: 0.5rem 0.25rem;
  background: var(--on-obi);
  color: var(--obi-tag-text);
  font-weight: 700;
}

/* The tool panel is one swappable wrapper; its children still sit in the sleeve grid. */
.panel {
  display: contents;
}

/* The tool panel beside the obi on Sources and Import. */
:is(.sleeve, .panel) > :not(.series-block):not(.dock) {
  grid-column: 2;
  width: var(--panel-w);
  margin-left: 0;
  margin-right: 0;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

:is(.sleeve, .panel) > .add {
  margin-top: 1.75rem;
  margin-bottom: 0;
}

:is(.sleeve, .panel) > .hint {
  margin-top: 1rem;
}

:is(.sleeve, .panel) > .message {
  margin-top: 1.5rem;
  padding: 0.875rem 1rem;
  width: calc(var(--panel-w) - 3rem);
  margin-left: 1.5rem;
  border: 1px solid var(--cyan);
  font-weight: 500;
}

:is(.sleeve, .panel) > .message::before {
  content: "[ ";
  color: var(--cyan);
}

:is(.sleeve, .panel) > .message::after {
  content: " ]";
  color: var(--cyan);
}

.sleeve:has(.add) {
  border-right: 1px solid var(--line);
}

.sleeve .add button {
  background: var(--text);
  color: var(--ground);
}

.sleeve .add button:hover {
  background: transparent;
  color: var(--text);
}

:is(.sleeve, .panel) > .hint:last-child {
  padding-bottom: 1.5rem;
}

/* ---- The sheet -------------------------------------------------------------------------- */

.sheet {
  container: sheet / inline-size;
  min-width: 0;
  padding: 1.75rem var(--gutter) 4rem;
}

.sheet:focus {
  outline: none;
}

.sheet-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--text);
  position: relative;
}

/* Registration crosses at the panel's top corners. */
.sheet-head::before,
.sheet-head::after {
  content: "";
  position: absolute;
  top: -1.125rem;
  width: 0.75rem;
  height: 0.75rem;
  background: linear-gradient(var(--text-3), var(--text-3)) center / 1px 100% no-repeat,
    linear-gradient(var(--text-3), var(--text-3)) center / 100% 1px no-repeat;
}

.sheet-head::before {
  left: -1.125rem;
}

.sheet-head::after {
  right: -1.125rem;
}

.sheet-title {
  margin: 0;
  text-wrap: balance;
  font: 800 1.75rem/1 var(--display);
  font-stretch: var(--display-stretch);
  text-transform: uppercase;
}

.group-sheet .sheet-title,
#summary .sheet-title {
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--text);
}

.view-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
}

.view-actions form {
  margin: 0;
}

/* Mark all read is the sheet's one filled control. */
.view-actions button {
  background: var(--text);
  color: var(--ground);
}

.view-actions button:hover {
  background: transparent;
  color: var(--text);
}

.sheet-note {
  max-width: 40rem;
  margin: 1.5rem 0;
  font-size: 1rem;
  color: var(--text-2);
}

/* ---- Release rows ----------------------------------------------------------------------- */

.item {
  display: grid;
  grid-template-columns: 7rem 4rem minmax(0, 1fr) 12.5rem 13.5rem;
  grid-template-areas: "cat art body date actions";
  align-items: center;
  gap: 0.25rem 1.25rem;
  padding: 0.875rem 0.75rem;
  margin: 0 -0.75rem;
  border-bottom: 1px solid var(--line);
  transition: background-color var(--quick);
}

.item:hover {
  background: var(--raised);
}

.cat {
  grid-area: cat;
  font: 500 0.75rem/1.2 var(--mono);
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--text);
}

.thumb,
.blank {
  grid-area: art;
  width: 4rem;
  height: 4rem;
}

.thumb {
  object-fit: cover;
  display: block;
  background: var(--sunk);
}

.body {
  grid-area: body;
  min-width: 0;
}

.title {
  display: block;
  font: 500 1rem/1.35 var(--read);
  text-decoration: none;
  overflow-wrap: anywhere;
}

a.title:hover {
  text-decoration: underline;
}

.item .source {
  display: block;
  margin-top: 0.25rem;
  font: 400 0.75rem/1.3 var(--mono);
  color: var(--text-2);
}

.date {
  grid-area: date;
  font: 400 0.75rem/1.3 var(--mono);
  color: var(--text-2);
  white-space: nowrap;
}

.actions {
  grid-area: actions;
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

.actions form {
  margin: 0;
}

.item.read .title,
.item.read .cat {
  color: var(--text-3);
}

.item.read .title {
  text-decoration: line-through;
  text-decoration-color: var(--line-strong);
}

/* What plays is marked like the deck: a red number cell and a filled Playing control. */
.item.playing {
  background: var(--raised);
}

.item.playing .cat {
  justify-self: start;
  padding: 0.25rem 0.375rem;
  background: var(--red);
  color: var(--on-red);
}

.item.playing .play {
  background: var(--red);
  border-color: var(--red);
  color: var(--on-red);
}

.load-more {
  display: flex;
  width: 100%;
  min-height: 3.25rem;
  margin: 1.5rem 0;
  font-size: 0.75rem;
}

.caught-up {
  margin: 2.5rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--text);
  font: 800 1.5rem/1.05 var(--display);
  font-stretch: var(--display-stretch);
  text-transform: uppercase;
}

/* Narrow sheets (a smaller window, or the video pane beside it) fold the row: art left,
   number and date on top, title full width, actions below. The fixed date and action columns
   need about 37rem, so the fold comes before the title column gets squeezed. */
@container sheet (max-width: 44rem) {
  .sheet-head {
    flex-wrap: wrap;
    row-gap: 0.75rem;
  }

  .view-actions {
    flex-basis: 100%;
    margin-left: 0;
  }
}

@container sheet (max-width: 64rem) {
  /* Medium: number and title on the left, date with the buttons under it on the right. */
  .item {
    grid-template-columns: 3.5rem minmax(0, 1fr) auto;
    grid-template-areas:
      "art cat date"
      "art body actions";
    align-items: start;
  }

  .thumb,
  .blank {
    width: 3.5rem;
    height: 3.5rem;
  }

  .date {
    justify-self: end;
  }

  .actions {
    justify-content: flex-end;
    align-self: start;
    margin-top: 0.375rem;
  }
}

@container sheet (max-width: 34rem) {
  /* Narrow: too tight for a right column, so the buttons go under the title. */
  .item {
    grid-template-areas:
      "art cat date"
      "art body body"
      "art actions actions";
  }

  .actions {
    justify-content: flex-start;
  }
}

/* ---- The player deck -------------------------------------------------------------------- */

/* The dock region holds the deck; the deck itself is fixed to the viewport. */
.dock {
  grid-column: 2;
  width: 0;
  height: 0;
}

.player {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  display: grid;
  grid-template-columns: auto minmax(14rem, 22rem) minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 1.25rem;
  min-height: 5.5rem;
  padding: 0 1.25rem 0 0;
  padding-bottom: env(safe-area-inset-bottom);
  border-top: 1px solid var(--red);
  background: var(--raised);
}

.now {
  display: contents;
}

.now-art {
  width: 5.5rem;
  height: 5.5rem;
  object-fit: cover;
  display: block;
}

.now-blank {
  display: flex;
  align-items: flex-end;
  padding: 0.5rem;
  background: var(--red);
  color: var(--on-red);
  font: 600 0.625rem/1 var(--mono);
}

.now-text {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.now-cat {
  font: 800 1.125rem/1 var(--display);
  font-stretch: var(--display-stretch);
  color: var(--red-text);
}

.now-title {
  font: 600 0.9375rem/1.25 var(--read);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.ticker {
  display: inline-block;
}

/* Hold at the start, slide to show the end, hold, come back. */
.now-title.ticking {
  text-overflow: clip;
  mask-image: linear-gradient(90deg, #000 calc(100% - 1.5rem), transparent);
}

.now-title.ticking .ticker {
  animation: tick var(--tick, 8s) ease-in-out infinite;
}

.now-title.ticking:hover .ticker {
  animation-play-state: paused;
}

@keyframes tick {
  0%, 18% { transform: translateX(0); }
  50%, 68% { transform: translateX(var(--shift, 0)); }
  100% { transform: translateX(0); }
}

.now-source {
  font: 400 0.75rem/1.2 var(--mono);
  color: var(--text-2);
}

/* Transport: play/pause, elapsed, the waveform as the seek bar, total. */
.transport {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  min-width: 0;
}

.pause {
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  flex: none;
}

.pause .icon-play,
.paused .pause .icon-pause {
  display: none;
}

.paused .pause .icon-play {
  display: block;
}

.elapsed,
.total {
  flex: none;
  min-width: 3.5rem;
  font: 400 0.75rem/1 var(--mono);
  color: var(--text-2);
}

.total {
  text-align: right;
}

.wave {
  position: relative;
  flex: 1;
  min-width: 6rem;
  height: 3rem;
}

.bars {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: var(--line-strong);
}

.bars.lit {
  fill: var(--red);
  clip-path: inset(0 calc((1 - var(--progress)) * 100%) 0 0);
}

.seek {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.seek:disabled {
  cursor: default;
}

.wave:has(.seek:focus-visible) {
  outline: 2px solid var(--cyan);
  outline-offset: 4px;
}

/* Volume: a thin level line with a square thumb. */
.volume {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
  color: var(--text-2);
}

.level {
  --level: 0.8;
  appearance: none;
  width: 6rem;
  height: 1.5rem;
  margin: 0;
  background: linear-gradient(var(--text), var(--text)) left center / calc(var(--level) * 100%) 2px no-repeat,
    linear-gradient(var(--line-strong), var(--line-strong)) center / 100% 2px no-repeat;
  cursor: pointer;
}

.level::-webkit-slider-thumb {
  appearance: none;
  width: 0.75rem;
  height: 0.75rem;
  border: 0;
  border-radius: 0;
  background: var(--text);
}

.level::-moz-range-thumb {
  width: 0.75rem;
  height: 0.75rem;
  border: 0;
  border-radius: 0;
  background: var(--text);
}

.level:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}

.placement {
  display: flex;
}

.placement button {
  min-height: 2.25rem;
  border-color: var(--line-strong);
}

.placement button + button {
  margin-left: -1px;
}

.placement button[aria-pressed="true"] {
  border-color: var(--cyan);
  color: var(--cyan);
  position: relative;
}

.placement button[aria-pressed="true"]:hover {
  background: var(--cyan);
  color: var(--ground);
}

.player > .stop {
  grid-column: -2;
}

/* SoundCloud's own widget stays out of view while the deck drives it ... */
.sc-frame {
  position: fixed;
  left: -200vw;
  top: 0;
  width: 20rem;
  border: 0;
}

/* ... unless its handshake never arrives: then it shows above the deck as the controls. */
.player.native .transport {
  visibility: hidden;
}

.player.native .sc-frame {
  left: auto;
  right: 1.25rem;
  top: auto;
  bottom: calc(100% + 1rem);
  width: min(30rem, calc(100vw - 2.5rem));
  border: 1px solid var(--red);
}

.player-off {
  grid-column: 3 / span 2;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.player-off p {
  margin: 0;
  color: var(--text-2);
}

.player-off a {
  font: 500 0.75rem/1 var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--red-text);
}

/* ---- The video: pane, mini or deck. Only its box moves; the iframe never does. ---------- */

.ratio-16x9 {
  position: fixed;
  z-index: 25;
  background: #000;
  border: 1px solid var(--red);
  transition: top var(--move), left var(--move), right var(--move), bottom var(--move),
    width var(--move), height var(--move);
}

.ratio-16x9 iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Pane: theatre size beside the list. */
body[data-video="pane"] .ratio-16x9 {
  top: calc(var(--bar-h) + 1.75rem);
  left: calc(var(--list-x, var(--obi-w)) + var(--list-w) + 1.5rem);
  right: var(--gutter);
  bottom: calc(var(--deck-h) + 1.75rem);
  width: auto;
  height: auto;
}

body[data-video="pane"] .label {
  grid-template-columns: auto var(--list-w) minmax(0, 1fr);
}

body[data-video="pane"] .sheet {
  padding-right: 1.75rem;
  border-right: 1px solid var(--line);
  min-height: calc(100dvh - var(--bar-h) - var(--deck-h));
}

/* The splitter on the list's right edge: drag to resize; narrow enough, the list folds. */
.splitter {
  display: none;
}

body[data-video="pane"] .splitter {
  display: block;
  position: fixed;
  z-index: 26;
  top: var(--bar-h);
  bottom: var(--deck-h);
  left: calc(var(--list-x, var(--obi-w)) + var(--list-w) - 0.375rem);
  width: 0.75rem;
  cursor: col-resize;
  touch-action: none;
}

body[data-video="pane"] .splitter::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0.25rem;
  width: 0.25rem;
  height: 3rem;
  margin-top: -1.5rem;
  background: var(--line-strong);
  transition: background-color var(--quick), height var(--quick);
}

body[data-video="pane"] .splitter:hover::after,
body[data-video="pane"] .splitter:focus-visible::after,
body.resizing .splitter::after {
  background: var(--cyan);
  height: 5rem;
  margin-top: -2.5rem;
}

.splitter:focus-visible {
  outline: none;
}

body.resizing,
body.resizing * {
  user-select: none;
}

body.resizing .label,
body.resizing .ratio-16x9 {
  transition: none;
}

/* The fold: list width, the video's left edge and the splitter slide together, and the
   rows fade into their new form. Plays even while dragging, so the snap reads as a move. */
body.folding .label,
body.resizing.folding .label {
  transition: grid-template-columns 280ms cubic-bezier(0.16, 1, 0.3, 1);
}

body.folding .ratio-16x9,
body.resizing.folding .ratio-16x9 {
  transition: left 280ms cubic-bezier(0.16, 1, 0.3, 1);
}

body.folding .splitter {
  transition: left 280ms cubic-bezier(0.16, 1, 0.3, 1);
}

body.folding #timeline,
body.folding .sheet-head {
  animation: fold-in 280ms cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes fold-in {
  from {
    opacity: 0;
    transform: translateX(-0.75rem);
  }
}

/* Strip: the list folds to its catalogue numbers and the video takes the room. */
body[data-video="pane"][data-list="strip"] {
  --list-w: var(--strip-w);
}

body[data-video="pane"][data-list="strip"] .sheet {
  padding-left: 1rem;
  padding-right: 1rem;
}

body[data-video="pane"][data-list="strip"] .sheet-head {
  border-bottom-color: var(--line);
}

body[data-video="pane"][data-list="strip"] .sheet-head::before,
body[data-video="pane"][data-list="strip"] .sheet-head::after,
body[data-video="pane"][data-list="strip"] .sheet-title,
body[data-video="pane"][data-list="strip"] .view-actions,
body[data-video="pane"][data-list="strip"] .sheet-note,
body[data-video="pane"][data-list="strip"] .caught-up,
body[data-video="pane"][data-list="strip"] .item .thumb,
body[data-video="pane"][data-list="strip"] .item .blank,
body[data-video="pane"][data-list="strip"] .item .body,
body[data-video="pane"][data-list="strip"] .item .date,
body[data-video="pane"][data-list="strip"] .item .actions {
  display: none;
}

body[data-video="pane"][data-list="strip"] .item {
  display: block;
  margin: 0;
  padding: 0.625rem 0.25rem;
}

body[data-video="pane"][data-list="strip"] .load-more {
  min-height: 2.25rem;
  font-size: 0.625rem;
}

/* Mini: a small window above the deck's right end. */
body[data-video="mini"] .ratio-16x9 {
  top: auto;
  left: auto;
  right: 1.25rem;
  bottom: calc(var(--deck-h) + 1rem);
  width: var(--mini-w, 20rem);
  height: calc(var(--mini-w, 20rem) * 9 / 16);
}

/* Deck: a 16:9 slot standing up out of the deck's right end. */
body[data-video="deck"] .ratio-16x9 {
  top: auto;
  left: auto;
  right: 0;
  bottom: 0;
  width: var(--deck-w, 24rem);
  height: calc(var(--deck-w, 24rem) * 9 / 16);
  border-width: 1px 0 0 1px;
}

body[data-video="deck"] .player {
  grid-template-columns: auto minmax(9rem, 14rem) minmax(0, 1fr) auto auto;
  padding-right: calc(var(--deck-w, 24rem) + 1.25rem);
}

body[data-video="deck"] .elapsed,
body[data-video="deck"] .total {
  min-width: 0;
}

body[data-video="deck"] .level {
  width: 4rem;
}

/* The slot stands over the sheet's lower right, so the sheet ends above it. */
body[data-video="deck"] .sheet {
  padding-bottom: calc(var(--deck-w, 24rem) * 9 / 16 + 1rem);
}

.shield {
  position: absolute;
  inset: 0;
  cursor: pointer;
}

.ratio-16x9:fullscreen {
  border: 0;
}

/* The resize grip: top-left corner of the mini and deck video, never over the pane. */
.grip {
  display: none;
  position: absolute;
  top: -1px;
  left: -1px;
  width: 1.75rem;
  min-height: 1.75rem;
  padding: 0;
  border: 0;
  background: var(--red);
  color: var(--on-red);
  cursor: nwse-resize;
  touch-action: none;
}

.grip:hover {
  background: var(--text);
  color: var(--ground);
}

body[data-video="mini"] .grip,
body[data-video="deck"] .grip {
  display: inline-flex;
}

/* While dragging, the iframe must not swallow the pointer, and nothing animates. */
body.resizing .ratio-16x9 {
  transition: none;
}

body.resizing .ratio-16x9 iframe {
  pointer-events: none;
}

/* ---- Sources and Import ----------------------------------------------------------------- */

.roster {
  margin: 0;
  padding: 0;
  list-style: none;
}

.roster > li {
  display: grid;
  align-items: center;
  gap: 0.5rem 1.25rem;
  padding: 0.875rem 0.75rem;
  margin: 0 -0.75rem;
  border-bottom: 1px solid var(--line);
}

.roster > .source {
  grid-template-columns: 7.5rem minmax(0, 1fr) minmax(12rem, 17rem) auto;
  grid-template-areas: "cat body status actions";
}

.source .cat {
  grid-area: cat;
}

.source .body {
  grid-area: body;
}

.source .status {
  grid-area: status;
}

.source .actions {
  grid-area: actions;
}

.source .title {
  display: block;
  font: 500 1rem/1.35 var(--read);
}

.url,
.last-fetched,
.not-fetched {
  display: block;
  font: 400 0.75rem/1.4 var(--mono);
  color: var(--text-2);
  overflow-wrap: anywhere;
}

.url {
  margin-top: 0.25rem;
}

.error {
  margin: 0 0 0.375rem;
  font-size: 0.8125rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

/* A failing source carries the obi's red as a tag. */
.tag {
  display: inline-block;
  margin-right: 0.375rem;
  padding: 0.25rem 0.375rem;
  background: var(--red);
  color: var(--on-red);
  font: 600 0.6875rem/1 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.regroup {
  display: flex;
  gap: 0.5rem;
  margin: 0;
}

.regroup .field {
  width: 11rem;
  font-size: 0.8125rem;
}

.source .actions form {
  margin: 0;
}

.group-sheet {
  margin-top: 4rem;
}

.new-group {
  max-width: 36rem;
  margin: 1.5rem 0;
}

.roster > .group {
  grid-template-columns: minmax(0, 1fr) 8rem auto;
}

.roster > .group form {
  margin: 0;
}

.rename {
  display: flex;
  gap: 0.5rem;
  max-width: 36rem;
}

.rename .field {
  flex: 1;
  font-weight: 500;
}

.members {
  font: 400 0.75rem/1.3 var(--mono);
  color: var(--text-2);
  text-align: right;
}

#summary .roster > li {
  display: flex;
  justify-content: space-between;
  font: 400 0.875rem/1.4 var(--mono);
}

#summary .roster > li span {
  font-weight: 600;
}

.failure {
  color: var(--red-text);
}

@container sheet (max-width: 68rem) {
  .roster > .source {
    grid-template-columns: 7.5rem minmax(0, 1fr) auto;
    grid-template-areas:
      "cat body actions"
      "cat status actions";
  }

  .source .cat {
    align-self: start;
    padding-top: 0.25rem;
  }
}

/* ---- Narrow screens --------------------------------------------------------------------- */

@media (max-width: 60rem) {
  :root {
    --gutter: 1rem;
  }

  .bar {
    position: static;
    flex-wrap: wrap;
    gap: 0 1.5rem;
    padding-top: 0.75rem;
  }

  .pages {
    min-height: 2.5rem;
  }

  .groups {
    order: 3;
    flex-basis: 100%;
    min-height: 2.75rem;
    padding-left: 0;
    border-left: 0;
    border-top: 1px solid var(--line);
    margin: 0 calc(-1 * var(--gutter));
    padding: 0 var(--gutter);
  }

  .label {
    display: block;
  }

  /* The obi turns horizontal: a red band under the bar. */
  .sleeve {
    position: static;
    display: block;
    height: auto;
    border-right: 0;
  }

  .series-block {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    height: auto;
    padding: 1rem var(--gutter);
  }

  .series {
    writing-mode: horizontal-tb;
    transform: none;
    max-block-size: none;
    max-inline-size: none;
    white-space: normal;
    overflow: visible;
    font-size: clamp(1.25rem, 7vw, 1.75rem);
  }

  .series-meta {
    writing-mode: horizontal-tb;
    align-items: flex-end;
    gap: 0.25rem;
    text-align: right;
  }

  .theme-switch {
    order: 3;
    margin-top: 0;
    width: 2.25rem;
    height: 1.25rem;
    min-height: 1.25rem;
  }

  :is(.sleeve, .panel) > :not(.series-block):not(.dock) {
    width: auto;
    padding: 0 var(--gutter);
  }

  :is(.sleeve, .panel) > .message {
    width: auto;
    margin: 1rem var(--gutter) 0;
  }

  :is(.sleeve, .panel) > .hint:last-child {
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--line);
  }

  .sheet-head {
    flex-wrap: wrap;
  }

  .sheet-head::before,
  .sheet-head::after {
    display: none;
  }

  .sheet-title {
    font-size: 1.25rem;
  }

  .view-actions {
    margin-left: 0;
    flex-basis: 100%;
  }

  /* The row layout itself follows the sheet's width (the container queries above). */
  .item {
    padding: 0.875rem var(--gutter);
    margin: 0 calc(-1 * var(--gutter));
  }

  .actions button {
    min-height: 2.75rem;
  }

  .field {
    font-size: 1rem;
    min-height: 2.75rem;
  }

  .roster > .source,
  .roster > .group {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: none;
  }

  .roster > .source > *,
  .roster > .group > * {
    grid-area: auto;
  }

  .regroup .field {
    width: auto;
    flex: 1;
  }

  .members {
    text-align: left;
  }

  /* The deck: art, title and transport on two lines. */
  .player {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "art text stop"
      "transport transport transport";
    gap: 0.5rem 0.75rem;
    padding: 0.625rem 0.75rem 0.5rem;
    padding-bottom: calc(0.5rem + env(safe-area-inset-bottom));
  }

  .now-title {
    line-height: 1.35;
  }

  .now-art {
    grid-area: art;
    width: 3.5rem;
    height: 3.5rem;
  }

  .now-text {
    grid-area: text;
  }

  .now-cat {
    font-size: 0.875rem;
  }

  .player > .stop {
    grid-area: stop;
  }

  .transport {
    grid-area: transport;
    gap: 0.5rem;
  }

  .wave {
    height: 2.25rem;
    min-width: 0;
  }

  .elapsed,
  .total {
    min-width: 0;
    font-size: 0.6875rem;
  }

  .volume {
    gap: 0.25rem;
  }

  /* Phones always show the video in the pane above the deck: no placement choice. */
  .placement {
    display: none;
  }

  .player-off {
    grid-column: 1 / -1;
  }

  /* Pane on a phone: the full width just above the deck. */
  body[data-video="pane"] .label,
  body[data-video="deck"] .label {
    display: block;
  }

  body[data-video] .ratio-16x9 {
    top: auto;
    left: 0;
    right: 0;
    bottom: var(--deck-h);
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    max-height: 40dvh;
    border-width: 1px 0 0;
  }

  body[data-video] {
    padding-bottom: calc(var(--deck-h) + min(56.25vw, 40dvh));
  }

  body[data-video="pane"] .sheet {
    border-right: 0;
    padding-right: var(--gutter);
  }

  body[data-video] .splitter {
    display: none;
  }

  body[data-video="deck"] .player {
    padding-right: 0.75rem;
  }

  .level {
    width: 3.5rem;
  }

  body[data-video] .grip {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
  }

  .now-title.ticking {
    text-overflow: ellipsis;
    mask-image: none;
  }

  .now-title.ticking .ticker,
  .theme-switch:hover,
  .theme-switch:focus-visible,
  .shutter-scan::before,
  .tab.here::before,
  body.folding #timeline,
  body.folding .sheet-head {
    animation: none;
  }
}
