/* Atlas-ACMS app shell -- extends the identity already established in
   the dashboard artifact (series colors, status semantics, Futura
   wordmark, the "Balance" mark) rather than inventing a new one. */

:root {
  color-scheme: light;
  --page:           #f9f9f7;
  --surface:        #fcfcfb;
  --surface-raised: #ffffff;
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --text-muted:     #898781;
  --grid:           #e1e0d9;
  --border:         rgba(11,11,11,0.10);
  --border-strong:  rgba(11,11,11,0.18);

  --series-1:       #2a78d6;
  --series-2:       #eb6834;
  --series-3:       #1baf7a;
  --status-good:    #0ca30c;
  --status-warning: #fab219;
  --status-critical:#d03b3b;

  --sidebar-bg:     #14181f;
  --sidebar-text:   #c3c8d1;
  --sidebar-text-dim: #7c828e;
  --sidebar-active: #1e2531;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --page:           #0d0d0d;
    --surface:        #161615;
    --surface-raised: #1c1c1b;
    --text-primary:   #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted:     #898781;
    --grid:           #2c2c2a;
    --border:         rgba(255,255,255,0.10);
    --border-strong:  rgba(255,255,255,0.18);
    --series-1:       #3987e5;
    --series-2:       #d95926;
    --series-3:       #199e70;
    --status-good:    #0ca30c;
    --status-warning: #fab219;
    --status-critical:#e66767;
    --sidebar-bg:     #0a0d12;
    --sidebar-text:   #c3c8d1;
    --sidebar-text-dim: #626875;
    --sidebar-active: #161b24;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --page:           #0d0d0d;
  --surface:        #161615;
  --surface-raised: #1c1c1b;
  --text-primary:   #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted:     #898781;
  --grid:           #2c2c2a;
  --border:         rgba(255,255,255,0.10);
  --border-strong:  rgba(255,255,255,0.18);
  --series-1:       #3987e5;
  --series-2:       #d95926;
  --series-3:       #199e70;
  --sidebar-bg:     #0a0d12;
  --sidebar-active: #161b24;
}

/* ---- Named theme palettes (opt-in personalization, static/app.js's
   THEME_PALETTES) -- an alternative to "Balance" (the theme above,
   which keeps following prefers-color-scheme automatically). Each of
   these is a single fixed look rather than a light/dark pair, so
   picking one also pins data-theme to the matching mode (see
   applyThemePalette()) -- otherwise a light palette like Hill Country
   could render body-level dark-mode elements (whatever hasn't been
   fully converted to CSS variables yet) alongside it. Core palette +
   accent hex values are exact, as given; surface/text/border/sidebar
   shades in between are derived by hand to fit the existing token
   set. Hill Country is the one light theme (its own lead color,
   Limestone, is light) -- all the others lead with a dark base.
   Status-good/warning stay at the shared semantic baseline everywhere;
   status-critical gets the same brightened-red contrast bump on dark
   surfaces the plain dark theme above already uses, so it never reads
   as calmer than a light-mode critical would. */
