/* AnexConnect — application styles on top of the vendor theme.
 *
 * Additions only.  Not one colour here is new: the accent is the theme's own
 * .btn-primary blue (#4d83ff), and sidebar, navbar and card backgrounds are
 * left exactly as style.css sets them.  What this file does own is typography,
 * spacing and the small set of components the portal's pages need.
 *
 * Two rules keep it coherent:
 *   1. One type scale.  Sizes come from the --fs-* tokens below, never from a
 *      number typed at the point of use.  The theme mixes 1rem, .875rem, .9rem
 *      and .95rem for the same kind of text, which is what made the pages look
 *      unsettled.
 *   2. One spacing rhythm.  Vertical space is a multiple of 4px, so headings,
 *      cards and tables line up instead of each carrying its own margin.
 */

:root {
  /* --- palette (unchanged, carried over from the theme) --- */
  --ac-accent: #4d83ff;
  --ac-accent-soft: rgba(77, 131, 255, 0.09);
  --ac-border: #ebedf2;
  --ac-muted: #9b9b9b;
  --ac-ink: #3f4254;
  --ac-good: #34b1aa;
  --ac-bad: #fe7c96;

  /* --- type --- */
  --ac-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --ac-font-mono: "JetBrains Mono", "SFMono-Regular", Menlo, Consolas,
             "Liberation Mono", monospace;

  --fs-xs:   0.6875rem;   /* 11px — pills, captions, table meta            */
  --fs-sm:   0.75rem;     /* 12px — column headers, labels, helper figures */
  --fs-base: 0.8125rem;   /* 13px — table cells, form controls, body copy  */
  --fs-md:   0.875rem;    /* 14px — emphasised body, card titles          */
  --fs-lg:   1rem;        /* 16px — page title                            */
  --fs-xl:   1.5rem;      /* 24px — stat tile figures                     */

  /* --- rhythm --- */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --radius: 6px;
}

/* ---------------------------------------------------------------------------
 * One family, everywhere.
 *
 * The theme declares a font stack in eight separate places, so a page ended up
 * mixing the system stack with whatever a component set.  This settles it in
 * one rule.  Icon glyphs live on ::before pseudo-elements and keep the icon
 * font; only real elements are matched here.
 * ------------------------------------------------------------------------- */
body,
body * {
  font-family: var(--ac-font);
}

samp, code, pre, kbd, tt, .mono, .console-log {
  font-family: var(--ac-font-mono);
}

body {
  font-size: var(--fs-base);
  line-height: 1.55;
  color: var(--ac-ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: -0.003em;
}

/* Figures read down a column, not as prose: serials, ports, counts, dates. */
.table td, .table th, .detail-list dd, .stat-value,
samp, code, pre, .mono, .pill, .console-log {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* --- headings ------------------------------------------------------------ */
h1, h2, h3, h4, h5, h6 {
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ac-ink);
  margin: 0 0 var(--sp-3);
  line-height: 1.35;
}
h1 { font-size: var(--fs-lg); }
h2 { font-size: var(--fs-lg); }
h3 { font-size: var(--fs-md); }
h4, h5, h6 { font-size: var(--fs-md); }

p { margin: 0 0 var(--sp-3); }
p:last-child { margin-bottom: 0; }

a { color: var(--ac-accent); }
a:hover { color: var(--ac-accent); }

/* ---------------------------------------------------------------------------
 * Page frame
 * ------------------------------------------------------------------------- */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  min-height: 34px;
}
.page-header .page-title {
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.3;
}
.page-header .form-inline { flex-wrap: nowrap; }

.card {
  border: 1px solid var(--ac-border);
  border-radius: var(--radius);
  box-shadow: none;
}
.card-body { padding: var(--sp-5); }
.card-title {
  font-size: var(--fs-md);
  font-weight: 600;
  margin: 0 0 var(--sp-4);
  letter-spacing: -0.01em;
}
/* The theme's grid-margin is 22.5px, which does not line up with anything
 * else on the page. */
