/**
 * Video library (/my-videos): the creator's own synced uploads.
 *
 * Lives here rather than being imported from the React bundle. The react_app
 * library declares only main.min.js; webpack does emit main.min.css, but
 * nothing attaches it, so a stylesheet imported from the bundle is silently
 * dead. Fifth component to hit that trap - see the notes on social-composer,
 * setup-checklist, audit-headline and guided-help in the libraries file.
 *
 * Every colour is a token from ts_design_tokens/css/tokens.css. The fallbacks
 * in each var() are the token's own value, so the page still renders if this
 * file is ever loaded on a surface where the token sheet is not.
 */

.ts-vl-page {
  --ts-vl-gap: var(--ts-space-4, 16px);
  display: flex;
  flex-direction: column;
  gap: var(--ts-space-5, 24px);
  color: var(--ts-color-ink, #3e2155);
}

/* -------------------------------------------------------------------------
 * Header and the summary of the filtered set
 * ---------------------------------------------------------------------- */

.ts-vl-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ts-space-4, 16px);
}

.ts-vl-header__title {
  margin: 0;
  font-family: var(--ts-font-display, 'Satoshi', system-ui, sans-serif);
  font-weight: var(--ts-font-display-weight, 900);
  font-size: var(--ts-text-h2, 1.5rem);
  color: var(--ts-color-ink, #3e2155);
}

/* The title block takes whatever the summary tiles leave.
 *
 * There is deliberately NO max-width here, which is a departure from the ~75ch
 * measure the styleguide asks for elsewhere. That rule exists so the eye can
 * find the start of the NEXT line; this is a single supporting sentence, and
 * on a desktop header there is no next line for it to find. Capping it only
 * forces a wrap that buys nothing.
 *
 * It was capped at 48ch, then at 72ch. The sentence is 107 characters, so
 * neither could ever have held it on one line - the cap was raised twice
 * without being measured against the string it was capping.
 *
 * It still wraps on a narrow viewport, which is correct: there the width is
 * genuinely not available. */
.ts-vl-header > div:first-child {
  flex: 1 1 420px;
  min-width: 0;
}

.ts-vl-header__sub {
  margin: var(--ts-space-1, 4px) 0 0;
  font-size: var(--ts-text-small, 0.875rem);
  color: var(--ts-color-muted, #6b6880);
  /* Where it does wrap, avoid a single orphaned word on the last line.
     Ignored by browsers that do not support it, which changes nothing. */
  text-wrap: pretty;
}

.ts-vl-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ts-space-2, 8px);
  margin: 0;
  /* flex-shrink stays at 1, and min-width: 0 lets it act.
   *
   * This was flex: 0 0 auto, to stop the tiles giving their space to the
   * subtitle. It did that, and it also froze the row at its max-content width
   * - the four tiles side by side, about 392px - at EVERY width below that,
   * so the last tile hung off the edge of the panel on any narrow screen.
   *
   * Pinning was never needed. The basis is already max-content, so while the
   * header has room the tiles are never asked to shrink and the subtitle still
   * runs full width up to them. Shrinking only starts once the two genuinely
   * do not fit, which is exactly when it should. */
  flex: 0 1 auto;
  min-width: 0;
}

