/* AXING Transcoder UI · v0.1.0 · for firmware 6.5.7.x · 2026-05-06 */
/* ==========================================================================
   AXING IPTV Transcoder UI
   --------------------------------------------------------------------------
   Transcoder-specific extensions on top of axing.custom.css. Tokens, theme,
   surface hierarchy, button patterns, layout etc. all live in
   axing.custom.css (same folder) — this file only adds what is
   transcoder-specific (channel status colors, CPU/GPU heatmap, channel
   list/card layout helpers).

   Load order: bootstrap.css -> axing.custom.css -> icons.css -> THIS.
   ========================================================================== */


/* --- Page layout --------------------------------------------------------- */
/* Footer always at bottom: body as flex column, main grows to fill. */
body {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
main {
  flex: 1;
}

/* Cap the fluid container so content doesn't stretch edge-to-edge on very
   wide displays — beyond ~1800px the channel list/editor split gets too
   airy and reading distances grow uncomfortable. */
.container-fluid {
  max-width: 1800px;
}

/* Light-mode body: pull the recessed surface a bit darker than BS' default
   tertiary-bg (#f8f9fa). The card-header BS-default cap tint is rgba(0,0,0,.03)
   over white = ~#f7f7f7, which collides with #f8f9fa. A cooler, ~L93% gray
   gives the body clear presence against both card body (#fff) and header
   tint. Direct body override (not a --bs-tertiary-bg token override),
   because tertiary-bg also drives form-control backgrounds — those should
   stay subtle. Dark mode keeps BS/AXING defaults. */
[data-bs-theme="light"] body {
  background-color: #e6eaef;
}


/* --- Navbar adjustments -------------------------------------------------- */
/* axing.custom.css pins all .navbar-brand img to 28px; override the compact
   mobile 'a' mark to 24px. */
.navbar-brand .d-lg-none {
  height: 24px;
}

/* axing.custom.js rewrites themeIcon.className on every theme cycle,
   stripping ico-24. Pin the size via ID so it survives the overwrite. */
#themeIcon {
  width: 20px;
  height: 20px;
}

/* Brand title in the header — slightly larger than body, but smaller than
   h5. Keeps the wordmark + title pair visually balanced against the
   28px logo. Also used in help.html. */
.tx-brand-title {
  font-size: 1.25rem;
}

/* Bootstrap .collapse stays display:block; make the nav flex so gap works.
   Column on mobile, row on lg+ (mirrors navbar-expand-lg behaviour). */
.navbar-collapse {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1rem;
  padding-left: 0.5rem;
}
@media (min-width: 576px) {
  .navbar-collapse { padding-left: 4rem; }
}
@media (min-width: 768px) {
  .navbar-collapse { padding-left: 4rem; }
}
@media (min-width: 992px) {
  .navbar-collapse {
    flex-direction: row;
    align-items: center;
    padding-left: 0;
  }
}


/* --- Form controls — inset tint vs. card surface ------------------------- */
/* BS' .form-control / .form-select default to --bs-body-bg, which is identical
   to .card / .bg-body. Inputs disappear against the card surface. Pull them
   down one surface level to --bs-tertiary-bg so they read as a flat inset.
   Token resolves per theme: #f8f9fa (light) / #13161a (dark). */
.form-control,
.form-select {
  background-color: var(--bs-tertiary-bg);
}
/* Disabled: flip back up to the card-level surface (--bs-body-bg) so the
   field reads as locked/quiet against the recessed enabled state, plus a
   0.65 opacity fade to match BS' disabled-button convention. */
.form-control:disabled,
.form-select:disabled {
  background-color: var(--bs-body-bg);
  opacity: 0.65;
}


/* --- Channel-status tokens ----------------------------------------------- */
/* Mapped onto Bootstrap semantic colors so dark-mode inversion comes for
   free. Consumed by .status-{state} below and any future status-bar /
   timeline component. */