.grid-margin { margin-bottom: var(--sp-5); }
.row + .row { margin-top: 0; }

/* ---------------------------------------------------------------------------
 * Stat tiles
 * ------------------------------------------------------------------------- */
.stat-tile {
  border: 1px solid var(--ac-border);
  border-left-width: 3px;
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-5);
  height: 100%;
  background: #fff;
  transition: box-shadow .18s ease;
}
.stat-tile:hover { box-shadow: 0 2px 12px rgba(0, 0, 0, .06); }
.stat-tile .stat-label {
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ac-muted);
  margin-bottom: var(--sp-1);
  line-height: 1.3;
}
.stat-tile .stat-value {
  font-size: var(--fs-xl);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.025em;
}
.stat-tile .stat-value small {
  font-size: var(--fs-sm);
  font-weight: 400;
  color: var(--ac-muted);
}
.stat-tile.is-accent { border-left-color: var(--ac-accent); }
.stat-tile.is-good   { border-left-color: var(--ac-good); }
.stat-tile.is-bad    { border-left-color: var(--ac-bad); }

/* ---------------------------------------------------------------------------
 * Tables — the portal's main way of showing anything
 * ------------------------------------------------------------------------- */
.table { margin-bottom: 0; color: var(--ac-ink); }

.table thead th,
.table-compact th {
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ac-muted);
  border-top: 0;
  border-bottom: 1px solid var(--ac-border);
  white-space: nowrap;
  padding: var(--sp-2) var(--sp-3);
  vertical-align: middle;
}

.table td,
.table-compact td {
  font-size: var(--fs-base);
  vertical-align: middle;
  padding: var(--sp-2) var(--sp-3);
  border-top: 1px solid var(--ac-border);
  line-height: 1.45;
}

.table tbody tr:hover,
.table-compact tbody tr:hover { background: var(--ac-accent-soft); }

/* A second line inside a cell — a serial under a name, a detail under a
 * status. Kept on its own scale so rows stay one height apart. */
.table td .cell-meta,
.cell-meta {
  display: block;
  font-size: var(--fs-xs);
  color: var(--ac-muted);
  line-height: 1.4;
  margin-top: 1px;
}

/* Wide tables scroll inside their card rather than pushing the page sideways. */
.table-scroll { overflow-x: auto; max-width: 100%; }
.table-scroll-y { overflow-y: auto; max-height: 420px; }

.sortable-header { color: inherit; text-decoration: none; white-space: nowrap; }
.sortable-header:hover { color: var(--ac-accent); text-decoration: none; }
.sortable-header .mdi { opacity: .45; font-size: var(--fs-base); }

/* ---------------------------------------------------------------------------
 * Key/value panels, as two ruled columns.
 *
 * These are <dl>s. Stacked dt-over-dd, a dozen of them ran together into an
 * unreadable column, which is what made the device page feel crowded. As a
 * two-column grid they read like the tables everywhere else in the portal.
 * ------------------------------------------------------------------------- */
.detail-list {
  display: grid;
  grid-template-columns: minmax(130px, 38%) 1fr;
  margin: 0;
  border-top: 1px solid var(--ac-border);
}
.detail-list dt,
.detail-list dd {
  margin: 0;
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--ac-border);
  min-width: 0;
}
.detail-list dt {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ac-muted);
  padding-right: var(--sp-3);
  line-height: 1.45;
}
.detail-list dd {
  font-size: var(--fs-base);
  color: var(--ac-ink);
  word-break: break-word;
  line-height: 1.45;
}
/* Secondary lines inside a value: never crowd the value itself. */
.detail-list dd .text-tiny,
.detail-list dd .cell-meta {
  display: block;
  font-size: var(--fs-xs);
  color: var(--ac-muted);
  margin-top: 1px;
  line-height: 1.4;
}

@media (max-width: 575.98px) {
  .detail-list { grid-template-columns: 1fr; }
  .detail-list dt { border-bottom: 0; padding-bottom: 0; }
}

/* ---------------------------------------------------------------------------
 * Status pills
 * ------------------------------------------------------------------------- */
