/* ═══════════════════════════════════════════════════════════════
   Page-specific patterns
   Matches · Competitions · Channels · Geo · News
   ═══════════════════════════════════════════════════════════════ */

/* ══════════ MATCHES LIST ══════════ */

.filter-bar { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }

.date-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-surface);
}

.date-display {
  min-width: 190px;
  text-align: center;
  padding: 0 var(--s-3);
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

/* competition group */
.competition-card {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-bottom: var(--s-4);
}

.competition-header {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line);
}

.competition-logo { width: 20px; height: 20px; object-fit: contain; flex-shrink: 0; }

.competition-title {
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  flex: 1;
  min-width: 0;
}

/* one match row */
.match-item {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line-subtle);
  transition: background var(--fast);
}

.match-item:last-child { border-bottom: 0; }
.match-item:hover { background: var(--bg-raised); }
.match-item.is-live { box-shadow: inset 3px 0 0 var(--live); }

/* teams block: home | score | away, home first = right in RTL */
.match-teams {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}

.match-team { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
.match-team.away { flex-direction: row-reverse; }

.team-name {
  font-size: var(--t-sm);
  font-weight: var(--fw-med);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.team-logo { width: 22px; height: 22px; object-fit: contain; flex-shrink: 0; }

.match-score {
  min-width: 54px;
  text-align: center;
  padding: 3px 10px;
  border-radius: var(--r-sm);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-family: var(--font-num);
  font-size: var(--t-sm);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}

.match-status-area { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.match-actions { display: flex; gap: 6px; }

@media (max-width: 768px) {
  .match-item { grid-template-columns: 1fr; gap: var(--s-3); }
  .match-status-area { flex-direction: row; align-items: center; }
}

/* ══════════ COMPETITIONS ══════════
   Accordion of country cards, each holding competition rows with a toggle.
   These class names come from competitions.js — they previously lived only in
   the retired components.css, which is why this page lost all its styling
   (and rendered the collapse arrow at full SVG size). */

.country-card {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-3);
  overflow: hidden;
  transition: border-color var(--fast);
}

.country-card:hover { border-color: var(--line-strong); }
.country-card.open { border-color: var(--accent-line); }

.country-card-header {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  cursor: pointer;
  user-select: none;
  transition: background var(--fast);
}

.country-card-header:hover { background: var(--bg-raised); }

.country-card.open .country-card-header {
  background: var(--accent-soft);
  border-bottom: 1px solid var(--line);
}

.country-card-flag { font-size: 1.35rem; line-height: 1; flex-shrink: 0; }

.country-card-info { flex: 1; min-width: 0; }

.country-card-name {
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--tx);
}

.country-card-meta {
  font-size: var(--t-xs);
  color: var(--tx-3);
  margin-top: 1px;
}

.country-card-actions {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-shrink: 0;
}

/* The arrow is an inline SVG: without an explicit size it fills the card. */
.country-card-arrow {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--tx-3);
  transition: transform var(--base), color var(--fast);
}

.country-card.open .country-card-arrow { transform: rotate(180deg); color: var(--accent); }

.country-card-body { display: none; flex-direction: column; }
.country-card.open .country-card-body { display: flex; }

/* one competition inside a country card */
.competition-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line-subtle);
  transition: background var(--fast);
}

.competition-row:last-child { border-bottom: 0; }
.competition-row:hover { background: var(--bg-raised); }

.competition-info {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}

.competition-name {
  font-size: var(--t-sm);
  font-weight: var(--fw-med);
  color: var(--tx);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.competition-country { font-size: var(--t-xs); color: var(--tx-3); }

/* ── toggle switch (enabled / disabled) ── */
.toggle, .toggle-switch {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 22px;
  flex-shrink: 0;
}

.toggle input, .toggle-switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-slider {
  position: absolute;
  inset: 0;
  cursor: pointer;
  border-radius: var(--r-full);
  background: var(--bg-overlay);
  border: 1px solid var(--line-strong);
  transition: background var(--fast), border-color var(--fast);
}

.toggle-slider::before {
  content: '';
  position: absolute;
  top: 2px;
  right: 2px;
  width: 16px;
  height: 16px;
  border-radius: var(--r-full);
  background: var(--tx-3);
  transition: transform var(--fast), background var(--fast);
}

input:checked + .toggle-slider { background: var(--ok); border-color: var(--ok); }

/* RTL: the knob starts on the right and slides left when on */
input:checked + .toggle-slider::before { background: #fff; transform: translateX(-18px); }

/* display order list */
/* Two columns: 22 enabled competitions in a single column ran taller than the
   viewport. `grid-auto-flow: column` fills TOP-TO-BOTTOM — the first column
   holds ranks 1..n and the second continues from there, so each column reads
   as a continuous run. Row-flow would interleave them (1,2 / 3,4), which does
   not match how a ranked list is read. The row count is set inline per render
   from ceil(items / 2). */
.ord-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 6px var(--s-3);
  align-items: start;
}

.ord-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  min-width: 0;
  cursor: grab;
  transition: all var(--fast);
}

.ord-row:hover { border-color: var(--accent-line); }
.ord-row:active { cursor: grabbing; }
.ord-row.dragging { opacity: 0.4; }

.ord-row.over {
  border-color: var(--accent);
  box-shadow: inset 0 2px 0 var(--accent);
}

.ord-handle { color: var(--tx-3); letter-spacing: -2px; user-select: none; flex-shrink: 0; }

.ord-num {
  display: grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-family: var(--font-num);
  font-size: var(--t-xs);
  font-weight: var(--fw-bold);
  flex-shrink: 0;
}

.ord-logo { width: 22px; height: 22px; object-fit: contain; flex-shrink: 0; }

.ord-name {
  flex: 1;
  min-width: 0;
  font-size: var(--t-sm);
  font-weight: var(--fw-med);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Half-width rows leave little room, so the country is allowed to truncate
   before the name does rather than pushing the buttons out of the row. */
.ord-country {
  font-size: var(--t-xs);
  color: var(--tx-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 68px;
  flex-shrink: 1;
}

.ord-btns { display: flex; gap: 2px; flex-shrink: 0; }
.ord-btns .icon-btn { width: 26px; height: 26px; font-size: 11px; }

/* One column once a half-width row can no longer hold name + country + buttons.
   Back to row flow, and the inline grid-template-rows must be overridden or
   column flow would keep splitting the list. */
@media (max-width: 900px) {
  .ord-grid {
    grid-auto-flow: row;
    grid-template-rows: none !important;
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 640px) { .ord-country { display: none; } }

/* ══════════ STREAMS PAGE SHELL ══════════
   The page used to open with four stacked full-width blocks — title row, two
   settings panels and a tip note — before a single channel appeared. That is
   what pushed the content into the middle of the screen and left the empty
   band underneath. Everything above the list is now one toolbar plus two
   compact chips. */

.st-bar {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  margin-bottom: var(--s-3);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.st-lead { display: flex; align-items: baseline; gap: var(--s-3); min-width: 0; }
.st-lead h3 { font-size: var(--t-lg); font-weight: var(--fw-bold); letter-spacing: -0.01em; }

.st-count {
  font-size: var(--t-xs);
  color: var(--tx-3);
  white-space: nowrap;
}

.st-tools {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}

.st-tools .form-input { width: 190px; }
.st-tools .form-select { width: 140px; }

/* settings chips — sized to their content, not stretched across the row */
.st-settings {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}

.st-chip {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
  text-align: start;
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
}

.st-chip:hover { border-color: var(--line-strong); }
.st-chip.open { border-color: var(--accent); background: var(--accent-soft); }

.st-chip-i {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--r-md);
  background: var(--bg-raised);
  font-size: 1rem;
  flex-shrink: 0;
}

.st-chip-t { flex: 1; min-width: 0; }
.st-chip-t b { display: block; font-size: var(--t-sm); font-weight: var(--fw-semi); color: var(--tx); }

/* The status is a small pill sized to its text — as a full-width block it
   stretched into a wide coloured band across the chip. */
.st-chip-t small {
  display: inline-block;
  margin-top: 3px;
  padding: 1px 8px;
  border-radius: var(--r-full);
  background: var(--bg-raised);
  font-size: var(--t-xs);
  color: var(--tx-3);
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* State is carried by the icon tile and the status pill */
.st-chip.on    .st-chip-i { background: var(--ok-soft); color: var(--ok); }
.st-chip.on    .st-chip-t small {
  background: var(--ok-soft); color: var(--ok); font-weight: var(--fw-semi);
}
.st-chip.alert .st-chip-i { background: var(--warn-soft); color: var(--warn); }
.st-chip.alert .st-chip-t small {
  background: var(--warn-soft); color: var(--warn); font-weight: var(--fw-semi);
}

/* The expandable panels are driven by the chips above them */
.st-panel { margin-bottom: var(--s-4); }
.st-panel:not([open]) { display: none; }

/* ══════════ CHANNELS ══════════ */

/* Cards in a responsive grid, not full-width rows stacked down the page.
   A channel is an object with an identity (logo, name) and a few options —
   a tile suits that far better than a wide bar whose content hugs one edge. */
/* Wide cards. Squeezing three into a row shrank every element inside them;
   two generous cards read far better and match the intended proportions. */
.ch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
  gap: var(--s-4);
  align-items: start;
}

.ch-card {
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  /* NOT overflow:hidden — that would clip the ⋯ dropdown. The hero gradient is
     clipped by its own radius instead. */
  transition: border-color var(--fast), box-shadow var(--fast), transform var(--fast);
}

/* A card with its menu open must sit above its neighbours */
.ch-card:has(.ch-menu.open) { z-index: 5; }

.ch-card:hover {
  border-color: var(--accent-line);
  box-shadow: var(--sh-md);
  transform: translateY(-2px);
}

/* A disabled channel is dimmed but still legible */
.ch-card.off { opacity: 0.7; }
.ch-card.off .ch-logo { filter: grayscale(1); }

/* Identity block — the logo leads, centred, on a textured ground */
.ch-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-3);
  padding: 34px var(--s-4) 22px;
  background: var(--bg-raised);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

/* Dot grid in the corner + a soft wave — texture, not decoration for its own
   sake: it separates the identity area from the plain panel underneath. */
/* Square dot block in the top-right corner (top-left visually is where the
   rank/menu controls live, so the texture takes the opposite corner). */
/* Dot block on the LEFT — physical, not logical: the reference places it on
   the visual left, and inset-inline-end mirrors to the wrong side under RTL. */
.ch-hero::before {
  content: '';
  position: absolute;
  top: 26px;
  left: 26px;
  width: 96px;
  height: 96px;
  background-image: radial-gradient(circle, currentColor 1.6px, transparent 1.6px);
  background-size: 13px 13px;
  color: var(--tx-3);
  opacity: 0.3;
  pointer-events: none;
}

/* Flowing curves across the hero, drawn as an inline SVG so no asset is needed.
   currentColor picks up the theme, keeping it visible in dark mode too. */
.ch-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  color: var(--accent);
  background:
    radial-gradient(130% 80% at 50% -10%, var(--accent-soft), transparent 62%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 200' preserveAspectRatio='none'%3E%3Cpath d='M0 120 C 90 60 150 150 260 95 S 360 60 400 100' fill='none' stroke='%236b62d6' stroke-width='1.4' opacity='.18'/%3E%3Cpath d='M0 150 C 100 100 170 175 280 125 S 370 95 400 130' fill='none' stroke='%236b62d6' stroke-width='1.2' opacity='.12'/%3E%3C/svg%3E");
  background-size: auto, 100% 100%;
  background-repeat: no-repeat;
  pointer-events: none;
}

/* Circular logo with a thick white ring and a halo behind it */
.ch-logo {
  position: relative;
  z-index: 1;
  width: 84px;
  height: 84px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--bg-surface);
  border: 5px solid var(--bg-surface);
  box-shadow: 0 0 0 9px color-mix(in srgb, var(--tx) 3.5%, transparent),
              var(--sh-md);
  font-size: 1.7rem;
  overflow: hidden;
  flex-shrink: 0;
}

.ch-logo img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-full); }

