/* ===========================================================================
   Alliance printer dashboard - shared stylesheet
   ===========================================================================

   Every page links this file; templates keep only rules unique to themselves.
   Before this existed the same ~939 lines of CSS were pasted into four
   templates, and the tokens had already drifted apart - index.html defined
   --warn and --offline, trends.html did not, and the three page widths were
   800px, 1000px and 1000px by accident rather than intent.

   THEMES
   The bare :root block below is the complete light palette. Dark redefines
   only tokens, in two places: a prefers-color-scheme block (guarded so an
   explicit light choice still wins) and a [data-theme="dark"] block. No
   component rule may declare a colour inside either block - a colour whose
   only definition sits behind a media query never applies when the OS is in
   light mode, which is how you end up with one theme's text on the other
   theme's background.

   CONTRAST
   The old palette had two failures against WCAG AA: white on #4B9CD3 was
   3.00:1 and the #6B7C89 label grey was 4.31:1. The brand blue is kept for
   fills and tints, where it never carries small text; --accent (#1F6FA8,
   5.39:1) is used wherever white text sits on colour, and the label grey is
   now #5F707D (5.12:1).
   ========================================================================= */

:root {
  --ground:      #F4F7FA;
  --surface:     #FFFFFF;
  --surface-2:   #EDF2F7;
  --surface-3:   #E3EBF2;
  --border:      #DDE5EC;
  --border-firm: #C6D3DE;

  --ink:         #14212B;
  --ink-soft:    #5F707D;

  --accent:      #1F6FA8;
  --accent-deep: #17567F;
  --brand:       #4B9CD3;
  --brand-tint:  #E8F3FB;

  --ok:          #16855A;
  --warn:        #B87503;
  --crit:        #C0392B;
  --offline:     #7B8B96;

  --series-a:    #2A78D6;
  --series-b:    #EB6834;

  /* Toner bar colours. Deliberately not the literal ink hues - a pure yellow
     bar is invisible on white - but close enough to read at a glance. */
  --toner-k:     #3F4C56;
  --toner-c:     #17A2C4;
  --toner-m:     #C9508F;
  --toner-y:     #C79213;

  --shadow-sm:   0 1px 2px rgba(20, 33, 43, 0.06);
  --shadow-md:   0 4px 14px rgba(20, 33, 43, 0.08);
  --shadow-lg:   0 12px 32px rgba(20, 33, 43, 0.12);

  --sans: 'IBM Plex Sans', -apple-system, 'Segoe UI', Arial, sans-serif;
  --mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, 'Courier New', monospace;
  --ease: cubic-bezier(0.32, 0.72, 0, 1);

  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:      #101820;
    --surface:     #18222B;
    --surface-2:   #1F2C37;
    --surface-3:   #263644;
    --border:      #2B3B48;
    --border-firm: #3A4C5B;

    --ink:         #E8EEF3;
    --ink-soft:    #9FB0BC;

    --accent:      #5FB0E8;
    --accent-deep: #8AC6F0;
    --brand:       #4B9CD3;
    --brand-tint:  #1B2E3D;

    --ok:          #3FBE86;
    --warn:        #E0A02E;
    --crit:        #EF6E60;
    --offline:     #6E7F8B;

    --series-a:    #5AA2EA;
    --series-b:    #F28355;

    --toner-k:     #93A3AE;
    --toner-c:     #3EBCD8;
    --toner-m:     #E074A9;
    --toner-y:     #DFAE3A;

    --shadow-sm:   0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md:   0 4px 14px rgba(0, 0, 0, 0.45);
    --shadow-lg:   0 12px 32px rgba(0, 0, 0, 0.55);
  }
}

:root[data-theme="dark"] {
  --ground:      #101820;
  --surface:     #18222B;
  --surface-2:   #1F2C37;
  --surface-3:   #263644;
  --border:      #2B3B48;
  --border-firm: #3A4C5B;

  --ink:         #E8EEF3;
  --ink-soft:    #9FB0BC;

  --accent:      #5FB0E8;
  --accent-deep: #8AC6F0;
  --brand:       #4B9CD3;
  --brand-tint:  #1B2E3D;

  --ok:          #3FBE86;
  --warn:        #E0A02E;
  --crit:        #EF6E60;
  --offline:     #6E7F8B;

  --series-a:    #5AA2EA;
  --series-b:    #F28355;

  --toner-k:     #93A3AE;
  --toner-c:     #3EBCD8;
  --toner-m:     #E074A9;
  --toner-y:     #DFAE3A;

  --shadow-sm:   0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md:   0 4px 14px rgba(0, 0, 0, 0.45);
  --shadow-lg:   0 12px 32px rgba(0, 0, 0, 0.55);
}