.pill {
  display: inline-block;
  padding: 1px 9px;
  border-radius: 10px;
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: .01em;
  line-height: 1.6;
  white-space: nowrap;
}
.pill-online   { background: rgba(52, 177, 170, .14); color: #2b8f89; }
.pill-offline  { background: rgba(254, 124, 150, .16); color: #c85a71; }
.pill-unknown  { background: rgba(155, 155, 155, .16); color: #6f6f6f; }
.pill-warning  { background: rgba(255, 191, 0, .18);  color: #a37a00; }
/* The appliance family. Neutral on purpose: it is a category, not a health
   signal, and colouring it like one would make an estate of USGs read as a
   page full of warnings. */
.pill-type     { background: var(--ac-accent-soft); color: var(--ac-accent); }

.badge { font-size: var(--fs-xs); font-weight: 500; padding: 3px 8px; }

/* ---------------------------------------------------------------------------
 * Filter chips -- the device-type filter above the dashboard table
 * ------------------------------------------------------------------------- */
.filter-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.filter-chips .pill {
  padding: 3px 11px;
  text-decoration: none;
  border: 1px solid transparent;
  transition: border-color .12s ease;
}
.filter-chips .pill:hover { border-color: var(--ac-border); text-decoration: none; }
.filter-chips .chip-count {
  font-variant-numeric: tabular-nums;
  opacity: .65;
  margin-left: 3px;
}

/* ---------------------------------------------------------------------------
 * Device detail
 * ------------------------------------------------------------------------- */
.nav-tabs { border-bottom: 1px solid var(--ac-border); margin-bottom: var(--sp-4); }
.nav-tabs .nav-link {
  font-size: var(--fs-base);
  font-weight: 500;
  color: #6c7293;
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-bottom: 2px solid transparent;
}
.nav-tabs .nav-link:hover { border-color: transparent transparent var(--ac-border); }
.nav-tabs .nav-link.active {
  color: var(--ac-accent);
  background: transparent;
  border-bottom-color: var(--ac-accent);
}

.console-log {
  background: #2a2a34;
  color: #d6d6de;
  font-size: var(--fs-xs);
  line-height: 1.5;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  max-height: 300px;
  overflow: auto;
  white-space: pre-wrap;
  margin: 0;
}

.chart-box { position: relative; height: 250px; }
.chart-box-tall { position: relative; height: 310px; }

/* ---------------------------------------------------------------------------
 * Forms
 * ------------------------------------------------------------------------- */
.form-group { margin-bottom: var(--sp-4); }

label {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ac-ink);
  margin-bottom: var(--sp-1);
  line-height: 1.4;
}

.form-control {
  font-size: var(--fs-base);
  line-height: 1.5;
  height: auto;
  padding: 7px var(--sp-3);
  border-radius: 5px;
  border-color: var(--ac-border);
  color: var(--ac-ink);
}
.form-control:focus {
  border-color: var(--ac-accent);
  box-shadow: 0 0 0 3px var(--ac-accent-soft);
}
.form-control::placeholder { color: var(--ac-muted); }
/* A native <select> keeps the platform's own control: different metrics from
   the text inputs beside it, a system dropdown arrow, and on several platforms
   a lighter rendering that reads as placeholder text rather than a chosen
   value. These rules make it the same control as every other field -- same
   ink, same height, same padding -- with one drawn chevron so it still looks
   like something you can open. The arrow is an inline SVG in the existing
   muted colour, so no file is fetched and no new colour is introduced. */
select.form-control:not([multiple]) {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  color: var(--ac-ink);
  background-color: #fff;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='none' stroke='%239b9b9b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  padding-right: var(--sp-6);
}
/* THE reason a <select> looked like empty or hidden text: the vendor theme
   sets

       select.form-control, .dataTables_wrapper select { color: #c9c8c8; }

   which is near-white on a white field, so "Viewer" in the Role box read as a
   faint ghost next to the solid text inputs around it. Restated here for every
   select, single and multiple alike, and at a specificity the theme cannot
   take back. The weight is left alone: .form-control is already 400 for every
   field, so a select matches its neighbours once the colour is right. */
select.form-control,
.form-group select.form-control,
select.form-control:not([multiple]) { color: var(--ac-ink); }
/* Firefox renders the closed value greyed while the list is open unless the
   options carry the colour themselves. */
select.form-control option { color: var(--ac-ink); }
select.form-control:disabled { color: var(--ac-muted); }
textarea.form-control { line-height: 1.5; }

.form-section-title {
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ac-muted);
  margin: var(--sp-6) 0 var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--ac-border);
}
.form-section-title:first-child { margin-top: 0; }

/* A tickbox and its caption are one control, so they are laid out as one: the
 * label wraps the input, and flex keeps them on a single baseline.
 *
 * The selector carries `.form-check` to outrank the theme's
 *
 *     .form-check .form-check-label { display: block; margin-left: 1.75rem; }
 *
 * which is two classes to a bare `.form-check-label`'s one. Left to win, it
 * turned the label back into a block -- so `gap` applied to nothing and the box
 * sat hard against its caption -- and added a 28px indent, the room the theme
 * reserves for the absolutely-positioned box it draws itself and we do not. */
.form-check .form-check-label,
.form-check-label {
  font-weight: 400;
  font-size: var(--fs-base);
  line-height: 1.5;
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  min-height: 0;
  cursor: pointer;
}
/* The vendor theme hides the real control and paints its own box from an
 * `<i class="input-helper">` sibling it expects the markup to carry:
 *
 *     .form-check .form-check-label input { position: absolute; opacity: 0; }
 *
 * These forms do not emit that element, so the theme's rule left a checkbox
 * that occupied space and drew nothing -- a caption with no box beside it.
 * Rather than thread a decorative element through every form, the real control
 * is put back and tinted to the portal accent. Native checkboxes are keyboard
 * and screen-reader correct for free, which a div pretending to be one is not.
 *
 * The selectors carry `.form-check` so they outrank the theme's, and cover both
 * checkboxes and radios: the theme's rule hides any input inside a form-check.
 */
.form-check .form-check-label input[type="checkbox"],
.form-check .form-check-label input[type="radio"],
.form-check-label input[type="checkbox"],
.form-check-label input[type="radio"],
.radio-stack input[type="radio"] {
  position: static;
  opacity: 1;
  filter: none;
  z-index: auto;
  margin: 0;
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  accent-color: var(--ac-accent);
  cursor: pointer;
}
/* Nothing emits one, and an empty inline element still takes a text-space of
 * width -- which is the gap that made the caption sit off-centre. */
.form-check .input-helper { display: none; }

/* Bootstrap's .form-check indents for a floated input this layout does not use. */
.form-check { padding-left: 0; }

/* Radio choices: one per line, box and caption on the same baseline. The
 * template renders `<label>{{ choice.tag }} {{ choice.choice_label }}</label>`,
 * so the label is the row. */
.radio-stack label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  font-size: var(--fs-base);
  margin: 0 0 var(--sp-2);
  cursor: pointer;
}
.radio-stack label:last-of-type { margin-bottom: 0; }
/* Tickboxes read as settings rather than fields, so they sit closer together
   than the text inputs above them. */
