:root {
  --ink: oklch(20% 0.005 220);
  --ink-soft: oklch(46% 0.01 220);
  --line: oklch(88% 0.006 220);
  --surface: #fff;
  --canvas: oklch(96% 0.004 220);
  --sidebar: oklch(19% 0.035 155);
  --sidebar-line: oklch(26% 0.03 155);
  --sidebar-ink: oklch(75% 0.01 220);
  --primary: oklch(42% 0.15 148);
  --forecast: oklch(48% 0.16 240);
  --critical: oklch(50% 0.19 25);
  --warning: oklch(48% 0.14 70);
  --radius: 10px;
  --radius-sm: 8px;
  --radius-lg: 16px;
  /* Elevation — a soft, large-blur shadow plus a tight 1px one underneath it reads as
     a gentler, more "floating" lift than one single hard-edged shadow does. Used on
     cards/KPIs/dropdowns/modals; kept subtle so dense pages (tables, dashboards) don't
     turn visually noisy. */
  --shadow-sm: 0 1px 2px rgba(20, 30, 25, .04), 0 1px 3px rgba(20, 30, 25, .06);
  --shadow-md: 0 8px 24px rgba(20, 30, 25, .08), 0 2px 6px rgba(20, 30, 25, .05);
  --shadow-lg: 0 20px 48px rgba(10, 30, 20, .18), 0 2px 6px rgba(10, 30, 20, .08);
}

* { box-sizing: border-box; }
body { margin: 0; background: var(--canvas); color: var(--ink);
  font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 16px; }

/* Top progress bar (YouTube/GitHub/Linear-style) — the page itself stays fully
   visible and interactive underneath; this is just a "something is happening"
   signal, not a blocking overlay. Since every trigger (nav link, form submit via
   requestSubmit) leads to a real full-page navigation, there's no reliable
   "done" event to animate to 100% on — the whole DOM (bar included) gets
   replaced the instant the new page arrives, so the animation only ever needs
   to feel like it's making progress, capping below 100% rather than ever
   needing to resolve itself. Re-adding the element to layout (clearing
   [hidden]) is what (re)starts the animation — no JS beyond toggling
   `hidden` needed. */
.page-loader-bar { position: fixed; top: 0; left: 0; height: 3px; width: 0; z-index: 9999;
  background: linear-gradient(90deg, var(--primary), var(--forecast));
  box-shadow: 0 0 10px oklch(55% 0.17 220 / 55%), 0 0 2px oklch(55% 0.17 220 / 70%); }
.page-loader-bar:not([hidden]) { display: block;
  animation: page-loader-progress 1.4s cubic-bezier(.25, .65, .2, 1) forwards; }
@keyframes page-loader-progress {
  0% { width: 0%; }
  45% { width: 55%; }
  80% { width: 78%; }
  100% { width: 88%; }
}
h1, h2, h3 { margin: 0; font-weight: 700; }
a { color: var(--forecast); text-decoration: none; }
a:hover { color: oklch(56% 0.16 240); }
.mono { font-family: 'IBM Plex Mono', monospace; font-variant-numeric: tabular-nums; }
.strong { font-weight: 600; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: oklch(80% 0.006 220); border-radius: 4px; }

/* Shell ------------------------------------------------------------------ */
.app { display: flex; height: 100vh; overflow: hidden; position: relative; }
.sidebar { width: 258px; flex: none; background: var(--sidebar);
  border-right: 1px solid var(--sidebar-line); display: flex; flex-direction: column;
  overflow-y: auto; color: var(--sidebar-ink); transition: width .2s ease; }
/* Desktop: collapsing shrinks it to 0 width, same push-layout column it always
   was — .main just expands to fill the freed space. Mobile below overrides this
   with its own off-canvas transform instead, since collapsing width doesn't
   make sense once the sidebar is position:fixed and overlaying the page.
   Keyed off <html>, not the sidebar's own class — an inline <head> script sets
   this before the sidebar even renders (avoiding a flash of it open), which
   only <html> exists early enough in the document for. */
@media (min-width: 861px) {
  html.sidebar-collapsed .sidebar { width: 0; overflow: hidden; border-right: none; }
}
.sidebar-toggle { display: flex; align-items: center; justify-content: center; flex: none;
  width: 34px; height: 34px; border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--surface); font-size: 18px; cursor: pointer; }