/* Rank and menu float over the card corners */
/* Rank and menu sit together at the top-left, as one pair of square controls */
.ch-rank {
  position: absolute;
  top: 12px;
  inset-inline-start: 46px;
  z-index: 2;
  min-width: 30px;
  height: 30px;
  border-radius: 9px;
  background: #6d4aff;
  font-size: var(--t-xs);
  box-shadow: 0 3px 9px rgba(109, 74, 255, 0.28);
}

.ch-more {
  position: absolute;
  top: 12px;
  inset-inline-start: 12px;
  z-index: 3;
}

.ch-more > .icon-btn {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: var(--bg-surface);
  border: 0;
  color: var(--tx-2);
  font-size: 0.95rem;
  box-shadow: var(--sh-sm);
}

.ch-name {
  position: relative;
  z-index: 1;
  font-size: var(--t-lg);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
  line-height: 1.25;
}

.ch-off-tag {
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: var(--bg-sunken);
  color: var(--tx-3);
  font-size: 0.625rem;
  font-weight: var(--fw-semi);
  white-space: nowrap;
}

/* One row of small facts. Separators come from the pill shapes, not from
   stray "·" characters glued to the text. */
.ch-meta {
  position: relative;
  z-index: 1;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* Facts read as raised white pills against the textured hero */
.ch-meta .ch-bit,
.ch-meta .ch-flag,
.ch-meta .ch-off-tag {
  padding: 4px 11px;
  border-radius: var(--r-full);
  background: var(--bg-surface);
  border: 0;
  box-shadow: 0 2px 6px rgba(16, 24, 40, 0.06);
  font-size: var(--t-xs);
  font-weight: var(--fw-med);
  color: var(--tx-2);
}

.ch-meta .ch-flag { color: var(--accent); }

.ch-bit {
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  white-space: nowrap;
}

/* Library position — this is the order the app shows channels in.
   Solid (not tinted) because it floats over the gradient hero. */
/* NOTE: sizing/colour live in the .ch-rank rule further up. A second full rule
   used to sit here and silently reset both back to a 24px blue chip — the badge
   appeared unchanged no matter what was edited above. Only the shared
   typography stays here. */
.ch-rank {
  display: grid;
  place-items: center;
  border: 0;
  color: #fff;
  font-family: var(--font-num);
  font-weight: var(--fw-bold);
  flex-shrink: 0;
}

/* geo / schedule / usage markers */
.ch-flag {
  padding: 1px 7px;
  border-radius: var(--r-full);
  background: var(--info-soft);
  color: var(--info);
  font-weight: var(--fw-semi);
  white-space: nowrap;
}

.ch-actions { display: flex; align-items: center; gap: var(--s-2); flex-shrink: 0; }

/* Reorder arrows stack vertically in one small control instead of sitting in
   the same row as the real actions. */
.ch-move { display: flex; flex-direction: column; gap: 2px; }
.ch-move .icon-btn { width: 24px; height: 20px; font-size: 9px; opacity: 0.6; }
.ch-move .icon-btn:hover:not(:disabled) { opacity: 1; }
.ch-move .icon-btn:disabled { opacity: 0.2; }

/* overflow menu — .ch-more is positioned in the rule further up; redeclaring
   `position: relative` here used to override it and drop the button back into
   the flow. */
.ch-menu {
  position: absolute;
  top: calc(100% + 6px);
  /* Anchored to the same edge as its trigger, which now sits top-left */
  inset-inline-start: 0;
  z-index: 20;
  display: none;
  flex-direction: column;
  min-width: 168px;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-overlay);
  box-shadow: var(--sh-lg);
}

.ch-menu.open { display: flex; }

.ch-menu button {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--tx);
  font: inherit;
  font-size: var(--t-sm);
  text-align: start;
  white-space: nowrap;
  cursor: pointer;
}

.ch-menu button:hover:not(:disabled) { background: var(--bg-raised); }
.ch-menu button:disabled { opacity: 0.35; cursor: default; }
.ch-menu button.danger { color: var(--danger); }
.ch-menu button.danger:hover { background: var(--danger-soft); }

/* (The trigger's appearance is set in the .ch-more > .icon-btn rule above;
   a second copy here re-added a border and undid the styling.) */

.ch-count {
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  white-space: nowrap;
}

.ch-servers {
  margin-top: auto;              /* pins the panel to the card bottom */
  padding: var(--s-3) var(--s-4) var(--s-4);
}

/* An empty channel offers the fix instead of only stating the problem */
.ch-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-5);
  text-align: center;
  font-size: var(--t-sm);
  color: var(--tx-3);
}

.srv-acts { display: flex; align-items: center; gap: var(--s-2); flex-shrink: 0; }

/* ── quality tabs ──
   A channel's servers are the same stream at different qualities, so they read
   as one thing with variants — not as N identical stacked rows. This mirrors
   the tab pattern already used on the match-details page (underline on the
   active item, muted otherwise). */
/* Quality chips — pick one, its panel shows below */
.srv-tabs {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}

.srv-tab {
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--bg-raised);
  color: var(--tx-2);
  font-family: var(--font-num);
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  cursor: pointer;
  transition: all var(--fast);
}

.srv-tab:hover { border-color: var(--accent-line); color: var(--tx); }

.srv-tab.on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* ── server panel: stacked, not a horizontal strip ── */
.srv { min-width: 0; }

/* Only the selected quality's panel is shown */
.ch-servers.multi .srv { display: none; }
.ch-servers.multi .srv.on { display: block; }

.srv.off { opacity: 0.6; }

/* Type on one edge, test result on the other — they are different kinds of
   information and reading them apart is faster than scanning one clump. */
.srv-tags {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}

.srv-tags .srv-status { margin-inline-start: auto; }

/* URL field — bordered, with its copy action inside it */
.srv-link {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 5px 5px 5px var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-surface);
  min-width: 0;
}

.srv-link-ico { font-size: 0.8rem; opacity: 0.55; flex-shrink: 0; }

.srv-copy {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-raised);
  color: var(--tx-2);
  font-size: 0.9rem;
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--fast), color var(--fast);
}

.srv-copy:hover { background: var(--accent-soft); color: var(--accent); }

/* Actions sit on their own line under the URL */
.srv-foot {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-3);
}

/* The primary action — deliberately large; it is what the card is for */
.srv-play {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  height: 42px;
  border: 0;
  border-radius: var(--r-md);
  /* Explicit violet ramp, not derived from --accent: the reference's button is
     noticeably deeper than the dashboard's default blue. */
  background: linear-gradient(100deg, #7f5bff 0%, #6d3aec 55%, #5b21d6 100%);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
  box-shadow: 0 5px 14px rgba(109, 58, 236, 0.28);
  transition: filter var(--fast), transform var(--fast), box-shadow var(--fast);
}

.srv-play:hover {
  filter: brightness(1.06);
  box-shadow: 0 10px 26px rgba(109, 58, 236, 0.42);
}

.srv-play:active { transform: scale(0.99); }
.srv-play-ico { font-size: 0.8rem; }

.srv-foot .icon-btn {
  width: 42px;
  height: 42px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-surface);
  font-size: 0.95rem;
}

/* Distinct tints so the two secondary actions are told apart at a glance
   rather than reading as one grey pair. */
.srv-foot .icon-btn.tone-violet:hover { background: var(--accent-soft); border-color: var(--accent-line); }
.srv-foot .icon-btn.tone-orange:hover { background: var(--warn-soft); border-color: transparent; }