:root {
  --tx-status-idle:     var(--bs-secondary-color);    /* dim, neutral */
  --tx-status-running:  var(--bs-success);             /* green */
  --tx-status-preview:  var(--ax);                      /* AXING teal */
  --tx-status-error:    var(--bs-danger);               /* red */
  --tx-status-warning:  var(--bs-warning);              /* amber */
  --tx-status-stopping: var(--bs-warning);              /* amber, transient */
}


/* --- Channel-list status pill -------------------------------------------- */
/* Rendered inside the channel table/card status column. Combines a colored
   dot with a label. Color comes from the matching --tx-status-* token. */
.status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.875rem;
  line-height: 1.2;
}
.status::before {
  content: "";
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background-color: var(--tx-status-color, var(--tx-status-idle));
  flex-shrink: 0;
}
.status-idle     { --tx-status-color: var(--tx-status-idle); }
.status-running  { --tx-status-color: var(--tx-status-running); }
.status-preview  { --tx-status-color: var(--tx-status-preview); }
.status-error    { --tx-status-color: var(--tx-status-error); }
.status-warning  { --tx-status-color: var(--tx-status-warning); }
.status-stopping { --tx-status-color: var(--tx-status-stopping); }


/* --- CPU / GPU heatmap --------------------------------------------------- */
/* Original UI wrote inline rgb() per cell from loadToRGB(load). We use a
   data-load attribute (rounded to 10s) + CSS step classes so the palette
   is themable and overrideable. The hue ramp goes green -> amber -> red,
   matching the original's intent. Dark-mode uses identical hues (the
   alpha mixes against the dark surface naturally). */
.tx-load-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 3px;
  counter-reset: core-counter;
}
@media (min-width: 576px) {
  .tx-load-grid {
    grid-template-columns: repeat(8, 1fr);
  }
}
.tx-core-cell {
  padding: 0.3rem 0.25rem 0.25rem;
  text-align: center;
  font-size: 0.875rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  border-radius: var(--bs-border-radius-sm);
  background-color: var(--tx-load-bg, var(--bs-tertiary-bg));
  color: var(--tx-load-fg, var(--bs-body-color));
  counter-increment: core-counter;
}
.tx-core-cell::before {
  content: "Core " counter(core-counter);
  display: block;
  font-size: 0.875rem;
  font-weight: 400;
  opacity: 0.65;
  margin-bottom: 0.15rem;
}
/* GPU heatmap shares the cell markup but reports a single value, not
   per-core load — override the label so it doesn't read "Core 1". */