/* ============================================================== base ===== */
* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  padding-bottom: 60px;
}

h1, h2, h3 { margin: 0; text-wrap: balance; }

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

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

:where(a, button, select, input, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* One width for every page. The old templates each set their own. */
.shell { width: min(1680px, 100% - 3rem); margin-inline: auto; }

/* ============================================================ topbar ===== */
.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}

.topbar-inner {
  display: flex;
  align-items: center;
  gap: 22px;
  padding-block: 14px;
  flex-wrap: wrap;
}

.brandmark { display: flex; align-items: center; gap: 11px; margin-right: auto; min-width: 0; }

/* A live health dot rather than a logo: on a monitoring tool the first thing
   you should see is whether anything is wrong. */
.pulse {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
  flex: none;
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.pulse.is-warn { background: var(--warn); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent); }
.pulse.is-crit { background: var(--crit); box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit) 22%, transparent); }

.brandmark strong { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; white-space: nowrap; }
.brandmark span { color: var(--ink-soft); font-size: 13px; white-space: nowrap; }

.nav { display: flex; gap: 2px; flex-wrap: wrap; }

.nav a {
  display: block;
  padding: 7px 13px;
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}

.nav a:hover { background: var(--surface-2); color: var(--ink); }

.nav a[aria-current="page"] {
  background: var(--brand-tint);
  color: var(--accent);
  font-weight: 600;
}

/* =========================================================== buttons ===== */
.btn {
  --btn-bg: var(--surface);
  --btn-ink: var(--ink);
  --btn-bd: var(--border-firm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 15px;
  border: 1px solid var(--btn-bd);
  border-radius: var(--r-sm);
  background: var(--btn-bg);
  color: var(--btn-ink);
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s var(--ease), box-shadow 0.18s var(--ease),
              background 0.18s var(--ease), border-color 0.18s var(--ease);
}

.btn:hover { box-shadow: var(--shadow-sm); border-color: var(--ink-soft); }
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn:disabled { opacity: 0.55; cursor: default; transform: none; box-shadow: none; }

.btn-primary { --btn-bg: var(--accent); --btn-ink: #FFFFFF; --btn-bd: var(--accent); font-weight: 600; }
.btn-primary:hover:not(:disabled) { --btn-bg: var(--accent-deep); --btn-bd: var(--accent-deep); }

.btn-sm { padding: 6px 11px; font-size: 12.5px; }

.btn svg { width: 14px; height: 14px; flex: none; }

.btn .spin { animation: spin 0.7s linear infinite; }

/* ========================================================== controls ===== */
select, input[type="text"], input[type="password"] {
  font-family: var(--sans);
  font-size: 13.5px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-firm);
  border-radius: var(--r-sm);
  padding: 8px 11px;
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}

select:hover, input:hover { border-color: var(--ink-soft); }

input::placeholder { color: var(--ink-soft); }

.segmented {
  display: inline-flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 2px;
  gap: 2px;
}

.segmented button {
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  padding: 5px 12px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}

.segmented button:hover { color: var(--ink); }

.segmented button.active {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  box-shadow: var(--shadow-sm);
}

.controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.controls-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* ============================================================ layout ===== */
/* Main column plus a sticky rail; the rail drops below on narrow screens
   rather than squeezing both into nothing. */
.layout { display: grid; grid-template-columns: minmax(0, 1fr) 344px; gap: 22px; align-items: start; }

@media (max-width: 1180px) { .layout { grid-template-columns: 1fr; } }

.sec-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 30px 0 14px; }
.sec-head h2 { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.sec-head p { margin: 0; font-size: 13px; color: var(--ink-soft); }
.sec-head .spacer { margin-left: auto; }

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 22px 24px 24px;
}

.panel > h2 {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-bottom: 16px;
}

/* =========================================================== summary ===== */
.summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-block: 24px 20px;
}

.stat { background: var(--surface); padding: 17px 20px; }

.stat-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.stat-value { font-size: 27px; font-weight: 600; letter-spacing: -0.02em; margin-top: 5px; line-height: 1.1; }
.stat-value.sm { font-size: 19px; }
.stat-value .qual { font-size: 16px; font-weight: 500; color: var(--ink-soft); }