.srv-dim { color: var(--tx-3); font-weight: var(--fw-med); }

/* Resolved source id / required headers, shown inline on the server row */
.srv-tag {
  padding: 1px 6px;
  border-radius: var(--r-sm);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-family: var(--font-num);
  font-size: 0.625rem;
  font-weight: var(--fw-med);
  color: var(--tx-3);
  direction: ltr;
}

.srv-tag.ok  { background: var(--ok-soft);     border-color: transparent; color: var(--ok); }
.srv-tag.bad { background: var(--danger-soft); border-color: transparent; color: var(--danger); }

/* Live feedback under the URL field while typing a YouTube/Twitch link */
.srv-resolved {
  margin-top: 6px;
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.srv-resolved.ok  { color: var(--ok); }
.srv-resolved.bad { color: var(--danger); }
.srv-resolved code {
  font-family: var(--font-num);
  background: var(--bg-app);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 5px;
}

/* Reference detail. Kept to one truncated line: a card is narrow, and a long
   token-laden URL would otherwise wrap to six lines and dwarf everything else. */
.srv-url {
  display: block;
  flex: 1;
  min-width: 0;
  font-family: var(--font-num);
  font-size: 0.6875rem;
  color: var(--tx-3);
  direction: ltr;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.srv-type {
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--bg-surface);
  font-size: var(--t-xs);
  font-weight: var(--fw-med);
  color: var(--tx-2);
  white-space: nowrap;
  flex-shrink: 0;
}

/* Test result sits inline with the other tags. It used to be a fixed 96px
   column rendering "—" on every untested row, which added a dead gap to each
   line; an empty result now takes no space at all. */
.srv-status {
  padding: 3px 9px;
  border-radius: var(--r-full);
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  white-space: nowrap;
  text-align: center;
}

/* An untested row shows nothing, but the column keeps its place so the buttons
   do not shift sideways the moment a test finishes. */
.srv-status:empty { visibility: hidden; }

.srv-status.testing { background: var(--bg-sunken);    color: var(--tx-2); }
.srv-status.ok      { background: var(--ok-soft);      color: var(--ok); }
.srv-status.warn    { background: var(--warn-soft);    color: var(--warn); }
.srv-status.bad     { background: var(--danger-soft);  color: var(--danger); }

/* server editor in the modal */
.srv-edit {
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  margin-bottom: var(--s-3);
}

.srv-edit-top {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding-bottom: var(--s-2);
  margin-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
}

.srv-edit-n {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-family: var(--font-num);
  font-size: var(--t-xs);
  font-weight: var(--fw-bold);
}

.srv-edit-t { font-size: var(--t-sm); font-weight: var(--fw-semi); }
.srv-edit-acts { margin-inline-start: auto; display: flex; align-items: center; gap: var(--s-2); }
.srv-edit-grid { display: grid; grid-template-columns: 120px 160px 1fr; gap: var(--s-3); }

@media (max-width: 640px) {
  .srv-edit-grid { grid-template-columns: 1fr 1fr; }
  .srv-edit-grid > :last-child { grid-column: 1 / -1; }
  .srv { flex-wrap: wrap; }
  .srv-status { min-width: 0; }
}

/* channel picker (link to match) */
.ch-pick-list { display: flex; flex-direction: column; gap: var(--s-2); }

.ch-pick {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  cursor: pointer;
  transition: all var(--fast);
}

.ch-pick:hover { border-color: var(--accent-line); }
.ch-pick.on { border-color: var(--accent); background: var(--accent-soft); }

/* video preview verdict */
.pv-note { margin-top: var(--s-3); font-size: var(--t-sm); font-weight: var(--fw-semi); }
.pv-note.testing { color: var(--tx-2); }
.pv-note.ok  { color: var(--ok); }
.pv-note.bad { color: var(--danger); }
.pv-info { font-size: var(--t-xs); color: var(--tx-3); margin-top: 4px; line-height: 1.7; }

/* ══════════ GEO ══════════ */

.geo-box {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-4);
  overflow: hidden;
}

.geo-sum {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  cursor: pointer;
  list-style: none;
  user-select: none;
  font-size: var(--t-sm);
}

.geo-sum::-webkit-details-marker { display: none; }
.geo-sum::marker { content: ''; }
.geo-sum:hover { background: var(--bg-raised); }

.geo-state {
  margin-inline-start: auto;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--bg-raised);
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  color: var(--tx-3);
  white-space: nowrap;
}

.geo-state.on   { background: var(--ok-soft);   border-color: transparent; color: var(--ok); }
.geo-state.warn { background: var(--warn-soft); border-color: transparent; color: var(--warn); }

.geo-caret { color: var(--tx-3); transition: transform var(--fast); }
.geo-box[open] .geo-caret { transform: rotate(180deg); }

.geo-body { padding: var(--s-4); border-top: 1px solid var(--line); }

/* ══════════ SWITCH ROWS ══════════
   These lived only in the retired tables.css, which is in .assetsignore and
   never served — so the label, the description and the checkbox all collapsed
   into one unspaced line. Defined here, in a stylesheet that actually loads. */

.geo-switch {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
}

.geo-switch:hover { border-color: var(--accent-line); }

/* A checked row is visibly "on" without reading the text */
.geo-switch:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.geo-switch > input {
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  accent-color: var(--accent);
  flex-shrink: 0;
  cursor: pointer;
}

/* min-width:0 lets long Arabic descriptions wrap instead of stretching the row */
.geo-switch-t { flex: 1; min-width: 0; }

.geo-switch-t b {
  display: block;
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--tx);
  line-height: 1.5;
}

.geo-switch-t small {
  display: block;
  margin-top: 4px;
  font-size: var(--t-xs);
  color: var(--tx-3);
  line-height: 1.75;
}

/* A <b> inside the description is emphasis, not a new line. Without this it
   inherits the block display from `.geo-switch-t b` above and shatters the
   sentence across four lines. */
.geo-switch-t small b {
  display: inline;
  font-size: inherit;
  color: var(--tx-2);
  font-weight: var(--fw-semi);
}

/* The emergency row must read as dangerous even before it is switched on */
.geo-switch.danger:has(input:checked) {
  border-color: var(--danger);
  background: var(--danger-soft);
}

.geo-switch.danger:has(input:checked) .geo-switch-t b { color: var(--danger); }

/* ══════════ SETTINGS ROWS ══════════
   Label on one side, control on the other, hairline dividers between. The
   previous layout stacked separate bordered boxes in four different colours,
   which read as clutter rather than as a settings sheet. */

.set-list {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bg-surface);
}

.set-row {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line-subtle);
  cursor: pointer;
  transition: background var(--fast);
}

.set-row:last-child { border-bottom: 0; }
.set-row:hover { background: var(--bg-raised); }

.set-txt { flex: 1; min-width: 0; }

/* Direct child only. A <b> used for emphasis INSIDE the description also
   matched this rule and inherited display:block, so every emphasised phrase
   started its own line and shattered the sentence across five rows. */
.set-txt > b {
  display: block;
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--tx);
}

/* Emphasis within the description stays in the flow of the sentence. */
.set-txt small b {
  display: inline;
  font-weight: var(--fw-semi);
  color: var(--tx-2);
}

.set-txt small {
  display: block;
  margin-top: 3px;
  font-size: var(--t-xs);
  color: var(--tx-3);
  line-height: 1.6;
}

/* A row whose control is a grid rather than a single switch: it stacks under
   the label instead of sitting beside it, so the options get full width. */
.set-row.set-stack {
  display: block;
  cursor: default;
}

.set-row .switch { flex-shrink: 0; }
.set-input { width: 260px; max-width: 45%; flex-shrink: 0; }

/* The destructive row is tinted only once it is actually on */
.set-row.danger:has(input:checked) { background: var(--danger-soft); }
.set-row.danger:has(input:checked) .set-txt > b { color: var(--danger); }
.set-row.danger input:checked { background: var(--danger); border-color: var(--danger); }

/* Result line under the rows — text with a colour, not another box */
.set-note {
  font-size: var(--t-xs);
  line-height: 1.7;
  color: var(--tx-2);
  padding-inline-start: var(--s-3);
  border-inline-start: 2px solid var(--line-strong);
}

.set-note b { color: var(--tx); }
.set-note.ok     { border-inline-start-color: var(--ok); }
.set-note.warn   { border-inline-start-color: var(--warn); }
.set-note.danger { border-inline-start-color: var(--danger); }
.set-note.danger b { color: var(--danger); }

/* section heading inside a panel */
.set-head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
}

.set-head > b { font-size: var(--t-sm); font-weight: var(--fw-semi); }
.set-head-acts { margin-inline-start: auto; display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* One rhythm for the whole panel instead of a margin on every child */
.auth-body {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

.auth-body > .form-group { margin: 0; }

.auth-actions {
  display: flex;
  justify-content: flex-end;
  padding-top: var(--s-2);
  border-top: 1px solid var(--line);
}

/* An empty preview slot must not add a gap of its own */
#authPreview:empty { display: none; }

.geo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--s-2);
}

.geo-c {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  cursor: pointer;
  min-width: 0;
  transition: all var(--fast);
}

.geo-c:hover { border-color: var(--accent-line); }
.geo-c.on { border-color: var(--accent); background: var(--accent-soft); }