.form-group-check { margin-bottom: var(--sp-3); }

.btn {
  font-size: var(--fs-base);
  font-weight: 500;
  letter-spacing: .005em;
  padding: 7px var(--sp-4);
  border-radius: 5px;
  line-height: 1.5;
}
.btn-sm { font-size: var(--fs-sm); padding: 5px var(--sp-3); }
.btn:focus, .btn.focus { box-shadow: 0 0 0 3px var(--ac-accent-soft); }
.btn .mdi { font-size: var(--fs-md); line-height: 1; vertical-align: -2px; }

/* ---------------------------------------------------------------------------
 * Sidebar and navbar
 * ------------------------------------------------------------------------- */
.sidebar .nav .nav-item .nav-link {
  font-size: var(--fs-base);
  padding: 9px var(--sp-5);
}
.sidebar .nav .nav-item .nav-link .menu-title { font-weight: 500; }
.sidebar .nav .nav-item .nav-link i.menu-icon {
  font-size: var(--fs-lg);
  line-height: 1;
}
/* The arrow that says a menu opens.  The theme's own glyph reference was for
 * an older icon font and rendered as blank space; the codepoints in style.css
 * are corrected, and this only settles its size and alignment. */
.sidebar .nav .nav-item .nav-link i.menu-arrow { font-size: var(--fs-base); }
/* Vertical rhythm only. The left padding is load-bearing: the theme draws each
 * sub-item's bullet as an absolutely-positioned ::before with no offsets, so it
 * is placed entirely by this element's padding-left. Overriding that sends
 * every bullet to the far left of the sidebar. */