.stat-note { font-size: 12.5px; color: var(--ink-soft); margin-top: 3px; }
.stat-note b { color: var(--ink); font-weight: 600; }

/* ==================================================== printer cards ====== */
.fleet { display: grid; grid-template-columns: repeat(auto-fill, minmax(335px, 1fr)); gap: 16px; }

.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 18px 20px 20px 22px;
  overflow: hidden;
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
  animation: rise 0.5s var(--ease) both;
}

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

/* Severity as geometry, not only hue: a 3px stripe survives greyscale
   printing and colour-blind vision. */
.card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--stripe, var(--border-firm));
}

.card.is-ok      { --stripe: var(--ok); }
.card.is-warn    { --stripe: var(--warn); }
.card.is-crit    { --stripe: var(--crit); }
.card.is-offline { --stripe: var(--offline); background: var(--surface-2); }

.card-top { display: flex; align-items: flex-start; gap: 12px; }
.card-id { min-width: 0; }

.card-name {
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-ip { font-family: var(--mono); font-size: 12px; color: var(--ink-soft); margin-top: 2px; }

.pill {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
}

.pill i { width: 6px; height: 6px; border-radius: 50%; background: var(--stripe, var(--ink-soft)); flex: none; }

.card-pages {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin-top: 15px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}

.card-pages b { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; }
.card-pages span { font-size: 12.5px; color: var(--ink-soft); }

/* ========================================================== supplies ==== */
.supplies { display: grid; gap: 9px; margin-top: 14px; }

.supply { display: grid; grid-template-columns: 62px 1fr 40px; align-items: center; gap: 10px; }
.supply-name { font-size: 12.5px; color: var(--ink-soft); }

.track { display: block; position: relative; height: 7px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }

.fill {
  /* display:block is load-bearing. .track is not a flex or grid container, so
     without this the fill stays an inline span - and inline non-replaced
     elements ignore width, height and transform outright. The bar renders at
     zero size and you see only the percentage beside an empty track. */
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--supply, var(--brand));
  width: var(--pct, 0%);
  transform-origin: left;
  animation: grow 0.9s var(--ease) both;
}

/* The alert threshold, drawn on the track, so a bar close to the line is
   readable without checking the number against alert_config.json. */
.track::after {
  content: "";
  position: absolute;
  inset-block: -2px;
  left: var(--threshold, 15%);
  width: 1px;
  background: var(--border-firm);
}

.supply-pct { font-family: var(--mono); font-size: 12px; text-align: right; font-variant-numeric: tabular-nums; }

.s-k { --supply: var(--toner-k); }
.s-c { --supply: var(--toner-c); }
.s-m { --supply: var(--toner-m); }
.s-y { --supply: var(--toner-y); }
.s-low { --supply: var(--crit); }

/* Consumables the printer tracks but never reports a level for - waste toner,
   staples. Listing them as rows of "—" was four dead lines per card. */
.unmetered { margin-top: 12px; font-size: 11.5px; color: var(--ink-soft); line-height: 1.5; }

.offline-msg { margin-top: 15px; font-size: 13px; color: var(--ink-soft); }

/* ============================================================ chips ===== */
.alerts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--ink);
}

.chip::before { content: ""; width: 5px; height: 5px; border-radius: 1px; background: var(--chip, var(--ink-soft)); flex: none; }

.chip.c { --chip: var(--crit); }
.chip.w { --chip: var(--warn); }
.chip.i { --chip: var(--offline); }

/* ==================================================== notification rail == */
.rail { position: sticky; top: 80px; display: grid; gap: 12px; }

@media (max-width: 1180px) { .rail { position: static; } }

.rail-head { display: flex; align-items: center; gap: 9px; padding: 0 2px 2px; }
.rail-head h2 { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }

.count {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--crit);
  color: #FFFFFF;
}

.count.is-zero { background: var(--ok); }

.live { margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ink-soft); }
.live i { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); animation: beat 2.4s ease-in-out infinite; }
.live.is-stale i { background: var(--crit); animation: none; }

.feed { display: grid; gap: 9px; }

.notif {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 3px 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--sev, var(--border-firm));
  border-radius: var(--r-md);
  padding: 12px 13px 12px 14px;
  animation: slidein 0.42s var(--ease) both;
  transition: box-shadow 0.18s var(--ease), transform 0.18s var(--ease);
}