.geo-f { font-size: 1rem; flex-shrink: 0; }
.geo-n { flex: 1; min-width: 0; font-size: var(--t-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.geo-code { font-family: var(--font-num); font-size: 0.625rem; color: var(--tx-3); flex-shrink: 0; }

/* A hidden match stays listed in the dashboard (so it can be restored) but must
   read as inactive at a glance. */
.match-item.is-hidden-match {
  opacity: 0.5;
  background: repeating-linear-gradient(
    45deg, transparent, transparent 8px,
    var(--bg-raised) 8px, var(--bg-raised) 16px);
}

/* Team names open the edit dialog, so they need to look interactive */
.match-team .team-name {
  cursor: pointer;
  border-bottom: 1px dashed transparent;
  transition: color var(--fast), border-color var(--fast);
}

.match-team .team-name:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ══════════ APP MEMBERS ══════════ */

.usr {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-3);
  min-width: 0;
}

/* A banned account must read as inactive without hiding its details */
.usr.off { opacity: 0.62; border-color: var(--danger-soft); }

.usr-avatar {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: var(--fw-bold);
  font-size: var(--t-sm);
  text-transform: uppercase;
  overflow: hidden;
  flex-shrink: 0;
}

.usr-avatar img { width: 100%; height: 100%; object-fit: cover; }

.usr-main { flex: 1 1 0; min-width: 0; }

.usr-top {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}

.usr-name { font-size: var(--t-md); font-weight: var(--fw-semi); }

.usr-email {
  font-family: var(--font-num);
  font-size: var(--t-xs);
  color: var(--tx-3);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.usr-tag {
  padding: 1px 7px;
  border-radius: var(--r-full);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-size: 0.625rem;
  color: var(--tx-3);
}

/* How the member signed up — a Google account has no password to reset, so
   this distinction changes what the admin can actually do. */
.usr-prov {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 7px;
  border-radius: var(--r-full);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-size: 0.625rem;
  font-weight: var(--fw-semi);
  color: var(--tx-3);
  white-space: nowrap;
}

.usr-prov.google { background: rgba(66,133,244,.14); border-color: transparent; color: #6ba4f8; }

.usr-reason {
  margin-top: 5px;
  font-size: var(--t-xs);
  color: var(--danger);
  font-weight: var(--fw-med);
}

.usr-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: var(--t-xs);
  color: var(--tx-2);
  white-space: nowrap;
  flex-shrink: 0;
}

.usr-k { color: var(--tx-3); margin-inline-end: 4px; }

.usr-actions { display: flex; gap: var(--s-2); flex-shrink: 0; }

/* Stack on narrow screens rather than squeezing four columns together */
@media (max-width: 900px) {
  .usr { flex-wrap: wrap; }
  .usr-meta { flex-direction: row; gap: var(--s-3); flex-wrap: wrap; width: 100%; }
  .usr-actions { width: 100%; }
}

/* pager */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  padding: var(--s-4) 0;
}

.pager-info { font-size: var(--t-xs); color: var(--tx-3); font-family: var(--font-num); }

/* global blocks list */
.blk {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  margin-bottom: var(--s-2);
}

.blk-val {
  font-family: var(--font-num);
  font-size: var(--t-xs);
  color: var(--tx);
  background: var(--bg-app);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 2px 7px;
}

.blk-reason { flex: 1; min-width: 0; font-size: var(--t-xs); color: var(--tx-3); }

/* ══════════ DATA LIBRARY ══════════ */

#libError:not(:empty) { margin-bottom: var(--s-4); }

.stat-sub { margin-top: 4px; font-size: var(--t-xs); color: var(--tx-3); }

/* "30/324" — the denominator is context, so it must not compete with the count */
.stat-value .of { font-size: var(--t-md); color: var(--tx-3); font-weight: var(--fw-med); }

/* Coverage percentage, coloured by how complete the library is */
.cov { font-weight: var(--fw-bold); }
.cov.ok   { color: var(--ok); }
.cov.warn { color: var(--warn); }
.cov.bad  { color: var(--danger); }

.backfill-box {
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
}

.bf-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}

.bf-row .form-select { max-width: 200px; }
.bf-row:last-child { margin-bottom: 0; }

.bf-progress { margin-top: var(--s-3); }

.bf-bar {
  height: 8px;
  border-radius: var(--r-full);
  background: var(--bg-sunken);
  overflow: hidden;
}

.bf-fill {
  height: 100%;
  width: 0;
  border-radius: var(--r-full);
  background: var(--accent);
  transition: width var(--base);
}

.bf-text {
  margin-top: 6px;
  font-size: var(--t-xs);
  color: var(--tx-2);
  font-family: var(--font-num);
}

/* entity lookup result */
.ent-card {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  margin-top: var(--s-3);
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
}

.ent-img {
  width: 56px;
  height: 56px;
  object-fit: contain;
  border-radius: var(--r-md);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  flex-shrink: 0;
}

.ent-body { flex: 1; min-width: 0; }
.ent-name { font-size: var(--t-md); font-weight: var(--fw-semi); }
.ent-meta { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 5px; }
.ent-dates { margin-top: 6px; font-size: var(--t-xs); color: var(--tx-3); }

/* ══════════ ADD-SOURCE PICKER ══════════
   Asked before the channel form: the source type decides which fields matter,
   and burying YouTube inside a dropdown three levels deep hid it completely. */

.src-pick { display: flex; flex-direction: column; gap: var(--s-3); }

.src-opt {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  width: 100%;
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-raised);
  color: var(--tx);
  text-align: start;
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast), transform var(--fast);
}

.src-opt:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: translateX(-3px);   /* nudges toward the arrow, which points ← in RTL */
}

.src-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-md);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  font-size: 1.35rem;
  flex-shrink: 0;
}

.src-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.src-text b { font-size: var(--t-md); font-weight: var(--fw-semi); }
.src-text small { font-size: var(--t-xs); color: var(--tx-3); line-height: 1.5; }
.src-go { color: var(--tx-3); font-size: 1.1rem; flex-shrink: 0; }
.src-opt:hover .src-go { color: var(--accent); }

/* ══════════ LOGO PICKER ══════════ */

.ch-form-top { display: flex; gap: var(--s-4); align-items: flex-start; margin-bottom: var(--s-4); }
.ch-form-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--s-4); }

.logo-pick {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  flex-shrink: 0;
}

/* Fixed square so the form does not reflow when the preview image swaps in */
.logo-prev {
  position: relative;
  width: 84px;
  height: 84px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-raised);
  overflow: hidden;
}

.logo-prev img { width: 100%; height: 100%; object-fit: contain; }
.logo-ph { font-size: 1.6rem; color: var(--tx-3); }
.logo-ph.bad { color: var(--warn); }

/* Mirrors the server-generated placeholder, so the preview matches production */
.logo-initials {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  background: linear-gradient(180deg, #6b62d6, #4f46a8);
  color: #fff;
  font-size: 1.5rem;
  font-weight: var(--fw-bold);
}

.logo-warn {
  position: absolute;
  top: 3px;
  inset-inline-end: 3px;
  font-size: 0.8rem;
}

/* ══════════ INLINE DRAFT PLAYER ══════════
   Mounted inside the server card instead of a popup window: popups get blocked,
   and a second modal would discard the half-filled form. */

.draft-player:empty { display: none; }
.draft-player { margin: var(--s-3) 0; }

.dp-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #000;
  overflow: hidden;
}

.dp-wrap video,
.dp-wrap iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  background: #000;
}

.dp-load, .dp-msg {
  padding: var(--s-5);
  text-align: center;
  font-size: var(--t-sm);
  color: var(--tx-2);
  background: var(--bg-raised);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  flex-wrap: wrap;
}

.dp-bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  background: var(--bg-raised);
  border-top: 1px solid var(--line);
}

/* ═══════════ MEMBERSHIP TIERS ═══════════
   Each level carries its own colour, set by the admin and passed in as --tier,
   so adding a level needs no new CSS. */

.tier-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  line-height: 1.7;
  white-space: nowrap;
  color: var(--tier, #64748b);
  background: color-mix(in srgb, var(--tier, #64748b) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--tier, #64748b) 35%, transparent);
}

.tier-pick {
  display: grid;
  gap: var(--s-2);
}

/* The radio itself is hidden; the whole card is the target, which is a much
   larger hit area and lets the level's colour do the signalling. */
.tier-opt {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  cursor: pointer;
  background: var(--bg-surface);
  transition: border-color var(--fast), background var(--fast);
}

.tier-opt:hover { border-color: color-mix(in srgb, var(--tier) 45%, var(--line)); }

.tier-opt.on {
  border-color: var(--tier);
  background: color-mix(in srgb, var(--tier) 8%, var(--bg-surface));
}

.tier-opt input { position: absolute; opacity: 0; pointer-events: none; }

.tier-opt-b {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 15px;
  color: var(--tier);
  background: color-mix(in srgb, var(--tier) 15%, transparent);
}

.tier-opt-t { min-width: 0; }
.tier-opt-t b { display: block; font-size: var(--t-sm); color: var(--tx); }
.tier-opt-t small {
  display: block;
  margin-top: 2px;
  font-size: var(--t-xs);
  color: var(--tx-3);
}

.quick-dates {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
  margin-top: var(--s-3);
}

/* ── moderator permissions ── */

.perm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--s-2);
}

.perm-opt {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  cursor: pointer;
  font-size: var(--t-sm);
  background: var(--bg-surface);
  transition: border-color var(--fast), background var(--fast);
}

.perm-opt:hover { border-color: var(--line-strong); }

/* ── tier settings cards ──
   One card per level, tinted by that level's own colour so the three are told
   apart at a glance rather than by reading the labels. */

.tset {
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--tier, #64748b);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-3);
  overflow: hidden;
  background: var(--bg-surface);
}

.tset-head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  background: color-mix(in srgb, var(--tier) 7%, transparent);
  border-bottom: 1px solid var(--line-subtle);
}

.tset-badge {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--tier);
  background: color-mix(in srgb, var(--tier) 16%, transparent);
}