#tblGpu .tx-core-cell::before {
  content: "GPU";
}
[data-load="0"]   { --tx-load-bg: rgba( 46, 158,  92, 0.15); }   /* green */
[data-load="10"]  { --tx-load-bg: rgba( 46, 158,  92, 0.22); }
[data-load="20"]  { --tx-load-bg: rgba( 46, 158,  92, 0.32); }
[data-load="30"]  { --tx-load-bg: rgba( 46, 158,  92, 0.45); }
[data-load="40"]  { --tx-load-bg: rgba(212, 154,  29, 0.35); }   /* amber */
[data-load="50"]  { --tx-load-bg: rgba(212, 154,  29, 0.50); }
[data-load="60"]  { --tx-load-bg: rgba(212, 154,  29, 0.65); }
[data-load="70"]  { --tx-load-bg: rgba(200,  69,  61, 0.45); }   /* red */
[data-load="80"]  { --tx-load-bg: rgba(200,  69,  61, 0.60); }
[data-load="90"]  { --tx-load-bg: rgba(200,  69,  61, 0.75); }
[data-load="100"] { --tx-load-bg: rgba(200,  69,  61, 0.88); --tx-load-fg: #fff; }


/* --- Channels section — column-width overrides -------------------------- */
/* Three layout stages on top of the col-md-5 / col-md-7 markup:
   - <1140px:    5/7 (narrow list, wide editor — gives form fields room)
   - 1140-1439:  6/6 (equal split — list is still the card stack)
   - ≥1440px:    7/5 (wide list — gives the now-table-mode list room) */
@media (min-width: 1140px) {
  .tx-channels-row > [class*="col-"] {
    flex: 0 0 auto;
    width: 50%;
  }
}
@media (min-width: 1440px) {
  .tx-channels-row > [class*="col-"]:nth-child(1) { width: 58.33333%; }
  .tx-channels-row > [class*="col-"]:nth-child(2) { width: 41.66667%; }
}


/* --- Channel list — desktop table ---------------------------------------- */
/* From 1440px up we render a Bootstrap table inside #channelList. Below
   that, the card stack takes over. Default display is hidden for the table
   and visible for the card stack; the media query flips that.
   Columns: #, Source, Interface, Label, Status, Actions. The supplier UI used
   colResizable for column widths; we fix sensible widths via CSS instead.
   1440px is custom (BS' xxl = 1400px) — picked so the table only shows when
   the col-xl-8 channels column has comfortable headroom for 6 columns. */
#channelList .channel-table { display: none; }
@media (min-width: 1440px) {
  #channelList .channel-table { display: table; }
  #channelList .channel-card-stack { display: none; }

  #channelList .channel-table th,
  #channelList .channel-table td {
    vertical-align: middle;
  }
  #channelList .channel-table th:first-child,
  #channelList .channel-table td:first-child { width: 3rem; text-align: center; }
  #channelList .channel-table th.col-source,
  #channelList .channel-table td.col-source {
    max-width: 0; /* enables ellipsis on flex-grow column */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #channelList .channel-table th.col-bind,
  #channelList .channel-table td.col-bind   { width: 6rem; }
  #channelList .channel-table th.col-memo,
  #channelList .channel-table td.col-memo   { width: 10rem; }
  #channelList .channel-table th.col-status,
  #channelList .channel-table td.col-status { width: 8rem; }
  /* col-actions holds: btn-group [play|stop|preview] + gap + danger
     trash button. ~12rem leaves a hair of room on small md viewports. */
  #channelList .channel-table th.col-actions,
  #channelList .channel-table td.col-actions { width: 12rem; text-align: right; }

  #channelList .channel-table tbody tr { cursor: pointer; }
  #channelList .channel-table tbody tr.selected,
  #channelList .channel-table tbody tr.selected:hover {
    /* Beat .table-hover's --bs-table-hover-bg via a strong tinted bg on
       both td (cells) and tr (border-fallback). The inset border-left
       lives on the row's first td so it doesn't get clipped by the
       cell-by-cell background. */
    --bs-table-bg: rgba(var(--ax-rgb), 0.10);
    --bs-table-hover-bg: rgba(var(--ax-rgb), 0.10);
  }
  #channelList .channel-table tbody tr.selected td:first-child {
    box-shadow: inset 3px 0 0 var(--ax);
  }
}


/* --- Channel list — mobile card stack ----------------------------------- */
/* Below 1440px we drop the table for a stack of compact cards with
   source-as-title, status-as-badge, action-buttons-as-toolbar. The stack's
   display:none above 1440px lives in the table block above. */
.channel-card {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--bs-border-color);
  cursor: pointer;
}
.channel-card:last-child { border-bottom: 0; }
.channel-card.selected {
  background-color: rgba(var(--ax-rgb), 0.10);
  box-shadow: inset 3px 0 0 var(--ax);
}
.channel-card-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 500;
}
.channel-card-head .channel-num {
  flex-shrink: 0;
  width: 2rem;
  font-variant-numeric: tabular-nums;
  color: var(--bs-secondary-color);
  font-weight: 400;
}
.channel-card-head .channel-source {
  flex-grow: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--bs-font-monospace);
  font-size: 0.85rem;
}
.channel-card-meta {
  font-size: 0.8rem;
  color: var(--bs-secondary-color);
  padding-left: 2rem;
  min-height: 1em;
}
.channel-card-actions {
  display: flex;
  gap: 0.25rem;
  padding-left: 2rem;
  margin-top: 0.25rem;
  flex-wrap: wrap;
}


/* --- Section switching --------------------------------------------------- */
/* main contains all top-level sections; only the active one is visible.
   JS toggles .d-none on the others when an aside nav-link is clicked. */
main > section { scroll-margin-top: calc(var(--ax-navbar-h, 56px) + 1rem); }


