/*
 * Copyright (C) 2025-2026 Virtastic - https://virtastic.app
 * SPDX-License-Identifier: GPL-3.0-or-later | part of openmw-web
 *
 * THE LAUNCHER'S SKIN, OVER ADMINLTE.
 *
 * Values below are lifted verbatim from play/launcher.html's :root, not approximated, so the
 * two surfaces cannot drift apart by a shade. The dashboard used to be a light Bootstrap page
 * with an ember accent while the launcher was dark volcanic with bronze hardware: same
 * product, two visual worlds, and the admin side looked like somebody else's software.
 *
 * What carries over: the palette, the ground, Palatino headings over Optima body, buttons as
 * small uppercase letterspaced plates with a gold-to-bronze primary, and bronze hairlines on
 * panels lit by a 160deg gradient.
 *
 * What deliberately does NOT: the corner brackets. They are the launcher's signature because
 * it shows four cards; this page shows thirty settings sections and a dozen tables, and a
 * bracket on each would be wallpaper. They appear once, on the wizard card, where the page is
 * a plaque rather than a dashboard.
 */

:root {
  /* Volcanic Vvardenfell: ash + Dwemer bronze + a low Red Mountain ember.
     Verbatim from play/launcher.html. */
  --vt-ash: #0c0a09;
  --vt-panel: #16110d;
  --vt-panel-2: #1e1711;
  --vt-line: #3a2e20;
  --vt-ink: #ece0c6;
  --vt-muted: #a5987e;
  --vt-faint: #7c705c;
  --vt-bronze: #8a6838;
  --vt-bronze-hi: #c69a52;
  --vt-gold: #d3a84e;
  --vt-gold-hi: #eecb78;
  --vt-ember: #c65a2e;
  --vt-err: #e0836a;
  --vt-ok: #a9c48a;

  --vt-serif: "Hoefler Text", "Baskerville", "Palatino Linotype", Palatino, Georgia, serif;
  --vt-sans: "Optima", "Gill Sans", "Gill Sans MT", -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Bootstrap's own tokens, repointed. Restating these rather than fighting each component
     later is what keeps the override sheet short. */
  --bs-body-bg: var(--vt-ash);
  --bs-body-color: var(--vt-ink);
  --bs-body-font-family: var(--vt-sans);
  --bs-emphasis-color: var(--vt-ink);
  --bs-secondary-color: var(--vt-muted);
  --bs-tertiary-bg: var(--vt-panel);
  --bs-border-color: var(--vt-line);
  --bs-primary: var(--vt-gold);
  --bs-primary-rgb: 211, 168, 78;
  --bs-link-color: var(--vt-gold);
  --bs-link-hover-color: var(--vt-gold-hi);
  --bs-heading-color: var(--vt-ink);
  --bs-code-color: var(--vt-bronze-hi);
}

body {
  background: var(--vt-ash);
  color: var(--vt-ink);
  font-family: var(--vt-sans);
  -webkit-font-smoothing: antialiased;
}

/* THE GROUND IS THREE LAYERS, copied from play/launcher.html rather than approximated.
   A flat ash was close enough to look deliberate and wrong enough to notice next to the
   launcher: what is missing is the light. Red Mountain glows from BELOW the viewport
   (at 132%, off the bottom edge), not from above, and a grain sits over everything to kill
   the flat-digital look. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(150% 72% at 50% 132%, rgba(198, 90, 46, .30), rgba(198, 90, 46, .05) 34%, transparent 60%),
    radial-gradient(90% 60% at 50% 134%, rgba(233, 180, 90, .16), transparent 56%);
  animation: vt-ember 9s ease-in-out infinite;
}
/* Faint ashfall grain. */
body::after {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: .05; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@keyframes vt-ember { 0%, 100% { opacity: .82 } 50% { opacity: 1 } }

/* Those layers sit at negative z-index, so every ancestor between them and the content has
   to stay transparent or the atmosphere is painted over and the ground goes flat again.
   AdminLTE gives app-main its own background, which did exactly that. */
.app-wrapper, .app-main, .app-content, .app-content-header { background: transparent !important; }

h1, h2, h3, h4, h5, .card-title, #pageTitle {
  font-family: var(--vt-serif);
  color: var(--vt-ink);
  letter-spacing: .005em;
}
#pageTitle { font-weight: 600; }