.tset-name { flex: 1; min-width: 0; }
.tset-icon { width: 62px; flex-shrink: 0; text-align: center; }

/* The native colour swatch, sized to match the inputs beside it */
.tset-color {
  width: 40px;
  height: 34px;
  flex-shrink: 0;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-surface);
  cursor: pointer;
}

.tset-body { padding: var(--s-4); }

/* Quality and device count: two fields side by side, wrapping on narrow
   screens rather than being squeezed. */
.tset-limits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--s-4);
  margin-bottom: var(--s-4);
}

.tset-flabel {
  margin-bottom: var(--s-2);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line-subtle);
}

/* ── capability toggles ──
   A card per capability. `.check` in ui.css styles the INPUT itself (a 17px
   box), so it must sit on the input — putting it on the label made the label a
   17px square, which is what shattered the text and left a stray empty box. */
.feat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
  gap: var(--s-2);
}

.feat {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
  /* Labels stay on one line; the grid column is what wraps, not the words */
  white-space: nowrap;
  overflow: hidden;
}

.feat:hover { border-color: color-mix(in srgb, var(--tier) 45%, var(--line)); }

.feat:has(input:checked) {
  border-color: var(--tier);
  background: color-mix(in srgb, var(--tier) 10%, transparent);
}

.feat-i { flex-shrink: 0; font-size: 13px; line-height: 1; }

.feat-t {
  min-width: 0;
  font-size: var(--t-xs);
  color: var(--tx-2);
  overflow: hidden;
  text-overflow: ellipsis;
}

.feat:has(input:checked) .feat-t { color: var(--tx); font-weight: var(--fw-semi); }

/* Already granted by the member's level: shown ticked but not editable here,
   since a personal grant is additive and cannot revoke it. */
.feat.have {
  opacity: 0.62;
  cursor: default;
  border-style: dashed;
}

.feat-tag {
  flex-shrink: 0;
  margin-inline-start: auto;
  font-size: 9px;
  color: var(--tx-3);
}

.add-tier {
  width: 100%;
  margin-top: var(--s-2);
  border-style: dashed;
}

/* ═══════════ CLUBS ═══════════
   A grid of crests: ~1000 clubs are found by badge far faster than by reading
   Arabic names, so the logo is the primary object and the name supports it. */

.club-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: var(--s-3);
  margin-top: var(--s-4);
}

.club-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
  cursor: pointer;
  text-align: center;
  font: inherit;
  transition: border-color var(--fast), transform var(--fast), box-shadow var(--fast);
}

.club-card:hover {
  border-color: var(--primary);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgb(0 0 0 / 0.10);
}

.club-logo {
  width: 56px;
  height: 56px;
  object-fit: contain;
  flex-shrink: 0;
}

.club-name {
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--tx);
  /* Two lines then ellipsis: long names must not stretch one card taller than
     its neighbours and break the grid's rhythm. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.35;
}

.club-meta {
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* A national side's country IS its name, so repeating it under the crest says
   nothing — this marks what the entity is instead. */
/* ── Reader comments ──────────────────────────────────────── */

.cm-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.cm-stat {
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-1);
  text-align: center;
}
.cm-stat b { display: block; font-size: var(--t-xl); font-weight: 700; }
.cm-stat small { color: var(--tx-3); font-size: var(--t-xs); }
/* The queue is the number that needs action, so it carries the accent */
.cm-stat.warn b { color: #f59e0b; }
.cm-stat.ok b { color: #22c55e; }
.cm-stat.bad b { color: #ef4444; }
.cm-stat.muted b { color: var(--tx-3); }

.cm-card {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-1);
  margin-bottom: var(--s-3);
}
.cm-pick { padding-top: 2px; }
.cm-body { flex: 1; min-width: 0; }

.cm-head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  margin-bottom: var(--s-2);
}
.cm-head b { font-size: var(--t-sm); }
.cm-time { color: var(--tx-3); font-size: var(--t-xs); margin-inline-start: auto; }

.cm-tag {
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 14%, transparent);
  color: var(--brand);
  font-size: var(--t-xs);
}
.cm-tag.pin { background: color-mix(in srgb, #f59e0b 18%, transparent); color: #f59e0b; }

.cm-status {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: var(--t-xs);
  font-weight: 600;
}
.cm-status.warn { background: color-mix(in srgb, #f59e0b 16%, transparent); color: #f59e0b; }
.cm-status.ok { background: color-mix(in srgb, #22c55e 16%, transparent); color: #22c55e; }
.cm-status.bad { background: color-mix(in srgb, #ef4444 16%, transparent); color: #ef4444; }
.cm-status.muted { background: var(--bg-2); color: var(--tx-3); }

/* Wraps rather than truncating: moderating text you cannot fully read is
   guesswork, so the whole comment is always shown. */
.cm-text {
  margin: 0 0 var(--s-2);
  line-height: 1.7;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.cm-meta { color: var(--tx-3); font-size: var(--t-xs); margin-bottom: var(--s-3); }
.cm-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* Queue size in the sidebar, so pending comments are visible without
   opening the page. */
.nav-badge {
  margin-inline-start: auto;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: #ef4444;
  color: #fff;
  font-size: var(--t-xs);
  font-weight: 700;
  text-align: center;
}

.check-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-sm);
  color: var(--tx-2);
  white-space: nowrap;
}

.club-nat {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 14%, transparent);
  color: var(--brand);
  font-size: var(--t-xs);
  font-weight: 600;
}

/* Small corner marks: has typed data / has been edited */
.club-badges {
  position: absolute;
  top: 6px;
  inset-inline-start: 6px;
  display: flex;
  gap: 3px;
}

.club-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
}

.club-dot.done { background: var(--success, #22c55e); }
.club-dot.edit { background: var(--warn, #f59e0b); }

.club-more {
  display: flex;
  justify-content: center;
  margin: var(--s-5) 0;
}

/* ── one club ── */

.club-hero {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r-lg);
  /* The club's own colours, kept faint so text stays readable on either */
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--c1) 18%, transparent),
    color-mix(in srgb, var(--c2) 18%, transparent));
  border: 1px solid var(--line);
}

.club-hero-logo {
  width: 68px;
  height: 68px;
  object-fit: contain;
  flex-shrink: 0;
}

.club-hero-t b {
  display: block;
  font-size: var(--t-lg, 17px);
  color: var(--tx);
}

.club-hero-t small {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 6px;
}

.chip {
  padding: 2px 9px;
  border-radius: 999px;
  font-size: var(--t-xs);
  color: var(--tx-2);
  background: var(--bg-raised);
  border: 1px solid var(--line);
}

.club-comps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-2);
}

.club-comp {
  padding: 4px 11px;
  border-radius: 999px;
  font-size: var(--t-xs);
  color: var(--tx-2);
  background: var(--bg-raised);
  border: 1px solid var(--line);
}

/* The club's main competition, distinguished from the cups it also enters */
.club-comp.main {
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 40%, transparent);
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  font-weight: var(--fw-semi);
}

/* ── suggestion ──
   Visibly a PROPOSAL, not saved data: dashed border and its own tint, so it is
   never mistaken for what the club already holds. */

.sug {
  margin: var(--s-3) 0 var(--s-4);
  border: 1px dashed var(--primary);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--primary) 6%, transparent);
  overflow: hidden;
}

/* Data from the upstream source is matched by ID and cannot be the wrong club;
   Wikidata is matched by name and occasionally is. A solid green border marks
   the trustworthy one, so the dashed "proposal" styling reads as the caution
   it is meant to be. */