:root[data-palette="vegas"] {
  color-scheme: dark;
  --page:           #0b0b0d;
  --surface:        #202126;
  --surface-raised: #27282e;
  --text-primary:   #f2efea;
  --text-secondary: #c9c2b4;
  --text-muted:     #8c8579;
  --grid:           #2a2a30;
  --border:         rgba(242,239,234,0.10);
  --border-strong:  rgba(242,239,234,0.20);
  --series-1:       #d4af37;
  --series-2:       #c1121f;
  --series-3:       #3f9142;
  --status-critical:#e64c4c;
  --sidebar-bg:     #0b0b0d;
  --sidebar-text:   #e8e4dc;
  --sidebar-text-dim: #8c8579;
  --sidebar-active: #201014;
}
:root[data-palette="la-mula"] {
  color-scheme: dark;
  --page:           #28221c;
  --surface:        #322a22;
  --surface-raised: #3d3327;
  --text-primary:   #e7ddc8;
  --text-secondary: #c7b89c;
  --text-muted:     #9c8768;
  --grid:           #3a3025;
  --border:         rgba(231,221,200,0.10);
  --border-strong:  rgba(231,221,200,0.20);
  --series-1:       #b08d57;
  --series-2:       #79553a;
  --series-3:       #8f9b72;
  --status-critical:#e66767;
  --sidebar-bg:     #201a15;
  --sidebar-text:   #e7ddc8;
  --sidebar-text-dim: #9c8768;
  --sidebar-active: #3d3327;
}
:root[data-palette="hill-country"] {
  color-scheme: light;
  --page:           #e4ded0;
  --surface:        #ede8dc;
  --surface-raised: #f7f4ec;
  --text-primary:   #3a2e24;
  --text-secondary: #705747;
  --text-muted:     #9c8975;
  --grid:           #dcd4c2;
  --border:         rgba(58,46,36,0.14);
  --border-strong:  rgba(58,46,36,0.26);
  --series-1:       #5969a6;
  --series-2:       #b5652f;
  --series-3:       #59634a;
  --sidebar-bg:     #59634a;
  --sidebar-text:   #ede8dc;
  --sidebar-text-dim: #a8b39b;
  --sidebar-active: #465038;
}
:root[data-palette="permian"] {
  color-scheme: dark;
  --page:           #161719;
  --surface:        #1d1f22;
  --surface-raised: #24262a;
  --text-primary:   #e3d6be;
  --text-secondary: #c6ae87;
  --text-muted:     #8f8168;
  --grid:           #26282c;
  --border:         rgba(227,214,190,0.10);
  --border-strong:  rgba(227,214,190,0.20);
  --series-1:       #b66a3c;
  --series-2:       #626970;
  --series-3:       #c6ae87;
  --status-critical:#e66767;
  --sidebar-bg:     #101113;
  --sidebar-text:   #d9cdb3;
  --sidebar-text-dim: #8f8168;
  --sidebar-active: #24262a;
}
:root[data-palette="gulf-coast"] {
  color-scheme: dark;
  --page:           #0b253a;
  --surface:        #0f2e46;
  --surface-raised: #143a56;
  --text-primary:   #e4f0f1;
  --text-secondary: #b7d2d6;
  --text-muted:     #7fa0a6;
  --grid:           #163c58;
  --border:         rgba(228,240,241,0.10);
  --border-strong:  rgba(228,240,241,0.20);
  --series-1:       #64b6ac;
  --series-2:       #287b91;
  --series-3:       #9fd8ce;
  --status-critical:#e66767;
  --sidebar-bg:     #081c2c;
  --sidebar-text:   #d3e6e8;
  --sidebar-text-dim: #7fa0a6;
  --sidebar-active: #143a56;
}
:root[data-palette="midnight"] {
  color-scheme: dark;
  --page:           #090f1c;
  --surface:        #14213d;
  --surface-raised: #1b2b4c;
  --text-primary:   #edeff3;
  --text-secondary: #b7bdc9;
  --text-muted:     #7c8493;
  --grid:           #202b44;
  --border:         rgba(237,239,243,0.10);
  --border-strong:  rgba(237,239,243,0.20);
  --series-1:       #3a86ff;
  --series-2:       #6c7486;
  --series-3:       #7fb2ff;
  --status-critical:#e66767;
  --sidebar-bg:     #060a14;
  --sidebar-text:   #d7dae2;
  --sidebar-text-dim: #7c8493;
  --sidebar-active: #1b2b4c;
}
:root[data-palette="black-gold"] {
  color-scheme: dark;
  --page:           #0c0c0c;
  --surface:        #292929;
  --surface-raised: #333333;
  --text-primary:   #f1e9d2;
  --text-secondary: #c7bc9c;
  --text-muted:     #8c846c;
  --grid:           #303030;
  --border:         rgba(241,233,210,0.10);
  --border-strong:  rgba(241,233,210,0.20);
  --series-1:       #c9a227;
  --series-2:       #e0c468;
  --series-3:       #8c846c;
  --status-critical:#e66767;
  --sidebar-bg:     #181818;
  --sidebar-text:   #e3d9bc;
  --sidebar-text-dim: #8c846c;
  --sidebar-active: #292929;
}
:root[data-palette="copperhead"] {
  color-scheme: dark;
  --page:           #141210;
  --surface:        #221c17;
  --surface-raised: #49392c;
  --text-primary:   #eee1cf;
  --text-secondary: #c9b49a;
  --text-muted:     #8f7b63;
  --grid:           #2c241c;
  --border:         rgba(238,225,207,0.10);
  --border-strong:  rgba(238,225,207,0.20);
  --series-1:       #e09f3e;
  --series-2:       #b6633b;
  --series-3:       #d9b36b;
  --status-critical:#e66767;
  --sidebar-bg:     #0d0b09;
  --sidebar-text:   #dccbb0;
  --sidebar-text-dim: #8f7b63;
  --sidebar-active: #2c241c;
}
:root[data-palette="republic"] {
  color-scheme: dark;
  --page:           #152238;
  --surface:        #1b2c46;
  --surface-raised: #223655;
  --text-primary:   #f3efe4;
  --text-secondary: #c7c2b6;
  --text-muted:     #8e8a80;
  --grid:           #23375a;
  --border:         rgba(243,239,228,0.10);
  --border-strong:  rgba(243,239,228,0.20);
  --series-1:       #a72b2a;
  --series-2:       #6b7280;
  --series-3:       #4a6fa5;
  --status-critical:#e2554f;
  --sidebar-bg:     #0f1b2e;
  --sidebar-text:   #e3dece;
  --sidebar-text-dim: #8e8a80;
  --sidebar-active: #223655;
}
/* PFC Midstream's own brand palette (pfcmidstream.com) -- steel blue
   #558ABE as the primary/link color with #1F4972 as its darker hover
   shade, #FCBF10 gold as their accent/CTA color, #4B4E53 as both their
   body text and their site's own fixed header bar background. Light
   theme like Hill Country (their real site is light, white-paged, with
   that dark slate bar fixed on top) -- status colors stay at the
   shared baseline for the same reason Hill Country's do. */
:root[data-palette="midstream"] {
  color-scheme: light;
  --page:           #f4f5f7;
  --surface:        #ffffff;
  --surface-raised: #ffffff;
  --text-primary:   #2b2d31;
  --text-secondary: #4b4e53;
  --text-muted:     #82858a;
  --grid:           #e2e4e8;
  --border:         rgba(43,45,49,0.12);
  --border-strong:  rgba(43,45,49,0.22);
  --series-1:       #558abe;
  --series-2:       #fcbf10;
  --series-3:       #1f4972;
  --sidebar-bg:     #4b4e53;
  --sidebar-text:   #eef0f2;
  --sidebar-text-dim: #a9acb1;
  --sidebar-active: #3a3c40;
}

/* Any plain link gets an accent-color hover shift so it's always clear
   what's clickable -- more specific hover rules elsewhere (.nav-link,
   .btn, etc.) already have their own tailored hover treatment and
   naturally win over this default via specificity. */