/* --- bare chrome: login and first-run --------------------------------------------------- */
/* Signed out, the page is a centred sheet, not a dashboard with nobody in it. The ember
   bloom overhead is the launcher's own Red Mountain glow. */
body.vt-bare .app-sidebar,
body.vt-bare .app-header,
body.vt-bare .app-footer { display: none; }
body.vt-bare .app-wrapper { display: block; }
/* No bloom of its own: the real one is on body::before, rising from below like the
   launcher's. This used to paint a second, top-down glow that fought it. */
body.vt-bare .app-main { min-height: 100vh; margin-left: 0; }
body.vt-bare .app-content-header { padding-top: 3rem; text-align: center; }
body.vt-bare .app-content-header .breadcrumb { display: none; }
body.vt-bare .app-content-header .row > div { width: 100%; }
body.vt-bare #pageTitle {
  color: var(--vt-ink);
  text-shadow: 0 1px 0 rgba(0, 0, 0, .5), 0 0 34px rgba(211, 168, 78, .10);
}

/* --- chrome ------------------------------------------------------------------------- */
.app-header {
  background: var(--vt-panel) !important;
  border-bottom: 1px solid var(--vt-line) !important;
}
.app-header .nav-link { color: var(--vt-muted); }
.app-header .nav-link:hover { color: var(--vt-gold-hi); }

.app-sidebar { background: var(--vt-panel) !important; border-right: 1px solid var(--vt-line); }
.sidebar-brand { background: var(--vt-panel-2); border-bottom: 1px solid var(--vt-line); }
/* The mark is wide (an infinity knot), so it gets width and keeps its own aspect; 28px and
   the AdminLTE opacity-75 dim made it read as a smudge. Full strength, sized to the bar. */
.brand-image { width: 40px; height: auto; margin-right: .5rem; }

/* THE MARK AND THE WORDMARK SIT ON ONE BASELINE. AdminLTE lays the brand out as inline
   content, so the image and the text each aligned to whatever the line box gave them: the
   knot rode high against a wordmark set in a serif with its own ascent, and the pair drifted
   left of the menu icons below. A flex row with a fixed height centres both against each
   other and against the bar, and the padding lines the mark up with the icon column of the
   navigation rather than hanging outside it. */
.sidebar-brand .brand-link {
  display: flex;
  align-items: center;
  height: 3.5rem;
  padding: 0 .85rem;
}
.sidebar-brand .brand-link .brand-image { margin: 0 .6rem 0 0; flex: 0 0 auto; }
.sidebar-brand .brand-link .brand-text { line-height: 1; }
/* AdminLTE sets .brand-link colour on the anchor, which wins over a bare .brand-text rule
   by specificity. Scoped here so the gold actually lands rather than only appearing to. */
.sidebar-brand .brand-link .brand-text {
  color: var(--vt-gold-hi); font-family: var(--vt-serif); letter-spacing: .02em;
}

/* AdminLTE drives the sidebar entirely through its own --lte-* variables, from selectors
   like `.sidebar-wrapper .sidebar-menu > .nav-item > .nav-link`. Out-specifying every one of
   those is a losing game and leaves the next AdminLTE update to break it; setting the
   variables it already reads is the same result in five lines. */
/* The attribute selector is load-bearing, not decoration: AdminLTE ships
   `[data-bs-theme=dark] .app-sidebar { --lte-sidebar-color: … }`, which at (0,2,0) beats a
   bare `.app-sidebar`. Matching its shape ties the specificity and this sheet loads second,
   so the cascade settles it. */