.sug.trusted {
  border-style: solid;
  border-color: var(--success, #22c55e);
  background: color-mix(in srgb, var(--success, #22c55e) 7%, transparent);
}

.sug.trusted .sug-head {
  border-bottom-color: color-mix(in srgb, var(--success, #22c55e) 25%, transparent);
}

.sug.trusted .sug-actions {
  border-top-color: color-mix(in srgb, var(--success, #22c55e) 25%, transparent);
}

.sug-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--tx);
  border-bottom: 1px solid color-mix(in srgb, var(--primary) 20%, transparent);
}

.sug-link {
  font-size: var(--t-xs);
  font-weight: 400;
  color: var(--primary);
  text-decoration: none;
}

.sug-link:hover { text-decoration: underline; }

.sug-body { padding: var(--s-2) var(--s-4); }

.sug-desc {
  padding: var(--s-2) 0;
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* Honours read off the source. Scrolls rather than stretching the panel: a
   long-standing club can list a dozen, which would push the accept button out
   of view. */
.sug-titles {
  padding: var(--s-2) 0;
  border-top: 1px solid var(--line);
}
.sug-titles small {
  color: var(--tx-3);
  font-size: var(--t-xs);
}
.sug-titles ul {
  margin: var(--s-1) 0 0;
  padding: 0 var(--s-4) 0 0;
  max-height: 132px;
  overflow-y: auto;
  list-style: disc;
}
.sug-titles li {
  font-size: var(--t-xs);
  color: var(--tx-2);
  padding: 1px 0;
}

.sug-actions {
  display: flex;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid color-mix(in srgb, var(--primary) 20%, transparent);
}

.sug-alts {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line-subtle);
}

.sug-alts small { color: var(--tx-3); font-size: var(--t-xs); }

/* ═══════════ TEAM PICKER ═══════════
   Replaces <datalist>, which the browser renders on its own terms — unstyled,
   and with no room for a crest beside each name. */

.tp { position: relative; }

/* The crest sits INSIDE the field; the input is padded to make room rather
   than overlaid, so long names never run under the logo. */
.tp-field {
  position: relative;
  display: flex;
  align-items: center;
}

.tp-logo {
  position: absolute;
  inset-inline-start: 10px;
  width: 22px;
  height: 22px;
  object-fit: contain;
  border-radius: 4px;
  pointer-events: none;
  z-index: 1;
}

.tp-logo-ph {
  display: grid;
  place-items: center;
  font-size: 13px;
  opacity: 0.45;
}

.tp-input {
  padding-inline-start: 40px !important;
  padding-inline-end: 32px !important;
}

.tp-clear {
  position: absolute;
  inset-inline-end: 8px;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  font-size: 11px;
  cursor: pointer;
  color: var(--tx-3);
  background: transparent;
  transition: background var(--fast), color var(--fast);
}

.tp-clear:hover { background: var(--bg-raised); color: var(--tx); }

/* Anchored to the field and above the modal's own stacking context, or it
   would be clipped by the dialog's scroll container. */
.tp-list {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + 4px);
  z-index: 60;
  max-height: 260px;
  overflow-y: auto;
  padding: var(--s-1);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.14);
}

.tp-item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 7px 10px;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background var(--fast);
}

/* One highlight class for hover AND keyboard, so the two never disagree */
.tp-item.on { background: color-mix(in srgb, var(--primary) 14%, transparent); }

.tp-item-logo {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  object-fit: contain;
  border-radius: 4px;
}

.tp-item-name {
  min-width: 0;
  font-size: var(--t-sm);
  color: var(--tx);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tp-empty {
  padding: 14px 12px;
  text-align: center;
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* ── counts per level ── */

.tier-bar {
  display: flex;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-top: var(--s-4);
}

.tier-stat {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid color-mix(in srgb, var(--tier) 28%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--tier) 8%, transparent);
}

.tier-stat-b { color: var(--tier); }
.tier-stat-n { font-weight: var(--fw-bold, 700); color: var(--tx); }
.tier-stat-l { font-size: var(--t-xs); color: var(--tx-3); }
/* Mixed from --primary rather than a --primary-soft token, which this
   stylesheet does not define. */
.perm-opt:has(input:checked) {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, transparent);
}
.perm-opt input { flex-shrink: 0; accent-color: var(--primary); }

.dp-note, .dp-ok {
  flex: 1;
  min-width: 0;
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  color: var(--tx-2);
}

.dp-note.ok   { color: var(--ok); }
.dp-note.bad  { color: var(--danger); }
.dp-note.warn { color: var(--warn); }
.dp-ok { color: var(--ok); }

/* Collapsible sub-panel inside a modal — per-channel geo, schedule and the
   optional request headers. Collapsed by default so the common case (name +
   URL) stays a short form and the advanced fields are there when needed. */
.sub-box {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
  overflow: hidden;
}

.sub-box > summary {
  padding: var(--s-3) var(--s-4);
  cursor: pointer;
  list-style: none;
  user-select: none;
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--tx-2);
}

.sub-box > summary::-webkit-details-marker { display: none; }
.sub-box > summary::marker { content: ''; }
.sub-box > summary::after { content: ' ▾'; color: var(--tx-3); }
.sub-box[open] > summary::after { content: ' ▴'; }
.sub-box > summary:hover { background: var(--bg-overlay); color: var(--tx); }

.sub-body { padding: var(--s-4); border-top: 1px solid var(--line); background: var(--bg-surface); }

/* Sub-label under a section heading (channel/server counts) */
.sec-sub { display: block; font-size: var(--t-xs); color: var(--tx-3); margin-top: 2px; }

/* ══════════ SHARED SECTION / MINI-CARD PATTERNS ══════════
   Used by the broadcast tab, channel modal and match info blocks. */

.sec-head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}

.sec-head::after {
  content: '';
  flex: 1;
  min-width: 16px;
  height: 1px;
  background: var(--line);
  order: 3;
}

.sec-title {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-md);
  font-weight: var(--fw-semi);
  white-space: nowrap;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: var(--s-3);
}

.mini-card {
  padding: var(--s-4);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.card-top { display: flex; align-items: flex-start; gap: var(--s-3); margin-bottom: var(--s-3); }

.card-ico {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  background: var(--bg-raised);
  font-size: 15px;
  flex-shrink: 0;
}

.card-nm { font-size: var(--t-sm); font-weight: var(--fw-semi); line-height: 1.4; overflow-wrap: anywhere; }
.card-sub { font-size: var(--t-xs); color: var(--tx-3); margin-top: 1px; }
.card-acts { margin-inline-start: auto; display: flex; gap: 4px; flex-shrink: 0; }

.tag-row { display: flex; flex-wrap: wrap; gap: 5px; }

.tag {
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--bg-raised);
  font-size: var(--t-xs);
  color: var(--tx-2);
  white-space: nowrap;
}

.tag.hd  { background: var(--ok-soft);   border-color: transparent; color: var(--ok); }
.tag.geo { background: var(--warn-soft); border-color: transparent; color: var(--warn); }

.kv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: var(--s-2); }

.kv-row {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: 10px var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-raised);
}

.kv-k { font-size: var(--t-xs); color: var(--tx-3); }
.kv-v { font-size: var(--t-sm); font-weight: var(--fw-semi); overflow-wrap: anywhere; }

/* player / squad row */
.pr-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 6px 0;
  font-size: var(--t-sm);
  border-bottom: 1px solid var(--line-subtle);
}

.pr-row:last-child { border-bottom: 0; }

.pr-num {
  width: 22px;
  text-align: center;
  font-family: var(--font-num);
  font-size: var(--t-xs);
  font-weight: var(--fw-bold);
  color: var(--tx-3);
  flex-shrink: 0;
}

.pr-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.pr-meta { font-size: var(--t-xs); color: var(--tx-2); white-space: nowrap; }
.pr-pos { font-size: var(--t-xs); color: var(--tx-3); }

/* ══════════ NEWS ══════════ */

.news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--s-4);
}

.news-card {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color var(--fast);
}

.news-card:hover { border-color: var(--line-strong); }

.news-card-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--bg-raised);
}

.news-card-body { padding: var(--s-4); display: flex; flex-direction: column; gap: var(--s-2); flex: 1; }

.news-card-body h4 {
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  line-height: 1.5;
}

.news-card-body p {
  font-size: var(--t-xs);
  color: var(--tx-3);
  line-height: 1.7;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.news-card-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  margin-top: auto;
  padding-top: var(--s-3);
  border-top: 1px solid var(--line-subtle);
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* ══════════ LOGIN ══════════ */

.login-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--s-5);
  background:
    radial-gradient(60% 50% at 50% 0%, var(--accent-soft), transparent 70%),
    var(--bg-app);
}

.login-card {
  width: 100%;
  max-width: 380px;
  padding: var(--s-8) var(--s-6);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-lg);
  text-align: center;
}

.login-logo {
  width: 52px;
  height: 52px;
  margin: 0 auto var(--s-4);
  display: grid;
  place-items: center;
  border-radius: var(--r-lg);
  background: var(--accent);
  font-size: 24px;
}

.login-card h1 { font-size: var(--t-xl); font-weight: var(--fw-bold); }
.login-card h1 span { color: var(--accent); }
.login-subtitle { font-size: var(--t-sm); color: var(--tx-3); margin: 6px 0 var(--s-6); }

.login-form { display: flex; flex-direction: column; gap: var(--s-4); text-align: right; }
.login-form .btn { margin-top: var(--s-2); }

.login-error {
  display: none;
  padding: 9px var(--s-3);
  margin-bottom: var(--s-4);
  border-radius: var(--r-md);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--t-xs);
  font-weight: var(--fw-med);
}

.login-error.show { display: block; }

.form-input-icon-wrapper { position: relative; }

.form-input-icon-wrapper .input-icon {
  position: absolute;
  inset-inline-start: 11px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--tx-3);
  pointer-events: none;
}

.form-input-icon-wrapper .form-input { padding-inline-start: var(--s-8); }

/* ═══ AUTO-PUBLISHING ═══════════════════════════════════════
   Campaign management. Uses the theme tokens (--bg-surface, --tx, --line);
   an earlier standalone version invented its own names, they resolved to
   nothing, and every panel fell back to near-black on white. */

.ap-tabs {
  display: flex;
  gap: var(--s-1);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s-5);
  flex-wrap: wrap;
}

.ap-tab {
  padding: var(--s-3) var(--s-4);
  border: 0;
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--t-md);
  font-weight: var(--fw-semi);
  color: var(--tx-3);
  border-bottom: 2px solid transparent;
  transition: color var(--fast), border-color var(--fast);
}
.ap-tab:hover { color: var(--tx); }
.ap-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.ap-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--s-4);
}
.ap-full { grid-column: 1 / -1; }

.ap-divider {
  border-top: 1px solid var(--line);
  margin: var(--s-2) 0;
}

.ap-inline-form {
  display: flex;
  gap: var(--s-3);
  align-items: flex-end;
  flex-wrap: wrap;
}

.ap-check label {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  cursor: pointer;
  font-size: var(--t-md);
  color: var(--tx);
}
.ap-check input { width: 16px; height: 16px; accent-color: var(--accent); }

.ap-actions {
  text-align: start;
  white-space: nowrap;
}
.ap-actions .btn { margin-inline-start: var(--s-1); }

/* ═══════════ AUTO-PUBLISH ═══════════ */

/* The four figures that answer "is this working right now" */
.ap-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}

/* `display` in a class beats the hidden attribute, so these must say so
   explicitly or toggling `el.hidden` would do nothing. */
.ap-stats[hidden],
.ap-heartbeat[hidden],
.ap-bar[hidden],
.ap-tabs[hidden] { display: none !important; }

.ap-stat {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
}

/* Circular, matching the reference — a rounded square reads as a button, a
   circle reads as a badge, which is what these are. */
.ap-stat-ico {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 17px;
}