a:hover { color: var(--series-1); }

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  background: var(--page);
  color: var(--text-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px;
  line-height: 1.5;
}
h1, h2, h3 { text-wrap: balance; margin: 0; }
code, .mono { font-family: ui-monospace, "SF Mono", "JetBrains Mono", Consolas, monospace; }

/* ---- Splash / launch screen ---- */
#splash {
  position: fixed; inset: 0; z-index: 200; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; background: var(--sidebar-bg, #14181f);
  animation: splash-out 400ms cubic-bezier(0.4, 0, 0.2, 1) 3000ms forwards;
}
#splash.splash-skip { animation: splash-out 300ms cubic-bezier(0.4, 0, 0.2, 1) 200ms forwards; }
@keyframes splash-out { to { opacity: 0; visibility: hidden; pointer-events: none; } }

/* The mark's own viewBox has real empty space below the curve/nodes
   (they only occupy roughly y 32-68 of a 0-100 box) -- a negative
   bottom margin pulls the wordmark up into that dead space instead of
   just shrinking the flex gap, which alone can't close it. */
.splash-mark { width: 296px; height: 296px; position: relative; margin-bottom: -70px; }
.splash-mark svg { width: 100%; height: 100%; display: block; overflow: visible; }
.splash-mark #splashLine {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: splash-draw 700ms 100ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.splash-mark .splash-node {
  opacity: 0; transform-origin: center; transform-box: fill-box;
  animation: splash-pop 420ms cubic-bezier(0.2, 1.1, 0.3, 1) forwards;
}
.splash-mark .n1 { animation-delay: 200ms; }
.splash-mark .n2 { animation-delay: 320ms; }
.splash-mark .n3 { animation-delay: 420ms; }
@keyframes splash-draw { to { stroke-dashoffset: 0; } }
@keyframes splash-pop { from { opacity: 0; transform: scale(0.4); } to { opacity: 1; transform: scale(1); } }