[data-bs-theme] .app-sidebar,
.app-sidebar {
  --lte-sidebar-color: var(--vt-muted);
  --lte-sidebar-hover-color: var(--vt-ink);
  --lte-sidebar-hover-bg: rgba(211, 168, 78, .08);
  --lte-sidebar-menu-active-color: var(--vt-gold-hi);
  --lte-sidebar-menu-active-bg: rgba(211, 168, 78, .14);
  --lte-sidebar-submenu-bg: transparent;
  --lte-sidebar-header-color: var(--vt-bronze-hi);
}
.sidebar-menu .nav-link { border-radius: 4px; margin: 1px .5rem; }
.sidebar-menu .nav-link.active { box-shadow: inset 2px 0 0 var(--vt-gold); }
.sidebar-menu .nav-header {
  color: var(--vt-bronze-hi); text-transform: uppercase; font-size: .68rem;
  letter-spacing: .12em; padding: .85rem 1rem .3rem;
}
.app-footer {
  background: var(--vt-panel) !important; border-top: 1px solid var(--vt-line) !important;
  color: var(--vt-faint);
}

/* --- buttons: small uppercase plates, as the launcher sets them --------------------- */
.btn {
  font: 600 12.5px/1 var(--vt-sans);
  letter-spacing: .12em; text-transform: uppercase;
  padding: 12px 20px; border-radius: 4px; border: 1px solid transparent;
}
.btn-sm { padding: 8px 13px; font-size: 11.5px; }
.btn-lg { padding: 14px 26px; font-size: 13px; }
.btn-primary {
  background: var(--vt-gold);
  color: #1a1207; border-color: var(--vt-gold-hi);
}
.btn-primary:hover, .btn-primary:focus {
  background: var(--vt-gold-hi);
  color: #1a1207; border-color: var(--vt-gold-hi);
}
.btn-outline-secondary, .btn-secondary {
  background: transparent; color: var(--vt-gold); border-color: var(--vt-bronze);
}
.btn-outline-secondary:hover, .btn-secondary:hover {
  background: rgba(211, 168, 78, .10); color: var(--vt-gold-hi); border-color: var(--vt-gold);
}
.btn-danger, .btn-outline-danger {
  background: transparent; color: var(--vt-err); border-color: rgba(224, 131, 106, .5);
}
.btn-danger:hover, .btn-outline-danger:hover {
  background: rgba(224, 131, 106, .12); color: var(--vt-err); border-color: var(--vt-err);
}
.btn-warning { background: transparent; color: var(--vt-gold); border-color: var(--vt-bronze); }
.btn-warning:hover { background: rgba(211, 168, 78, .12); color: var(--vt-gold-hi); }
.btn:focus-visible { outline: 2px solid var(--vt-gold); outline-offset: 3px; }
.btn-tool { border: 0; color: var(--vt-muted); padding: 4px 8px; }
.btn-tool:hover { color: var(--vt-gold-hi); background: none; }
/* The tri-state marks and inline links inside prose must not become plates. */
.btn-link { text-transform: none; letter-spacing: 0; font-weight: 400; }

/* --- panels: bronze hairline over a lit gradient ------------------------------------ */
.card, .small-box, .accordion-item {
  background: linear-gradient(160deg, var(--vt-panel-2), var(--vt-panel));
  border: 1px solid rgba(138, 104, 56, .38);
  border-radius: 7px;
  color: var(--vt-ink);
}
.card-header {
  background: transparent; border-bottom: 1px solid var(--vt-line);
  display: flex; align-items: center; gap: .5rem;
}
.card-header .card-title { float: none; margin: 0; font-size: 1.05rem; }
/* AdminLTE redefines Bootstrap's .card-title as `float:left; margin:0`, which is right for
   a card HEADER (title left, tool buttons right) and wrong everywhere else: inside a card
   body the heading floats and the next paragraph wraps around it. */
.card-body > .card-title,
.card-body > h5.card-title {
  float: none; display: block; margin-bottom: .5rem; font-size: 1.05rem; font-weight: 600;
}
/* AdminLTE's coloured outlines become bronze-family hairlines: on this ground its stock
   blues and greens read as another product's palette leaking through. */
.card-outline { border-top: 2px solid var(--vt-bronze); }
.card-primary.card-outline { border-top-color: var(--vt-gold); }
.card-success.card-outline { border-top-color: var(--vt-ok); }
.card-warning.card-outline { border-top-color: var(--vt-ember); }
.card-danger.card-outline { border-top-color: var(--vt-err); }
.card-info.card-outline, .card-secondary.card-outline { border-top-color: var(--vt-bronze); }