/* Tinted per figure so the row is scannable without reading the labels */
.ap-stat-ico.violet { color: #7c3aed; background: color-mix(in srgb, #7c3aed 14%, transparent); }
.ap-stat-ico.green  { color: #16a34a; background: color-mix(in srgb, #16a34a 14%, transparent); }
.ap-stat-ico.amber  { color: #d97706; background: color-mix(in srgb, #d97706 14%, transparent); }
.ap-stat-ico.blue   { color: #2563eb; background: color-mix(in srgb, #2563eb 14%, transparent); }

.ap-stat-t b {
  display: block;
  font-size: 21px;
  font-weight: var(--fw-bold, 700);
  color: var(--tx);
  line-height: 1.2;
}

.ap-stat-t small {
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* Campaigns only run from the cron, so its last beat is the first thing to
   check when nothing is publishing. */
.ap-heartbeat {
  display: flex;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-raised);
  font-size: var(--t-xs);
  color: var(--tx-3);
}

/* Nothing scheduled is worth noticing, not just reporting */
.ap-heartbeat.idle {
  border-color: color-mix(in srgb, var(--warn, #f59e0b) 40%, transparent);
  background: color-mix(in srgb, var(--warn, #f59e0b) 7%, transparent);
}

.ap-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}

.ap-bar-title {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--t-md, 15px);
  color: var(--tx);
}

.ap-bar-icon { font-size: 17px; }

.ap-bar-actions {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
}

/* ── the table ── */

.ap-table .ap-num {
  width: 44px;
  text-align: center;
  color: var(--tx-3);
  font-size: var(--t-xs);
}

.ap-table .ap-count {
  font-weight: var(--fw-semi);
  color: var(--tx);
}

.ap-type {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: var(--t-xs);
  color: #0d9488;
  background: color-mix(in srgb, #0d9488 12%, transparent);
  border: 1px solid color-mix(in srgb, #0d9488 30%, transparent);
  white-space: nowrap;
}

.ap-acts-h { text-align: center; }

/* Icon buttons: five TEXT buttons per row made the table scroll sideways and
   hid the ones at the end. The icons are SVG rather than glyphs (▶ ❚❚ ⧉) —
   glyphs drew at whatever weight each font provided, so the row looked
   mismatched and faint. */
/* Sized and coloured to match the Gulf Echo dashboard exactly:
     .btn        border-radius 8px
     .btn-sm     padding 5px 10px
     .btn-success #388e3c   .btn-info #7b1fa2   .btn-danger #d32f2f
     .btn-outline transparent with a #ddd border
   Its hover lifts the button by 1px with a soft shadow, which is what makes the
   row feel responsive rather than flat. */
.ap-act {
  /* 5px padding around a 14px icon + 2px of border = 26px, which is what the
     reference's `.btn-sm { padding:5px 10px }` produces. Mine was 34px and
     looked oversized beside it. */
  width: 28px;
  height: 28px;
  display: inline-grid;
  place-items: center;
  margin-inline-start: 6px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: transparent;
  color: var(--tx-2);
  cursor: pointer;
  transition: all 0.2s;
  vertical-align: middle;
}

.ap-act svg {
  width: 14px;
  height: 14px;
  display: block;
}

.ap-act:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.15);
  background: var(--bg-raised);
  color: var(--tx);
}
.ap-act:disabled { opacity: 0.5; cursor: default; transform: none; box-shadow: none; }

/* The three that carry weight — run, switch, delete — are filled; the rest stay
   outlined so the eye lands on these first. */
.ap-act.go {
  color: #fff;
  background: #388e3c;
  border-color: #388e3c;
}

.ap-act.power {
  color: #fff;
  background: #7b1fa2;
  border-color: #7b1fa2;
}

.ap-act.danger {
  color: #fff;
  background: #d32f2f;
  border-color: #d32f2f;
}

/* Filled buttons keep their colour on hover; only the lift applies */
.ap-act.go:hover,
.ap-act.power:hover,
.ap-act.danger:hover { color: #fff; }
.ap-act.go:hover { background: #388e3c; }
.ap-act.power:hover { background: #7b1fa2; }
.ap-act.danger:hover { background: #d32f2f; }

.ap-form-actions {
  display: flex;
  gap: var(--s-2);
  justify-content: flex-end;
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}

.ap-note {
  font-size: var(--t-sm);
  color: var(--tx-3);
  line-height: 1.7;
  margin-bottom: var(--s-4);
}

.ap-log {
  font-family: var(--font-num);
  font-size: var(--t-xs);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  margin-bottom: var(--s-1);
  word-break: break-word;
  background: var(--bg-raised);
  color: var(--tx);
}
.ap-log-time { color: var(--tx-3); margin-inline-end: var(--s-2); }
.ap-log-success { background: var(--ok-soft); }
.ap-log-warning { background: var(--warn-soft); }
.ap-log-error   { background: var(--danger-soft); }
/* News card additions — status/category chips and the no-image placeholder.
   .grid-auto and .text-danger were already referenced by the news markup but
   never defined, so the card grid never actually laid out in columns. */
.grid-auto {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--s-4);
}

.news-card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  margin: var(--s-2) 0;
}

.news-card-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  color: var(--tx-3);
  background: var(--bg-raised);
}

.text-danger { color: var(--danger); }
/* ═══ NEWS: list, editor, media library ═════════════════════ */

.news-filters {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr auto;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
@media (max-width: 720px) { .news-filters { grid-template-columns: 1fr 1fr; } }

.news-bulk {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-3);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  color: var(--tx);
}
.news-bulk-actions { display: flex; gap: var(--s-1); flex-wrap: wrap; }

.news-table .col-check { width: 36px; }
.news-table .col-thumb { width: 56px; }
.news-thumb {
  width: 44px; height: 44px;
  object-fit: cover;
  border-radius: var(--r-sm);
  display: block;
}
.news-thumb-empty {
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-raised); color: var(--tx-3);
}
.news-row-tags { display: flex; gap: 4px; margin-top: 4px; flex-wrap: wrap; }

.news-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  margin-top: var(--s-4);
  color: var(--tx-3);
  font-size: var(--t-sm);
}

/* Editor: article body on one side, publishing options on the other. */
.news-editor {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: var(--s-5);
  align-items: start;
}
@media (max-width: 900px) { .news-editor { grid-template-columns: 1fr; } }

.news-editor-side {
  border-inline-start: 1px solid var(--line);
  padding-inline-start: var(--s-4);
}
@media (max-width: 900px) {
  .news-editor-side { border-inline-start: 0; padding-inline-start: 0; border-top: 1px solid var(--line); padding-top: var(--s-4); }
}

.news-seo { margin-top: var(--s-4); border-top: 1px solid var(--line); padding-top: var(--s-3); }
.news-seo summary { cursor: pointer; font-weight: var(--fw-semi); color: var(--tx); margin-bottom: var(--s-3); }
.hint-ok { color: var(--ok); }
.hint-warn { color: var(--warn); }

.news-featured {
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-md);
  min-height: 120px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  overflow: hidden;
  background: var(--bg-raised);
}
.news-featured img { width: 100%; height: 100%; object-fit: cover; display: block; }
.news-featured-empty { color: var(--tx-3); font-size: var(--t-sm); padding: var(--s-4); text-align: center; }

.tag-box { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: var(--s-2); min-height: 24px; }
.tag-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-full);
  padding: 2px 10px;
  font-size: var(--t-xs);
  cursor: pointer;
}
.tag-chip b { opacity: .6; }
.tag-chip:hover b { opacity: 1; }
.tag-empty { color: var(--tx-3); font-size: var(--t-xs); }

/* Quill has to inherit the dashboard theme; its default skin is light-only. */
#newsEditor { min-height: 320px; background: var(--bg-raised); border-radius: 0 0 var(--r-sm) var(--r-sm); }
.ql-toolbar.ql-snow, .ql-container.ql-snow { border-color: var(--line) !important; }
.ql-toolbar.ql-snow { background: var(--bg-surface); border-radius: var(--r-sm) var(--r-sm) 0 0; }
.ql-snow .ql-stroke { stroke: var(--tx-2); }
.ql-snow .ql-fill { fill: var(--tx-2); }
.ql-snow .ql-picker { color: var(--tx-2); }
.ql-snow .ql-picker-options { background: var(--bg-overlay); border-color: var(--line); }
.ql-editor { color: var(--tx); font-family: var(--font); font-size: var(--t-md); line-height: 1.9; }
.ql-editor.ql-blank::before { color: var(--tx-3); font-style: normal; }

.media-toolbar { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: var(--s-3);
  max-height: 55vh;
  overflow-y: auto;
}
.media-item {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--bg-raised);
  cursor: pointer;
  padding: 0;
  aspect-ratio: 1;
}
.media-item:hover { border-color: var(--accent); }
.media-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-del {
  position: absolute; top: 4px; inset-inline-end: 4px;
  width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.6); color: #fff;
  border-radius: var(--r-full);
  font-size: 12px;
  opacity: 0;
  transition: opacity var(--fast);
}
.media-item:hover .media-del { opacity: 1; }
/* Editor as a full page rather than a dialog. The action buttons sit in the
   section header, and the body gets the room a rich-text field needs. */
.editor-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; }

.editor-title {
  font-size: var(--t-xl);
  font-weight: var(--fw-bold);
}

#page-editor .news-editor-side {
  position: sticky;
  top: var(--s-4);
}
@media (max-width: 900px) {
  #page-editor .news-editor-side { position: static; }
}

#page-editor #newsEditor { min-height: 460px; }

/* ═══ NEWS LIST ═════════════════════════════════════════════
   Search and filters sit in .section-header-right like the members page, so
   nothing here re-lays-out the header. These rules only cover the table. */

.news-table .col-check { width: 40px; }
.news-table .col-thumb { width: 64px; }