.splash-word {
  font-family: "Futura", "Century Gothic", "Avenir Next", "Avenir", system-ui, sans-serif;
  font-weight: 700; font-size: 34px; letter-spacing: 0.06em; color: #fff;
  opacity: 0; transform: translateY(10px);
  animation: splash-rise 450ms 520ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.splash-sub {
  font-size: 13px; letter-spacing: 0.03em; color: var(--sidebar-text-dim, #7c828e);
  opacity: 0; transform: translateY(8px);
  animation: splash-rise 450ms 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes splash-rise { to { opacity: 1; transform: translateY(0); } }

@media (prefers-reduced-motion: reduce) {
  #splash { animation-duration: 10ms; animation-delay: 500ms; }
  .splash-mark #splashLine, .splash-mark .splash-node, .splash-word, .splash-sub {
    animation: none !important; opacity: 1 !important; transform: none !important; stroke-dashoffset: 0 !important;
  }
  .login-mark .login-splash-line, .login-mark .login-node, .login-title {
    animation: none !important; opacity: 1 !important; transform: none !important; stroke-dashoffset: 0 !important;
  }
}

/* ---- Login screen -- roadmap item #3, see the approved plan. Shown
   in place of #app until GET /api/v1/auth/me confirms a valid session;
   same dark sidebar-bg ground and wordmark as #splash for continuity,
   deliberately not animated/timed the way #splash is (it stays up
   until the user actually logs in). ---- */
#loginScreen {
  position: fixed; inset: 0; z-index: 150; overflow: auto;
  display: flex; align-items: center; justify-content: center;
  background: var(--sidebar-bg, #14181f); padding: 24px;
}
.login-card {
  width: 100%; max-width: 420px; display: flex; flex-direction: column; align-items: center;
}
/* Matched to the rendered width of .login-title ("ATLAS-ACMS" at
   40px/700/0.06em, measured ~295px) so the mark and the lettering read
   as the same size -- 296px coincidentally matches #splash's own mark
   size already, so its margin-bottom is reused too. */
.login-mark { width: 296px; height: 296px; margin-bottom: -70px; }
/* Same draw-in/pop/rise treatment as #splash's mark (reuses its
   keyframes below) -- runs each time #loginScreen goes from display:none
   to visible (a CSS animation restarts whenever its element re-enters
   rendering), so it replays after logout too, not just on first load. */
.login-mark .login-splash-line {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: splash-draw 700ms 100ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.login-mark .login-node {
  opacity: 0; transform-origin: center; transform-box: fill-box;
  animation: splash-pop 420ms cubic-bezier(0.2, 1.1, 0.3, 1) forwards;
}
.login-mark .n1 { animation-delay: 200ms; }
.login-mark .n2 { animation-delay: 320ms; }
.login-mark .n3 { animation-delay: 420ms; }
.login-title {
  font-family: "Futura", "Century Gothic", "Avenir Next", "Avenir", system-ui, sans-serif;
  font-weight: 700; font-size: 40px; letter-spacing: 0.06em; color: #fff; margin-bottom: 36px;
  opacity: 0; transform: translateY(10px);
  animation: splash-rise 450ms 520ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.login-form { width: 100%; display: flex; flex-direction: column; gap: 14px; }
.login-form .field label { color: var(--sidebar-text-dim, #7c828e); }
.login-form .field input {
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14); color: #fff;
}
.login-form .field input:focus { border-color: var(--series-1); outline: none; }
.login-form .btn-primary { width: 100%; margin-top: 4px; }
.login-error { font-size: 12.5px; color: var(--status-critical); }

/* ---- App shell ---- */
#app { display: flex; min-height: 100vh; }

#sidebar {
  width: 236px; flex: none;
  background: var(--sidebar-bg);
  display: flex; flex-direction: column;
  padding: 20px 0 16px;
  /* The nav list can run taller than the viewport (lots of groups) --
     without this, overflow had nowhere to go inside the sidebar's own
     box, so a scroll gesture over it fell through to whatever WAS
     scrollable behind it (the page background on mobile's fixed-
     position drawer) instead of revealing the rest of the list. */
  overflow-y: auto;
}
.sidebar-brand {
  display: flex; align-items: center; gap: 12px;
  padding: 0 20px 18px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  margin-bottom: 10px;
}
.sidebar-brand svg { width: 32px; height: 32px; flex: none; }
.nav-toggle {
  display: none; margin-left: auto; width: 30px; height: 30px; flex: none;
  align-items: center; justify-content: center; border-radius: 6px;
  background: rgba(255,255,255,0.06); border: none; color: #fff; font-size: 20px; line-height: 1;
  cursor: pointer;
}
.menu-btn {
  display: none; width: 34px; height: 34px; flex: none; margin-right: 4px;
  align-items: center; justify-content: center; border-radius: 6px;
  background: none; border: 1px solid var(--border-strong); color: var(--text-primary); cursor: pointer;
}
#sidebarScrim { display: none; }
.sidebar-brand .word-atlas {
  font-family: "Futura", "Century Gothic", "Avenir Next", "Avenir", system-ui, sans-serif;
  font-weight: 700; font-size: 16.5px; color: #fff; letter-spacing: 0.005em; line-height: 1.15;
  white-space: nowrap;
}
.sidebar-brand .word-acms { font-weight: 300; }
.pipeline-badge {
  margin: 0 20px 4px; padding: 10px 12px; border-radius: 8px;
  background: var(--sidebar-active); border: 1px solid rgba(255,255,255,0.06);
}
.pipeline-badge .pb-label { font-size: 9.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sidebar-text-dim); }
.pipeline-badge .pb-name { font-size: 13px; font-weight: 700; color: #fff; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pipeline-badge .pb-switch { font-size: 11px; color: var(--series-1); cursor: pointer; margin-top: 4px; display: inline-block; }
.pipeline-badge .pb-switch:hover { text-decoration: underline; }

.user-badge {
  margin: 0 20px 12px; padding: 0 12px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.user-badge .ub-name { font-size: 11.5px; color: var(--sidebar-text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-badge .ub-logout { font-size: 11px; color: var(--series-1); cursor: pointer; flex: none; }
.user-badge .ub-logout:hover { text-decoration: underline; }

/* Theme picker -- lives in the topbar (top-right), not the sidebar, so
   it's reachable without opening the mobile drawer. A button + popover
   instead of a <select> since the topbar is a tight strip and a native
   select's own styling doesn't fit the compact-pill language the rest
   of topbar-right uses. */
.theme-picker { position: relative; }
.theme-picker-btn {
  display: flex; align-items: center; gap: 6px; font: inherit; font-size: 12px;
  padding: 5px 10px; border-radius: 6px; cursor: pointer;
  background: var(--surface-raised); color: var(--text-secondary); border: 1px solid var(--border);
}
.theme-picker-btn svg { flex: none; }
.theme-picker-btn:hover { border-color: var(--series-1); color: var(--text-primary); }
.theme-picker-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 50;
  min-width: 170px; padding: 6px; border-radius: 8px;
  background: var(--surface-raised); border: 1px solid var(--border-strong);
  box-shadow: 0 8px 24px rgba(0,0,0,0.28);
  display: flex; flex-direction: column; gap: 1px;
}
.theme-picker-menu[hidden] { display: none; }
.theme-picker-option {
  text-align: left; font: inherit; font-size: 12.5px; padding: 7px 10px; border-radius: 6px;
  background: none; border: none; cursor: pointer; color: var(--text-primary);
}
.theme-picker-option:hover { background: var(--surface); color: var(--series-1); }
.theme-picker-option.active { background: var(--surface); font-weight: 600; color: var(--series-1); }

.pipeline-gate { max-width: 640px; margin: 40px auto; }

.nav-group { margin-top: 14px; }
.nav-group-label {
  font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--sidebar-text-dim); padding: 0 20px; margin-bottom: 4px;
}
.nav-link {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 20px; color: var(--sidebar-text); text-decoration: none;
  font-size: 13px; cursor: pointer; border-left: 3px solid transparent;
  white-space: nowrap;
  transition: background-color 100ms ease, color 100ms ease;
}
.nav-link:hover { background: rgba(255,255,255,0.04); color: #fff; }
.nav-link:active { background: rgba(255,255,255,0.09); }
.nav-link.active { background: var(--sidebar-active); color: #fff; border-left-color: var(--series-1); font-weight: 600; }
.nav-link .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--sidebar-text-dim); }
.nav-link .dot.good { background: var(--status-good); }
.nav-link .dot.warn { background: var(--status-warning); }
.nav-link .dot.crit { background: var(--status-critical); }

#main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
#topbar {
  height: 56px; flex: none; display: flex; align-items: center; justify-content: space-between;
  padding: 0 28px; border-bottom: 1px solid var(--border); background: var(--surface);
}
#topbar .page-title { font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
#topbar .topbar-right { display: flex; align-items: center; gap: 14px; font-size: 12px; color: var(--text-muted); }
.conn-pill { display: inline-flex; align-items: center; gap: 6px; }
.conn-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--status-good); }
.conn-dot.off { background: var(--status-critical); }

#content { flex: 1; overflow-y: auto; padding: 24px 28px 60px; }
.page-desc { color: var(--text-secondary); font-size: 13px; max-width: 78ch; margin: -6px 0 20px; }

/* ---- Desktop: auto-hide sidebar, full-screen content ----
   Opt-out via the sidebarModePicker topbar toggle (applySidebarMode in
   app.js): [data-sidebar-mode="fixed"] on <html> means this whole block
   is scoped out, so #sidebar just falls back to its base rule above --
   a normal, permanently-visible 236px-wide flex child, no position/
   transform override at all. Default (no attribute, or any other
   value) keeps the original auto-hide-to-a-peek-strip behavior. */
@media (min-width: 861px) {
  :root:not([data-sidebar-mode="fixed"]) #sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 110;
    /* 236px wide, tucked to a 14px peek strip so there's still a visible
       edge to hover into -- hovering the sidebar (or tabbing a link into
       focus) slides it fully into view; moving away hides it again, so
       #content gets the full window width at rest. */
    transform: translateX(-222px);
    transition: transform 160ms ease;
    box-shadow: 8px 0 24px rgba(0,0,0,0.25);
  }
  :root:not([data-sidebar-mode="fixed"]) #sidebar:hover,
  :root:not([data-sidebar-mode="fixed"]) #sidebar:focus-within { transform: translateX(0); }
}

/* ---- Mobile: off-canvas sidebar drawer ---- */
@media (max-width: 860px) {
  .menu-btn { display: flex; }
  .nav-toggle { display: flex; }
  #sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 110;
    transform: translateX(-100%); transition: transform 220ms ease;
    box-shadow: 8px 0 24px rgba(0,0,0,0.3);
  }
  #sidebar.open { transform: translateX(0); }
  #sidebarScrim {
    display: block; position: fixed; inset: 0; z-index: 105;
    background: rgba(0,0,0,0.45); opacity: 0; pointer-events: none;
    transition: opacity 220ms ease;
  }
  #sidebarScrim.open { opacity: 1; pointer-events: auto; }
  #topbar { padding: 0 16px; }
  #topbar .page-title { font-size: 15.5px; }
  #topbar .topbar-right { gap: 8px; font-size: 11px; }
  #topbar #clock { display: none; }
  .theme-picker-btn span { display: none; } /* icon-only -- theme names vary in length, don't risk the topbar wrapping */
  /* Fixed/hover only affects the desktop auto-hide behavior above (see
     that block's own comment) -- the mobile drawer pattern below always
     applies regardless of data-sidebar-mode, so this toggle would be a
     dead control here: it'd visibly flip its own icon/label but change
     nothing about how the sidebar actually behaves. Hide it entirely
     rather than leave something clickable that lies about doing
     anything. */
  #sidebarModePicker { display: none; }
  #content { padding: 16px 16px 48px; }
  .form-grid { grid-template-columns: 1fr 1fr; }
  .stat-row { gap: 10px; }
  .stat-tile { min-width: 130px; }
}
@media (max-width: 480px) {
  .form-grid { grid-template-columns: 1fr; }
}