.small-box .inner h3 { font-family: var(--vt-serif); color: var(--vt-ink); font-weight: 600; }
.small-box .inner p { color: var(--vt-muted); }
.small-box .small-box-icon { color: rgba(211, 168, 78, .16); }
.small-box .small-box-footer { color: var(--vt-bronze-hi); background: rgba(0, 0, 0, .18); }
.small-box .small-box-footer:hover { color: var(--vt-gold-hi); }
/* text-bg-* on a small-box paints a stock Bootstrap fill, which is the loudest way the old
   palette shows through. Neutralise the fill and let the accent live in the icon and rule.
   Matched on the PREFIX rather than a list of four: the list silently did not cover
   text-bg-info, so the first card to use it came back in Bootstrap cyan. A neutraliser that
   has to be extended every time somebody picks a colour is not neutralising anything. */
.small-box[class*="text-bg-"] {
  background: linear-gradient(160deg, var(--vt-panel-2), var(--vt-panel)) !important;
  color: var(--vt-ink) !important;
}
.small-box.text-bg-primary { border-top: 2px solid var(--vt-gold); }
.small-box.text-bg-success { border-top: 2px solid var(--vt-ok); }
.small-box.text-bg-warning { border-top: 2px solid var(--vt-ember); }
.small-box.text-bg-secondary, .small-box.text-bg-info { border-top: 2px solid var(--vt-bronze); }

/* EQUAL HEIGHT ACROSS THE ROW. Some stat cards carry a second line ("58 MB of 15.5 GB") and
   some do not, which left the row ragged: the COLUMNS stretch to the tallest, but the box
   inside each one was only as tall as its own text, so half the cards floated short with a
   gap under them. Fill the column, and pin the footer to the bottom so the strips line up
   even when the cards above them hold different amounts of text. */
.row > [class*="col-"] > .small-box { height: 100%; display: flex; flex-direction: column; }
.small-box > .inner { flex: 1 1 auto; }
.small-box > .small-box-footer { margin-top: auto; }

.accordion-button {
  background: var(--vt-panel-2); color: var(--vt-ink); font-family: var(--vt-serif);
}
.accordion-button:not(.collapsed) { background: var(--vt-panel); color: var(--vt-gold-hi); }
.accordion-button:focus { box-shadow: 0 0 0 .2rem rgba(211, 168, 78, .2); }
.accordion-body { background: transparent; }

.callout {
  background: rgba(211, 168, 78, .07); border-left: 3px solid var(--vt-gold);
  border-radius: 0 6px 6px 0; padding: .9rem 1.1rem;
}
.callout-warning { border-left-color: var(--vt-ember); background: rgba(198, 90, 46, .09); }
.callout-danger { border-left-color: var(--vt-err); background: rgba(224, 131, 106, .09); }
.callout h5 { font-size: 1rem; margin-bottom: .35rem; }

/* --- forms -------------------------------------------------------------------------- */
.form-control, .form-select {
  background: rgba(0, 0, 0, .28); border: 1px solid var(--vt-line); color: var(--vt-ink);
  border-radius: 4px;
}
.form-control:focus, .form-select:focus {
  background: rgba(0, 0, 0, .28); border-color: var(--vt-gold); color: var(--vt-ink);
  box-shadow: 0 0 0 .2rem rgba(211, 168, 78, .16);
}
.form-control::placeholder { color: var(--vt-faint); }
.form-text, .form-label { color: var(--vt-muted); }
.form-label { font-size: .9rem; }
.input-group-text { background: var(--vt-panel-2); border-color: var(--vt-line); color: var(--vt-muted); }
.form-check-input { background-color: rgba(0, 0, 0, .3); border-color: var(--vt-bronze); }
.form-check-input:checked { background-color: var(--vt-gold); border-color: var(--vt-gold-hi); }

.table { --bs-table-color: var(--vt-ink); --bs-table-bg: transparent;
  --bs-table-border-color: var(--vt-line); }