.sidebar .nav.sub-menu { padding-top: var(--sp-1); padding-bottom: var(--sp-2); }
.sidebar .nav.sub-menu .nav-item .nav-link {
  font-size: var(--fs-base);
  padding: 6px 1rem;
  line-height: 1.45;
}
.navbar .navbar-nav .nav-link { font-size: var(--fs-base); font-weight: 500; }
.navbar .dropdown-menu { font-size: var(--fs-base); }
.dropdown-item { font-size: var(--fs-base); padding: var(--sp-2) var(--sp-4); }

/* Notification rows are <button>s so that clicking one toggles read/unread
   rather than navigating. Bootstrap styles .dropdown-item for anchors, so the
   button defaults -- full width, left alignment, no border -- are restored
   here rather than by adding utility classes to every row. */
.notification-toggle { margin: 0; }
.notification-toggle > .dropdown-item {
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  cursor: pointer;
}
/* Unread is carried by weight and a marker in the gutter, not by a new colour:
   the severity badge on the same row is already doing the colour work. */
.notification-toggle > .dropdown-item.is-unread { font-weight: 600; }
.notification-toggle > .dropdown-item.is-unread .preview-subject::before {
  content: '\2022';
  margin-right: .4rem;
}
/* When the alert was raised. A caption under the message -- smaller than the
   body, so it dates the row without competing with it. Even when the row is
   unread (600 weight), the time stays regular. */
.notification-time {
  font-size: var(--fs-xs);
  font-weight: 400;
  margin-top: 2px;
}
/* RadioSelect option labels (e.g. Alert Type: Real Time / Summary) render as
   bare <label>s and inherit the bold, dark field-caption style. The option is
   body text, not a heading -- match the rest of the page. The caption label
   above the group has no radio inside it, so it stays bold. */
.form-group label:has(> input[type="radio"]) {
  font-weight: 400;
  font-size: var(--fs-base);
  color: var(--ac-ink);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  margin-bottom: 4px;
}
/* Show the whole alert message in the bell. The vendor theme clamps
   .dropdown-item .ellipsis to 200px with text-overflow, so a long alert ends in
   "…". This matches that selector exactly (and loads later) to undo the clamp:
   the message wraps and the card grows to fit. */
.navbar .navbar-menu-wrapper .navbar-nav .nav-item.dropdown .navbar-dropdown .dropdown-item .ellipsis {
  max-width: none;
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  word-break: break-word;
}

/* ---------------------------------------------------------------------------
 * Multi-select dropdown  (progressive enhancement — see portal.js)
 * ------------------------------------------------------------------------- */
.ms-wrap { position: relative; }
.ms-wrap .ms-native { display: none; }

.ms-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  text-align: left;
  cursor: pointer;
  background: #fff;
}
.ms-toggle:focus { outline: 0; box-shadow: 0 0 0 3px var(--ac-accent-soft); }
.ms-toggle .ms-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-toggle .ms-summary.is-empty { color: var(--ac-muted); }
.ms-toggle .ms-count {
  flex: none;
  margin-left: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 500;
  padding: 0 7px;
  border-radius: 9px;
  background: var(--ac-accent-soft);
  color: var(--ac-accent);
}
.ms-toggle .ms-caret {
  flex: none;
  margin-left: var(--sp-1);
  color: var(--ac-muted);
  font-size: var(--fs-md);
  transition: transform .15s ease;
}