/* ---- Primitives ---- */
.stat-row { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 22px; }
.stat-tile {
  background: var(--surface-raised); border: 1px solid var(--border); border-radius: 8px;
  padding: 14px 16px; min-width: 140px; flex: 1;
}
.stat-tile .stat-label { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
.stat-tile .stat-value { font-size: 24px; font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
.stat-tile .stat-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.stat-tile.accent-good .stat-value { color: var(--status-good); }
.stat-tile.accent-warn .stat-value { color: var(--status-warning); }
.stat-tile.accent-crit .stat-value { color: var(--status-critical); }

.card {
  background: var(--surface-raised); border: 1px solid var(--border); border-radius: 10px;
  padding: 18px 20px; margin-bottom: 18px;
}
.card-title { font-size: 14px; font-weight: 700; margin-bottom: 10px; display: flex; align-items: center; }
.card-drag-handle {
  cursor: grab; color: var(--text-muted); margin-right: 8px; font-size: 13px;
  user-select: none; line-height: 1; flex: none;
}
.card-drag-handle:hover { color: var(--text-primary); }
.card-drag-handle:active { cursor: grabbing; }
.card.card-dragging { opacity: 0.4; }
.card.card-drag-over { border-color: var(--series-1); }

table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 8px; }
th { text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted);
     padding: 9px 12px; border-bottom: 1px solid var(--border-strong); white-space: nowrap; background: var(--surface); position: sticky; top: 0; }
td { padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; font-variant-numeric: tabular-nums; }
/* A bare YYYY-MM-DD has no spaces, but the hyphen itself is a valid
   soft-wrap point per the browser's default line-breaking rules, so a
   date cell can still visually break onto two lines ("2026-02-\n18")
   in a wide multi-column table (like the survey grids) once other
   columns crowd it -- this keeps a date on one row and lets
   .table-wrap's horizontal scroll take over instead. */
.cell-nowrap { white-space: nowrap; }
tr:last-child td { border-bottom: none; }
tr.clickable-row { cursor: pointer; }
tr.clickable-row:hover td { background: var(--surface); }

/* ---- Resizable table columns (makeTableResizable) -- a thin drag
   handle on each th's right edge. table-layout switches to fixed only
   once JS has frozen every column's current width into a <col>, so a
   table's initial look is unaffected until someone actually drags. */