.sidebar-overlay { display: none; }
.brand { display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 22px 12px 16px; border-bottom: 1px solid var(--sidebar-line); text-align: center; }
.brand img { width: 64px; height: auto; }
.brand-name { font-size: 17px; font-weight: 700; letter-spacing: .12em; color: #fff; }
.brand-sub { font-size: 16px; color: oklch(68% 0.02 155); }

.nav { display: flex; flex-direction: column; gap: 2px; padding: 12px 10px;
  border-bottom: 1px solid var(--sidebar-line); }
.nav-item { padding: 9px 12px; border-radius: var(--radius-sm); font-size: 16px;
  font-weight: 500; color: var(--sidebar-ink); transition: background .15s, color .15s; }
.nav-item:hover { background: oklch(24% 0.04 155); color: #fff; }
.nav-item.is-active { background: var(--primary); color: oklch(97% 0.004 220); font-weight: 700;
  box-shadow: var(--shadow-sm); }

.sidebar-section { padding: 12px 10px; flex: 1; }
.sidebar-label { font-size: 16px; letter-spacing: .14em; text-transform: uppercase;
  color: oklch(60% 0.02 155); padding: 0 6px 8px; }
.sub-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
.sub-list a { display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  border-radius: 4px; font-size: 16px; color: oklch(85% 0.008 220); }
.sub-list a:hover { background: oklch(24% 0.04 155); }
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.sub-name { flex: 1; }
.sub-pct { font-family: 'IBM Plex Mono', monospace; font-size: 16px; color: oklch(70% 0.01 220); }

.sidebar-foot { padding: 12px; border-top: 1px solid var(--sidebar-line); margin-top: auto; }
.role-badge { font-size: 16px; font-weight: 600; color: #fff; margin-bottom: 2px; }
.role-tag { font-size: 16px; text-transform: uppercase; letter-spacing: .06em;
  color: oklch(60% 0.02 155); margin-bottom: 6px; }
.role-switch { display: flex; gap: 4px; }
.role-switch a { flex: 1; text-align: center; padding: 4px; border-radius: 4px; font-size: 16px;
  text-transform: uppercase; letter-spacing: .06em; background: oklch(24% 0.04 155);
  color: var(--sidebar-ink); }
.role-switch a.is-active { background: var(--primary); color: #fff; }

/* Login -------------------------------------------------------------------- */
.login-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(oklch(20% 0 0 / 55%), oklch(20% 0 0 / 55%)),
    url("../img/bg.png") center / cover no-repeat fixed; }
/* .card.login-card (not just .login-card): both classes land on the same element
   and .card now sets its own box-shadow too — matching specificity would otherwise
   leave the winner decided purely by which rule happens to come later in the file,
   silently flattening this page's intentionally bolder shadow if .card ever moves. */
.card.login-card { width: 320px; box-shadow: var(--shadow-lg); border-radius: var(--radius-lg); }
.login-brand { display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-align: center; margin-bottom: 18px; }
.login-brand img { width: 64px; height: auto; }
.login-card h2 { margin-bottom: 14px; text-align: center; }
.field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.field label { font-size: 16px; color: var(--ink-soft); }
.field input { font-family: inherit; font-size: 16px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); transition: border-color .15s, box-shadow .15s; }
.login-card .btn { width: 100%; text-align: center; margin-top: 4px; }
.flash { font-size: 16px; color: var(--critical); margin-bottom: 12px; }
.login-hint { font-size: 16px; color: var(--ink-soft); margin-top: 14px; line-height: 1.5; }

.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 22px; background: var(--surface); border-bottom: 1px solid var(--line); }
.topbar h1 { font-size: 18px; }
.topbar-sub { margin: 2px 0 0; font-size: 16px; color: var(--ink-soft); }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.sync { font-size: 16px; color: var(--ink-soft); }
.content { flex: 1; overflow-y: auto; padding: 18px 22px 40px;
  display: flex; flex-direction: column; gap: 16px; }

/* Controls --------------------------------------------------------------- */
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
select, .btn, button, input[type="date"] { font-family: inherit; font-size: 16px; padding: 7px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
  color: var(--ink); cursor: pointer; transition: border-color .15s, color .15s, background .15s, box-shadow .15s; }
.btn:hover, button:hover { border-color: var(--forecast); color: var(--forecast); }
/* A visible focus ring (keyboard nav, or a form re-surfacing after a validation
   error) instead of relying on the browser's own inconsistent default outline —
   kept to :focus-visible so a plain mouse click doesn't ring every button clicked. */
select:focus-visible, .btn:focus-visible, button:focus-visible, input:focus-visible,
textarea:focus-visible, .dropzone:focus-visible {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px oklch(58% 0.17 148 / 20%); }
.inline { display: inline; margin-left: 6px; }
.inline button { padding: 1px 6px; font-size: 16px; }
.inline button.btn { padding: 6px 10px; }
/* .inline button above is more specific than .btn-danger/.btn-edit's own padding
   (a class + element beats a single class), so a styled button like Delete —
   which lives inside a .inline form for its POST — was silently getting this
   generic compact sizing instead of matching its Edit counterpart's actual
   padding. Restore it explicitly, just for these two. */
.inline .btn-danger, .inline .btn-edit { padding: 6px 10px; }

.btn-primary, .btn-danger, .btn-edit { font-family: inherit; font-size: 16px; padding: 7px 12px;
  border-radius: var(--radius-sm); cursor: pointer; display: inline-block; text-decoration: none;
  transition: background .15s, color .15s, border-color .15s, transform .15s, box-shadow .15s; }
.btn-primary { border: 1px solid var(--primary); background: var(--primary); color: #fff; }
.btn-primary:hover { background: oklch(38% 0.15 148); color: #fff; transform: translateY(-1px);
  box-shadow: 0 4px 12px oklch(42% 0.15 148 / 30%); }
.btn-danger { border: 1px solid var(--critical); background: #fff; color: var(--critical); }
.btn-danger:hover { background: var(--critical); color: #fff; }
/* Same outline weight/size as .btn-danger, so an Edit/Delete pair reads as a
   matched set of row actions instead of one bold button next to plain text —
   .btn's border is the same light neutral as every table cell divider, which
   made it nearly invisible next to .btn-danger's much bolder red outline. */
.btn-edit { border: 1px solid var(--forecast); background: #fff; color: var(--forecast); }
.btn-edit:hover { background: var(--forecast); color: #fff; }
.row-actions { display: flex; gap: 6px; flex-wrap: nowrap; }
.row-actions .inline { margin-left: 0; vertical-align: middle; }

.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 14px; }
.form-grid .field input, .form-grid .field select, .form-grid .field textarea {
  font-family: inherit; font-size: 16px; padding: 8px 10px; width: 100%;
  border: 1px solid var(--line); border-radius: var(--radius-sm); transition: border-color .15s, box-shadow .15s; }
.form-grid .field textarea { resize: vertical; }

.notif-bell { position: relative; }
.notif-dot { position: absolute; top: -4px; right: -4px; background: var(--critical); color: #fff;
  border-radius: 50%; font-size: 9px; min-width: 15px; height: 15px; display: flex;
  align-items: center; justify-content: center; padding: 0 3px; }
.badge-real { color: var(--primary); margin-left: 4px; font-size: 16px; }

.admin-control { border: 1px dashed var(--line); border-radius: var(--radius); padding: 10px;
  display: flex; flex-direction: column; gap: 8px; }
.admin-control h3 { font-size: 16px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }
.admin-feeder-toggles { display: flex; flex-wrap: wrap; gap: 6px; }
.admin-feeder-toggles button { font-size: 16px; padding: 3px 7px; }


details summary { cursor: pointer; font-size: 16px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-soft); padding: 4px 0; }
details[open] summary { color: var(--ink); }

/* Cards ------------------------------------------------------------------ */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 16px; box-shadow: var(--shadow-sm); }
/* A wide table scrolls inside its own wrapper (added around every <table> at
   runtime — see base.html) rather than the whole card, and rather than putting
   display:block on the <table> itself — that breaks the browser's column-width
   sync between rows/thead/tbody, which is exactly what caused visible text to
   overlap between columns. Bounded height + overflow-y:auto makes this wrapper
   the *actual* scrolling viewport (not just the page around it) so the sticky
   header row and sticky first column below have a real scrollport to stick
   within — position:sticky is inert without one. Harmless for short tables:
   they're never taller than 70vh, so no scrollbar ever appears for them. */
.table-scroll { overflow: auto; max-height: 70vh; }
.card-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 4px 12px; margin-bottom: 12px; }
.card-head h2 { font-size: 16px; letter-spacing: .04em; text-transform: uppercase; }
.card-note { font-size: 16px; color: var(--ink-soft); }

/* Data Import dropzone ---------------------------------------------------- */
#data-import-card .card-head { margin-bottom: 18px; }
#data-import-card #data-import-close { border-radius: 999px; width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center; font-size: 13px; }
.dropzone { border: 1.5px dashed oklch(80% 0.01 220); border-radius: var(--radius-lg); padding: 32px 20px;
  text-align: center; color: var(--ink-soft); cursor: pointer;
  transition: border-color .18s, background .18s, box-shadow .18s;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  background: linear-gradient(180deg, oklch(98% 0.004 220), oklch(96% 0.006 220)); }
.dropzone:hover, .dropzone.is-dragover { border-color: var(--primary); border-style: solid;
  background: oklch(97% 0.03 148); box-shadow: 0 0 0 4px oklch(94% 0.04 148); }
.dropzone input[type="file"] { display: none; }
.dropzone-icon-badge { width: 56px; height: 56px; border-radius: 50%; display: flex;
  align-items: center; justify-content: center; background: oklch(93% 0.05 148); color: var(--primary);
  transition: transform .18s; }
.dropzone:hover .dropzone-icon-badge, .dropzone.is-dragover .dropzone-icon-badge { transform: scale(1.06); }
.dropzone-text { font-size: 17px; color: var(--ink); max-width: 320px; line-height: 1.45; }
.dropzone-text .dropzone-sub { display: block; font-size: 14px; color: var(--ink-soft);
  margin-top: 4px; letter-spacing: .03em; }
.dropzone-divider { display: flex; align-items: center; gap: 10px; width: 100%; max-width: 200px;
  color: var(--ink-soft); font-size: 13px; letter-spacing: .08em; }
.dropzone-divider::before, .dropzone-divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.dropzone-browse { display: inline-block; padding: 10px 26px; border-radius: 999px; border: none;
  background: var(--primary); color: #fff; font-size: 15px; font-weight: 600;
  box-shadow: 0 6px 16px oklch(55% 0.15 148 / 35%); transition: background .18s, transform .18s, box-shadow .18s; }
.dropzone:hover .dropzone-browse, .dropzone.is-dragover .dropzone-browse { background: oklch(38% 0.15 148);
  transform: translateY(-1px); box-shadow: 0 8px 20px oklch(55% 0.15 148 / 45%); }
.file-chip-list { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.file-chip { font-size: 16px; padding: 3px 11px; border-radius: 999px; border: 1px solid oklch(85% 0.05 148);
  background: oklch(96% 0.03 148); color: oklch(32% 0.1 148); }
/* Shared password-reconfirmation step for admin writes (substation create/edit/
   delete) — a second card over a backdrop, not a plain browser confirm()/prompt(),
   so the message and field match the rest of the app's own styling. Same backdrop
   treatment as .sidebar-overlay.is-open, just not scoped to mobile-only. Every
   modal in the app (password confirm, peak detail, Data Import, alerts) shares
   this one elevated-card treatment — a floating surface with a soft shadow and a
   gentle scale/fade entrance — so they all read as the same component instead of
   each modal inventing its own look. */
.confirm-modal-overlay, .alert-modal-overlay { display: none; position: fixed; inset: 0; z-index: 1200;
  background: rgba(10,16,14,.45); backdrop-filter: blur(3px); align-items: center; justify-content: center; }
.alert-modal-overlay { z-index: 1300; }
.confirm-modal-overlay.is-open, .alert-modal-overlay.is-open { display: flex; }
.confirm-modal-card, .alert-modal-card, .peak-modal-card {
  border-radius: var(--radius-lg); border: none; box-shadow: var(--shadow-lg);
  transform: translateY(8px) scale(.97); opacity: 0;
  transition: transform .18s ease, opacity .18s ease; }
.confirm-modal-overlay.is-open .confirm-modal-card, .alert-modal-overlay.is-open .alert-modal-card,
.confirm-modal-overlay.is-open .peak-modal-card {
  transform: translateY(0) scale(1); opacity: 1; }
.confirm-modal-card { width: 100%; max-width: 380px; margin: 16px; }
.alert-modal-card { width: 100%; max-width: 400px; margin: 16px; padding: 28px 24px 22px;
  border-top: 4px solid var(--warning); text-align: center; }
.alert-modal-icon { width: 52px; height: 52px; margin: 0 auto 14px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: oklch(95% 0.05 70); color: var(--warning); }
/* The --info variant toggles this icon on/off via JS (severity-explain modal has no
   icon for a non-alerting "ok" gauge) — plain [hidden] alone loses to the class rule
   above (same specificity tier, this one's authored later), so without this override
   the icon would stay visibly flex-displayed despite the hidden attribute. */
.alert-modal-icon[hidden] { display: none; }
.alert-modal-card--success { border-top-color: var(--primary); }
.alert-modal-card--success .alert-modal-icon { background: oklch(94% 0.05 148); color: var(--primary); }
.alert-modal-card--error { border-top-color: var(--critical); }
.alert-modal-card--error .alert-modal-icon { background: oklch(95% 0.04 25); color: var(--critical); }
.alert-modal-card p + p { margin-top: 8px; }
.alert-modal-card--info { border-top-color: var(--line); text-align: left; max-width: 440px; }
.alert-modal-card--info h2 { text-transform: uppercase; letter-spacing: .04em; font-size: 17px; }

.pill.sev-pill-btn { font-family: inherit; cursor: pointer; appearance: none; -webkit-appearance: none; }
.pill.sev-pill-btn:hover, .pill.sev-pill-btn:focus-visible { filter: brightness(0.96); outline: none; }

.download-toast { position: fixed; right: 20px; bottom: 20px; z-index: 1250;
  display: flex; align-items: center; gap: 12px; padding: 12px 16px; max-width: 360px;
  background: var(--surface); border: 1px solid var(--line); border-left: 4px solid var(--primary);
  border-radius: var(--radius); box-shadow: 0 8px 20px rgba(0,0,0,.14); color: var(--ink); font-size: 15px; }
.download-toast[hidden] { display: none; }
.download-toast-spinner { width: 18px; height: 18px; flex: none; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--primary); animation: download-spin .8s linear infinite; }
.download-toast-spinner[hidden] { display: none; }
@keyframes download-spin { to { transform: rotate(360deg); } }
.alert-modal-card h2 { margin-bottom: 8px; }
.alert-modal-card p { color: var(--ink-soft); line-height: 1.5; margin-bottom: 20px; }
/* Severity-explain modal's gauge/timeline + status tracker — the visual summary
   that lets someone skip the sentence explanations entirely (see _severity_modal.html,
   which keeps those sentences but collapsed behind <details> below this). */
.alert-modal-card--info .gauge-wrap { margin: 4px 0 18px; text-align: left; }
.gauge-value { font-family: 'IBM Plex Mono', monospace; font-size: 30px; font-weight: 700;
  text-align: center; margin-bottom: 10px; }
.gauge-track { height: 10px; }
.gauge-tick-labels { position: relative; height: 16px; margin-top: 4px; font-size: 11px;
  font-family: 'IBM Plex Mono', monospace; color: var(--ink-soft); }
.gauge-tick-labels span { position: absolute; transform: translateX(-50%); }
/* The one line someone actually needs, always visible (never collapsed) — see
   _severity_modal.html's CAPTIONS. Everything else in the modal just backs this up. */
.gauge-caption { text-align: center; font-size: 15px; font-weight: 600; margin-top: 14px; }
.alert-modal-card--info details { text-align: left; margin-bottom: 10px; }
.alert-modal-card--info details p { margin: 8px 0 0; font-size: 15px; }
.alert-modal-card--info details summary { font-size: 14px; }
.status-stepper { display: flex; align-items: stretch; gap: 4px; margin: 16px 0 10px; }
.status-step { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-align: center; font-size: 12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-soft); padding-bottom: 8px; border-bottom: 3px solid var(--line); }
.status-step-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--line);
  border: 2px solid var(--line); box-sizing: content-box; }