/* --- Editor card --------------------------------------------------------- */
/* On lg+ the editor card sticks beside the channel list, with a max-height
   so a long form scrolls inside the card rather than running off-viewport.
   Top offset = navbar height + small breathing gap. */
@media (min-width: 992px) {
  #channelEditor {
    position: sticky;
    top: calc(var(--ax-navbar-h, 56px) + 1rem);
    max-height: calc(100dvh - var(--ax-navbar-h, 56px) - 2rem);
    overflow-y: auto;
  }
}

/* Editor form — slim labels, fieldset legends with leading checkbox */
.channel-editor-body fieldset > legend {
  font-size: 0.95rem;
  font-weight: 500;
  border-bottom: 0;
}
.channel-editor-body .form-label {
  margin-bottom: 0.15rem;
  color: var(--bs-secondary-color);
}

/* Edit-mode visual cue: subtle teal outline + slightly elevated shadow.
   Outline (not border) keeps the layout from shifting on toggle. */
#channelEditor.editing {
  outline: 2px solid var(--ax);
  outline-offset: -2px;
  box-shadow: 0 0 0 4px rgba(var(--ax-rgb), 0.10), var(--axing-shadow-1, 0 1px 3px rgba(0,0,0,.08));
}

/* Modal body — preserve '\n' line breaks coming from JS strings.
   Consumed by app/js/modal.js (window.modal.alert / .confirm). */
#appModal .modal-body {
  white-space: pre-line;
}

/* Picker tables (DeckLink devices/formats, NDI sources). Click-to-select
   with a tinted highlight on tr.selected. Hover-state from Bootstrap's
   .table-hover stays compatible. */
.tx-pick-table tbody tr {
  cursor: pointer;
}
.tx-pick-table tbody tr.selected,
.tx-pick-table tbody tr.selected:hover {
  --bs-table-bg: rgba(var(--ax-rgb), 0.12);
  --bs-table-hover-bg: rgba(var(--ax-rgb), 0.12);
}
.tx-pick-table tbody tr.selected td:first-child {
  box-shadow: inset 3px 0 0 var(--ax);
}

/* Picker-table scroll wrappers — three preset heights matching typical
   content (devices < formats < sources). Used inside .table-responsive
   on DeckLink + NDI modals. */
.tx-pick-scroll-sm { max-height:  9rem; }
.tx-pick-scroll-md { max-height: 14rem; }
.tx-pick-scroll-lg { max-height: 18rem; }

/* Adv-Format MPEG-TS column widths. Positional :nth-child rules are
   acceptable here because the modal markup is static and the column
   order won't change without a corresponding CSS update. */
.tx-fmt-pid-table  th:nth-child(1) { width: 40%; }
.tx-fmt-pid-table  th:nth-child(2) { width: 50%; }
.tx-fmt-pid-table  th:nth-child(3) { width: 10%; }
.tx-fmt-prog-table th:last-child   { width: 2.5rem; }

/* Misc helpers used in modals + editor. */
.tx-input-narrow  { width: 6rem; }       /* NDI search-timeout input */
.tx-editing-badge { font-size: 0.65em; } /* "editing" pill in editor card-header */

/* HLS preview player — black-letterbox container, fills modal width,
   capped at 70vh so the modal doesn't shoot off-screen on small viewports. */
.tx-preview-body {
  min-height: 12rem;
}
.tx-preview-video {
  max-height: 70vh;
  background: #000;
}
.tx-preview-url {
  max-width: 100%;
  font-size: 0.75rem;
}


/* MBR (multi-bitrate) tabs — compact pills, just an index per target */
.tx-mbr-tabs {
  --bs-nav-pills-link-active-bg: var(--ax);
  --bs-nav-link-padding-y: 0.15rem;
  --bs-nav-link-padding-x: 0.6rem;
  --bs-nav-link-font-size: 0.85rem;
  flex-wrap: wrap;
  gap: 0.25rem;
}
.tx-mbr-tabs .nav-link {
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--bs-border-color);
}
.tx-mbr-tabs .nav-link.active {
  border-color: var(--ax);
}