.col-resize-handle {
  position: absolute; top: 0; right: -3px; width: 7px; height: 100%;
  cursor: col-resize; z-index: 3; touch-action: none;
}
.col-resize-handle:hover, .col-resize-handle.col-resizing { background: color-mix(in srgb, var(--series-1) 45%, transparent); }
table.col-resize-active { table-layout: fixed; width: auto; }
table.col-resize-active th, table.col-resize-active td { overflow: hidden; text-overflow: ellipsis; }
.col-resizing-active, .col-resizing-active * { cursor: col-resize !important; user-select: none !important; }

.pill { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; padding: 2px 8px; border-radius: 999px; font-weight: 600; white-space: nowrap; }
.pill-good { background: color-mix(in srgb, var(--status-good) 18%, transparent); color: var(--status-good); }
.pill-warn { background: color-mix(in srgb, var(--status-warning) 22%, transparent); color: #8a5a00; }
:root[data-theme="dark"] .pill-warn { color: var(--status-warning); }
@media (prefers-color-scheme: dark) { :root:where(:not([data-theme="light"])) .pill-warn { color: var(--status-warning); } }
.pill-crit { background: color-mix(in srgb, var(--status-critical) 20%, transparent); color: var(--status-critical); }
.pill-crit-solid { background: var(--status-critical); color: white; }
.pill-neutral { background: var(--grid); color: var(--text-secondary); }

.btn {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600;
  padding: 7px 14px; border-radius: 6px; border: 1px solid var(--border-strong);
  background: var(--surface-raised); color: var(--text-primary); cursor: pointer;
  transition: border-color 100ms ease, background-color 100ms ease, opacity 100ms ease, transform 80ms ease;
}
.btn:hover { border-color: var(--series-1); }
.btn:active { transform: translateY(1px); background: var(--surface); }
.btn-primary { background: var(--series-1); border-color: var(--series-1); color: white; }
.btn-primary:hover { opacity: 0.9; }
.btn-primary:active { opacity: 0.8; transform: translateY(1px); }
.btn-sm { padding: 4px 10px; font-size: 11.5px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn:disabled:active { transform: none; }

.form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; align-items: end; }
.field { display: flex; flex-direction: column; gap: 4px; }
.field label { font-size: 11px; color: var(--text-muted); font-weight: 600; }
.field input, .field select, .field textarea {
  font: inherit; font-size: 13px; padding: 7px 9px; border-radius: 6px;
  border: 1px solid var(--border-strong); background: var(--page); color: var(--text-primary);
}
.field textarea { resize: vertical; min-height: 50px; }

.empty-state { padding: 34px 20px; text-align: center; color: var(--text-muted); font-size: 13px; }
.toast {
  position: fixed; bottom: 20px; right: 20px; z-index: 50;
  background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: 8px;
  padding: 10px 16px; font-size: 12.5px; box-shadow: 0 8px 24px rgba(0,0,0,0.2);
  max-width: 360px;
}
.toast.toast-error { border-color: var(--status-critical); color: var(--status-critical); }
.toast.toast-good { border-color: var(--status-good); }
.toast.toast-warn { border-color: var(--status-warning); color: var(--status-warning); }

.tabs-row { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.tab-btn { padding: 8px 14px; font-size: 12.5px; font-weight: 600; color: var(--text-muted); background: none; border: none;
           border-bottom: 2px solid transparent; cursor: pointer; transition: color 100ms ease, border-color 100ms ease; }
.tab-btn:hover { color: var(--text-primary); }
.tab-btn:active { opacity: 0.7; }
.tab-btn.active { color: var(--text-primary); border-bottom-color: var(--series-1); }

/* ---- Master data grid toolbar + column picker ---- */
.grid-toolbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.column-picker { display: grid; grid-template-columns: 1fr auto 1fr; gap: 16px; align-items: center; }
.column-picker-list { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto;
                       border: 1px solid var(--border); border-radius: 8px; padding: 10px; }
.column-picker-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; padding: 3px 4px; border-radius: 4px; cursor: pointer; }
.column-picker-row:hover { background: var(--surface); }
.column-picker-arrows { display: flex; flex-direction: column; gap: 8px; }

/* ---- PCS-style inline-editable grid cells (opt in via opts.editableGrid) ---- */
td.cell-editable { padding: 3px 5px; }
td.cell-editable input[type=text], td.cell-editable input[type=number],
td.cell-editable input[type=date], td.cell-editable select {
  width: 100%; min-width: 68px; box-sizing: border-box; font: inherit; font-size: 12.5px; padding: 5px 7px; border-radius: 5px;
  border: 1px solid color-mix(in srgb, var(--series-1) 22%, transparent);
  background: color-mix(in srgb, var(--series-1) 7%, transparent); color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
td.cell-editable input[type=date] { min-width: 128px; }
td.cell-editable input[type=text]:hover, td.cell-editable input[type=number]:hover,
td.cell-editable input[type=date]:hover, td.cell-editable select:hover {
  border-color: color-mix(in srgb, var(--series-1) 45%, transparent);
  background: color-mix(in srgb, var(--series-1) 12%, transparent);
}
td.cell-editable input[type=text]:focus, td.cell-editable input[type=number]:focus,
td.cell-editable input[type=date]:focus, td.cell-editable select:focus {
  border-color: var(--series-1); background: var(--surface); outline: none;
}
td.cell-editable input[type=checkbox] { width: 15px; height: 15px; cursor: pointer; vertical-align: middle; }
tr.row-dirty > td { background: color-mix(in srgb, var(--status-warning) 10%, transparent); }
td.cell-save-cell { width: 1%; white-space: nowrap; }
.cell-save-indicator { font-size: 10.5px; color: var(--text-muted); }
.cell-save-indicator.saving { color: var(--text-muted); }
.cell-save-indicator.saved { color: var(--status-good); }
.cell-save-indicator.error { color: var(--status-critical); cursor: help; font-weight: 600; }
/* A cell that's genuinely not applicable (e.g. Casing to Soil on a
   station with no linked casing) -- distinct from a real reading that's
   just missing this visit, which stays a normal fillable box. */
td.cell-na { background: color-mix(in srgb, var(--text-muted) 6%, transparent); }

/* Documents & photos table: filename input + its own open-in-new-tab
   arrow share one cell, since the filename itself is now editable
   rather than doubling as the link. */
td.doc-filename-cell { display: flex; align-items: center; gap: 6px; }
td.doc-filename-cell input { flex: 1; }
.doc-open-link { color: var(--text-muted); text-decoration: none; font-size: 13px; flex: none; }
.doc-open-link:hover { color: var(--series-1); }

.badge-count { display: inline-flex; align-items: center; justify-content: center; min-width: 17px; height: 17px;
               padding: 0 4px; border-radius: 999px; background: var(--status-warning); color: #1a1a1a;
               font-size: 10px; font-weight: 700; margin-left: auto; }

.disclaimer { font-size: 11px; color: var(--text-muted); }

/* ---- Trend charts (station detail) ---- */
.trend-label { font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px; }
.trend-legend-note { font-weight: 400; color: var(--text-muted); font-size: 11.5px; }
.flag-swatch { display: inline-block; width: 9px; height: 9px; border-radius: 50%; border: 2px solid var(--status-warning); vertical-align: middle; }
.chart-wrap { position: relative; }
.trend-svg { display: block; overflow: visible; cursor: crosshair; }
.trend-tooltip {
  position: absolute; opacity: 0; pointer-events: none; transform: translate(-50%, -130%);
  background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: 6px;
  padding: 6px 9px; font-size: 11.5px; white-space: nowrap; box-shadow: 0 4px 14px rgba(0,0,0,0.18);
  transition: opacity 100ms ease;
}

/* ---- Map view (Google Maps) ---- */
.map-container { height: 62vh; min-height: 420px; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
.map-legend { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; font-size: 12px; color: var(--text-secondary); margin-top: 12px; }
.map-legend .legend-item { display: inline-flex; align-items: center; gap: 6px; }
.map-legend .dot { width: 11px; height: 11px; border-radius: 50%; flex: none; box-shadow: 0 0 0 1px rgba(0,0,0,0.15); }

/* ---- 7-day forecast card (below Digital Twin's map) ---- */
.weather-strip { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
.weather-tile {
  flex: 0 0 64px; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 4px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
  text-align: center; cursor: default;
}
.weather-tile-name { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.weather-tile-temp { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.weather-tile-precip { font-size: 10px; color: var(--series-1); font-weight: 600; }
/* Google's InfoWindow renders in its own shadow-free DOM subtree --
   these classes are applied directly to the content string we hand it,
   same content markup as before. Its chrome (the box itself, not our
   content) IS reachable from here via Google's own gm-style-iw-*
   classes -- overridden below with a liquid-glass treatment (frosted,
   translucent, floating) instead of Google's flat opaque white
   default. content stays dark-on-light (its own hardcoded #111 text)
   regardless of app theme, since it's outside our normal CSS-variable
   theming. */
.map-popup-row { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; }
.map-popup-row + .map-popup-row { margin-top: 2px; }

.gm-style-iw-c {
  background: rgba(255, 255, 255, 0.6) !important;
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border-radius: 18px !important;
  border: 1px solid rgba(255, 255, 255, 0.55);
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.6) !important;
}
.gm-style-iw-d { overflow: visible !important; }
.gm-style-iw-tc::after { background: rgba(255, 255, 255, 0.6) !important; }
.gm-ui-hover-effect { border-radius: 999px; }

/* ---- Digital Twin: ops-center home page (class names below kept as
   "command-*" -- internal implementation detail, not user-facing).
   Overlay "glass" panels
   docked on the map itself rather than a card below it -- a wall you
   look at, not a page you scroll. Reuses .map-container/.map-legend
   from Map view (same map, same legend primitive) plus the token-based
   palette everywhere else in this file -- no new colors invented. ---- */
.command-pipeline-picker { display: flex; align-items: end; gap: 14px; margin-bottom: 18px; }
.command-pipeline-picker .field { min-width: 240px; }
.command-pipeline-picker a { font-size: 12px; color: var(--series-1); padding-bottom: 8px; }
.command-pipeline-picker a:hover { text-decoration: underline; }
.switch { position: relative; display: inline-block; width: 40px; height: 22px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch-track { position: absolute; inset: 0; background: var(--grid); border-radius: 22px; cursor: pointer; transition: background 0.15s; }
.switch-track::before { content: ""; position: absolute; width: 16px; height: 16px; left: 3px; top: 3px; background: var(--surface-raised); border-radius: 50%; transition: transform 0.15s; box-shadow: 0 1px 2px rgba(0,0,0,0.3); }
.switch input:checked + .switch-track { background: var(--status-good); }
.switch input:checked + .switch-track::before { transform: translateX(18px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--series-1); outline-offset: 2px; }
.switch-sm { width: 32px; height: 18px; }
.switch-sm .switch-track::before { width: 12px; height: 12px; left: 3px; top: 3px; }
.switch-sm input:checked + .switch-track::before { transform: translateX(14px); }
.module-toggles-card { margin-bottom: 18px; }
.module-toggles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px 20px; }
.module-toggle-item { display: flex; align-items: center; gap: 10px; font-size: 13px; cursor: pointer; }
.command-map-wrap { position: relative; margin-bottom: 12px; }
.command-map { height: 68vh; min-height: 460px; }
/* Black liquid glass -- deliberately theme-independent (always dark),
   since these two panels float over a live map whose own colors swing
   wildly (satellite green/brown, light roadmap, colored markers) --
   a fixed dark glass slab stays legible over all of them rather than
   flipping with the app's light/dark theme like an ordinary card. Three
   layers do the "liquid" read: a top sheen gradient for the glossy
   catch-light, heavy blur+saturate for the frosted refraction, and an
   inset rim-light border simulating light bending through a glass edge. */
.hud-panel {
  /* top: 64px, not 16px -- Google's own map-type toggle (top-left) and
     fullscreen control (top-right) occupy roughly y:11-51 in the map's
     own coordinate space (measured directly against the live control
     elements), so docking here instead of flush with the map edge
     keeps both panels clear of native controls rather than covering
     them. */
  position: absolute; top: 64px; width: 260px; max-height: calc(100% - 80px); overflow-y: auto;
  background:
    linear-gradient(165deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.02) 22%, rgba(255,255,255,0) 45%),
    linear-gradient(rgba(8,10,14,0.62), rgba(8,10,14,0.62));
  backdrop-filter: blur(22px) saturate(150%); -webkit-backdrop-filter: blur(22px) saturate(150%);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 16px; padding: 14px 16px;
  box-shadow:
    0 16px 40px rgba(0,0,0,0.45),
    0 2px 8px rgba(0,0,0,0.3),
    inset 0 1px 0 rgba(255,255,255,0.16),
    inset 0 0 0 1px rgba(0,0,0,0.2);
}
.hud-panel-tl { left: 16px; }
.hud-panel-tr { right: 16px; }
.hud-panel-title { display: flex; align-items: center; gap: 6px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(255,255,255,0.6); margin-bottom: 10px; }
.hud-panel-count {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Consolas, monospace; font-variant-numeric: tabular-nums;
  background: rgba(255,255,255,0.12); color: rgba(255,255,255,0.8); border-radius: 8px;
  padding: 1px 6px; font-size: 10px; font-weight: 700;
}
.hud-panel-collapse-btn {
  margin-left: auto; flex-shrink: 0; width: 18px; height: 18px; padding: 0; line-height: 1;
  border: none; background: transparent; color: rgba(255,255,255,0.5);
  font-size: 10px; cursor: pointer; border-radius: 4px;
}
.hud-panel-collapse-btn:hover { color: #fff; background: rgba(255,255,255,0.14); }
.hud-panel-collapsed { width: auto; }
.hud-panel-collapsed .hud-panel-title { margin-bottom: 0; }
.hud-panel-collapsed .hud-panel-body { display: none; }
.hud-rows { display: flex; flex-direction: column; gap: 7px; }
.hud-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 12px; }
.hud-row .hud-row-label { color: rgba(255,255,255,0.82); }
.hud-row .hud-row-value { font-family: ui-monospace, "SF Mono", "JetBrains Mono", Consolas, monospace; font-variant-numeric: tabular-nums; font-weight: 600; color: #fff; }
.hud-row-note { color: rgba(255,255,255,0.5); font-size: 10px; font-weight: 400; }
.hud-row-check { display: flex; align-items: center; gap: 7px; cursor: pointer; min-width: 0; }
.hud-row-check input { flex-shrink: 0; accent-color: var(--series-1); cursor: pointer; }
.hud-row-hidden .hud-row-label, .hud-row-hidden .hud-row-value { opacity: 0.4; }
.hud-row-dismissable .hud-row-label { flex: 1; min-width: 0; }
.alert-dismiss-btn {
  flex-shrink: 0; width: 16px; height: 16px; padding: 0; line-height: 1;
  border: none; background: transparent; color: rgba(255,255,255,0.45);
  font-size: 15px; cursor: pointer; border-radius: 3px;
}
.alert-dismiss-btn:hover { color: #fff; background: rgba(255,255,255,0.14); }
.hud-panel-empty { color: rgba(255,255,255,0.5); font-size: 12px; }
.hud-panel #cmdRebalanceBtn { background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.18); color: #fff; }
.hud-panel #cmdRebalanceBtn:hover { background: rgba(255,255,255,0.16); }
.hud-panel-alert {
  border-color: color-mix(in srgb, var(--status-critical) 55%, rgba(255,255,255,0.14));
  box-shadow:
    0 16px 40px rgba(0,0,0,0.45),
    0 2px 8px rgba(0,0,0,0.3),
    inset 0 1px 0 rgba(255,255,255,0.16),
    0 0 0 1px color-mix(in srgb, var(--status-critical) 30%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .hud-panel-alert { animation: hud-alert-pulse 2.4s ease-in-out infinite; }
}
@keyframes hud-alert-pulse {
  0%, 100% { box-shadow: 0 16px 40px rgba(0,0,0,0.45), 0 2px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.16), 0 0 0 1px color-mix(in srgb, var(--status-critical) 25%, transparent); }
  50%      { box-shadow: 0 16px 40px rgba(0,0,0,0.45), 0 2px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.16), 0 0 0 2px color-mix(in srgb, var(--status-critical) 60%, transparent); }
}
.map-legend .dot.diamond { border-radius: 2px; transform: rotate(45deg); }

@media (max-width: 860px) {
  .hud-panel { position: static; width: auto; max-height: none; margin-bottom: 10px; }
  .command-map { height: 46vh; min-height: 320px; }
}