.table > thead th {
  color: var(--vt-bronze-hi); text-transform: uppercase; font-size: .7rem;
  letter-spacing: .1em; font-weight: 600; border-bottom-color: var(--vt-line);
}
.table-hover > tbody > tr:hover > * { background: rgba(211, 168, 78, .06); color: var(--vt-ink); }

.badge.text-bg-success { background: rgba(169, 196, 138, .18) !important; color: var(--vt-ok) !important; }
.badge.text-bg-warning { background: rgba(198, 90, 46, .2) !important; color: var(--vt-gold-hi) !important; }
.badge.text-bg-danger { background: rgba(224, 131, 106, .18) !important; color: var(--vt-err) !important; }
.badge.text-bg-secondary { background: rgba(138, 104, 56, .22) !important; color: var(--vt-bronze-hi) !important; }

.alert { border-radius: 6px; border: 1px solid var(--vt-line); background: var(--vt-panel-2); color: var(--vt-ink); }
.alert-warning { border-color: rgba(198, 90, 46, .45); background: rgba(198, 90, 46, .10); }
.alert-danger { border-color: rgba(224, 131, 106, .45); background: rgba(224, 131, 106, .10); }
.alert-success { border-color: rgba(169, 196, 138, .4); background: rgba(169, 196, 138, .10); }
.alert-info, .alert-secondary { border-color: var(--vt-line); background: rgba(211, 168, 78, .07); }

.modal-content { background: linear-gradient(160deg, var(--vt-panel-2), var(--vt-panel));
  border: 1px solid var(--vt-line); color: var(--vt-ink); }
.modal-header, .modal-footer { border-color: var(--vt-line); }
.dropdown-menu {
  background: var(--vt-panel-2); border: 1px solid var(--vt-line); border-radius: 6px;
}
.dropdown-item { color: var(--vt-ink); }
.dropdown-item:hover { background: rgba(211, 168, 78, .1); color: var(--vt-gold-hi); }
.dropdown-header { color: var(--vt-bronze-hi); text-transform: uppercase;
  font-size: .68rem; letter-spacing: .1em; }
.progress { background: rgba(0, 0, 0, .35); }
.progress-bar { background: var(--vt-gold); }
.progress-bar.bg-success { background: var(--vt-ok); }
.progress-bar.bg-danger { background: var(--vt-err); }
.progress-bar.bg-warning { background: var(--vt-ember); }
.breadcrumb-item, .breadcrumb-item a { color: var(--vt-muted); }
.breadcrumb-item.active { color: var(--vt-bronze-hi); }

/* --- settings forms ---------------------------------------------------------------- */
/* Help sits under the field, always visible rather than behind a hover: a tooltip you have
   to discover is no help to the person who did not know the field existed. */
.vt-field { padding: .85rem 0; border-bottom: 1px solid var(--vt-line); }
.vt-field:last-child { border-bottom: 0; }
.vt-field-key {
  font-family: var(--bs-font-monospace); font-size: .9rem; font-weight: 600;
  color: var(--vt-ink);
}
.vt-field-help { font-size: .85rem; color: var(--vt-muted); margin-top: .35rem; }
.vt-field-danger {
  font-size: .85rem; margin-top: .35rem; padding: .5rem .65rem; border-radius: 4px;
  background: rgba(224, 131, 106, .1); border-left: 3px solid var(--vt-err);
}
.vt-overridden::after {
  content: "edited here"; font-size: .65rem; text-transform: uppercase; letter-spacing: .08em;
  background: var(--vt-gold); color: #1a1207; padding: .1rem .35rem; border-radius: 3px;
  margin-left: .5rem; vertical-align: middle;
}
.vt-section-note {
  font-size: .875rem; background: rgba(211, 168, 78, .07);
  border-left: 3px solid var(--vt-bronze); padding: .7rem .85rem;
  border-radius: 0 4px 4px 0; line-height: 1.5; color: var(--vt-ink);
}
.vt-section-note code, .vt-section-note pre {
  background: rgba(0, 0, 0, .35); color: var(--vt-bronze-hi); border-radius: 3px;
}
.vt-section-note code { padding: .05rem .3rem; }
.vt-section-note pre { padding: .5rem .65rem; }
.vt-section-note > :last-child { margin-bottom: 0; }