/* Fixed 4:3 box: source images arrive at wildly different aspect ratios and a
   height-only rule let tall ones stretch the row. */
.news-thumb {
  width: 56px;
  height: 42px;
  object-fit: cover;
  border-radius: var(--r-sm);
  display: block;
  background: var(--bg-raised);
}
.news-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  color: var(--tx-3);
}

.news-table .cell-title {
  font-weight: var(--fw-semi);
  color: var(--tx);
  line-height: 1.5;
}
.news-row-tags { display: flex; gap: 4px; margin-top: 4px; flex-wrap: wrap; }

/* Actions stay quiet until the row is hovered, so three buttons per row do not
   compete with the headlines for attention. */
.news-table .ap-actions { opacity: 0; transition: opacity var(--fast); }
.news-table tr:hover .ap-actions,
.news-table tr:focus-within .ap-actions { opacity: 1; }
@media (hover: none) { .news-table .ap-actions { opacity: 1; } }

.news-bulk {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-4);
  margin-bottom: var(--s-3);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  color: var(--tx);
}
.news-bulk-actions { display: flex; gap: var(--s-1); flex-wrap: wrap; }

.news-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  margin-top: var(--s-4);
  color: var(--tx-3);
  font-size: var(--t-sm);
}

#btnTrashToggle { white-space: nowrap; }

/* ═══ ARTICLE EDITOR (full page) ════════════════════════════ */

#page-editor .section-header { justify-content: space-between; }
.editor-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; }

.editor-title { font-size: var(--t-lg); font-weight: var(--fw-semi); }

.news-editor {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--s-5);
  align-items: start;
}
@media (max-width: 900px) { .news-editor { grid-template-columns: 1fr; } }

.news-editor-main,
.news-editor-side {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-5);
}
.news-editor-side { position: sticky; top: var(--s-4); }
@media (max-width: 900px) { .news-editor-side { position: static; } }

.news-seo { margin-top: var(--s-4); border-top: 1px solid var(--line); padding-top: var(--s-3); }
.news-seo summary { cursor: pointer; font-weight: var(--fw-semi); color: var(--tx); }
.news-seo[open] summary { margin-bottom: var(--s-3); }
.hint-ok { color: var(--ok); }
.hint-warn { color: var(--warn); }

.news-featured {
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-md);
  aspect-ratio: 16 / 9;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  overflow: hidden;
  background: var(--bg-raised);
}
.news-featured:hover { border-color: var(--accent); }
.news-featured img { width: 100%; height: 100%; object-fit: cover; display: block; }
.news-featured-empty { color: var(--tx-3); font-size: var(--t-sm); padding: var(--s-4); text-align: center; }

.tag-box { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: var(--s-2); min-height: 22px; }
.tag-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-full);
  padding: 2px 10px;
  font-size: var(--t-xs);
  cursor: pointer;
}
.tag-chip b { opacity: .55; font-weight: var(--fw-normal); }
.tag-chip:hover b { opacity: 1; }
.tag-empty { color: var(--tx-3); font-size: var(--t-xs); }

/* Quill ships a light-only skin; map it onto the theme tokens so it follows
   whichever theme is active instead of staying white in dark mode. */
#newsEditor { min-height: 420px; background: var(--bg-app); border-radius: 0 0 var(--r-sm) var(--r-sm); }
.ql-toolbar.ql-snow, .ql-container.ql-snow { border-color: var(--line) !important; }
.ql-toolbar.ql-snow { background: var(--bg-raised); border-radius: var(--r-sm) var(--r-sm) 0 0; }
.ql-snow .ql-stroke { stroke: var(--tx-2); }
.ql-snow .ql-fill { fill: var(--tx-2); }
.ql-snow .ql-picker { color: var(--tx-2); }
.ql-snow .ql-picker-options { background: var(--bg-overlay); border-color: var(--line); }
.ql-snow.ql-toolbar button:hover .ql-stroke { stroke: var(--accent); }
.ql-snow.ql-toolbar button.ql-active .ql-stroke { stroke: var(--accent); }
.ql-editor { color: var(--tx); font-family: var(--font); font-size: var(--t-md); line-height: 1.9; }
.ql-editor.ql-blank::before { color: var(--tx-3); font-style: normal; inset-inline-start: var(--s-4); }

/* ═══ MEDIA LIBRARY ═════════════════════════════════════════ */

.media-toolbar { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: var(--s-3);
  max-height: 55vh;
  overflow-y: auto;
}
.media-item {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--bg-raised);
  cursor: pointer;
  padding: 0;
  aspect-ratio: 1;
}
.media-item:hover { border-color: var(--accent); }
.media-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-del {
  position: absolute; top: 4px; inset-inline-end: 4px;
  width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.6); color: #fff;
  border-radius: var(--r-full);
  font-size: 12px;
  opacity: 0;
  transition: opacity var(--fast);
}
.media-item:hover .media-del { opacity: 1; }
/* ═══ CAMPAIGN TYPE PICKER ══════════════════════════════════
   Cards rather than a dropdown: the choice swaps a whole block of settings, so
   it needs to read as a mode switch, not a field. */
.type-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--s-3);
}
.type-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-4);
  text-align: start;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  cursor: pointer;
  font-family: inherit;
  transition: border-color var(--fast), background var(--fast);
}
.type-card:hover { border-color: var(--line-strong); }
.type-card.active { border-color: var(--accent); background: var(--accent-soft); }
.type-card-icon { font-size: 1.35rem; }
.type-card-title { font-weight: var(--fw-semi); color: var(--tx); font-size: var(--t-md); }
.type-card-sub { color: var(--tx-3); font-size: var(--t-xs); line-height: 1.5; }

.ap-section-title {
  font-weight: var(--fw-semi);
  color: var(--tx);
  font-size: var(--t-md);
  margin-bottom: var(--s-2);
  padding-top: var(--s-2);
}

.ap-advanced {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raised);
}
.ap-advanced summary {
  cursor: pointer;
  font-weight: var(--fw-semi);
  color: var(--tx-2);
  font-size: var(--t-sm);
}
.ap-advanced[open] summary { margin-bottom: var(--s-3); }
/* ═══ CAMPAIGN WIZARD ═══════════════════════════════════════ */

.wiz-steps {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-5);
  flex-wrap: wrap;
}
/* A button so any step can be reached directly; on an existing campaign every
   step is already filled in and stepping through them all is busywork. */
.wiz-step {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--bg-raised);
  color: var(--tx-3);
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--fast), color var(--fast), border-color var(--fast);
}
.wiz-step:hover { border-color: var(--accent-line); color: var(--tx); }
.wiz-step.current { background: var(--accent); color: #fff; }
.wiz-step.done { background: var(--ok-soft); color: var(--ok); }
.wiz-sep { flex: 1; height: 1px; background: var(--line); min-width: 12px; }

.wiz-card {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-5);
  margin-bottom: var(--s-4);
}
.wiz-card-title {
  font-weight: var(--fw-semi);
  color: var(--tx);
  margin-bottom: var(--s-3);
}

.wiz-inline { display: flex; gap: var(--s-2); align-items: center; }
.wiz-inline .form-input { flex: 1; }

.wiz-actions { display: flex; gap: var(--s-2); margin-top: var(--s-2); flex-wrap: wrap; }

/* A picked selector is long and structural — monospace keeps it readable. */
.form-input.mono { font-family: var(--font-num); font-size: var(--t-xs); direction: ltr; text-align: left; }

.wiz-picker-box {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-4);
  margin-bottom: var(--s-3);
  background: var(--bg-raised);
}

.wiz-test-btn { width: 100%; margin: var(--s-3) 0; }

.wiz-result {
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  margin-top: var(--s-3);
  font-size: var(--t-sm);
  color: var(--tx);
}
.wiz-result.ok { background: var(--ok-soft); border: 1px solid var(--ok); }
.wiz-result.warn { background: var(--warn-soft); border: 1px solid var(--warn); }
.wiz-result ul { margin: var(--s-2) 0 0; padding-inline-start: var(--s-5); }
.wiz-result li { font-family: var(--font-num); font-size: var(--t-xs); direction: ltr; text-align: left; margin-bottom: 2px; }

.wiz-preview {
  background: var(--bg-app);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--s-3);
  margin: var(--s-2) 0;
  max-height: 140px;
  overflow-y: auto;
  font-size: var(--t-xs);
  line-height: 1.8;
}
.wiz-preview-img { max-width: 180px; border-radius: var(--r-sm); margin-top: var(--s-2); display: block; }


.wiz-range { width: 100%; accent-color: var(--accent); }
.form-hint-inline { color: var(--tx-3); font-weight: var(--fw-normal); font-size: var(--t-xs); }

/* ═══ VISUAL PICKER ═════════════════════════════════════════
   Full-screen over the wizard: the proxied page needs the room, and the
   wizard's state is untouched underneath. */
.picker-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--bg-app);
  display: flex;
  flex-direction: column;
}
.picker-bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--line);
  color: var(--tx);
  font-size: var(--t-sm);
  flex-wrap: wrap;
}
.picker-selector {
  flex: 1;
  min-width: 200px;
  font-family: var(--font-num);
  font-size: var(--t-xs);
  direction: ltr;
  text-align: left;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--accent);
}
.picker-bar-actions { display: flex; gap: var(--s-2); }
.picker-frame { flex: 1; width: 100%; border: 0; background: #fff; }
/* Wizard as a full page: cards get the width a five-step form needs, and the
   step bar scrolls sideways on narrow screens rather than wrapping into rows. */
#page-campaign .wiz-steps {
  overflow-x: auto;
  padding-bottom: var(--s-2);
  flex-wrap: nowrap;
}
#page-campaign .wiz-card { max-width: 900px; }
#page-campaign .section-header { justify-content: space-between; }