.ms-panel {
  display: none;
  position: absolute;
  z-index: 1000;
  left: 0;
  right: 0;
  margin-top: 3px;
  background: #fff;
  border: 1px solid var(--ac-border);
  border-radius: var(--radius);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .11);
}
.ms-wrap.is-open .ms-panel { display: block; }
.ms-wrap.is-open .ms-caret { transform: rotate(180deg); }

.ms-panel .ms-search { padding: var(--sp-2); border-bottom: 1px solid var(--ac-border); }
.ms-panel .ms-actions {
  display: flex;
  gap: var(--sp-4);
  align-items: center;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--ac-border);
}
.ms-panel .ms-actions button {
  border: 0;
  background: none;
  padding: 0;
  color: var(--ac-accent);
  font-size: var(--fs-sm);
  font-weight: 500;
  cursor: pointer;
}
.ms-panel .ms-actions button:disabled { color: var(--ac-muted); cursor: default; }
.ms-panel .ms-actions .ms-chosen {
  margin-left: auto;
  color: var(--ac-muted);
  font-size: var(--fs-xs);
}

.ms-options { max-height: 230px; overflow-y: auto; margin: 0; padding: var(--sp-1) 0; list-style: none; }
.ms-options li { margin: 0; }
.ms-options label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 5px var(--sp-3);
  font-size: var(--fs-base);
  font-weight: 400;
  cursor: pointer;
}
.ms-options label:hover { background: var(--ac-accent-soft); }
.ms-options input { margin: 0; flex: none; }
.ms-options li.is-filtered { display: none; }
.ms-empty {
  padding: var(--sp-3);
  font-size: var(--fs-base);
  color: var(--ac-muted);
  text-align: center;
}

/* ---------------------------------------------------------------------------
 * Sign-in
 * ------------------------------------------------------------------------- */
.login-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6);
  background: #f4f5f7;
}
.login-card {
  width: 100%;
  max-width: 380px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 26px rgba(0, 0, 0, .09);
  padding: var(--sp-6) var(--sp-6) var(--sp-5);
}
.login-card .login-logo { display: block; margin: 0 auto var(--sp-6); max-width: 190px; }
.login-card .form-group { margin-bottom: var(--sp-3); }
.login-card .btn { width: 100%; padding: 9px var(--sp-4); }
.login-meta { text-align: center; margin-top: var(--sp-4); font-size: var(--fs-base); }
.login-meta a { font-weight: 500; }
.login-lead {
  font-size: var(--fs-base);
  color: var(--ac-muted);
  margin-bottom: var(--sp-4);
  line-height: 1.5;
}
/* The one-time code field. Wide tracking and a monospace face because these are
   read off a phone and typed under mild time pressure -- 0/O and 1/l have to be
   distinguishable at a glance. */
.otp-input {
  font-family: var(--ac-font-mono);
  font-size: 20px;
  letter-spacing: .35em;
  text-align: center;
}

/* ---------------------------------------------------------------------------
 * Two-factor enrolment
 * ------------------------------------------------------------------------- */
.enrol-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-4);
  align-items: start;
}
@media (max-width: 767.98px) { .enrol-grid { grid-template-columns: 1fr; } }
.enrol-qr {
  background: #fff;
  border: 1px solid var(--ac-border);
  border-radius: 6px;
  padding: var(--sp-3);
  line-height: 0;
}
.enrol-qr svg { width: 190px; height: 190px; }
.secret-key {
  font-family: var(--ac-font-mono);
  font-size: var(--fs-base);
  letter-spacing: .08em;
  background: var(--ac-accent-soft);
  border-radius: 4px;
  padding: 6px 10px;
  display: inline-block;
  word-break: break-all;
}
/* Recovery codes are shown once and are meant to be printed or pasted
   somewhere safe, so they are laid out to be read and copied as a block. */