.vt-card .table-sm td { padding: .4rem .5rem; border-color: rgba(138, 104, 56, .22); }
.vt-card .table-sm tr:last-child td { border-bottom: 0; }

/* --- wizard ------------------------------------------------------------------------ */
.vt-wizard { max-width: 44rem; margin: 0 auto; padding-top: .5rem; }

/* Named steps, not anonymous ticks: on a twelve-question form it matters that you can see
   where you are and roughly how much is left. */
.vt-steps { display: flex; gap: .3rem; margin-bottom: .35rem; }
.vt-step { flex: 1 1 0; min-width: 0; }
.vt-step-bar {
  display: block; height: 3px; border-radius: 2px; background: var(--vt-line);
  transition: background .2s;
}
.vt-step-label {
  display: block; margin-top: .35rem; font-size: .66rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--vt-faint); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis;
}
.vt-step.done .vt-step-bar { background: var(--vt-bronze); }
.vt-step.now .vt-step-bar { background: var(--vt-gold); }
.vt-step.now .vt-step-label { color: var(--vt-gold-hi); }
.vt-stepcount {
  font-size: .7rem; color: var(--vt-faint); text-align: right; margin: .5rem 0 .85rem;
  text-transform: uppercase; letter-spacing: .1em;
}

/* The wizard card is the one place the launcher's corner brackets belong: it is a single
   plaque carrying the whole conversation, which is exactly the shape they were drawn for. */
.vt-card {
  position: relative;
  background: linear-gradient(160deg, var(--vt-panel-2), var(--vt-panel));
  border: 1px solid rgba(138, 104, 56, .38);
  box-shadow: 0 18px 46px rgba(0, 0, 0, .45);
}
.vt-card::after {
  content: ''; position: absolute; inset: 13px; pointer-events: none; opacity: .7;
  --c: var(--vt-bronze);
  background:
    linear-gradient(var(--c), var(--c)) left top/17px 1.5px no-repeat,
    linear-gradient(var(--c), var(--c)) left top/1.5px 17px no-repeat,
    linear-gradient(var(--c), var(--c)) right top/17px 1.5px no-repeat,
    linear-gradient(var(--c), var(--c)) right top/1.5px 17px no-repeat,
    linear-gradient(var(--c), var(--c)) left bottom/17px 1.5px no-repeat,
    linear-gradient(var(--c), var(--c)) left bottom/1.5px 17px no-repeat,
    linear-gradient(var(--c), var(--c)) right bottom/17px 1.5px no-repeat,
    linear-gradient(var(--c), var(--c)) right bottom/1.5px 17px no-repeat;
}
.vt-card .card-body { padding: 2.1rem; position: relative; z-index: 1; }
.vt-card h5 {
  font-family: var(--vt-serif); font-size: 1.5rem; font-weight: 600;
  margin-bottom: .5rem; letter-spacing: .005em; color: var(--vt-ink);
}
.vt-card > .card-body > p:first-of-type { margin-bottom: 1.15rem; }

.vt-wizard-nav { display: flex; align-items: center; gap: .5rem; margin-top: 1.25rem; }
/* Sits beside a disabled Continue and says what is still needed. */
.vt-need { margin-left: .25rem; text-transform: none; letter-spacing: 0; }

.vt-choice {
  display: block; width: 100%; text-align: left; padding: 1rem 1.15rem; margin-bottom: .6rem;
  border: 1px solid var(--vt-line); border-radius: 5px;
  background: rgba(0, 0, 0, .22); color: var(--vt-ink);
  cursor: pointer; transition: border-color .16s, background .16s, transform .16s;
}
.vt-choice:hover { border-color: rgba(211, 168, 78, .55); transform: translateY(-1px); }
/* An experiment nobody has enabled: readable, obviously not pickable, and it says why.
   Opacity alone would take the explanation down with it, so the note keeps its own colour. */