.notif:hover { box-shadow: var(--shadow-sm); transform: translateX(2px); }

.notif.sev-crit { --sev: var(--crit); }
.notif.sev-warn { --sev: var(--warn); }
.notif.sev-info { --sev: var(--offline); }

/* The action, not the device's wording: "Replace the waste toner container"
   tells somebody what to carry to the printer. The raw string is kept
   underneath so it still matches the panel or a service call. */
.notif-action { font-size: 13.5px; font-weight: 600; line-height: 1.35; }
.notif-where { font-size: 12px; color: var(--ink-soft); }
.notif-where b { color: var(--ink); font-weight: 500; }
.notif-raw { font-family: var(--mono); font-size: 10.5px; color: var(--ink-soft); opacity: 0.85; }

.notif-age {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: start;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-soft);
  white-space: nowrap;
  padding-top: 2px;
}

.sev-tag {
  display: inline-block;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sev);
  margin-bottom: 3px;
}

.muted-group { border: 1px dashed var(--border-firm); border-radius: var(--r-md); padding: 10px 13px; }
.muted-group summary { cursor: pointer; font-size: 12px; color: var(--ink-soft); list-style: none; }
.muted-group summary::-webkit-details-marker { display: none; }
.muted-group summary::before { content: "\25B8  "; }
.muted-group[open] summary::before { content: "\25BE  "; }
.muted-group ul { margin: 9px 0 2px; padding-left: 17px; display: grid; gap: 5px; }
.muted-group li { font-size: 12px; color: var(--ink-soft); }
.muted-group code { font-family: var(--mono); font-size: 11px; }

.all-clear { text-align: center; padding: 26px 14px; color: var(--ink-soft); font-size: 13px; }
.all-clear b { display: block; color: var(--ink); font-size: 14px; font-weight: 600; margin-bottom: 4px; }

/* ============================================================ tables ==== */
.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }

th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: 9px 12px;
  border-bottom: 1px solid var(--border-firm);
  white-space: nowrap;
}

td { padding: 11px 12px; border-bottom: 1px solid var(--border); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background 0.15s var(--ease); }
tbody tr:hover { background: var(--surface-2); }

/* ============================================================ misc ====== */
.note-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -4px 0 16px;
  font-size: 12px;
  color: var(--ink-soft);
}

.note-line::before {
  content: "";
  width: 3px;
  align-self: stretch;
  background: var(--brand);
  border-radius: 2px;
  opacity: 0.5;
}

.empty-msg { text-align: center; padding: 32px 16px; color: var(--ink-soft); font-size: 13.5px; line-height: 1.6; }

.caveat {
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink-soft);
  border-left: 2px solid var(--border-firm);
  padding-left: 11px;
  margin-top: 18px;
}

.legend { display: flex; gap: 18px; flex-wrap: wrap; margin-bottom: 16px; }
.legend div { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--ink-soft); }
.legend i { width: 10px; height: 10px; border-radius: 3px; flex: none; }

canvas { max-height: 340px; }

/* =========================================================== motion ===== */
@keyframes rise    { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes grow    { from { transform: scaleX(0); }                    to { transform: scaleX(1); } }
@keyframes slidein { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
@keyframes spin    { to { transform: rotate(360deg); } }
@keyframes beat    { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.45; transform: scale(0.8); } }

.fleet .card:nth-child(1) { animation-delay: 0.02s; }
.fleet .card:nth-child(2) { animation-delay: 0.06s; }
.fleet .card:nth-child(3) { animation-delay: 0.10s; }
.fleet .card:nth-child(4) { animation-delay: 0.14s; }
.fleet .card:nth-child(5) { animation-delay: 0.18s; }
.fleet .card:nth-child(6) { animation-delay: 0.22s; }
.fleet .card:nth-child(7) { animation-delay: 0.26s; }
.fleet .card:nth-child(8) { animation-delay: 0.30s; }

.feed .notif:nth-child(1) { animation-delay: 0.05s; }
.feed .notif:nth-child(2) { animation-delay: 0.10s; }
.feed .notif:nth-child(3) { animation-delay: 0.15s; }
.feed .notif:nth-child(4) { animation-delay: 0.20s; }
.feed .notif:nth-child(5) { animation-delay: 0.25s; }
.feed .notif:nth-child(6) { animation-delay: 0.30s; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-delay: 0s !important;
    transition-duration: 0.001s !important;
  }
}