.ts-vl-summary__item {
  /* 92px is the floor because COMMENTS is the longest label: eight uppercase
     characters at 11px with 0.08em tracking, plus the tile padding. A single
     word cannot wrap, so a narrower tile does not shrink the text, it just
     lets it escape the box. Grow to fill so a wrapped row is not ragged. */
  flex: 1 1 92px;
  min-width: 92px;
  padding: var(--ts-space-2, 8px) var(--ts-space-3, 12px);
  /* Centred, label and value together. The label is always the wider of the
     two - COMMENTS against 16 - so left-aligning both left every number
     hanging off the left edge of a tile it did not fill. Centring the value
     alone would just move the problem to the label. */
  text-align: center;
  background: var(--ts-surface-card, #ffffff);
  border: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
  border-radius: var(--ts-radius-control, 10px);
  box-shadow: var(--ts-shadow-card, 0 1px 3px rgba(62, 33, 85, 0.08));
}

.ts-vl-summary__item dt {
  margin: 0;
  font-size: var(--ts-text-eyebrow, 0.6875rem);
  font-weight: var(--ts-eyebrow-weight, 700);
  letter-spacing: var(--ts-eyebrow-tracking, 0.08em);
  text-transform: uppercase;
  color: var(--ts-color-eyebrow, #6d648f);
}

.ts-vl-summary__item dd {
  margin: 0;
  font-size: var(--ts-text-h3, 1.25rem);
  font-weight: 700;
  color: var(--ts-color-ink, #3e2155);
}

/* -------------------------------------------------------------------------
 * Channel chips
 * ---------------------------------------------------------------------- */

.ts-vl-channels {
  display: flex;
  flex-direction: column;
  gap: var(--ts-space-3, 12px);
}

.ts-vl-channels__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ts-space-2, 8px);
}

.ts-vl-chip {
  display: flex;
  align-items: center;
  gap: var(--ts-space-2, 8px);
  padding: var(--ts-space-2, 8px) var(--ts-space-3, 12px);
  background: var(--ts-surface-card, #ffffff);
  /* The control border, not the divider: this is the edge of a control, and
     WCAG 2.1 asks 3:1 of a non-text boundary. */
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-pill, 999px);
  text-align: left;
  cursor: pointer;
}

.ts-vl-chip.is-active {
  background: var(--ts-btn-primary-bg, #672c72);
  border-color: var(--ts-btn-primary-bg, #672c72);
  color: var(--ts-btn-primary-fg, #ffffff);
}

.ts-vl-chip.is-active .ts-vl-chip__sub {
  color: var(--ts-btn-primary-fg, #ffffff);
  opacity: 0.85;
}

.ts-vl-chip:focus-visible {
  outline: 2px solid var(--ts-btn-primary-bg, #672c72);
  outline-offset: 2px;
}

.ts-vl-chip__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 32px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--ts-tint-component, #efedf7);
  color: var(--ts-color-ink, #3e2155);
  font-weight: 700;
  font-size: var(--ts-text-caption, 0.75rem);
}

.ts-vl-chip__avatar--all {
  font-size: 0.625rem;
  letter-spacing: 0.04em;
}

.ts-vl-chip__text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.ts-vl-chip__name {
  font-size: var(--ts-text-small, 0.875rem);
  font-weight: 600;
}

.ts-vl-chip__sub {
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-conn {
  padding: 2px var(--ts-space-2, 8px);
  border-radius: var(--ts-radius-pill, 999px);
  font-size: var(--ts-text-caption, 0.75rem);
  font-weight: 600;
  white-space: nowrap;
}

.ts-vl-conn--full {
  background: var(--ts-state-steady-tint, #edf2f7);
  color: var(--ts-state-steady-ink, #1f5f9b);
}

.ts-vl-conn--public {
  background: var(--ts-state-incomplete-tint, #f3f3f5);
  color: var(--ts-state-incomplete-ink, #6b6880);
}

/* The one-channel case: a status bar, not a selector. Flat rather than a
   pill, so it does not look like something you can click. */
/* A region inside the top panel, not a panel of its own. It carried a full
   card - fill, border, radius, shadow - which drew a box inside a box once the
   header moved onto a surface. A hairline above it is enough to separate it
   from the title block. */
.ts-vl-channelbar {
  display: flex;
  /* Wraps at whatever width it runs out, rather than at a chosen one. */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ts-space-3, 12px);
  padding-top: var(--ts-space-3, 12px);
  border-top: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
}

.ts-vl-channelbar__text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}

.ts-vl-channelbar__name {
  font-size: var(--ts-text-body, 1rem);
  font-weight: 700;
  color: var(--ts-color-ink, #3e2155);
}

.ts-vl-channelbar__sub {
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-channelbar__account {
  display: block;
  margin-top: 2px;
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-channelbar .ts-vl-conn {
  margin-left: auto;
}

.ts-vl-notice {
  padding: var(--ts-space-3, 12px) var(--ts-space-4, 16px);
  background: var(--ts-state-quick-win-tint, #f3eef4);
  border-left: 3px solid var(--ts-state-quick-win-accent, #672c72);
  border-radius: var(--ts-radius-input, 8px);
  font-size: var(--ts-text-small, 0.875rem);
  color: var(--ts-color-ink, #3e2155);
}

.ts-vl-notice__link {
  margin-left: var(--ts-space-2, 8px);
  color: var(--ts-btn-primary-bg, #672c72);
  font-weight: 600;
  text-decoration: underline;
}

/* -------------------------------------------------------------------------
 * Filter bar
 * ---------------------------------------------------------------------- */

/* The three panels: header block, filters, results.
 *
 * One class for all three rather than per-section chrome, so the glass
 * conversion has a single name to target and the page cannot half-convert.
 * This is the opaque fallback; glass.css restyles it on the converted page.
 * The house squared corner (14/14/14/3) is applied here too, so the silhouette
 * is right even before the glass rules land. */
.ts-vl-panel {
  padding: var(--ts-space-4, 16px);
  background: var(--ts-surface-card, #ffffff);
  border: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
  border-radius: 14px 14px 14px 3px;
  box-shadow: var(--ts-shadow-card, 0 1px 3px rgba(62, 33, 85, 0.08));
}

.ts-vl-top {
  display: flex;
  flex-direction: column;
  gap: var(--ts-space-4, 16px);
}

.ts-vl-filters {
  display: flex;
  flex-direction: column;
  gap: var(--ts-space-3, 12px);
}

/* align-items: end, and every child is the same two-part shape: one label line
 * then one 38px control. That is what makes the row line up, rather than each
 * field being nudged into place. Anything added here must keep that shape -
 * including a control with no label, which carries an empty one (see
 * .ts-vl-field--action). */
.ts-vl-filters__top,
.ts-vl-filters__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--ts-space-3, 12px);
}

.ts-vl-filters__right {
  display: flex;
  align-items: flex-end;
  gap: var(--ts-space-3, 12px);
  margin-left: auto;
}

.ts-vl-search {
  position: relative;
  flex: 1 1 260px;
  display: flex;
  align-items: center;
}

.ts-vl-search .bi-search {
  position: absolute;
  left: var(--ts-space-3, 12px);
  color: var(--ts-color-muted, #6b6880);
  pointer-events: none;
}

.ts-vl-search__input {
  width: 100%;
  height: 38px;
  box-sizing: border-box;
  padding: 0 var(--ts-space-6, 32px) 0 var(--ts-space-7, 48px);
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-pill, 999px);
  font-size: var(--ts-text-small, 0.875rem);
  color: var(--ts-color-ink, #3e2155);
  background: var(--ts-surface-card, #ffffff);
}

.ts-vl-search__input:focus {
  outline: 2px solid var(--ts-btn-primary-bg, #672c72);
  outline-offset: 1px;
}

.ts-vl-search__clear {
  position: absolute;
  right: var(--ts-space-3, 12px);
  border: 0;
  background: none;
  color: var(--ts-color-muted, #6b6880);
  font-size: var(--ts-text-caption, 0.75rem);
  cursor: pointer;
}

.ts-vl-field {
  display: flex;
  flex-direction: column;
  gap: var(--ts-space-1, 4px);
  margin: 0;
  border: 0;
  padding: 0;
}

/* A fixed line box, not the font's natural height. The labels differ in
   descenders ("PUBLISHED FROM" has none, "Visibility" does), and letting each
   size itself put the controls under them on slightly different baselines. */
.ts-vl-field__label {
  display: block;
  height: 14px;
  line-height: 14px;
  font-size: var(--ts-text-eyebrow, 0.6875rem);
  font-weight: var(--ts-eyebrow-weight, 700);
  letter-spacing: var(--ts-eyebrow-tracking, 0.08em);
  text-transform: uppercase;
  color: var(--ts-color-eyebrow, #6d648f);
  white-space: nowrap;
}

/* A control with no label of its own still needs the label's 14px, or it
   bottoms out level with the others while its top floats. */
.ts-vl-field--action {
  margin-left: auto;
}

/* 38px, set on height not min-height. A <select> and an <input type="date">
   size themselves from their own content and chrome and do NOT agree, which is
   why Format and Published From sat at different heights. */
.ts-vl-select {
  height: 38px;
  box-sizing: border-box;
  padding: 6px var(--ts-space-3, 12px);
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-input, 8px);
  background: var(--ts-surface-card, #ffffff);
  color: var(--ts-color-ink, #3e2155);
  font-size: var(--ts-text-small, 0.875rem);
  font-family: inherit;
}

.ts-vl-checks {
  display: flex;
  gap: var(--ts-space-3, 12px);
  height: 38px;
  align-items: center;
}

.ts-vl-check {
  display: inline-flex;
  align-items: center;
  gap: var(--ts-space-1, 4px);
  margin: 0;
  font-size: var(--ts-text-small, 0.875rem);
  color: var(--ts-color-ink, #3e2155);
  cursor: pointer;
}

.ts-vl-viewtoggle {
  display: inline-flex;
  height: 38px;
  box-sizing: border-box;
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-input, 8px);
  overflow: hidden;
}

/* 36px inside a 38px bordered wrapper. min-height: 38px here made the toggle
   40px overall and it rode 2px below the Sort select next to it. */
.ts-vl-viewtoggle button {
  min-width: 40px;
  height: 36px;
  border: 0;
  background: var(--ts-surface-card, #ffffff);
  color: var(--ts-color-muted, #6b6880);
  cursor: pointer;
}

.ts-vl-viewtoggle button.is-active {
  background: var(--ts-btn-primary-bg, #672c72);
  color: var(--ts-btn-primary-fg, #ffffff);
}

.ts-vl-clear {
  height: 38px;
  padding: 0 var(--ts-space-3, 12px);
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-input, 8px);
  background: none;
  color: var(--ts-btn-primary-bg, #672c72);
  font-size: var(--ts-text-small, 0.875rem);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

.ts-vl-resultshead {
  display: flex;
  align-items: baseline;
  gap: var(--ts-space-3, 12px);
  margin-bottom: var(--ts-space-3, 12px);
}

.ts-vl-resultshead__count {
  margin: 0;
  font-size: var(--ts-text-h3, 1.25rem);
  font-weight: 700;
  color: var(--ts-color-ink, #3e2155);
}

.ts-vl-resultshead__state {
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

/* -------------------------------------------------------------------------
 * Results: grid of cards
 * ---------------------------------------------------------------------- */

.ts-vl-results.is-refreshing {
  /* The rows stay put while a filter change is in flight. Replacing them with
     a spinner throws away the reader's place on every keystroke. */
  opacity: 0.55;
  transition: opacity 120ms ease-in;
}

.ts-vl-grid {
  display: grid;
  gap: var(--ts-space-4, 16px);

  /* Never more than four columns, and fluid below that.
   *
   * The page sizes are 12, 24, 48 and 60. Every one of them divides by 1, 2, 3
   * and 4, so at up to four columns the last row is always full. 24 also
   * divides by 6. The single count none of them divides is FIVE - which is
   * what a wide desktop was producing, leaving one empty cell at the end of a
   * 24-item page.
   *
   * Raising the page size to 25 fixes that one width and breaks the rest: 25
   * divides by 5 and 1 and nothing else, so a four-, three- or two-column
   * layout would end on a part-empty row instead. Capping the columns fixes it
   * at every width and for every page size.
   *
   * The cap is the max() term: a track may never be narrower than a quarter of
   * the row, so a fifth cannot fit. min(260px, 100%) stays as the fluid floor
   * below that, and keeps the grid inside its panel on a container narrower
   * than one card - a bare 260px floor does not collapse and would overflow. */
  grid-template-columns: repeat(
    auto-fill,
    minmax(max(min(260px, 100%), calc((100% - 3 * var(--ts-space-4, 16px)) / 4)), 1fr)
  );
}

/* Nested tiles, not panels. There are up to 60 of them on a page; the estate's
   rule (glass.css: "NESTED TILES do not") is that a signature corner on every
   one is noise, and a live blur per tile is 60 compositing passes for nothing.
   They read as grouped because they sit inside the results panel. */
.ts-vl-card {
  display: flex;
  flex-direction: column;
  background: var(--ts-surface-card, #ffffff);
  border: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
  border-radius: 12px 12px 12px 3px;
  overflow: hidden;
}

.ts-vl-card__media {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--ts-tint-component, #efedf7);
}

.ts-vl-card__thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The house placeholder still. object-fit: contain rather than cover, because
   it is artwork at exactly 16:9 rather than a photo - cover would crop the
   wrench off its own illustration at any tile width that is not 16:9. The tint
   behind it fills whatever contain leaves. */
.ts-vl-thumb--placeholder {
  object-fit: contain;
  background: var(--ts-tint-component, #efedf7);
}

.ts-vl-duration,
.ts-vl-badge,
.ts-vl-visibility {
  position: absolute;
  padding: 2px 6px;
  border-radius: var(--ts-radius-input, 8px);
  font-size: var(--ts-text-caption, 0.75rem);
  font-weight: 600;
  line-height: 1.4;
}

/* Black on the still, as YouTube does it: the thumbnail behind it is an
   arbitrary image, so no token pairing can guarantee contrast. A solid dark
   plate is the only thing that reads over every frame. */
.ts-vl-duration {
  right: var(--ts-space-2, 8px);
  bottom: var(--ts-space-2, 8px);
  background: rgba(0, 0, 0, 0.82);
  color: #ffffff;
}

.ts-vl-duration--sm {
  position: absolute;
  right: 4px;
  bottom: 4px;
  font-size: 0.625rem;
}

.ts-vl-badge {
  left: var(--ts-space-2, 8px);
  top: var(--ts-space-2, 8px);
  color: #ffffff;
}

.ts-vl-badge--short {
  background: var(--ts-btn-secondary-bg, #c2410c);
}

.ts-vl-badge--live {
  background: var(--ts-btn-danger-bg, #c62828);
}

.ts-vl-visibility {
  right: var(--ts-space-2, 8px);
  top: var(--ts-space-2, 8px);
  background: rgba(0, 0, 0, 0.82);
  color: #ffffff;
}

.ts-vl-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--ts-space-2, 8px);
  padding: var(--ts-space-3, 12px);
}

.ts-vl-card__title {
  margin: 0;
  font-size: var(--ts-text-small, 0.875rem);
  font-weight: 700;
  line-height: 1.35;
  /* Two lines, then ellipsis: titles run to YouTube's 100-character limit and
     an unclamped one makes every card in the row a different height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.7em;
}

.ts-vl-card__title a {
  color: var(--ts-color-ink, #3e2155);
  text-decoration: none;
}

.ts-vl-card__title a:hover,
.ts-vl-card__title a:focus {
  text-decoration: underline;
}

.ts-vl-card__meta {
  margin: 0;
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-dot {
  margin: 0 4px;
}

.ts-vl-stats {
  display: grid;
  /* Two by two at every width, not four across. ENGAGEMENT is ten uppercase
     characters and needs ~70px; four columns inside a 236px card gives each
     59px, so the two longest labels collided on the DESKTOP layout as well.
     This is not a narrow-screen concession, it is the size the row always
     needed. */
  grid-template-columns: repeat(2, 1fr);
  gap: var(--ts-space-1, 4px);
  margin: 0;
  padding-top: var(--ts-space-2, 8px);
  border-top: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
}

.ts-vl-stat dt {
  margin: 0;
  font-size: 0.625rem;
  font-weight: var(--ts-eyebrow-weight, 700);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ts-color-eyebrow, #6d648f);
}

.ts-vl-stat dd {
  margin: 0;
  font-size: var(--ts-text-small, 0.875rem);
  font-weight: 700;
  color: var(--ts-color-ink, #3e2155);
}

.ts-vl-card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ts-space-2, 8px);
  padding-top: var(--ts-space-2, 8px);
  border-top: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
}

.ts-vl-card__rate {
  margin-left: auto;
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-action {
  display: inline-flex;
  align-items: center;
  padding: 5px var(--ts-space-3, 12px);
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-pill, 999px);
  background: var(--ts-surface-card, #ffffff);
  color: var(--ts-color-ink, #3e2155);
  font-size: var(--ts-text-caption, 0.75rem);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.ts-vl-action:hover,
.ts-vl-action:focus {
  color: var(--ts-color-ink, #3e2155);
  border-color: var(--ts-btn-primary-bg, #672c72);
}

.ts-vl-action--primary {
  background: var(--ts-btn-primary-bg, #672c72);
  border-color: var(--ts-btn-primary-bg, #672c72);
  color: var(--ts-btn-primary-fg, #ffffff);
}

.ts-vl-action--primary:hover,
.ts-vl-action--primary:focus {
  background: var(--ts-btn-primary-hover-bg, #521f5a);
  border-color: var(--ts-btn-primary-hover-bg, #521f5a);
  color: var(--ts-btn-primary-fg, #ffffff);
}

/* -------------------------------------------------------------------------
 * Results: list view
 * ---------------------------------------------------------------------- */

.ts-vl-tablewrap {
  /* The table is wider than the content column below roughly 1100px. It
     scrolls inside its own box rather than making the page scroll sideways.
     No fill or border: it sits inside the results panel, which supplies both. */
  overflow-x: auto;
}

.ts-vl-table {
  width: 100%;
  /* 900px again. It went to 1040px to fit three text buttons; three 32px icons
     need about 110px rather than 250px, so the width goes back to the title
     column, which is the one that actually wanted it. */
  min-width: 900px;
  border-collapse: collapse;
  font-size: var(--ts-text-small, 0.875rem);
}

.ts-vl-table thead th {
  padding: var(--ts-space-3, 12px);
  background: var(--ts-tint-page, #f2eef8);
  text-align: left;
  font-size: var(--ts-text-eyebrow, 0.6875rem);
  font-weight: var(--ts-eyebrow-weight, 700);
  letter-spacing: var(--ts-eyebrow-tracking, 0.08em);
  text-transform: uppercase;
  color: var(--ts-color-eyebrow, #6d648f);
  white-space: nowrap;
}

.ts-vl-table td {
  padding: var(--ts-space-3, 12px);
  border-top: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
  vertical-align: middle;
}

.ts-vl-table .ts-vl-num,
.ts-vl-table th.ts-vl-num {
  text-align: right;
  /* Tabular figures so the columns line up digit for digit; proportional ones
     make a column of numbers look ragged and hard to compare. */
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ts-vl-row__video {
  display: flex;
  align-items: center;
  gap: var(--ts-space-3, 12px);
  min-width: 320px;
}

.ts-vl-row__media {
  position: relative;
  flex: 0 0 112px;
  width: 112px;
  aspect-ratio: 16 / 9;
  border-radius: var(--ts-radius-input, 8px);
  overflow: hidden;
  background: var(--ts-tint-component, #efedf7);
}

.ts-vl-row__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ts-vl-row__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.ts-vl-row__title {
  color: var(--ts-color-ink, #3e2155);
  font-weight: 600;
  text-decoration: none;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ts-vl-row__title:hover {
  text-decoration: underline;
}

.ts-vl-row__sub,
.ts-vl-row__age {
  display: block;
  font-size: var(--ts-text-caption, 0.75rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-row__date {
  display: block;
  white-space: nowrap;
}

/* Three icon buttons on one line. Without nowrap the cell is the narrowest
   column in the table, so the browser wraps them one per line and the row
   grows to three times its height. */
.ts-vl-row__actions {
  white-space: nowrap;
}

.ts-vl-row__actions .ts-vl-iaction + .ts-vl-iaction {
  margin-left: var(--ts-space-1, 4px);
}

/* The icon-only sibling of .ts-vl-action, for the list view.
   32px square. That is below the 44px touch target a phone wants, which is
   the trade for a dense table - and it is why the LIST is the only place
   icon-only is used: the grid, which is what a narrow screen falls back to,
   keeps full-width labelled buttons. */
.ts-vl-iaction {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-input, 8px);
  background: var(--ts-surface-card, #ffffff);
  color: var(--ts-color-ink, #3e2155);
  font-size: 0.9rem;
  line-height: 1;
  text-decoration: none;
}

.ts-vl-iaction:hover,
.ts-vl-iaction:focus-visible {
  border-color: var(--ts-btn-primary-bg, #672c72);
  color: var(--ts-btn-primary-bg, #672c72);
}

.ts-vl-iaction--primary {
  background: var(--ts-btn-primary-bg, #672c72);
  border-color: var(--ts-btn-primary-bg, #672c72);
  color: var(--ts-btn-primary-fg, #ffffff);
}

.ts-vl-iaction--primary:hover,
.ts-vl-iaction--primary:focus-visible {
  background: var(--ts-btn-primary-hover-bg, #521f5a);
  border-color: var(--ts-btn-primary-hover-bg, #521f5a);
  color: var(--ts-btn-primary-fg, #ffffff);
}

/* Glass: same treatment the other pale controls get, or the button reads as a
   hole in the panel rather than a control on it. */
body.ts-glass-page .ts-vl-iaction {
  background: rgba(255, 255, 255, 0.86);
}

body.ts-glass-page .ts-vl-iaction--primary {
  background: var(--ts-btn-primary-bg, #672c72);
}

.ts-vl-pill {
  display: inline-block;
  padding: 2px var(--ts-space-2, 8px);
  border-radius: var(--ts-radius-pill, 999px);
  font-size: var(--ts-text-caption, 0.75rem);
  font-weight: 600;
  white-space: nowrap;
}

.ts-vl-pill--public {
  background: var(--ts-state-steady-tint, #edf2f7);
  color: var(--ts-state-steady-ink, #1f5f9b);
}

.ts-vl-pill--unlisted {
  background: var(--ts-state-quick-win-tint, #f3eef4);
  color: var(--ts-state-quick-win-ink, #672c72);
}

.ts-vl-pill--private {
  background: var(--ts-state-incomplete-tint, #f3f3f5);
  color: var(--ts-state-incomplete-ink, #6b6880);
}

/* -------------------------------------------------------------------------
 * Pager and empty states
 * ---------------------------------------------------------------------- */

.ts-vl-pager {
  margin-top: var(--ts-space-4, 16px);
  padding-top: var(--ts-space-3, 12px);
  border-top: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ts-space-3, 12px);
  font-size: var(--ts-text-small, 0.875rem);
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-pager__list {
  display: flex;
  /* A windowed pager can still reach seven controls, which is ~276px of
     buttons - wider than a 320px viewport once padding is taken off. */
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin: 0 auto 0 0;
  padding: 0;
  list-style: none;
}

.ts-vl-pager__btn {
  min-width: 36px;
  min-height: 36px;
  padding: 0 var(--ts-space-2, 8px);
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: var(--ts-radius-input, 8px);
  background: var(--ts-surface-card, #ffffff);
  color: var(--ts-color-ink, #3e2155);
  font-size: var(--ts-text-small, 0.875rem);
  cursor: pointer;
}

.ts-vl-pager__btn.is-active {
  background: var(--ts-btn-primary-bg, #672c72);
  border-color: var(--ts-btn-primary-bg, #672c72);
  color: var(--ts-btn-primary-fg, #ffffff);
}

.ts-vl-pager__btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.ts-vl-pager__gap {
  padding: 0 4px;
  color: var(--ts-color-muted, #6b6880);
}

.ts-vl-pager__size {
  display: inline-flex;
  align-items: center;
  gap: var(--ts-space-2, 8px);
  margin: 0;
}

.ts-vl-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ts-space-3, 12px);
  padding: var(--ts-space-7, 48px) var(--ts-space-4, 16px);
  text-align: center;
  background: var(--ts-surface-card, #ffffff);
  border: 1px solid var(--ts-border-quiet, rgba(62, 33, 85, 0.1));
  border-radius: 14px 14px 14px 3px;
}

/* Inside the results panel, so it supplies no surface of its own. */
.ts-vl-empty--inline {
  background: none;
  border: 0;
  border-radius: 0;
  padding: var(--ts-space-6, 32px) var(--ts-space-4, 16px);
}

.ts-vl-empty h2 {
  margin: 0;
  font-size: var(--ts-text-h3, 1.25rem);
  font-weight: 700;
  color: var(--ts-color-ink, #3e2155);
}

.ts-vl-empty p {
  margin: 0;
  max-width: 46ch;
  font-size: var(--ts-text-small, 0.875rem);
  color: var(--ts-color-muted, #6b6880);
}

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

/* Tablet. A density preference, not a fix: the grid above already reflows on
   its own, this just lets a narrower card in so a tablet gets two columns
   where it would otherwise get one. */
@media (max-width: 767px) {
  .ts-vl-grid {
    grid-template-columns: repeat(
      auto-fill,
      minmax(max(min(200px, 100%), calc((100% - 3 * var(--ts-space-4, 16px)) / 4)), 1fr)
    );
  }

  .ts-vl-filters__right {
    margin-left: 0;
    width: 100%;
    justify-content: space-between;
  }
}

/* Narrow widths.
 *
 * Deliberately short. The layout above reflows on its own - the summary tiles,
 * the card stats, the channel bar and the pager all wrap at whatever width
 * they run out of, not at a width chosen here. Everything that WAS in this
 * block has been moved up into those rules, because a fix that only applies
 * below a breakpoint leaves the same defect sitting in the band above it,
 * which is why the overflow turned up at almost every width rather than only
 * on a phone.
 *
 * What is left is genuinely a small-screen decision rather than a fix. */
@media (max-width: 600px) {
  /* Buy back 8px a side. Worth it at 320px, pointless above it. */
  .ts-vl-panel {
    padding: var(--ts-space-3, 12px);
  }

  /* The FILTER ROW's controls take a line each. A date input is the reason:
     its intrinsic width is the dd/mm/yyyy mask plus the picker button, and
     unlike a select it will not shrink below that, so two side by side cannot
     fit. Scoped to that row - applied to every .ts-vl-select it would also
     stretch Sort, pushing the grid/list toggle onto its own line, and the
     pager's per-page select, which is two characters wide. */
  .ts-vl-filters__row .ts-vl-field,
  .ts-vl-filters__row .ts-vl-select {
    width: 100%;
  }

  .ts-vl-field--action {
    margin-left: 0;
  }

  /* Search takes the row; Sort and the view toggle share the next one. */
  .ts-vl-search {
    flex: 1 1 100%;
  }

  /* Centred rather than pushed left, now that both can wrap. */
  .ts-vl-pager__list,
  .ts-vl-pager__size {
    margin: 0 auto;
    justify-content: center;
  }
}

/* Respect a reader who has asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .ts-vl-results.is-refreshing {
    transition: none;
  }
}

/* -------------------------------------------------------------------------
 * Glass
 *
 * The page is on _tubespanner_purple_glass_pages(), so body.ts-glass-page is
 * set and the field is painted for us. What glass.css cannot do is convert
 * these panels: its selectors name .card, .middle-content-block and .ts-card,
 * and this page uses none of them.
 *
 * Reusing .ts-card was the obvious alternative and is a trap. It is defined in
 * connected-accounts.css, and its border comes from var(--ca-line) which is
 * scoped to .ts-accounts - so outside that page the class yields a white box
 * with no edge, plus a 16px margin and a 20/22px padding this page does not
 * want. The panels keep their own name and pick up the treatment here.
 *
 * Values are copied from glass.css deliberately rather than shared: that file
 * is the authority, and a divergence here should be visible as a divergence.
 * ---------------------------------------------------------------------- */

body.ts-glass-page .ts-vl-panel {
  background: rgba(255, 255, 255, 0.62);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter: blur(20px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.72);
  border-radius: 14px 14px 14px 3px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    0 12px 28px -16px rgba(62, 33, 85, 0.40);
}

/* Tiles inside the glass panel stay opaque and flat: a blur per tile buys
   nothing at this size and costs a compositing pass each. Slightly more opaque
   than the panel so the thumbnails keep their contrast against the field. */
body.ts-glass-page .ts-vl-card,
body.ts-glass-page .ts-vl-summary__item {
  background: rgba(255, 255, 255, 0.78);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-color: rgba(255, 255, 255, 0.66);
  box-shadow: none;
}

/* Controls need a readable fill on glass. At the panel's own translucency a
   select reads as a hole rather than a field. */
body.ts-glass-page .ts-vl-select,
body.ts-glass-page .ts-vl-search__input,
body.ts-glass-page .ts-vl-viewtoggle,
body.ts-glass-page .ts-vl-viewtoggle button,
body.ts-glass-page .ts-vl-chip {
  background: rgba(255, 255, 255, 0.86);
}

body.ts-glass-page .ts-vl-viewtoggle button.is-active,
body.ts-glass-page .ts-vl-chip.is-active {
  background: var(--ts-btn-primary-bg, #672c72);
}

/* The eyebrow concession glass.css documents: the house lavender measures
   4.25 against the darkest point of the field, under the 4.5 threshold, and
   the fill that would rescue it is not glass any more. So on this surface the
   eyebrow takes the body ink, exactly as the estate does elsewhere. */
body.ts-glass-page .ts-vl-field__label,
body.ts-glass-page .ts-vl-summary__item dt,
body.ts-glass-page .ts-vl-stat dt,
body.ts-glass-page .ts-vl-table thead th {
  color: #5b5872;
}

/* The list view's header band was a flat lavender fill, which on glass reads
   as a panel inside a panel. */
body.ts-glass-page .ts-vl-table thead th {
  background: rgba(255, 255, 255, 0.55);
}

/* Where backdrop-filter is unavailable the panels go plainly opaque rather
   than washed out. Contrast has to hold without the effect, not because of
   it - same fallback glass.css takes. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body.ts-glass-page .ts-vl-panel {
    background: rgba(255, 255, 255, 0.92);
  }
}