.status-step.is-done { color: var(--primary); border-bottom-color: var(--primary); }
.status-step.is-done .status-step-dot { background: var(--primary); border-color: var(--primary); }
.status-step.is-current { color: var(--ink); border-bottom-color: var(--forecast); font-weight: 700; }
.status-step.is-current .status-step-dot { background: var(--forecast); border-color: oklch(85% 0.05 240);
  box-shadow: 0 0 0 3px oklch(85% 0.05 240); }
.two-col { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
.two-col-even { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }

/* Comparison filter bar --------------------------------------------------- */
.filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
.filter-row form { display: contents; }
/* Flex items default to refusing to shrink below their own content's natural
   width (min-width: auto), which on a narrow phone left this whole row unable
   to compress and silently overflowing its card. min-width: 0 lets each piece
   actually shrink/wrap instead. */
.filter-row .dropdown, .filter-row select, .filter-row .segmented { min-width: 0; max-width: 100%; }
.segmented { display: inline-flex; flex-wrap: wrap; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; }
.segmented button { border: none; background: var(--surface); color: var(--ink); cursor: pointer;
  font-family: inherit; font-size: 14px; padding: 7px 14px; }
.segmented button + button { border-left: 1px solid var(--line); }
.segmented button.is-active { background: var(--primary); color: #fff; }
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--line); font-size: 14px; cursor: pointer; background: var(--surface);
  color: var(--ink); text-decoration: none; }
.chip input { width: 13px; height: 13px; accent-color: var(--primary); cursor: pointer; }
.chip.is-checked { border-color: var(--primary); background: oklch(93% 0.05 148); }
.chip.is-shortcut { font-weight: 600; color: var(--forecast); }

/* max-width + normal wrapping (not nowrap) — originally built only for short
   one-line chart point labels, but data-tip now also carries full sentence-length
   explanations (the alert severity/status legends), which would otherwise render
   as one unreadably long horizontal line running off-screen. */
.chart-tooltip { position: fixed; z-index: 10000; pointer-events: none;  /* above the map drawer (1100) and Leaflet panes */
  background: var(--ink); color: #fff; font-size: 13px; line-height: 1.4;
  padding: 7px 11px; border-radius: 6px; box-shadow: 0 4px 14px rgba(0,0,0,.22);
  white-space: normal; max-width: 280px; transform: translate(-50%, -100%) translateY(-10px); }
.chart-tooltip[hidden] { display: none; }
[data-tip] { cursor: help; }

.dropdown { position: relative; display: inline-block; }
.dropdown-trigger { font-family: inherit; font-size: 14px; padding: 7px 12px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
  color: var(--ink); cursor: pointer; }
.dropdown-trigger:hover { border-color: var(--forecast); color: var(--forecast); }
.dropdown-panel { position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
  min-width: 300px; max-width: 460px; max-height: 320px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 10px; box-shadow: var(--shadow-md);
  display: flex; flex-wrap: wrap; align-content: flex-start; gap: 6px; }
.dropdown-panel[hidden] { display: none; }
.dropdown--end .dropdown-panel { left: auto; right: 0; }
.export-panel { display: block; width: 420px; max-width: calc(100vw - 32px); max-height: none; }
.export-group { margin-bottom: 12px; }
.export-group > strong { display: block; margin-bottom: 6px; font-size: 13px; text-transform: uppercase; color: var(--ink-soft); }
.export-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.export-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.export-row select { margin-left: 6px; }
/* Same z-index mistake as every earlier floating control on this map (Leaflet's
   own panes reach ~1000) — this dropdown sits right above .map-shell, so the
   generic .dropdown-panel's z-index:30 rendered underneath the map instead of
   over it, clipping most of the panel from view. */
#sim-dropdown .dropdown-panel { z-index: 1100; max-width: none; }
#sim-dropdown .dropdown-panel:not([hidden]) { display: block; }

/* Heat-map matrix ---------------------------------------------------------- */
.heat-cell { text-align: center; font-family: 'IBM Plex Mono', monospace; font-weight: 600; }
.heat-rank { text-align: center; font-family: 'IBM Plex Mono', monospace; color: var(--ink-soft); }
.heat-cell-value { line-height: 1.2; }
.heat-cell-pct { font-size: 11px; font-weight: 500; opacity: .75; line-height: 1.2; }

/* Charts ----------------------------------------------------------------- */
.chart { position: relative; }
.chart-checklist { display: flex; flex-wrap: wrap; gap: 4px 16px; justify-content: center;
  margin-top: 10px; }