.vt-choice.vt-locked { opacity: .55; cursor: not-allowed; }
.vt-choice.vt-locked:hover { border-color: var(--vt-line); transform: none; }
.vt-choice .vt-lock-note {
  display: block; margin-top: .5rem; color: var(--vt-gold); opacity: .95;
}
.vt-choice.sel {
  border-color: var(--vt-gold); background: rgba(211, 168, 78, .1);
  box-shadow: inset 2px 0 0 var(--vt-gold);
}
.vt-choice strong { display: block; font-weight: 600; }
.vt-choice small { color: var(--vt-muted); line-height: 1.45; }
/* Checkbox variant: the box sits beside the text rather than inside the flow of it, so a
   two-line description does not tuck itself under the tick. */
.vt-choice.vt-check { display: flex; align-items: flex-start; gap: .65rem; margin-bottom: .5rem; }
.vt-choice.vt-check input { margin-top: .2rem; flex: 0 0 auto; }
.vt-choice.vt-check span { display: block; }
.vt-choice.vt-check small { display: block; }

/* --- console / logs ---------------------------------------------------------------- */
.vt-mono {
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .82rem;
  white-space: pre-wrap; word-break: break-word;
}
.vt-log, .vt-out {
  background: #060505; color: var(--vt-ink); border: 1px solid var(--vt-line);
  border-radius: 5px; padding: .75rem;
}
.vt-log { max-height: 60vh; overflow-y: auto; }
.vt-log div { padding: .1rem 0; border-bottom: 1px solid rgba(255, 255, 255, .04);
  display: flex; gap: .6rem; align-items: baseline; }
/* The clock never wraps and never shrinks; the message wraps inside its own column and stays
   aligned under itself. min-width:0 is what lets a flex child actually wrap instead of
   forcing the row wider than the box. */
