/* /seek — soulseek-clone with three-tab workspace.
   SEARCH    — search form + results table (default tab).
   TRANSFERS — split pane: your downloads list (left) + active queue (right).
   BROWSE    — currently-opened user's library (empty state until populated).

   Shell layout — viewport-locked, no body scroll.

   The whole page is a single fixed-height block: top-bar pinned at top,
   ticker pinned at bottom, <main> filling the band between them as a
   flex column (status row → tab strip → active panel). The active panel
   takes flex:1 and is the only thing that scrolls — each tab's content
   either flows into the panel's overflow or carves further internal
   scrollers (the results table body, the file tree inside BROWSE, the
   queue list, etc.). This avoids the gap-under-sticky-tab artefact the
   previous "body scrolls, tabs are sticky" approach exposed: with no
   body scroll, there's nothing to leak through any gap.

   On mobile (≤700px) the panel itself scrolls vertically and the
   TRANSFERS split flattens to a single column — that's the natural
   touchscreen reading model. */

body.seek-page {
  margin: 0;
  height: 100vh;
  overflow: hidden;
  /* Reserve vertical space for the fixed top-bar (above) and ticker
     (below). Sized empirically to match style.css's .top-bar and our
     own .seek__ticker — over-shoot by a hair so neither chrome layer
     bleeds into main's content area. */
  padding-top: 1.7em;
  padding-bottom: 1.6em;
  box-sizing: border-box;
}

.seek-page > main.seek {
  height: 100%;
  margin: 0 auto;
  max-width: 1100px;
  padding: 0.6em 0.8em 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ----------------------------------------------------------------------- */
/* tab panels — one is visible at a time. SEARCH is the default. Inactive
   panels are display:none via [hidden]; the active one becomes a flex
   column that fills the remaining main height after status + tabs. */

.seek__panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;        /* lets descendants' overflow:auto actually scroll */
}
.seek__panel[hidden] { display: none; }

/* TRANSFERS panel split — downloads list (left) wider than the queue
   (right). Both sub-panels are flex columns with their own internal
   list-scrollers, so on desktop the visual heights stay aligned and
   each side scrolls independently. On mobile the grid collapses to one
   column and the panel itself takes over scrolling (see the mobile
   block further down). */
.seek__split {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 0.8em;
  flex: 1;
  min-height: 0;
}
@media (max-width: 700px) {
  .seek__split {
    grid-template-columns: 1fr;
    flex: none;
  }
}

/* ----------------------------------------------------------------------- */
/* status header — online count + connect status */

.seek__hdr {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  padding: 0.3em 0.7em;
  margin-bottom: 0.6em;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 0.95em;
  letter-spacing: 0.04em;
  flex: 0 0 auto;
}
.seek__online i {
  font-style: normal;
  font-weight: bold;
}
.seek__status {
  opacity: 0.7;
  font-size: 0.85em;
}
@media (max-width: 540px) {
  .seek__hdr { flex-direction: column; align-items: flex-start; gap: 0.05em; }
}

/* ----------------------------------------------------------------------- */
/* search form */

.seek__form {
  display: flex;
  gap: 0.4em;
  margin-bottom: 0.8em;
  flex: 0 0 auto;
}
.seek__input {
  flex: 1;
  font: inherit;
  background: #fff;
  color: #000;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  padding: 0.15em 0.6em;
  letter-spacing: 0.02em;
  min-width: 0;
}
.seek__input:focus {
  outline: none;
  background: #efffef;
}
.seek__submit {
  font: inherit;
  background: #000;
  color: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  padding: 0.15em 0.9em;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: color 0.08s linear;
}
/* Phosphor hover — bg stays black so the button doesn't dissolve into the
   page green; only the text "lights up" like a CRT character. The flux
   .btn-inv pattern (bg→green) is fine inside modals where the surround
   is white, but here the surround IS the green, so we invert on text. */