.chart-checklist-item { display: flex; align-items: center; gap: 6px; font-size: 13px;
  color: var(--ink-soft); cursor: pointer; user-select: none; }
.chart-checklist-item input[type="checkbox"] { cursor: pointer; width: 14px; height: 14px; margin: 0; }
.chart-svg { width: 100%; height: auto; display: block; overflow: visible; }
.grid { stroke: oklch(92% 0.004 220); stroke-width: 1; }
.cap-line { stroke: var(--critical); stroke-width: 1.2; stroke-dasharray: 5 4; }
.warn-line { stroke: var(--warning); stroke-width: 1; stroke-dasharray: 3 4; }
.line-primary { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linejoin: round; }
.line-secondary { fill: none; stroke: oklch(60% 0.10 70); stroke-width: 1.6; }
.line-forecast { fill: none; stroke: var(--forecast); stroke-width: 2; stroke-dasharray: 6 4; }
.line-arima { fill: none; stroke: oklch(45% 0.18 300); stroke-width: 1.6; stroke-dasharray: 2 3; }
.line-prophet { fill: none; stroke: oklch(50% 0.20 350); stroke-width: 1.6; stroke-dasharray: 8 2 2 2; }
.swatch-arima { background: oklch(45% 0.18 300); }
.swatch-prophet { background: oklch(50% 0.20 350); }
.area { fill: oklch(88% 0.06 148); opacity: .55; stroke: none; }
.now-dot { fill: var(--forecast); stroke: #fff; stroke-width: 2; }
.hover-dot { fill: transparent; cursor: crosshair; }
.hover-dot:hover { fill: var(--forecast); fill-opacity: .3; }
.axis { position: absolute; inset: 0; pointer-events: none;
  font-family: 'IBM Plex Mono', monospace; font-size: 16px; color: var(--ink-soft); }
.axis span { position: absolute; }
.axis-y span { left: 0; transform: translateY(-50%); }
.axis-x span { bottom: -14px; transform: translateX(-50%); }
.legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px;
  font-size: 16px; color: var(--ink-soft); }
.legend span { display: flex; align-items: center; gap: 5px; }
.legend i { width: 14px; height: 4px; border-radius: 2px; display: inline-block; }
.legend .warn { color: var(--warning); font-weight: 600; }
.swatch-primary { background: var(--primary); }
.swatch-secondary { background: oklch(60% 0.10 70); }
.swatch-forecast { background: var(--forecast); }
.swatch-cap { background: var(--critical); }
.swatch-warn { background: var(--warning); }

/* Mini cards ------------------------------------------------------------- */
.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.mini-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px; display: flex; flex-direction: column; gap: 8px; color: var(--ink);
  box-shadow: var(--shadow-sm); transition: box-shadow .15s, border-color .15s; }
.mini-card:hover { border-color: var(--forecast); color: var(--ink); box-shadow: var(--shadow-md); }
.mini-card.is-offline { opacity: .55; }
.mini-head { display: flex; align-items: baseline; justify-content: space-between; }
.mini-name { font-size: 16px; font-weight: 700; letter-spacing: .06em; }
.mini-mw { font-family: 'IBM Plex Mono', monospace; font-size: 18px; font-weight: 600; }
.mini-mw em { font-size: 16px; font-style: normal; color: var(--ink-soft); }
.mini-chart { width: 100%; height: 66px; }
.mini-foot { display: flex; align-items: center; justify-content: space-between;
  font-size: 16px; color: var(--ink-soft); }

/* KPI -------------------------------------------------------------------- */
.kpi-row, .summary-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.kpi { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px;
  box-shadow: var(--shadow-sm); }
.kpi-label { font-size: 16px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }
.kpi-value { font-family: 'IBM Plex Mono', monospace; font-size: 26px; font-weight: 600; margin-top: 4px; }
.kpi-unit { font-size: 16px; font-weight: 500; color: var(--ink-soft); margin-left: 4px; }
.kpi-sub { font-size: 16px; color: var(--ink-soft); margin-top: 2px; }
.kpi-bar { position: relative; height: 6px; margin: 8px 0 4px; border-radius: 999px; background: var(--line); }
.kpi-bar > i { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 999px; }
.kpi-bar > b { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--warning); opacity: .6; }

.pill { display: inline-block; padding: 1px 7px; border-radius: 10px; border: 1px solid;
  font-size: 16px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }

.model-badge { display: flex; align-items: center; gap: 8px; font-size: 16px;
  border: 1px solid var(--line); border-radius: 20px; padding: 3px 10px; background: var(--canvas); }
.model-name { font-weight: 700; color: var(--forecast); }
.model-metrics, .model-horizon { font-family: 'IBM Plex Mono', monospace; color: var(--ink-soft); }

/* Tables ----------------------------------------------------------------- */
.data-table { width: 100%; border-collapse: collapse; font-size: 16px; }
.data-table th { text-align: left; font-size: 16px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-soft); font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--line);
  white-space: nowrap; }
.data-table td { padding: 6px 8px; border-bottom: 1px solid oklch(94% 0.004 220); vertical-align: middle; }
.data-table--compact td, .data-table--compact th { padding: 4px 6px; font-size: 16px; }
/* A light hover highlight so a wide/dense table reads as rows you can track
   across the screen, not a flat grid — skipped on the Logsheet's own spreadsheet-
   style grid (.data-table--logsheet) and the heat-mapped matrix, both of which
   already use their own cell-level color coding that this would fight with. */
.data-table tbody tr:hover td { background: oklch(97% 0.01 148 / 60%); }
.data-table--logsheet tbody tr:hover td, .data-table--matrix tbody tr:hover td { background: inherit; }
/* No display:block here (see .table-scroll above) — that broke column-width sync
   between rows exactly like it did for .card table, which is what caused the
   feeder-code/value text to visibly overlap. Horizontal scroll now comes from the
   .table-scroll wrapper every table gets automatically (base.html). */
/* border-collapse:separate + a real border-spacing gives each heat-mapped cell
   a small gap from its neighbors instead of one solid wall of color — the main
   thing making this table feel crowded, on top of the 2-decimal values below. */
.data-table--matrix { white-space: nowrap; border-collapse: separate; border-spacing: 2px 4px; }
.data-table--matrix td, .data-table--matrix th { padding: 8px 14px; border-bottom: none; }
.data-table--matrix .heat-cell { border-radius: 4px; }
/* Month/year headers sit centred over their heat cells, whose values are centred. */
.data-table--matrix thead th:not(:first-child) { text-align: center; }
/* _logtable.html only (not Dashboard's own differently-shaped matrix, which still
   wants its Substation-name column free to size itself against short month
   columns) — with browser default auto layout, column widths are computed from
   EVERY row's content, not just what's currently visible. A day with real data on
   only a couple of hours (confirmed real: most hours blank, one hour holding an
   actual reading further down the table) let those few populated cells stretch
   their own columns wider than the empty ones above them, so the header no longer
   lined up with the (visibly empty) body cells directly under it — not a rendering
   bug, just auto-layout doing exactly what it's told with very sparse data. Fixed
   layout makes every column's width purely a function of its header cell, which
   is always present and identically shaped across every row. */
.data-table--logtable { table-layout: fixed; white-space: normal; font-size: 14px; }
/* Explicit per-column widths (see the <colgroup> in _logtable.html) replace the
   naive equal-13-way split fixed layout defaults to — that first attempt made
   every column, including 4-letter "TIME", clip down to "T…": .data-table--matrix's
   own 8px/14px padding (sized for Dashboard's 11-row heat-map, not a 13-column
   dense report) was eating most of an equal slice's width on its own. Tighter
   padding and a smaller font here (still legible, this is a dense data table, not
   a heading) free up real room; no forced single-line ellipsis truncation means a
   value still too wide for its column wraps onto a second line instead of
   silently losing characters. The first colgroup attempt was still too tight for
   real mono-font numeric content (confirmed: "452.00"-style values visibly running
   into their neighbors) — current widths budget realistically per column instead
   of a rough guess, with kWhr (up to "944949.24", the longest real value in this
   table) given the most room. */
.data-table--logtable th, .data-table--logtable td { padding: 4px 6px; }
.sticky-col { position: sticky; left: 0; background: inherit; z-index: 1; }
/* Sticky headers only make sense inside a bounded, scrollable container
   (.table-scroll--sticky — Logsheet/Maintenance's long row-grids) — scoped here on
   purpose, not applied to every .data-table unconditionally. Confirmed real bug
   when it was unscoped: the standalone print/PDF report (_logtable.html, no scroll
   container at all) got "position: sticky" with nothing to stick within, which
   print rendering handles inconsistently — showing as the header visibly
   overlapping the row content once printed. Dashboard's Peak MVA matrix doesn't
   need this either (12 columns, rarely tall enough to need a pinned header). */