.vt-log .ts { flex: 0 0 auto; color: var(--vt-faint); font-variant-numeric: tabular-nums; }
.vt-log .msg { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.vt-log .lvl-error { color: var(--vt-err); }
.vt-log .lvl-warn { color: var(--vt-gold); }
.vt-log .lvl-debug { color: var(--vt-faint); }
.vt-log .ev { color: var(--vt-ok); }
.vt-out { min-height: 6rem; max-height: 40vh; overflow-y: auto; }

/* --- upload ------------------------------------------------------------------------ */
.vt-drop {
  border: 1px dashed var(--vt-bronze); border-radius: 5px; padding: 1.5rem;
  text-align: center; background: rgba(0, 0, 0, .18);
  transition: border-color .16s, background .16s;
}
.vt-drop.over { border-color: var(--vt-gold); background: rgba(211, 168, 78, .1); }

/* --- misc -------------------------------------------------------------------------- */
.vt-drag { cursor: grab; user-select: none; }
.vt-drag:active { cursor: grabbing; }
tr.vt-dragging { opacity: .4; }
.vt-empty { color: var(--vt-faint); text-align: center; padding: 2.5rem 1rem; }
.text-secondary { color: var(--vt-muted) !important; }
.text-success { color: var(--vt-ok) !important; }
.text-danger { color: var(--vt-err) !important; }
code { color: var(--vt-bronze-hi); }

/* AdminLTE's sidebar collapses under lg; make sure long tables scroll instead of pushing
   the layout sideways on a phone, since an operator checking in from one is a real case. */
.table-responsive { -webkit-overflow-scrolling: touch; }
@media (max-width: 575.98px) {
  .app-content-header h3 { font-size: 1.25rem; }
  .vt-wizard { padding: 0 .25rem; }
  .vt-card .card-body { padding: 1.4rem; }
  .vt-card::after { inset: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .vt-choice, .btn, .vt-step-bar { transition: none; }
  body::before { animation: none; }
}

/* --- the loading sheet ---------------------------------------------------------------- */
/* Lifted from play/index.html's #loading, which is what covers the canvas while the engine
   downloads and boots. The dashboard makes an operator wait exactly once, at the restart
   that ends setup, and that moment should look like the product rather than like a Bootstrap
   spinner on an empty page. */
body.vt-loading .app-sidebar,
body.vt-loading .app-header,
body.vt-loading .app-footer,
body.vt-loading .app-content-header { display: none; }
body.vt-loading .app-wrapper { display: block; }

.ld-sheet {
  position: fixed; inset: 0; z-index: 900;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; text-align: center;
  background: var(--vt-ash); color: var(--vt-ink);
}
.ld-sheet .ld-inner { position: relative; width: 100%; max-width: 440px; }
.ld-glyph {
  width: 56px; height: 56px; margin: 0 auto 24px; color: var(--vt-bronze-hi);
  animation: ld-spin 7s linear infinite;
}
.ld-glyph svg { width: 100%; height: 100%; display: block; }
@keyframes ld-spin { to { transform: rotate(360deg); } }
.ld-title {
  margin: 0 0 10px; font-family: var(--vt-serif); font-weight: 600;
  font-size: clamp(28px, 5vw, 40px); letter-spacing: .01em; color: var(--vt-ink);
  text-shadow: 0 1px 0 rgba(0, 0, 0, .5), 0 0 34px rgba(211, 168, 78, .10);
}
.ld-status {
  margin: 0 0 22px; font-size: 11.5px; letter-spacing: .2em; text-transform: uppercase;
  font-weight: 600; color: var(--vt-bronze-hi);
}
.ld-bar {
  position: relative; height: 4px; border-radius: 3px; overflow: hidden;
  background: rgba(138, 104, 56, .28);
}
.ld-fill {
  height: 100%; width: 34%; border-radius: 3px;
  background: linear-gradient(90deg, var(--vt-bronze), var(--vt-gold), var(--vt-gold-hi));
  animation: ld-slide 1.25s ease-in-out infinite;
}
@keyframes ld-slide { 0% { transform: translateX(-110%); } 100% { transform: translateX(330%); } }
.ld-detail {
  margin-top: 13px; min-height: 16px; color: var(--vt-faint);
  font: 12px/1.4 ui-monospace, Menlo, Consolas, monospace;
}
@media (prefers-reduced-motion: reduce) {
  .ld-glyph { animation: none; }
  .ld-fill { animation: none; width: 100%; }
}

/* THE SIDEBAR DOES NOT COLLAPSE.
 *
 * Removing the toggle button takes the control away, but not the STATE: AdminLTE collapses by
 * putting `sidebar-collapse` on the body and remembers that in localStorage, so anyone who had
 * already clicked it would come back to a stripe of unlabelled icons with nothing left on the
 * page to undo it. It also applies the class itself below its `sidebar-expand-lg` breakpoint.
 *
 * So the state is neutralised rather than merely unreachable. This navigation is the only way
 * around the dashboard, the content beside it does not reflow into anything better, and a
 * narrow window is a reason to scroll, not to hide the menu.
 */
body.sidebar-collapse .app-sidebar,
body.sidebar-collapse .app-sidebar .sidebar-brand,
body.sidebar-collapse .app-sidebar .sidebar-wrapper {
  --lte-sidebar-width: 250px;
  width: 250px;
  margin-left: 0;
  overflow: visible;
}
/* AdminLTE hides the labels rather than the bar, so the width alone is not enough. */
body.sidebar-collapse .app-sidebar .nav-link p,
body.sidebar-collapse .app-sidebar .brand-text,
body.sidebar-collapse .app-sidebar .nav-header {
  display: inline-block;
  visibility: visible;
  opacity: 1;
}

/* SIGN-IN PROVIDER BUTTONS: launcher.html's .mp-prov, so the dashboard login matches the
   players' front door (play.html .prov) instead of Bootstrap's grey outline. */
.vt-prov {
  display: flex; align-items: center; justify-content: center; gap: 11px; width: 100%;
  padding: 12px 16px; margin-bottom: 9px; border-radius: 5px; text-decoration: none;
  font: 600 14px/1 "Optima", "Gill Sans", -apple-system, "Segoe UI", sans-serif; letter-spacing: .01em;
  background: #f6f4ee; color: #20242b; border: 1px solid #d9d3c4;
  transition: background .14s ease, transform .06s ease;
}
.vt-prov:hover { background: #fff; color: #20242b; }
.vt-prov:active { transform: translateY(1px); }
.vt-prov:focus-visible { outline: 2px solid #d3a84e; outline-offset: 2px; }
.vt-prov svg { width: 19px; height: 19px; flex: none; }