.recovery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--sp-2);
  margin: var(--sp-3) 0;
}
.recovery-grid code {
  font-family: var(--ac-font-mono);
  font-size: var(--fs-base);
  letter-spacing: .06em;
  background: var(--ac-accent-soft);
  border-radius: 4px;
  padding: 6px 10px;
  text-align: center;
}
.channel-fields[hidden], .auth-fields[hidden],
.summary-only[hidden] { display: none; }
/* The authentication fields belong to the choice above them, so they are
   indented to read as part of it rather than as three more top-level fields. */
.auth-fields {
  border-left: 2px solid var(--ac-border);
  padding-left: var(--sp-3);
  margin-bottom: var(--sp-3);
}

/* ---------------------------------------------------------------------------
 * Alerts, pagination, empty states
 * ------------------------------------------------------------------------- */
.alert {
  font-size: var(--fs-base);
  padding: var(--sp-3) var(--sp-4);
  border-radius: 5px;
  margin-bottom: var(--sp-4);
}

.pagination { margin: var(--sp-4) 0 0; }
.page-link { font-size: var(--fs-base); padding: 5px var(--sp-3); }

.empty-state {
  text-align: center;
  padding: var(--sp-6) var(--sp-5);
  color: var(--ac-muted);
  font-size: var(--fs-base);
}
.empty-state .mdi {
  font-size: 2rem;
  opacity: .3;
  display: block;
  margin-bottom: var(--sp-2);
}

samp, .mono { font-size: var(--fs-base); }
.text-tiny { font-size: var(--fs-xs); line-height: 1.4; }
.text-muted { color: var(--ac-muted) !important; }

/* ---------------------------------------------------------------------------
 * Footer
 * ------------------------------------------------------------------------- */
.footer {
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--fs-sm);
  font-weight: 400;
  color: var(--ac-muted);
}
.footer samp { font-size: var(--fs-sm); }

/* ---------------------------------------------------------------------------
 * Utilities
 * ------------------------------------------------------------------------- */
/* Values that are a single token — serials, licence keys, ports, timestamps —
 * must not be broken across lines. The table scrolls instead. */
.nowrap { white-space: nowrap; }

.badge-light {
  background: var(--ac-accent-soft);
  color: var(--ac-ink);
  font-weight: 500;
}

/* ---------------------------------------------------------------------------
 * Wide tables: keep the action column reachable.
 *
 * The device list carries ten columns and will always be wider than a laptop
 * viewport at some point.  Letting it scroll is right; letting the Connect
 * button be the thing that scrolls out of sight is not, because it is the
 * reason the page exists.  So the last column is pinned to the right edge and
 * the rest slides underneath it.
 * ------------------------------------------------------------------------- */
.table-sticky-end th:last-child,
.table-sticky-end td:last-child {
  position: sticky;
  right: 0;
  z-index: 2;
  background: #fff;
}
.table-sticky-end th:last-child::before,
.table-sticky-end td:last-child::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 8px;
  transform: translateX(-100%);
  pointer-events: none;
  background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, .05));
}
/* The row tint is translucent, which a pinned cell cannot inherit — it would
 * show the scrolling columns through itself. This is that tint over white. */
.table-sticky-end tbody tr:hover td:last-child { background: #eff4ff; }

/* A stat whose value is a phrase rather than a count — "0.53 / 1.04 / 0.62",
 * "121.9 MB". Same tile, one step down the scale so it stays on one line. */
.stat-tile .stat-value.stat-value-sm {
  font-size: var(--fs-md);
  letter-spacing: -0.01em;
  line-height: 1.4;
}

/* Data tables hold one value per cell — a device name, a customer, a port, a
 * timestamp. Letting those wrap gives every row a different height and the
 * ragged left edge that made these pages look unsettled. They stay on one line
 * and the table scrolls instead, with its action column pinned.
 *
 * Cells that really do hold a sentence opt out with .wrap. */
.table-compact td,
.table-compact th { white-space: nowrap; }
.table-compact td.wrap,
.table-compact th.wrap { white-space: normal; min-width: 220px; }