.table-scroll--sticky .data-table thead th { position: sticky; top: 0; background: var(--surface); z-index: 2; }
.data-table thead th.sticky-col { z-index: 3; }
/* Maintenance's columns don't group into Logsheet's electrical categories (Bus
   Voltage/Ampere/3-Phase/...), so there's no natural multi-color-per-group split
   to copy — this applies the same "the header is a visible colored bar, not just
   plain white" treatment Logsheet has, as one consistent brand tint instead. */
.data-table--maintenance thead th { background: oklch(92% 0.05 148); color: oklch(30% 0.1 148); }
/* Logsheet inline grid — cells read like a spreadsheet: no border until focused,
   so a row of blank cells doesn't look like a form, just an empty sheet. */
.data-table--logsheet td { padding: 4px 6px; }
.cell-input { width: 84px; border: 1px solid transparent; border-radius: 3px; background: transparent;
  font: inherit; font-family: 'IBM Plex Mono', monospace; font-size: 16px; color: var(--ink);
  padding: 3px 5px; text-align: right; }
.cell-input--text { width: 60px; text-align: center; }
.cell-input--select { width: 96px; text-align: left; }
.data-table--logsheet thead th { text-align: center; background: var(--surface); }
.table-scroll--sticky { max-height: 85vh; overflow: auto; }
.table-scroll--sticky .data-table--logsheet thead { position: sticky; top: 0; z-index: 3; }
.data-table--logsheet thead th.hdr-time { background: #CCECFF; }
.data-table--logsheet thead th.hdr-remarks { background: #E7E6E6; width: 320px; white-space: normal; line-height: 1.3; }
.cell-input--remarks { width: 320px; text-align: left; }
.data-table--logsheet thead th.hdr-weather { background: #BDD7EE; }
.data-table--logsheet thead th.hdr-bus { background: #66FFCC; }
.data-table--logsheet thead th.hdr-avrv { background: #66CCFF; }
.data-table--logsheet thead th.hdr-amp { background: #CCFFCC; }
.data-table--logsheet thead th.hdr-3ph, .data-table--logsheet thead th.hdr-avrtap { background: #99FF99; }
.data-table--logsheet thead th.hdr-thd { background: #FF9933; }
.data-table--logsheet thead th.hdr-tdd, .data-table--logsheet thead th.hdr-brk { background: #FFCC99; }
.data-table--logsheet thead th.hdr-xf { background: #FFFFCC; }
.data-table--logsheet thead th.hdr-sf6 { background: #D9D9D9; }
.data-table--logsheet thead th.hdr-n2 { background: #00B0F0; }
.row-locked .cell-input { color: var(--ink-soft); cursor: default; }
.row-locked .cell-input:hover { border-color: transparent; }
.data-table--logsheet tbody tr.row-editing td { background: oklch(94% 0.05 148); }
.data-table--logsheet tbody tr.row-editing td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
/* The logsheet is ~40 columns wide (Time + 2-row headers through Remarks), so the
   Edit/Save actions column — the last one — would normally need a long horizontal
   scroll to even see. Pinned to the right edge of the scroll container instead,
   same idea as the header's existing vertical stickiness just above. Needs its own
   opaque background per row state (row-editing's soft green, otherwise plain
   surface) since a sticky cell shows whatever's actually behind it, not the row's
   CSS cascade, once other cells scroll underneath. */
/* Not logsheet-specific — any wide, horizontally-scrolling table benefits from
   keeping its row actions reachable without scrolling all the way right first
   (confirmed real need: Maintenance's Edit/Delete buttons). */
.row-actions-cell { position: sticky; right: 0; z-index: 2; white-space: nowrap;
  background: var(--surface); box-shadow: -2px 0 4px rgba(0,0,0,.08); }
.row-actions-head { z-index: 4; }
.data-table--logsheet tbody tr.row-editing .row-actions-cell { background: oklch(94% 0.05 148); }
.cell-input:hover { border-color: var(--line); }
.cell-input:focus { outline: none; border-color: var(--primary); background: var(--surface);
  box-shadow: 0 0 0 2px oklch(58% 0.17 148 / 18%); }
/* A spreadsheet cell doesn't have up/down spinner arrows eating into its already-
   narrow space — type the value, like every other cell in this grid. */
.cell-input::-webkit-inner-spin-button, .cell-input::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0; }
.cell-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.btn-save-row { font-family: inherit; font-size: 16px; padding: 4px 10px; border-radius: 4px;
  border: 1px solid var(--primary); background: #fff; color: var(--primary); cursor: pointer; }
.btn-save-row:hover { background: var(--primary); color: #fff; }
.btn-edit-row { font-family: inherit; font-size: 16px; padding: 4px 10px; border-radius: 4px;
  border: 1px solid var(--forecast); background: #fff; color: var(--forecast); cursor: pointer; }
.btn-edit-row:hover { background: var(--forecast); color: #fff; }
.metric-table { width: 100%; border-collapse: collapse; font-size: 16px; }
.metric-table th { text-align: left; font-size: 16px; text-transform: uppercase;
  color: var(--ink-soft); padding-bottom: 6px; }
.metric-table td { padding-top: var(--pad-top, 2px); padding-bottom: 2px; }

/* Bars ------------------------------------------------------------------- */
.bar-row { display: grid; grid-template-columns: 110px 1fr 56px; align-items: center;
  gap: 10px; padding: 5px 0; font-size: 16px; }
.bar-track { position: relative; height: 8px; border-radius: 4px;
  background: oklch(93% 0.004 220); overflow: hidden; }
.bar-track i { position: absolute; top: 0; bottom: 0; left: 0; border-radius: 4px; }
.bar-row--ranked { grid-template-columns: 130px 1fr 190px; }
.bar-row--ranked .bar-value { white-space: nowrap; }
.bar-value { text-align: right; font-size: 16px; font-weight: 600; }

/* System Map ------------------------------------------------------------- */
/* isolation keeps Leaflet's panes and the floating map controls in their own stacking
   context, so none of them can paint over the off-canvas sidebar or modals. */
.map-shell { position: relative; isolation: isolate; min-height: 0; height: calc(100vh - 130px);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
/* The map is always full-bleed — the substation list is a dropdown floating
   over it now, not a persistent side panel, so nothing needs to share width
   with it via flex any more. */
.map-stage { position: absolute; inset: 0; }
/* z-index well above Leaflet's own panes/controls (which reach ~1000) — the
   same mistake broke every earlier version of this floating control: a low
   z-index here means the map's own tiles/controls paint over it instead. */
.map-side-dropdown { position: absolute; top: 12px; left: 12px; z-index: 1100; }
.map-heat-legend { position: absolute; bottom: 24px; left: 12px; z-index: 1100;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 8px 12px; box-shadow: 0 2px 8px rgba(0,0,0,.15); }
.heat-legend-row { display: flex; align-items: center; gap: 6px; font-size: 16px; }
.matrix-head-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 16px; }
.heat-swatch { width: 12px; height: 12px; border-radius: 3px; flex: none; }
/* Marching-dashes flow animation for the Load Simulator's connector line — the
   polyline's own two points are always ordered [from, to] (see map.js), and this
   always shifts the dash pattern from the path's start toward its end, so the
   apparent flow direction correctly reverses on its own whenever from/to are
   swapped, without needing two different animations. -18 = one full dash+gap
   cycle (dashArray '10, 8' in map.js), so the loop has no visible seam. */
.tie-flow-line { animation: tie-flow 0.6s linear infinite; }
@keyframes tie-flow { to { stroke-dashoffset: -18; } }
.map-side-trigger { box-shadow: 0 2px 8px rgba(0,0,0,.15); }
/* Overrides .dropdown-panel's default flex-wrap chip layout (built for the
   Dashboard's simple checkbox list) — this content is nested substation/feeder
   blocks, which want a plain scrollable column instead. */
.map-side-panel { display: block; width: 280px; max-height: calc(100vh - 200px); }
.map-side-all { display: flex; align-items: center; gap: 8px; padding: 6px 8px 10px;
  margin-bottom: 6px; border-bottom: 1px solid var(--line);
  font-size: 16px; font-weight: 600; color: var(--ink); }
.map-sub { border-radius: 4px; margin-bottom: 4px; }
.map-sub.is-selected .map-sub-head { background: var(--primary); color: #fff; border-radius: 4px; }
.map-sub.is-selected .sub-expand:hover, .map-sub.is-selected .sub-view:hover { color: #fff; }
.map-sub-head { display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  font-size: 16px; color: var(--ink); }
.sub-switch { position: relative; width: 30px; height: 16px; border-radius: 999px; border: none;
  flex: none; cursor: pointer; padding: 0; transition: background .15s ease; }
.sub-switch--on { background: oklch(58% 0.17 148); }
.sub-switch--mixed { background: oklch(62% 0.15 70); }
.sub-switch--off { background: oklch(55% 0.19 25); }
.sub-switch-knob { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px;
  border-radius: 50%; background: #fff; transition: left .15s ease; }
.sub-switch--on .sub-switch-knob { left: 16px; }
.sub-switch[disabled] { cursor: default; opacity: .5; }
.sub-expand { flex: 1; text-align: left; background: none; border: none; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer; white-space: nowrap; }
.sub-expand:hover { color: var(--ink-soft); }
.expand-arrow { display: inline-block; width: 12px; margin-right: 2px;
  color: var(--ink-soft); transition: transform .15s ease; }
.sub-expand.is-open .expand-arrow { transform: rotate(90deg); }
.sub-view { color: var(--ink-soft); text-decoration: none; font-size: 15px; line-height: 1;
  padding: 0 2px; white-space: nowrap; }
.sub-view:hover { color: var(--ink); }
.feeder-list { display: flex; flex-direction: column; gap: 5px; padding: 4px 8px 8px 30px; }
.feeder-list[hidden] { display: none; }
.feeder-toggle { display: flex; align-items: center; gap: 4px; font-size: 16px; flex: none;
  font-family: 'IBM Plex Mono', monospace; color: var(--ink-soft); cursor: pointer; }
.feeder-toggle input { width: 11px; height: 11px; accent-color: #2563eb; }

#map { position: absolute; inset: 0; }
.map-drawer { position: absolute; top: 0; right: 0; bottom: 0; width: 380px; z-index: 1100;
  background: rgba(255,255,255,.97); border-left: 1px solid var(--line);
  overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 14px; }
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.drawer-head h2 { font-size: 16px; }
.drawer-head p { margin: 3px 0 0; font-size: 16px; color: var(--ink-soft); }
.drawer-close { font-size: 20px; line-height: 1; color: var(--ink-soft); }
.drawer-chart h3 { font-size: 16px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: 6px; }

@media (max-width: 860px) {
  /* Above the map's floating controls and drawer (z-index 1100), below modals (1200+). */
  .sidebar { position: fixed; inset: 0 auto 0 0; z-index: 1160;
    transform: translateX(-100%); transition: transform .2s ease; }
  .sidebar.is-open { transform: translateX(0); }
  .sidebar-overlay.is-open { display: block; position: fixed; inset: 0;
    background: rgba(0,0,0,.4); z-index: 1150; }
  .topbar { flex-wrap: wrap; gap: 10px; }
  .two-col, .two-col-even, .card-grid { grid-template-columns: 1fr; }
  .map-side-panel { width: calc(100vw - 48px); max-width: 320px; }
  /* Phone: the drawer becomes a bottom sheet so the top of the map stays visible. */
  .map-drawer { width: 100%; top: auto; height: 62%; border-left: none;
    border-top: 1px solid var(--line); box-shadow: 0 -6px 18px rgba(0,0,0,.12); }
}

/* Phones: nothing may be wider than the screen; wide tables scroll inside their card. */
@media (max-width: 640px) {
  .content { padding: 12px 12px 32px; gap: 12px; }
  .topbar { padding: 10px 12px; }
  .card { padding: 12px; }
  .filters > *, .filters form > *, .filters select, .filters input { min-width: 0; max-width: 100%; }
  .filters form { display: flex; flex-wrap: wrap; gap: 8px; }
  .filters .segmented { flex-wrap: nowrap; }
  .dropdown-panel { min-width: 0; width: calc(100vw - 32px); max-width: calc(100vw - 32px); }
  .dropdown--end .dropdown-panel { right: auto; left: 0; }
  .form-grid { grid-template-columns: 1fr; }
  .kpi-row, .summary-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .kpi { padding: 10px; }
  .kpi-value { font-size: 22px; }
  .kpi-label { font-size: 13px; letter-spacing: .06em; }
  /* Fixed-layout hourly table: give it room to scroll instead of overlapping columns. */
  .data-table--logtable { min-width: 900px; }
  /* A pinned actions column would cover most of a narrow table. */
  .row-actions-cell { position: static; box-shadow: none; }
  .bar-row--ranked { grid-template-columns: 90px 1fr; }
  .bar-row--ranked .bar-value { grid-column: 1 / -1; text-align: left; }
  .bar-row.roadmap-grid { grid-template-columns: 110px 1fr 60px; gap: 8px; }
  .weather-stats { grid-template-columns: 1fr 1fr; }
  .download-toast { left: 12px; right: 12px; max-width: none; }
  .multi-grid { grid-template-columns: 1fr; }
}

@media print {
  .sidebar, .topbar-right, .btn { display: none; }
  .content { overflow: visible; }
  .card { break-inside: avoid; }
  /* A 13-column dense table (Reports' hourly readings) has nowhere near enough
     room on a portrait page even with the sidebar gone — standard paper is
     narrower than any on-screen layout this app uses. The @page landscape hint
     that actually fixes this can't be scoped by a class selector here (it's a
     page-level rule, not a per-element one) — see the dedicated <style> block on
     reports.html/report_print.html instead, so it only affects printing from
     those two pages, not every printable page in the app. This just shrinks the
     table's own font a bit further as a belt-and-suspenders measure. */
  .data-table--logtable { font-size: 11px; }
}

/* Dashboard monthly views: bullet, stacked, box, small multiples */
.bullet-track { position: relative; }
.bullet-track > i { position: absolute; top: 0; bottom: 0; left: 0; border-radius: 4px; }
.bullet-tick { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--ink); }
.box-track { position: relative; background: var(--line); height: 16px; }
.box-track > i, .box-track > b { position: absolute; }
.box-whisker { top: 50%; height: 2px; margin-top: -1px; background: var(--ink-soft); }
.box-iqr { top: 0; bottom: 0; opacity: .5; border-radius: 2px; }
.box-median { top: -3px; bottom: -3px; width: 3px; margin-left: -1px; background: var(--ink); }
.stack-chart { display: flex; gap: 6px; align-items: stretch; }
.stack-col { flex: 1; display: flex; flex-direction: column; align-items: center; min-width: 0; }
.stack-plot { height: 220px; width: 100%; display: flex; align-items: flex-end; }
.stack-bar { width: 100%; display: flex; flex-direction: column-reverse; }
.stack-bar i { display: block; width: 100%; }
.stack-label { margin-top: 6px; font-size: 13px; color: var(--ink-soft); }
.stack-total { font-size: 12px; color: var(--ink); }
.stack-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; font-size: 13px; }
.stack-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; }
.multi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.multi-grid[hidden] { display: none; }
.multi-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 12px; }
.multi-head, .multi-foot { display: flex; justify-content: space-between; font-size: 13px; color: var(--ink-soft); }
.multi-head { color: var(--ink); font-weight: 600; margin-bottom: 6px; }
.multi-svg { width: 100%; height: 110px; display: block; }
.multi-cap { stroke: var(--critical); stroke-dasharray: 4 3; stroke-width: 1; }

.data-table tr.is-archived td { color: var(--ink-soft); }

.peak-cell { cursor: pointer; }
.peak-cell:hover, .peak-cell:focus-visible { outline: 2px solid var(--forecast); outline-offset: -2px; }
.peak-modal-card { max-width: 560px; text-align: left; }
.peak-modal-card table { margin-top: 4px; }
.peak-modal-card .btn-primary { min-width: 90px; }

.row-bar { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 1100;
  display: flex; align-items: center; gap: 12px; padding: 10px 12px 10px 18px; max-width: calc(100vw - 32px);
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  box-shadow: 0 10px 28px rgba(0,0,0,.2); }
.row-bar[hidden] { display: none; }
.row-bar #row-bar-text { font-size: 15px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row-bar .btn, .row-bar .btn-primary { padding: 8px 18px; font-size: 15px; }


.roadmap-sub { display: block; font-size: 12px; color: var(--ink-soft); margin-top: 2px; }

.bar-row.roadmap-grid { grid-template-columns: 200px 1fr 90px; gap: 12px; }
.roadmap-ticks { position: relative; display: block; height: 18px; margin: 0 6px; font-size: 12px; color: var(--ink-soft); font-family: 'IBM Plex Mono', monospace; }
.roadmap-ticks span { position: absolute; top: 0; transform: translateX(-50%); }
.roadmap-track { background-image: repeating-linear-gradient(to right, var(--line) 0 1px, transparent 1px calc(100% / 11)); cursor: default; }
.roadmap-track i { z-index: 1; }
/* A single vertical line at the current year, repeated inside every row's own
   track (each track is independently %-positioned, like the bar fill already is,
   rather than one line spanning all rows — avoids needing to measure the grid's
   actual rendered column width in JS just to line up one marker). */
.roadmap-today-mark { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px;
  background: var(--forecast); z-index: 2; pointer-events: none; }
.roadmap-today-label { position: absolute; top: -3px; transform: translateX(-50%);
  font-size: 11px; color: var(--forecast); font-weight: 600; white-space: nowrap; }

/* Line-chart zoom (base.html renderLine): shown only while a chart is zoomed or panned. */
.chart-zoom-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 6px 0 0; }
.chart-zoom-hint { font-size: 12px; color: var(--ink-soft); margin-left: 4px; }
.chart-zoom-btn { font-size: 12px; padding: 4px 10px; min-width: 32px; }
.chart-reset-zoom[hidden] { display: none; }

/* Forecasting: Weather-Peak Correlation Analytics card */
.weather-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 14px; }
.weather-stat { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); }
.weather-stat-label { font-size: 14px; color: var(--ink-soft); }
.weather-stat strong { font-size: 22px; color: var(--ink); }
.weather-stat small { font-size: 13px; color: var(--ink-soft); }
.weather-subhead { font-size: 16px; margin: 18px 0 8px; color: var(--ink); }

/* Chart key-figure chips (base.html csRenderChips): short "Label value" pills */
.chart-stats { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
.chart-stat { display: inline-flex; align-items: baseline; gap: 4px; padding: 3px 9px; font-size: 13px;
  color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  font-variant-numeric: tabular-nums; }
.chart-stat b { font-weight: 600; color: var(--ink-soft); font-size: 12px; }
.chart-stat--warn { border-color: oklch(78% 0.12 75); background: oklch(97% 0.03 80); }
.chart-stat--bad { border-color: oklch(75% 0.14 25); background: oklch(96% 0.03 25); }
.roadmap-left { display: block; font-size: 11px; color: var(--ink-soft); font-weight: 500; }

.show-all-slot:empty { display: none; }
.chart-stats--head { margin: 0; justify-content: flex-end; }
.chart-stats--head:empty { display: none; }
.chart-stats--head .chart-stat { font-size: 15px; padding: 5px 12px; gap: 6px; }
.chart-stats--head .chart-stat b { font-size: 14px; }

/* Maintenance table: scroll sideways instead of squeezing (Suggested column added) */
.data-table--maintenance { min-width: 1450px; }
.data-table--maintenance td.mono { white-space: nowrap; }

/* Audit log Details column: context inline, the rest folded into a label/value list. */
.audit-details { min-width: 220px; max-width: 460px; overflow-wrap: anywhere; font-size: 14px; }
.audit-head { display: flex; flex-wrap: wrap; gap: 2px 12px; }
.audit-head b { font-weight: 600; color: var(--ink-soft); }
.audit-details details { margin-top: 4px; }
.audit-details summary { cursor: pointer; color: var(--primary); font-size: 13px; }
.audit-kv { display: grid; grid-template-columns: max-content 1fr; gap: 2px 12px; margin: 6px 0 0; }
.audit-kv dt { color: var(--ink-soft); }
.audit-kv dd { margin: 0; }

/* Audit log filter bar: a small caption above each control, buttons aligned to the inputs. */
.audit-filters { flex-wrap: wrap; align-items: flex-end; }
.filter-field { display: flex; flex-direction: column; gap: 4px; }
.filter-field > span { font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-soft); }
.filters input[type="text"] { font-family: inherit; font-size: 16px; padding: 7px 12px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); }

/* Logsheet: a value changed after it was first saved (hover title says by whom/when/was). */
.cell-edited, .cell-edited .cell-input, .data-table--logsheet .cell-edited .cell-input[readonly] {
  color: var(--critical); font-weight: 600; }
td.cell-edited { cursor: help; }
/* Sidebar foot: Change password / Log out stacked full-width, never wrapping mid-label */
.sidebar-foot .btn { display: block; width: 100%; box-sizing: border-box; text-align: center;
  white-space: nowrap; margin-top: 6px; }
.sidebar-foot .role-tag + .btn { margin-top: 10px; }

/* Short inline legend (dot/colour + a word or two) in place of explanatory sentences */
.legend-inline { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 16px;
  font-size: 13px; color: var(--ink-soft); }
.legend-inline > span { display: inline-flex; align-items: center; gap: 5px; }

/* Data import progress (background reading) */
.import-progress { max-width: 760px; }
.import-progress-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; margin-bottom: 12px; }
.import-progress-count { font-size: 14px; color: var(--ink-soft); white-space: nowrap; }
.import-progress-count b { font-size: 22px; color: var(--ink); }
.import-progress-bar { height: 10px; border-radius: 999px; background: var(--line); overflow: hidden; }
.import-progress-bar > i { display: block; height: 100%; border-radius: 999px; background: var(--primary);
  transition: width .4s ease; }
.import-progress .file-chip-list { margin-top: 14px; }
.file-chip.is-done { background: var(--soft-green); border-color: oklch(80% 0.08 148); }
.file-chip.is-done::before { content: "✓ "; }
.upload-size-note { font-size: 13px; color: var(--ink-soft); text-align: center; margin: 4px 0 0; }
.upload-size-note.is-over { color: var(--critical); font-weight: 600; }

/* Data import preview: summary + per-feeder hourly tables before adding */
.import-summary { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; }
.import-summary-main { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.import-files { justify-content: flex-start; }
.import-stats { display: flex; flex-wrap: wrap; gap: 8px 28px; }
.import-stats div { display: flex; align-items: baseline; gap: 6px; }
.import-stats b { font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums; }
.import-stats span { font-size: 14px; color: var(--ink-soft); }
.import-stats .is-warn b { color: var(--warning); }
.import-actions { display: flex; gap: 8px; }
.import-actions .btn-primary, .import-actions .btn { padding: 10px 20px; font-weight: 600; }
.import-hint { flex-basis: 100%; margin: 0; }
.import-feeder-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 8px 16px; margin-bottom: 12px; }
.import-feeder-head h2 { font-size: 17px; text-transform: none; letter-spacing: 0; }
.import-feeder-stats { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: 14px; color: var(--ink-soft); }
.import-feeder-stats b { color: var(--ink); }
.import-table-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin-bottom: 8px; font-size: 13px; color: var(--ink-soft); }
.import-legend { display: inline-flex; align-items: center; gap: 6px; }
.import-more { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; color: var(--ink); }
.import-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-left: 6px; vertical-align: middle; }
.import-legend .import-dot, .import-feeder-stats .import-dot { margin-left: 0; margin-right: 2px; }
.import-dot.is-exists { background: oklch(70% 0.01 220); }
.import-dot.is-filled { background: oklch(70% 0.14 70); }
.data-table--import th { font-size: 12px; letter-spacing: .04em; }
.data-table--import td { white-space: nowrap; font-size: 14px; font-variant-numeric: tabular-nums; }
.data-table--import tr.is-exists td { color: oklch(62% 0.01 220); }
.data-table--import .import-extra { display: none; }
.import-feeder.show-extra .data-table--import .import-extra { display: table-cell; }
.import-skipped { margin: 10px 0 0; padding-left: 18px; font-size: 14px; color: var(--ink-soft); line-height: 1.5; }
.import-skipped b { color: var(--ink); font-weight: 600; }
@media (max-width: 640px) {
  .import-actions { width: 100%; }
  .import-actions form, .import-actions .btn-primary, .import-actions .btn { flex: 1; width: 100%; }
}

/* Metrics min/max: hover definitions; values outside the normal range */
.metric-table td[data-tip] { cursor: help; }
.metric-table td.metric-bad { color: oklch(50% 0.19 25); font-weight: 600; text-decoration: underline dotted; }

/* ==========================================================================
   Modern theme (2026-10-20): light sidebar with icons, top bar with search and
   avatar, softer cards, KPI icon badges. PANELCO green stays the accent. Kept as
   one block of overrides at the end so it can be tuned (or removed) in one place.
   ========================================================================== */
:root {
  --canvas: #f4f6f8;
  --line: #e7eaee;
  --ink: #1d2433;
  --ink-soft: #6b7385;
  --radius: 12px;
  --radius-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .04);
  --shadow-md: 0 8px 24px rgba(16, 24, 40, .08);
  --soft-green: oklch(95.5% 0.035 148);
}
body { font-family: 'Inter', 'IBM Plex Sans', system-ui, sans-serif; font-size: 15px; }
.ico { width: 20px; height: 20px; flex: none; }

/* Sidebar: white, icon + label rows, soft green active pill */
.sidebar { background: #fff; color: var(--ink); border-right: 1px solid var(--line); width: 248px; }
.brand { flex-direction: row; align-items: center; justify-content: flex-start; gap: 10px;
  padding: 20px 20px 18px; border-bottom: none; text-align: left; }
.brand img { width: 38px; }
.brand-name { color: var(--ink); font-size: 18px; letter-spacing: 0; font-weight: 700; }
.brand-sub { color: var(--ink-soft); font-size: 12px; }
.nav { padding: 6px 12px; gap: 2px; border-bottom: none; }
.nav-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 10px;
  font-size: 14.5px; font-weight: 500; color: var(--ink-soft); }
.nav-item:hover { background: #f3f5f7; color: var(--ink); }
.nav-item.is-active { background: var(--soft-green); color: var(--primary); font-weight: 600; box-shadow: none; }
.nav-item.is-active .ico { stroke: var(--primary); }
.sidebar-foot { padding: 10px 12px 16px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 2px; }

/* Top bar: title, search, bell, avatar */
.topbar { background: #fff; border-bottom: 1px solid var(--line); padding: 12px 24px; }
.topbar h1 { font-size: 20px; font-weight: 700; }
.topbar-sub { font-size: 13px; }
.sidebar-toggle { border-color: var(--line); border-radius: 10px; }
.icon-btn { position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 999px; border: 1px solid var(--line); color: var(--ink-soft); background: #fff; }
.icon-btn:hover { color: var(--primary); border-color: var(--primary); }
.notif-dot { top: -2px; right: -2px; }
.user-chip { display: flex; align-items: center; gap: 10px; padding-left: 4px; }
.avatar { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px;
  border-radius: 999px; background: var(--primary); color: #fff; font-weight: 700; font-size: 14px; }
.user-chip-text { display: flex; flex-direction: column; line-height: 1.2; }
.user-chip-text b { font-size: 14px; }
.user-chip-text small { font-size: 12px; color: var(--ink-soft); }
.content { padding: 22px 24px 40px; gap: 18px; }

/* Cards: white, hairline border, soft shadow, sentence-case headings */
.card { border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); padding: 20px; }
.card-head h2 { font-size: 16px; letter-spacing: 0; text-transform: none; font-weight: 650; }
.card-note { font-size: 13.5px; }

/* KPI cards: icon badge, big number, short grey label */
.kpi-row, .summary-row { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; }
.kpi { border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 16px 18px; box-shadow: var(--shadow-sm); }
.kpi-top { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.kpi-icon { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px;
  border-radius: 999px; flex: none; }
.kpi-icon .ico { width: 19px; height: 19px; }
.kpi-label { font-size: 13.5px; letter-spacing: 0; text-transform: none; color: var(--ink-soft); font-weight: 500; }
.kpi-value { font-family: inherit; font-size: 28px; font-weight: 700; font-variant-numeric: tabular-nums; margin-top: 0; }
.kpi-unit { font-size: 14px; }
.kpi-sub { font-size: 13px; }
.tint-green { background: oklch(94% 0.05 148); color: oklch(45% 0.15 148); }
.tint-blue { background: oklch(94% 0.04 245); color: oklch(50% 0.15 245); }
.tint-amber { background: oklch(95% 0.06 80); color: oklch(55% 0.14 65); }
.tint-red { background: oklch(94.5% 0.04 25); color: oklch(52% 0.19 25); }
.tint-violet { background: oklch(94.5% 0.04 300); color: oklch(50% 0.16 300); }
.tint-teal { background: oklch(94.5% 0.04 190); color: oklch(50% 0.1 190); }

/* Controls and tables: rounder, lighter */
select, .btn, button, input[type="date"], .dropdown-trigger { border-radius: 10px; }
.btn-primary, .btn-danger, .btn-edit { border-radius: 10px; }
.data-table th { font-size: 12px; letter-spacing: .05em; color: var(--ink-soft); }
.data-table td { border-bottom-color: #f0f2f5; }
.segmented { border-radius: 10px; }

/* Numbers: same font as the text, evenly spaced digits (no typewriter font) */
.mono, .data-table td.mono, .cell-input, .heat-cell, .heat-rank { font-family: inherit; font-variant-numeric: tabular-nums; }

/* Badges: soft tinted rounded labels in sentence case (replaces the bold uppercase pills) */
.pill { text-transform: none; letter-spacing: 0; font-size: 12.5px; font-weight: 600; padding: 2px 10px; border-radius: 999px; }
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px;
  font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.badge-dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge-green { background: oklch(94.5% 0.045 148); color: oklch(42% 0.14 148); }
.badge-blue { background: oklch(94.5% 0.035 245); color: oklch(46% 0.14 245); }
.badge-violet { background: oklch(94.5% 0.04 300); color: oklch(46% 0.16 300); }
.badge-amber { background: oklch(95.5% 0.06 80); color: oklch(48% 0.13 60); }
.badge-red { background: oklch(95% 0.035 25); color: oklch(50% 0.19 25); }
.badge-muted { background: #eef0f3; color: var(--ink-soft); }

/* Person cell: round initials avatar + name over @username */
.person { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.person-text { display: flex; flex-direction: column; line-height: 1.25; }
.person-text b { font-weight: 600; }
.person-text small { color: var(--ink-soft); font-size: 12.5px; }
.avatar-sm { width: 34px; height: 34px; font-size: 12.5px; background: var(--soft-green); color: var(--primary); }
.avatar-sm.is-off { background: #eef0f3; color: var(--ink-soft); }

/* Buttons inside tables: compact, never wrapping mid-row */
.data-table .row-actions { flex-wrap: nowrap; }
.data-table .btn, .data-table .btn-primary, .data-table .btn-danger, .data-table .btn-edit,
.data-table .inline button, .data-table .inline .btn-danger, .data-table .inline .btn-edit {
  padding: 5px 11px; font-size: 13px; white-space: nowrap; }
.data-table td { vertical-align: middle; }

@media (max-width: 640px) {
  .user-chip-text { display: none; }
  .topbar { padding: 10px 12px; }
  .kpi-row, .summary-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .kpi { padding: 12px; }
  .kpi-value { font-size: 22px; }
  .kpi-icon { width: 32px; height: 32px; }
}

/* Visualization principles (2026-10-21): no ALL CAPS anywhere (typography), one quiet
   help icon instead of a sentence under every chart (simplicity), and a one-line
   insight under key charts (annotations). */
.data-table th, .metric-table th, .kpi-label, .pill, .filter-field > span, .export-group > strong,
details summary, .status-step, .drawer-chart h3, .admin-control h3, .alert-modal-card--info h2,
.role-tag, .sidebar-label, .card-head h2 { text-transform: none; letter-spacing: 0; }
.data-table th, .metric-table th { font-size: 13px; font-weight: 600; color: var(--ink-soft); }
.drawer-chart h3 { font-size: 15px; font-weight: 650; color: var(--ink); }
.chart-zoom-hint { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px;
  border-radius: 999px; color: var(--ink-soft); font-size: 14px; cursor: help; margin-left: 2px; }
.chart-zoom-hint:hover { color: var(--primary); background: var(--soft-green); }
.chart-insight { margin: -4px 0 12px; padding: 1px 0 1px 10px; border-left: 4px solid var(--primary);
  border-radius: 2px; font-size: 14.5px; color: var(--ink); line-height: 1.45; }
.chart-insight.is-warn { border-left-color: var(--warning); }
.chart-insight.is-bad { border-left-color: var(--critical); }
.chart-insight b { font-weight: 650; }
.curve-day-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
.curve-day-form input[type="date"] { padding: 5px 10px; font-size: 14px; }
.curve-day-form .btn { padding: 5px 12px; font-size: 13px; }