.seek__submit:hover { color: #00c800; }
.seek__submit:active {
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 #000;
  color: #fff;       /* drop the phosphor on press so the snap reads */
}

/* ----------------------------------------------------------------------- */
/* tab strip — three tabs, in normal flow at the top of <main> just
   below the status row. Doesn't need to be sticky any more: <main> is
   already viewport-height-locked, so the tabs are always visible. */

.seek__tabs {
  display: flex;
  gap: 0;
  margin-bottom: 0.6em;
  flex: 0 0 auto;
}
.seek__tab {
  font: inherit;
  background: #fff;
  color: #000;
  border: 2px solid #000;
  padding: 0.15em 0.9em;
  letter-spacing: 0.06em;
  cursor: pointer;
  flex: 1;
  text-align: center;
  transition: color 0.08s linear;
  /* Truncate the dynamic BROWSE: <name> tail when the tab is narrow. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.seek__tab + .seek__tab { border-left: 0; }
.seek__tab:hover { color: #00c800; background: #000; }
.seek__tab:focus-visible { outline: 2px solid #00c800; outline-offset: -4px; }
.seek__tab.is-active {
  background: #000;
  color: #fff;
  cursor: default;
}
.seek__tab.is-active:hover { color: #fff; background: #000; }

/* Badge on the TRANSFERS tab — shows count of active queued/transferring
   /stalled rows. Hidden when count is 0 (Phase 4 wires the count). */
.seek__tab-badge {
  display: inline-block;
  margin-left: 0.5em;
  padding: 0 0.4em;
  background: #00c800;
  color: #000;
  border: 1px solid currentColor;
  font-size: 0.75em;
  letter-spacing: 0.04em;
  vertical-align: middle;
  line-height: 1.4;
}
.seek__tab.is-active .seek__tab-badge {
  background: #00c800;
  color: #000;
  border-color: #00c800;
}
.seek__tab-badge[hidden] { display: none; }

/* Suffix on the BROWSE tab — populated to ": <name>" when a user is
   loaded. Empty span when no user is open. Truncates with the parent. */
.seek__tab-name { font-weight: normal; }
.seek__tab-name:not(:empty)::before { content: ""; }

/* Cross-tab flash — fires on an inactive tab when the user does
   something that affected its content (e.g. clicking ↓ on a SEARCH or
   BROWSE row pushes a transfer onto the queue, so the TRANSFERS tab
   flashes). Active tabs never flash; the user is already there. */
@keyframes seek-tab-flash {
  0%   { background: #00c800; color: #000; }
  100% { background: #fff;    color: #000; }
}
.seek__tab.has-flash:not(.is-active) {
  animation: seek-tab-flash 0.7s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .seek__tab.has-flash { animation: none; }
}

/* ----------------------------------------------------------------------- */
/* DOWNLOADS list — left half of the TRANSFERS split-pane. Flex column
   so the inner list scroller can flex:1 and fill the grid cell's
   height; min-height:0 lets descendants' overflow:auto actually
   activate (without it, flex children resolve to their content height). */

.seek__landing {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.seek__landing-h {
  background: #000;
  color: #fff;
  padding: 0.15em 0.6em;
  letter-spacing: 0.06em;
  font-size: 0.95em;
  flex-shrink: 0;
}
.seek__landing-list {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}
.seek__landing-list li::before { content: none; }
.seek__landing-row {
  padding: 0.35em 0.7em 0.45em;
  border-top: 1px solid #000;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 0.05em;
  text-align: left;
  width: 100%;
  font: inherit;
  background: transparent;
  color: inherit;
  border-left: 0;
  border-right: 0;
  border-bottom: 0;
}
.seek__landing-row:first-child { border-top: 0; }
.seek__landing-row:hover { background: #00c800; }
.seek__landing-row:focus-visible { outline: 2px solid #000; outline-offset: -4px; }

/* Top line: bold user handle on the left, faded "X min ago" on the right.
   Reads as "user did a thing, this long ago" — the event header. */
.seek__landing-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.6em;
  letter-spacing: 0.04em;
  font-size: 0.95em;
}
.seek__landing-user {
  font-weight: bold;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.seek__landing-when {
  opacity: 0.55;
  font-size: 0.85em;
  white-space: nowrap;
  flex-shrink: 0;
}

.seek__landing-folder {
  font-size: 0.95em;
  letter-spacing: 0.02em;
  word-break: break-all;
}
.seek__landing-meta {
  font-size: 0.8em;
  opacity: 0.7;
  display: flex;
  gap: 0.8em;
  flex-wrap: wrap;
}
.seek__landing-meta b { font-weight: bold; opacity: 1; }

/* Every landing row reserves space on the right for its play button.
   2.9em gives the timestamp + meta items a clear visible gap before the
   button starts (button is ~1.8em wide at right:0.7em, so its left edge
   sits at ~right:2.5em — without the 0.4em buffer the timestamp's right
   edge would butt directly against it). */
.seek__landing-row {
  padding-right: 2.9em;
}

/* Completion rows — surfaced from a just-finished TQ transfer. Subtle
   green left edge so the user can tell "you downloaded this" apart
   from "ambient peer share" without it shouting "NEW ITEM". */
.seek__landing-row--complete {
  border-left: 4px solid #00c800;
  padding-left: calc(0.7em - 4px);
}

/* Landing-list LI — relative anchor for the absolute play button. */
.seek__landing-li {
  position: relative;
}

/* Specificity bump (0,2,0) — the base .seek__play-btn rules live further
   down in this file with top:0.1em / right:0.4em, which would otherwise
   win on equal-specificity tie-break and pin the landing play button to
   the top edge of the LI (visually overlapping the header strip on the
   first row). The double-class form lets us declare the modifier early
   in the cascade where it logically belongs without losing the override. */
.seek__play-btn.seek__play-btn--landing {
  top: 0.55em;
  right: 0.7em;
  font-size: 0.95em;
  padding: 0.05em 0.55em;
}

/* ----------------------------------------------------------------------- */
/* results table — sticky thead, monospace tight rows, hover + expand */

.seek__results-wrap {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  overflow: auto;
  flex: 1;
  min-height: 0;
}
.seek__results-h {
  background: #000;
  color: #fff;
  padding: 0.15em 0.6em;
  letter-spacing: 0.06em;
  font-size: 0.95em;
}
.seek__results {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9em;
  letter-spacing: 0.02em;
}
.seek__results th,
.seek__results td {
  padding: 0.2em 0.5em;
  text-align: left;
  border-top: 1px solid #000;
  white-space: nowrap;
}
.seek__results thead th {
  background: #fff;
  border-top: 0;
  border-bottom: 2px solid #000;
  position: sticky;
  /* Sticky inside the .seek__results-wrap scroll container, so 0 sticks
     the thead to that container's top edge as the body scrolls. */
  top: 0;
  letter-spacing: 0.06em;
  text-transform: lowercase;
  font-weight: normal;
  font-size: 0.85em;
}
.seek__col-file  { width: auto; }
.seek__col-size,
.seek__col-kbps,
.seek__col-len,
.seek__col-slots,
.seek__col-speed { width: 1%; text-align: right; }
.seek__col-user  { width: 1%; }
.seek__col-act   { width: 1%; }

/* Trailing actions cell — ↓ button on each search-result row. Lets users
   queue a hit without first opening the user-pane (the only path before).
   No ▶ here: only downloaded files are playable (see seek.js buildRow).
   Note: do NOT use display:flex on the <td> — that drops it out of
   table-cell layout and the row's vertical-align: middle stops applying,
   so on multi-line rows the button would top-align. text-align:right +
   inline-block button keeps the cell a normal table-cell. */
.seek__act {
  text-align: right;
  white-space: nowrap;
  vertical-align: middle;
}
.seek__act .seek__dl-btn {
  position: static;
  top: auto;
  right: auto;
  display: inline-block;
  vertical-align: middle;
}

.seek__results tbody tr {
  cursor: pointer;
}
.seek__results tbody tr:hover { background: #00c800; }
.seek__results tbody tr.is-flac td { background: rgba(0, 200, 0, 0.06); }
.seek__results tbody tr.is-flac:hover td { background: #00c800; }
.seek__results tbody tr.is-low td { opacity: 0.78; }

.seek__results td.seek__file {
  word-break: break-all;
  white-space: normal;
  max-width: 30em;
}
.seek__results td.seek__user a,
.seek__results td.seek__user a:visited {
  color: #000;
  text-decoration: none;
  border-bottom: 1px dotted #000;
}
.seek__results td.seek__user a:hover {
  background: #000; color: #fff; border-bottom-color: transparent;
}

.seek__empty {
  margin: 0;
  padding: 0.6em 0.8em;
  font-size: 0.95em;
  opacity: 0.8;
  border-top: 2px solid #000;
}

/* ----------------------------------------------------------------------- */
/* BROWSE panel — formerly a slide-in pane, now a regular tab panel that
   fills the panel width. Empty state shows a hint until a user is
   loaded; populating clears .is-empty on the panel and reveals the
   pane. The pane itself keeps the same internal structure (header,
   stats, file tree) so paint code in seek.js is unchanged. */

.seek__browse-empty {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  margin: 0;
  padding: 1.2em 1em;
  text-align: center;
  font-size: 0.95em;
  opacity: 0.75;
  letter-spacing: 0.02em;
  flex: 0 0 auto;
}
.seek__panel--browse:not(.is-empty) .seek__browse-empty { display: none; }
.seek__panel--browse.is-empty .seek__user-pane          { display: none; }

.seek__user-pane {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex: 1;
  min-height: 0;
}

.seek__user-hdr {
  background: #fff;
  border-bottom: 2px solid #000;
  padding: 0.5em 0.7em;
}
.seek__user-hdr-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5em;
}
.seek__user-name {
  margin: 0;
  font-size: 1.25em;
  font-weight: normal;
  letter-spacing: 0.04em;
  word-break: break-all;
  border: 0;
}
.seek__user-name::before,
.seek__user-name::after { content: none; }
.seek__user-status {
  margin: 0.3em 0 0;
  font-size: 0.85em;
  opacity: 0.7;
  letter-spacing: 0.03em;
}
.seek__user-status::before { content: "⏵ "; opacity: 0.6; }
.seek__user-bio {
  margin: 0.4em 0 0;
  font-size: 0.9em;
  line-height: 1.25;
}
.seek__user-stats {
  list-style: none;
  margin: 0.4em 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em 0.8em;
  font-size: 0.8em;
  opacity: 0.7;
  letter-spacing: 0.04em;
}
.seek__user-stats li::before { content: none; }
.seek__user-stats li { padding: 0; }
.seek__user-stats b { font-weight: bold; opacity: 1; }

/* "sharing with: a, b, c" — subtle peer list under the stats row. Reads
   as Slsk's user-info pane peer-list. Each peer is a link that opens
   that user's pane (so the pane chains, just like browsing in real Slsk). */
.seek__user-shared {
  margin: 0.5em 0 0;
  font-size: 0.8em;
  letter-spacing: 0.03em;
  opacity: 0.7;
  line-height: 1.3;
  word-break: break-word;
}
.seek__user-shared[hidden] { display: none; }
.seek__user-shared-peer,
.seek__user-shared-peer:visited {
  color: #000;
  text-decoration: none;
  border-bottom: 1px dotted #000;
}
.seek__user-shared-peer:hover,
.seek__user-shared-peer:focus-visible {
  background: #000;
  color: #fff;
  border-bottom-color: transparent;
  outline: none;
}

.seek__user-tree {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  font-size: 0.85em;
}
.seek__user-tree > li::before { content: none; }
.seek__user-tree > li { padding: 0; border-top: 1px solid #000; }
.seek__user-tree > li:first-child { border-top: 0; }
.seek__user-folder {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  background: #fff;
  color: #000;
  border: 0;
  padding: 0.25em 0.6em;
  cursor: pointer;
  letter-spacing: 0.02em;
  word-break: break-all;
}
.seek__user-folder:hover { background: #00c800; }
.seek__user-folder::before { content: "▸ "; opacity: 0.6; }
.seek__user-folder.is-open::before { content: "▾ "; opacity: 0.9; }
.seek__user-files {
  list-style: none;
  margin: 0;
  padding: 0;
  background: #f7fff7;
  border-top: 1px solid #000;
}
.seek__user-files li::before { content: none; }
.seek__user-files li {
  padding: 0.15em 0.6em 0.15em 1.7em;
  border-top: 1px dotted #000;
  font-size: 0.95em;
  word-break: break-all;
}
.seek__user-files li:first-child { border-top: 0; }
.seek__user-files li .seek__file-meta {
  margin-left: 0.5em;
  font-size: 0.8em;
  opacity: 0.6;
  white-space: nowrap;
}

/* ↓ download buttons in the user-pane file/folder rows. Always-visible
   primary action — this is what the browse pane is FOR. The folder
   variant overlays the folder header (sibling positioning since we
   can't nest <button>s inside the folder toggle). */
.seek__user-files li {
  position: relative;
  padding-right: 2.4em;
}
.seek__dl-btn {
  position: absolute;
  font: inherit;
  background: transparent;
  color: #000;
  border: 1px solid #000;
  padding: 0 0.45em;
  line-height: 1.2;
  cursor: pointer;
  z-index: 2;
  top: 0.05em;
  right: 0.4em;
}
.seek__dl-btn:hover,
.seek__dl-btn:focus-visible {
  background: #000;
  color: #00c800;
  outline: none;
}
.seek__dl-btn:active {
  background: #00c800; color: #000;
  transform: translate(1px, 1px);
}

/* Folder-header variant. The user-pane LI containing a folder is also
   relative; the dl button anchors top-right of the folder row. */
.seek__user-tree > li {
  position: relative;
}
.seek__user-tree > li > .seek__user-folder {
  padding-right: 2.4em;       /* room for the absolute dl btn */
}
.seek__dl-btn--folder {
  top: 0.2em;
  right: 0.4em;
  font-size: 0.95em;
  padding: 0.05em 0.55em;
}

/* Play button — used on landing-feed file rows (folder-aggregate row
   expansion, and completion rows). Anchored top-right of its container.
   Always visible (play is the primary action on the discovery feed,
   unlike the browse pane where it'd just be one more option). */
.seek__play-btn {
  position: absolute;
  top: 0.1em;
  right: 0.4em;
  font: inherit;
  background: transparent;
  color: #000;
  border: 1px solid #000;
  padding: 0 0.45em;
  line-height: 1.2;
  cursor: pointer;
  z-index: 2;
}
.seek__play-btn:hover,
.seek__play-btn:focus-visible {
  background: #000;
  color: #00c800;
  outline: none;
}
.seek__play-btn:active {
  background: #00c800; color: #000;
  transform: translate(1px, 1px);
}
/* Brief flash on the thing you arrived in via.
   File-level — search-result row click; flashes the matching <li>.
   Folder-level — landing-list row click; flashes the folder header button.
   seek.js adds .is-highlight on render and strips it ~1.8s later; the
   transition on the host element handles the fade-out. */
.seek__user-files li.is-highlight,
.seek__user-folder.is-highlight {
  background: #ffff00;
  outline: 2px solid #000;
  outline-offset: -2px;
}
.seek__user-files li,
.seek__user-folder {
  transition: background 0.8s ease-out;
}
/* The folder button has its own :hover (page green); when both .is-open
   and .is-highlight apply the highlight should win for the brief flash
   window — yellow stays put, hover green doesn't override mid-flash. */
.seek__user-folder.is-highlight:hover { background: #ffff00; }


/* ----------------------------------------------------------------------- */
/* embedded audio player — always-on slim horizontal strip in the page
   chrome, between the status row and the tab strip. Empty state ("—
   OFF —") shows until the first ▶ click on a downloads row pushes a
   track via playInRadio(). Internal markup is the standard radio.css
   contract — we override the outer positioning to fit the in-flow
   chrome and the .radio__body's default vertical stack to a single
   horizontal row. */

.seek-page .seek__radio {
  /* In-flow flex item: doesn't grow, sits between status row and tabs. */
  position: static;
  width: 100%;
  max-width: none;
  margin: 0 0 0.6em;
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  flex: 0 0 auto;
}

/* Slim-bar body — single row instead of the default radio column. */
.seek-page .seek__radio .radio__body {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.6em;
  padding: 0.35em 0.55em;
}

/* Compact scope canvas — small visual identity element, doesn't need
   to be the focal point in the chrome strip. */
.seek-page .seek__radio .radio__scope-wrap {
  flex: 0 0 auto;
  width: 56px;
}
.seek-page .seek__radio .radio__scope {
  height: 22px;
}
.seek-page .seek__radio .radio__scope-loader {
  font-size: 11px;
  letter-spacing: 0.08em;
}

/* Track text and seek bar share the middle. Track ellipsizes; seek
   takes the larger share since position is the more glanceable info. */
.seek-page .seek__radio .radio__track {
  flex: 1 1 0;
  min-width: 0;
  font-size: 0.85em;
  letter-spacing: 0.03em;
}
.seek-page .seek__radio .radio__seek {
  flex: 2 1 0;
  min-width: 7em;
}
.seek-page .seek__radio .radio__row {
  flex: 0 0 auto;
}

/* Mobile (≤700px) — when narrow, the track/seek can get crushed. Keep
   the row layout but trim the scope and let the seek bar dominate.
   The unified-block layout already handles the rest of mobile:
   - <main> is viewport-locked and the active panel scrolls internally.
   - The TRANSFERS split collapses to a single column at ≤700px and the
     panel takes over scrolling so downloads + queue stack into one
     scrollable column. */
@media (max-width: 700px) {
  .seek__panel { overflow-y: auto; }
  .seek__landing,
  .seek-tq {
    overflow: visible;
    min-height: 0;
  }
  .seek__landing-list,
  .seek-tq__list {
    overflow: visible;
    flex: 0 0 auto;
  }
  .seek-page .seek__radio .radio__scope-wrap { width: 40px; }
  .seek-page .seek__radio .radio__track      { display: none; }
}

/* ----------------------------------------------------------------------- */
/* transfer queue — right half of the TRANSFERS split-pane. User-driven:
   each ↓ click on a search row, file row, or folder header pushes
   transfers onto the queue. State machine in seek.js cycles them
   through queued → transferring → (complete | failed) → removed.
   Bars animate continuously between ticks via the transition on
   .seek-tq__bar-fill width.
   Mirror of .seek__landing's flex/overflow setup so the two halves of
   the TRANSFERS split scroll independently and stay vertically aligned. */

.seek-tq {
  background: #fff;
  border: 2px solid #000;
  box-shadow: 4px 4px 0 #000;
  font-size: 0.85em;
  letter-spacing: 0.02em;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.seek-tq__bar {
  background: #000;
  color: #fff;
  padding: 0.1em 0.5em;
  letter-spacing: 0.06em;
  font-size: 0.95em;
}
.seek-tq__list {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}
.seek-tq__empty {
  margin: 0;
  padding: 0.4em 0.55em 0.5em;
  font-size: 0.85em;
  opacity: 0.65;
  text-align: center;
  letter-spacing: 0.02em;
}
.seek-tq__empty[hidden] { display: none; }
.seek-tq__list li::before { content: none; }
.seek-tq__row {
  padding: 0.25em 0.55em 0.3em;
  border-top: 1px solid #000;
}
.seek-tq__row:first-child { border-top: 0; }
.seek-tq__file {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.95em;
}
.seek-tq__bar-track {
  height: 0.5em;
  background: #000;
  margin: 0.2em 0 0.15em;
  position: relative;
  overflow: hidden;
}
.seek-tq__bar-fill {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  background: #00c800;
  width: 0%;
  /* Smooth between discrete ticks. Slightly under the tick interval so
     the bar reaches its target before the next tick redraws. */
  transition: width 1.3s linear;
}
.seek-tq__row--queued      .seek-tq__bar-fill { background: #404040; }
.seek-tq__row--stalled     .seek-tq__bar-fill { background: #a04040; }
.seek-tq__row--complete    .seek-tq__bar-fill { background: #00c800; }
.seek-tq__row--failed      .seek-tq__bar-fill { background: #800000; }
.seek-tq__row--complete    .seek-tq__file,
.seek-tq__row--failed      .seek-tq__file { opacity: 0.55; }

.seek-tq__meta {
  display: flex;
  justify-content: space-between;
  font-size: 0.85em;
  opacity: 0.75;
  white-space: nowrap;
  overflow: hidden;
}
.seek-tq__meta-l,
.seek-tq__meta-r {
  overflow: hidden;
  text-overflow: ellipsis;
}
.seek-tq__meta-r { text-align: right; flex-shrink: 0; }

/* ----------------------------------------------------------------------- */
/* ambient ticker — fixed bottom band, single line, cross-fade on text swap */

.seek__ticker {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: #000;
  color: #00c800;
  padding: 0.15em 0.8em;
  z-index: 90;
  font-family: vtFont, "Courier New", monospace;
  font-size: 0.85em;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-top: 2px solid #000;
}
.seek__ticker::before {
  content: "» ";
  opacity: 0.6;
}
.seek__ticker.is-fading {
  opacity: 0;
  transition: opacity 0.25s ease-out;
}
.seek__ticker:not(.is-fading) {
  opacity: 1;
  transition: opacity 0.25s ease-in;
}

/* ----------------------------------------------------------------------- */
/* mobile — small results re-flow as stacked cards. The native table grid
   is fine on tablet+, but at <540px column widths get crushed. Convert
   each tr to a block, hide thead, prefix each cell with its label. */

@media (max-width: 540px) {
  .seek__results, .seek__results tbody, .seek__results tr,
  .seek__results td, .seek__results th { display: block; width: auto; }
  .seek__results thead { position: absolute; left: -9999px; }
  .seek__results tbody tr {
    border-top: 2px solid #000;
    padding: 0.35em 0.6em;
  }
  .seek__results tbody tr:first-child { border-top: 0; }
  .seek__results td {
    border-top: 0;
    padding: 0;
    text-align: left;
    white-space: normal;
    font-size: 0.95em;
  }
  .seek__results td.seek__file {
    word-break: break-all;
    margin-bottom: 0.1em;
    font-size: 1em;
  }
  .seek__results td:not(.seek__file):not(.seek__act) {
    display: inline;
    font-size: 0.8em;
    opacity: 0.75;
  }
  .seek__results td:not(.seek__file):not(.seek__act)::before {
    content: attr(data-label) ": ";
    opacity: 0.6;
  }
  .seek__results td:not(.seek__file):not(.seek__act) + td:not(.seek__file):not(.seek__act)::before {
    content: " · " attr(data-label) ": ";
  }
  /* Action cell on mobile — its own row at the bottom of each card. The
     buttons stay full-opacity (they're the call to action, not metadata)
     and right-align like in the desktop table. */
  .seek__results td.seek__act {
    margin-top: 0.3em;
    opacity: 1;
  }
}
