:root {
  /* ----------------------------------------------------------------
     THEME — dark is the default (these :root values). The inline
     script in index.html always sets data-theme on <html>, and
     html[data-theme="light"] below overrides every theme token for
     the light theme. The whole app re-themes from this one place.

     The surface tokens (--field-bg, --chip-bg, --surface-2,
     --panel-deep) exist so the hard-coded dark backgrounds scattered
     through this file could be swept onto variables — their DARK
     values are exactly the hexes they replaced, so dark mode is
     visually unchanged; only light mode gains new values.
     ---------------------------------------------------------------- */
  --bg: #060a1b;
  --panel: #0d142f;
  --panel-soft: #111c3c;
  --panel-deep: #0b1330;
  --line: #1a2852;
  --text: #e8f0ff;
  --muted: #8fa4c8;
  --green: #1dd4a1;
  --red: #ff6b7b;
  --field-bg: #0e1736;
  --chip-bg: #0f1a3b;
  --surface-2: #0a1029;

  /* Breeze Airways brand palette.
     Source: Breeze brand guidelines.
     Used directly across the accent system below. */
  --breeze-warm:  #001633;   /* Warm Breeze Blue  — deep navy        */
  --breeze-blue:  #1F74DF;   /* Breeze Blue       — primary brand    */
  --breeze-cool:  #C1DDFF;   /* Cool Breeze Blue  — light accent     */
  --breeze-white: #FFFFFF;   /* Cloud White                          */
  --breeze-pink:  #FF527B;   /* Sunset Pink       — vibrant accent   */
  --breeze-tan:   #FFD4AA;   /* Sunrise Tan       — warm accent      */
  --breeze-cream: #FFF5ED;   /* Light Tan         — cream            */

  /* Map the brand palette into the existing accent slots so all the
     downstream rules keep working. --blue stays the primary brand
     color, the four accents follow Breeze brand guidance, and each
     major view is assigned a distinct one via --view-accent. */
  --blue:          var(--breeze-blue);
  --accent-pink:   var(--breeze-pink);
  --accent-tan:    var(--breeze-tan);
  --accent-sky:    var(--breeze-cool);
  /* Aliases retained so older rules referencing the prior names
     still resolve. New code should use the brand-aligned names above. */
  --accent-purple: var(--breeze-pink);
  --accent-amber:  var(--breeze-tan);
  --accent-teal:   var(--breeze-cool);

  --view-accent: var(--blue);
  --view-accent-soft: rgba(31, 116, 223, 0.22);

  /* ============================================================
     DEPTH TIER TOKENS (Phase 1 visual lift).
     Three elevation levels — `flat`, `raised`, `floating` — driving
     shadows, top-edge highlight, and the gradient overlay on top of
     `--panel`. Designed so swapping a card's tier is a class change,
     not a per-property rewrite. Light theme overrides further down.

     Cards (.card) pick up the `raised` tier by default. Add
     `.kpi-floating` on a card to bump to the `floating` tier (used
     for hero KPIs, modals, anything that needs to read above its
     neighbors). Phase 3+ will add `.kpi-flat` for tight grid cells
     that shouldn't be elevated.
     ============================================================ */
  --depth-radius:               14px;
  --depth-border:               var(--line);
  --depth-border-accent:        var(--view-accent-soft);

  /* Raised tier — default for .card */
  --depth-raised-shadow:        0 4px 14px rgba(0, 0, 0, 0.20);
  --depth-raised-edge:          rgba(255, 255, 255, 0.07);
  --depth-raised-overlay:
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.025) 0%,
      rgba(255, 255, 255, 0.00) 35%,
      rgba(0, 0, 0, 0.04) 100%);

  /* Floating tier — KPIs, modals, anything that should pop above */
  --depth-floating-shadow:      0 10px 32px rgba(0, 0, 0, 0.32),
                                0 1px 0 rgba(255, 255, 255, 0.04) inset;
  --depth-floating-edge:        rgba(255, 255, 255, 0.10);
  --depth-floating-overlay:
    radial-gradient(120% 100% at 20% 0%,
      rgba(255, 255, 255, 0.05) 0%,
      rgba(255, 255, 255, 0.00) 55%),
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.02) 0%,
      rgba(0, 0, 0, 0.10) 100%);

  /* Hover-lift — applied to every interactive surface. Pulls the
     element 1 px toward the viewer + adds a soft accent glow. */
  --hover-lift-y:               -1px;
  --hover-glow:                 0 0 0 1px var(--view-accent-soft),
                                0 6px 18px rgba(0, 0, 0, 0.28);
  --hover-glow-accent:          0 0 0 1px var(--view-accent),
                                0 8px 24px var(--view-accent-soft);
}

/* LIGHT theme removed. The inline <head> script in index.html now
   hardcodes data-theme="dark" pre-paint, so any rule scoped to
   html[data-theme="light"] still scattered through this file is
   dead — it'll never match. The main :root override (formerly
   here) and all per-component light overrides below are orphaned
   but harmless; deleting them is left as a future cleanup. */

/* Per-view accent. Cards + titles inside each view inherit
   --view-accent automatically. Override here only — don't
   sprinkle hardcoded colors into card rules. */
#universalView {
  --view-accent: var(--breeze-blue);
  --view-accent-soft: rgba(31, 116, 223, 0.22);
}
#supervisorView {
  --view-accent: var(--breeze-pink);
  --view-accent-soft: rgba(255, 82, 123, 0.22);
}
#deskAssignmentView {
  --view-accent: var(--breeze-tan);
  --view-accent-soft: rgba(255, 212, 170, 0.22);
}
#schedulerView {
  --view-accent: var(--breeze-cool);
  --view-accent-soft: rgba(193, 221, 255, 0.22);
}

* {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

body {
  margin: 0;
  font-family: Inter, system-ui, sans-serif;
  color: var(--text);
  background: var(--bg);
}
/* Dark theme — ambient overhead lighting + Breeze Dispatch brand
   background. Three radial gradients sit on top of the brand SVG so
   the logo + radar UI read as a faint, atmospheric layer without
   competing with the content. The SVG itself has
   preserveAspectRatio="xMidYMid slice" baked in; background-size:
   cover lets the browser handle the same crop-to-fill behavior, so
   the image is never stretched or distorted regardless of viewport
   aspect ratio — on ultrawide screens the top/bottom crops in, on
   narrow screens the sides crop, but the logo at center always stays
   centered.

   Light theme intentionally skips the brand background since the
   colors (deep navy with cyan logo) would clash with a white work
   surface; light theme stays clean. */
html[data-theme="dark"] body,
html:not([data-theme]) body {
  background-color: var(--bg);
  background-image:
    /* Top layer: subtle accent radial that picks up the view's color */
    radial-gradient(900px 600px at 50% -10%, var(--view-accent-soft) 0%, transparent 65%),
    /* Original corner glows — dim the brand layer enough that text reads */
    radial-gradient(1200px 800px at 0% 0%, rgba(26, 35, 72, 0.85) 0%, transparent 55%),
    radial-gradient(1000px 800px at 100% 100%, rgba(18, 31, 76, 0.85) 0%, transparent 60%),
    /* Dark vignette so the brand image fades to bg at the edges */
    radial-gradient(ellipse 110% 90% at 50% 50%, transparent 30%, rgba(6, 10, 27, 0.55) 80%, var(--bg) 100%),
    /* The brand background — sits at the bottom of the stack */
    url("../assets/breeze-dispatch-logo.svg");
  background-size:
    auto, auto, auto, auto,
    /* `contain` scales the SVG so the entire logo is always visible
       on every aspect ratio — no edge cropping on narrower laptop
       viewports, no logo focal point pushed off-screen. The cost is
       a small letterbox margin on aspect-mismatched viewports, but
       the SVG fades to a dark navy at its edges and the page
       background-color is the same navy, so the boundary is
       invisible to the eye. Previously `cover` was used, which kept
       the SVG filling the viewport at the cost of cropping. */
    contain;
  background-position:
    center, center, center, center,
    /* Center the SVG horizontally and vertically. `contain` makes
       the focal-bias trick from the old `cover` setup unnecessary —
       the whole logo is always shown. */
    center center;
  background-repeat:
    no-repeat, no-repeat, no-repeat, no-repeat,
    no-repeat;
  background-attachment: fixed;
}

.app-shell {
  min-height: 100vh;
  /* Single column — main fills the full viewport. Sidebar floats over
     the top as a drawer when toggled open. */
  display: block;
  position: relative;
}

/* ---- Beta-site banner --------------------------------------------
   Sits at the very top of the app shell, above the topbar. Sticky
   so it stays anchored as the user scrolls long pages. Single
   row, full width, gradient background with the brand pink/tan
   accents so it reads as a "this isn't production yet" cue
   without screaming at the user. To take the banner down: delete
   the .beta-banner element from index.html (no other code touches
   it). To change the message: edit the .beta-banner-text contents. */
/* BETA banner — only shown when bootstrap.js detects a beta hostname
   (hostname contains "-beta" or starts with "beta.") and adds the
   .is-beta class to <html>. Production hostnames (www.aero-it.com,
   aerodesk.aero-it.com, etc.) don't match, so the banner stays hidden.
   The element is always in the DOM so the layout is identical between
   prod and beta below the topbar — only the banner itself shows or
   hides. */
.beta-banner {
  display: none;
}
html.is-beta .beta-banner {
  display: flex;
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 6px 16px;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: #fff5ed;
  background: linear-gradient(
    90deg,
    rgba(255, 82, 123, 0.92) 0%,
    rgba(255, 152, 110, 0.92) 50%,
    rgba(255, 212, 170, 0.92) 100%
  );
  border-bottom: 1px solid rgba(0, 0, 0, 0.25);
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* When the BETA banner is in the DOM (beta deploy), the banner takes
   ~32 px at the top of the layout. Without this override the sticky
   sidebar (top: 0, height: 100dvh) extends past the viewport bottom
   and clips its own bottom-most actions (the "Saved" status, the
   draft-controls "Apply immediately" tag, etc.). With :has() we
   detect the banner and shrink the sidebar by the banner's height.
   Production (no banner) keeps the original 100dvh height. */
/* When the BETA banner is VISIBLE (.is-beta on html), the banner
   takes ~36 px at the top of the layout and would otherwise push the
   sticky sidebar's bottom past the viewport edge. Shrink the sidebar
   by the banner's height when beta is active. Production hostnames
   (no .is-beta class) keep the full 100dvh height. */
html.is-beta .app-sidebar {
  height: calc(100dvh - 36px);
}

.beta-banner-pill {
  display: inline-block;
  padding: 1px 9px;
  border-radius: 999px;
  background: rgba(0, 22, 51, 0.85);
  color: #ffd4aa;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-shadow: none;
}

.beta-banner-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* On narrow viewports the description text would wrap awkwardly —
   collapse to just the pill so the banner stays a single tidy row. */
@media (max-width: 640px) {
  .beta-banner-text {
    font-size: 0.7rem;
  }
}

.sidebar {
  background: linear-gradient(180deg, #081126 0%, #0a1432 100%);
  border-right: 1px solid var(--line);
  padding: 20px 14px;
  /* Drawer behavior: slide in from the left on top of main content.
     Default state is hidden (translated off-screen) and click-through
     disabled; `.is-open` brings it on-screen. */
  position: fixed;
  top: 0;
  left: 0;
  width: 280px;
  max-width: 86vw;
  height: 100vh;
  overflow-y: auto;
  z-index: 1100;
  transform: translateX(-100%);
  transition: transform 0.22s ease;
  box-shadow: 8px 0 24px rgba(0, 0, 0, 0.4);
}
.sidebar.is-open {
  transform: translateX(0);
}

.sidebar-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(4, 9, 24, 0.55);
  z-index: 1090;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease;
}
.sidebar-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* Hamburger button — sits at the left edge of the topbar. Three
   stacked bars; on hover the middle bar slides slightly to give a
   tiny affordance that it's interactive. */
.hamburger-btn {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  color: var(--breeze-white);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.hamburger-btn:hover,
.hamburger-btn:focus-visible {
  background: var(--panel-soft);
  border-color: var(--breeze-blue);
  outline: none;
}
.hamburger-bars {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  width: 18px;
}
.hamburger-bars span {
  display: block;
  height: 2px;
  background: #cfe0ff;
  border-radius: 1px;
  transition: transform 0.18s ease, opacity 0.18s ease;
}
/* When the drawer is open, morph the icon into an X. */
.hamburger-btn[aria-expanded="true"] .hamburger-bars span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}
.hamburger-btn[aria-expanded="true"] .hamburger-bars span:nth-child(2) {
  opacity: 0;
}
.hamburger-btn[aria-expanded="true"] .hamburger-bars span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 24px;
}

.brand-mark {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--breeze-pink), var(--breeze-blue));
  display: grid;
  place-items: center;
  font-weight: 700;
  color: var(--breeze-white);
}

.brand h1 {
  margin: 0;
  font-size: 1.05rem;
}

.brand p {
  margin: 2px 0 0;
  color: var(--muted);
  font-size: 0.78rem;
}

.nav-title {
  margin: 16px 8px 10px;
  color: #6f83a9;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.nav-link {
  width: 100%;
  text-align: left;
  border: 0;
  margin: 4px 0;
  padding: 10px 12px;
  border-radius: 10px;
  color: #b8c7e7;
  background: transparent;
  cursor: pointer;
}

.nav-link:hover,
.nav-link.active {
  /* Breeze Blue → Sunset Pink gradient — anchors the sidebar
     selected state in the brand palette. */
  background: linear-gradient(90deg, var(--breeze-blue) 0%, #5099ff 50%, var(--breeze-pink) 100%);
  color: var(--breeze-white);
  box-shadow: 0 0 0 1px rgba(255, 82, 123, 0.25);
}

.support {
  margin-top: 16px;
}

.support-link {
  display: block;
  color: #a6bae0;
  text-decoration: none;
  margin: 8px;
  font-size: 0.9rem;
}

.main {
  padding: 16px;
}

.topbar {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

/* Sticky topbar — the brand, inline page nav, clocks, draft chip,
   and sign-out all stay pinned to the top of the viewport when the
   page scrolls. z-index sits above .top-nav (now retired) and below
   modal dialogs / the context menu. */
.topbar-sticky {
  position: sticky;
  top: 0;
  z-index: 900;
  /* Slightly heavier shadow + a stronger background than the inline
     variant gave us, so the bar reads as foreground when content
     scrolls underneath. */
  background: rgba(10, 17, 41, 0.96);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

/* Page nav inline with the topbar. Sits between brand and the
   chip cluster, takes whatever middle space is free, and uses a
   pipe `|` between each button via ::after for a clean
   document-style menu. */
.topbar-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 4px;
}
.topbar-nav .nav-link {
  position: relative;
  flex: 0 0 auto;
  width: auto;
  margin: 0;
  padding: 4px 12px 4px 12px;
  font-size: 0.86rem;
  white-space: nowrap;
  background: transparent;
  border: 0;
  border-radius: 4px;
}
.topbar-nav .nav-link:hover {
  background: rgba(255, 255, 255, 0.06);
}
.topbar-nav .nav-link.active {
  color: var(--breeze-white, #fff);
  font-weight: 600;
}
/* Pipe separator after every nav link except the last. The pipe
   is positioned outside the button's clickable area (right: -1px,
   pushed slightly past the padding) so the separator visually sits
   between buttons rather than inside the trailing one. Hidden buttons
   (the unmatched-Super-User row) skip the separator too via
   :not([hidden]). */
.topbar-nav .nav-link:not([hidden]) ~ .nav-link:not([hidden])::before {
  content: "|";
  position: absolute;
  left: -2px;
  top: 50%;
  transform: translateY(-50%);
  color: rgba(255, 255, 255, 0.22);
  font-weight: 400;
  pointer-events: none;
}
@media (max-width: 760px) {
  /* On narrow viewports, the chip cluster wraps below the nav,
     which can leave the nav crammed against the brand. Give it a
     touch more breathing room. */
  .topbar-nav { padding: 4px 0; }
}

/* Brand block when used inside the topbar (replaced the hamburger
   drawer trigger). Smaller margin than the original sidebar variant
   since it shares a single row with the chip cluster. */
.brand-topbar {
  margin-bottom: 0;
  flex: 0 0 auto;
}
.brand-topbar h1 {
  font-size: 0.96rem;
}
.brand-topbar p {
  font-size: 0.72rem;
}

/* Sticky horizontal page nav. Sits directly under the topbar and
   stays pinned at the top of the viewport when the page scrolls so
   the user can jump pages without scrolling back up. Overflows
   horizontally with native momentum scroll on narrow viewports
   (rare — but the chip-style buttons + visible scrollbar make it
   discoverable when it happens). */
/* Standalone .top-nav row was retired in favor of the inline
   .topbar-nav inside .topbar. Display:none keeps the rule defensive
   in case any cached HTML still references the old element. */
.top-nav { display: none; }

/* Search input — removed from the topbar but kept here as a
   defensive zero-out in case any cached HTML still has the markup.
   The JS wireSearch() guard skips silently if el.searchInput is null. */
.search-wrap { display: none; }

/* Slide-out sidebar drawer + hamburger trigger — both retired in
   favor of the sticky .top-nav above. Hidden via display:none rather
   than removed from the DOM so any code path that still queries them
   no-ops cleanly. */
.sidebar,
.sidebar-backdrop,
.hamburger-btn {
  display: none !important;
}

input,
select,
textarea,
.btn {
  font: inherit;
}

.search-wrap input,
.controls-grid input,
.controls-grid select,
.request-form input,
.request-form select,
textarea {
  width: 100%;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  padding: 10px 12px;
}

.chip {
  border: 1px solid var(--line);
  background: var(--chip-bg);
  border-radius: 999px;
  padding: 8px 12px;
  font-size: 0.85rem;
  color: var(--text);
}

.chip-muted {
  font-size: 0.72rem;
  color: var(--muted);
  max-width: 200px;
  white-space: normal;
  text-align: center;
  line-height: 1.25;
}

/* Server-save status pill in the topbar.
   Reflects the lifecycle of persistAppState() → flushPendingSave():
     dirty   — local change committed, waiting for debounced server save
     saving  — POST in flight
     saved   — round-trip OK
     error   — POST failed; auto-retries every 5s
   Color is muted by default; only error/saving call attention. */
.save-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--chip-bg);
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}

.save-status::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.6;
}

.save-status--saved {
  color: #9be0a8;
  border-color: rgba(155, 224, 168, 0.35);
}

.save-status--saving {
  color: var(--breeze-cool);
  border-color: rgba(193, 221, 255, 0.4);
}

.save-status--saving::before {
  opacity: 1;
  animation: aerodesk-pulse 1s ease-in-out infinite;
}

.save-status--dirty {
  color: var(--breeze-tan);
  border-color: rgba(255, 212, 170, 0.35);
}

.save-status--error {
  color: var(--breeze-pink);
  border-color: rgba(255, 82, 123, 0.45);
}

@keyframes aerodesk-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

.time-module-sub {
  margin-top: 6px;
  line-height: 1.35;
}

.slot-zulu-line {
  font-size: 0.68rem;
  margin-bottom: 4px;
}

.month-cell-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 6px;
  margin-bottom: 4px;
}

.month-z-tag {
  font-size: 0.62rem;
  color: var(--muted);
  font-weight: 500;
}

.table-desk-assign th,
.table-desk-assign td {
  font-size: 0.78rem;
}

.select-chip {
  min-width: 190px;
}

.topbar-actions {
  display: flex;
  gap: 8px;
}

.view {
  display: none;
  margin-top: 16px;
}

.view.active {
  display: block;
}

/* Must beat .view.active (display:block). Flex-wrap: each widget has its own width/height, not shared grid tracks. */
.view.active.custom-layout-enabled {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: stretch;
  align-content: flex-start;
  width: 100%;
}

/* Phase 3 — Grid layout mode. Activated by the
   DASHBOARD_LAYOUT_MODE = "grid" flag in app.js, which adds the
   `.layout-grid` class to each customizable view. Inline styles
   from setupGridLayoutForView (display: grid, columns, row height,
   gap) win for current size; this rule is the visual contract. */
.view.active.custom-layout-enabled.layout-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: 80px;
  gap: 12px;
  width: 100%;
  align-content: start;
  align-items: stretch;
}
/* Title row pins to row 1, full-width, so it always sits at the
   top of the dashboard. Without `grid-row: 1` the auto-placement
   algorithm flows it into whichever cell it finds first — which can
   be the middle or bottom of the layout if other widgets have
   explicit grid-row positions. The widget placement below ensures
   no widget ever lands on row 1. */
.layout-grid > .title-row {
  grid-column: 1 / -1;
  grid-row: 1;
}
/* Each widget sits in a grid cell range set via inline styles. The
   default width/height/min-* values from .custom-widget would conflict,
   so reset them: grid manages the box. */
.layout-grid > .custom-widget {
  width: auto !important;
  height: auto !important;
  min-width: 0 !important;
  min-height: 0 !important;
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: visible;
  container-type: inline-size;
}
/* Drop ghost — visible outline of where the widget will land when
   the user releases the drag. Pointer-events:none so it doesn't
   intercept the drop. Default (blue) = manual placement;
   --autofit (green) = the widget will expand to fill this empty
   rectangle on release. */
.grid-drop-ghost {
  background: rgba(31, 116, 223, 0.18);
  border: 2px dashed var(--blue);
  border-radius: 12px;
  pointer-events: none;
  z-index: 50;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.grid-drop-ghost.grid-drop-ghost--autofit {
  background: rgba(60, 179, 113, 0.22);
  border-color: #6ee0a3;
}
.grid-dragging {
  opacity: 0.6;
  cursor: grabbing;
}
.grid-resizing {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
}
/* 8-direction resize handles. Edges are slim hit targets running
   the length of the side; corners are 14×14 squares pinned to the
   widget corners. Visible only on hover so they don't compete with
   the widget's own UI in a normal viewing state. */
.layout-grid > .custom-widget {
  position: relative;
}
.widget-grid-resize {
  position: absolute;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  z-index: 12;
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease;
}
.layout-grid > .custom-widget:hover .widget-grid-resize {
  opacity: 1;
}
.widget-grid-resize:hover,
.grid-resizing .widget-grid-resize {
  background: rgba(80, 153, 255, 0.18);
}
/* Edges — 6 px thick hit area along each side */
.widget-grid-resize--edge-n {
  top: -3px; left: 14px; right: 14px; height: 6px;
}
.widget-grid-resize--edge-s {
  bottom: -3px; left: 14px; right: 14px; height: 6px;
}
.widget-grid-resize--edge-e {
  top: 14px; bottom: 14px; right: -3px; width: 6px;
}
.widget-grid-resize--edge-w {
  top: 14px; bottom: 14px; left: -3px; width: 6px;
}
/* Corners — 14×14 squares with a subtle tint so they're visible */
.widget-grid-resize--corner-ne { top: -3px;    right: -3px; width: 14px; height: 14px; border-radius: 0 6px 0 0; }
.widget-grid-resize--corner-nw { top: -3px;    left: -3px;  width: 14px; height: 14px; border-radius: 6px 0 0 0; }
.widget-grid-resize--corner-se { bottom: -3px; right: -3px; width: 14px; height: 14px; border-radius: 0 0 6px 0; }
.widget-grid-resize--corner-sw { bottom: -3px; left: -3px;  width: 14px; height: 14px; border-radius: 0 0 0 6px; }
/* Touch devices: edges slightly thicker so they're easier to grab */
@media (pointer: coarse) {
  .widget-grid-resize--edge-n,
  .widget-grid-resize--edge-s { height: 12px; top: -6px; }
  .widget-grid-resize--edge-s { top: auto; bottom: -6px; }
  .widget-grid-resize--edge-e,
  .widget-grid-resize--edge-w { width: 12px; left: -6px; }
  .widget-grid-resize--edge-e { left: auto; right: -6px; }
  /* Show handles permanently on touch — no hover */
  .widget-grid-resize { opacity: 0.6; }
}
/* Mobile breakpoint — collapse the 12-column grid to a single column
   so widgets stack vertically and stay usable on phones until the
   native apps land. Inline grid-column / grid-row from JS gets
   overridden by `!important` here. */
@media (max-width: 760px) {
  .view.active.custom-layout-enabled.layout-grid {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
  }
  .layout-grid > .custom-widget {
    grid-column: 1 / -1 !important;
    grid-row: auto !important;
    min-height: 240px !important;
  }
}

.custom-layout-enabled > .title-row {
  flex: 1 1 100%;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
}

.layout-reset-btn {
  margin-left: auto;
  border: 1px dashed #4362a5;
  background: var(--chip-bg);
  color: #9db6e5;
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 0.72rem;
  line-height: 1.3;
  cursor: pointer;
}

.layout-reset-btn:hover {
  border-color: #7aa2ff;
  color: #d6e3ff;
}

.layout-reset-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  border-style: dotted;
}

/* New layout-controls cluster in each view's title row.
   Holds the auto-flow status label, "Save layout for this screen
   size" button, and "Reset to auto" button. Pushes itself to the
   far right with margin-left:auto and aligns its children inline. */
.layout-controls {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.layout-controls .layout-status {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 2px 10px;
  border-radius: 999px;
  background: rgba(143, 182, 255, 0.08);
  color: #8fb6ff;
}

/* Inside the layout-controls wrapper, the buttons reset their
   margin-left:auto so they sit next to the status label rather than
   each pushing themselves to the far right. */
.layout-controls .layout-save-btn,
.layout-controls .layout-reset-btn {
  margin-left: 0;
}

.layout-save-btn {
  border: 1px solid #4362a5;
  background: var(--chip-bg);
  color: #9db6e5;
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 0.72rem;
  line-height: 1.3;
  cursor: pointer;
}

.layout-save-btn:hover {
  border-color: #7aa2ff;
  color: #d6e3ff;
  background: var(--panel-soft);
}

/* "Live" status pill — sits in dashboard title rows next to the
   page heading. Uses a small colored dot + label that updates
   based on the live-poll status. The dot pulses softly when live
   to draw the eye to "yes, this page is auto-updating". */
.live-indicator {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
  margin-left: 8px;
}
.live-indicator-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.live-indicator--live {
  background: rgba(139, 192, 107, 0.12);
  color: #b9d18a;
}
.live-indicator--live .live-indicator-dot {
  animation: livePulse 2s ease-in-out infinite;
}
.live-indicator--connecting {
  background: rgba(214, 227, 255, 0.06);
  color: var(--muted);
}
.live-indicator--offline {
  background: rgba(244, 183, 96, 0.12);
  color: var(--breeze-tan);
}
@keyframes livePulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: 0.4; transform: scale(0.85); }
}

.custom-layout-enabled > .custom-widget {
  flex: 0 0 auto;
  width: 400px;
  max-width: 100%;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  /* The widget itself becomes a flex column so the inner .widget-scroll-body
     can absorb overflow while the absolute drag/resize handles stay pinned
     to the widget's corners. Padding moves to the scroll body so the
     scrollbar sits flush with the card border. */
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: visible;
  container-type: inline-size;
}

.widget-scroll-body {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  padding: 10px 38px 20px 12px;
  overflow-x: hidden;
  scrollbar-gutter: stable;
  /* Make the scroll body a flex column so any inner "primary content"
     wrapper tagged .widget-fill (or one of the legacy .junior-log-
     table-wrap / .flight-list-body / .desk-schedule-table-wrap /
     .my-shifts-list / .request-list classes treated as fill targets
     below) can flex-grow vertically when the user resizes the widget
     taller. Without flex on the scroll body, the inner wrapper's
     hardcoded max-height was a hard ceiling and the extra widget
     height showed up as empty space. */
  display: flex;
  flex-direction: column;
}

.custom-widget .widget-scroll-body *,
.custom-widget .card-header,
.custom-widget .schedule-card-header-tools,
.custom-widget .schedule-toolbar,
.custom-widget .tabs,
.custom-widget .link-grid,
.custom-widget .request-form,
.custom-widget .controls-grid,
.custom-widget .ops-grid,
.custom-widget .table,
.custom-widget .schedule-grid {
  min-width: 0;
}

/* ------------------------------------------------------------------
   Universal widget-content fill.

   Widgets are flex columns; the .widget-scroll-body inside is a
   flex item that grows to fill the card. But its OWN children, by
   default, sit at their natural height — so when you resize the
   widget taller, the inner table/list/chart stays the same size and
   the extra height shows up as empty space at the bottom.

   This rule makes the LAST primary child of every scroll body
   flex-grow. It's the "main content" of the widget — header / intro
   text sit above as fixed-size siblings, the data area below
   absorbs the slack.

   Opt-out: add `.widget-no-fill` to any element whose natural
   height should be respected (e.g. a single stat strip, a button
   row at the very bottom that shouldn't stretch). That class has a
   matching reset rule below.

   Tables: <table> elements don't naturally flex-grow even when
   they're the last child, because their layout is computed from
   cell content rather than container size. To make the rendered
   rows visible without scrolling, wrap tables in a div whose
   class ends in "-wrap" — that wrapper participates in flex-fill
   and the table inside scrolls instead of overflowing. The
   widget-fill class works as an explicit alternative.
   ------------------------------------------------------------------ */
.custom-widget .widget-scroll-body > *:last-child:not(.widget-no-fill):not(button):not(input):not(select):not(.btn):not(.muted):not(.help) {
  flex: 1 1 auto;
  min-height: 0;
  /* Override any per-class max-height — those caps are useful for
     non-widget contexts (a 280px chat list on a marketing page),
     but inside a widget the user has explicitly resized to a given
     height and we should fill it. */
  max-height: none;
}

/* Generic "wrap" containers + lists + the explicit .widget-fill
   class always grow inside a scroll body, regardless of whether
   they're the last child. Catches widgets where help text or a
   secondary toolbar sits AFTER the main content. */
.custom-widget .widget-scroll-body > .widget-fill,
.custom-widget .widget-scroll-body > [class$="-wrap"],
.custom-widget .widget-scroll-body > [class*="-wrap "],
.custom-widget .widget-scroll-body > [class$="-list"],
.custom-widget .widget-scroll-body > [class*="-list "],
.custom-widget .widget-scroll-body > [class$="-body"],
.custom-widget .widget-scroll-body > [class*="-body "],
.custom-widget .widget-scroll-body > [class$="-table"],
.custom-widget .widget-scroll-body > [class*="-table "] {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

/* Inside a -wrap container, tables and any inner data block fill
   the wrap's width so resizing the widget wider stretches the
   actual data instead of leaving margin. */
.custom-widget .widget-scroll-body > [class$="-wrap"] {
  display: flex;
  flex-direction: column;
}
.custom-widget .widget-scroll-body > [class$="-wrap"] > table {
  width: 100%;
}

/* Explicit opt-out: if a widget's last child genuinely should stay
   at its natural height (e.g. a single big stat number, a button
   row), tag it with .widget-no-fill and the universal rule above
   skips it. */
.custom-widget .widget-scroll-body > .widget-no-fill {
  flex: 0 0 auto;
}

.title-row h2 {
  margin: 4px 0;
  position: relative;
  padding-left: 14px;
}

/* Accent bar to the left of each view's H2.
   Matches the per-view accent so navigating between views
   gives an immediate visual cue. */
.title-row h2::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 5px;
  height: 1.2em;
  border-radius: 3px;
  background: var(--view-accent);
  box-shadow: 0 0 14px var(--view-accent-soft);
}

.title-row p {
  margin: 0;
  color: var(--muted);
}

.metrics-row {
  margin-top: 14px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

/* ============================================================
   Card depth — Phase 1.

   Every .card is "raised" by default: gradient overlay, soft top-edge
   highlight, drop shadow, 1px border. Adding `.kpi-floating` to a
   card bumps it to the heavier "floating" tier (used for hero KPIs,
   anything that should read above its neighbors). `.kpi-flat` opts
   back into the legacy flat appearance for tight grid cells where
   shadows would be visual noise.

   Per-view accent rail on top is preserved (the existing ::before).
   ============================================================ */
.card {
  border: 1px solid var(--depth-border);
  background: var(--depth-raised-overlay), var(--panel);
  border-radius: var(--depth-radius);
  padding: 12px;
  position: relative;
  box-shadow: var(--depth-raised-shadow);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
/* Top-edge inner highlight — the bright line at the top that gives
   the card a "raised" glassmorphic feel. Sits ABOVE the accent rail. */
.card::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--depth-raised-edge) 25%,
    var(--depth-raised-edge) 75%,
    transparent 100%
  );
  pointer-events: none;
  border-radius: var(--depth-radius) var(--depth-radius) 0 0;
}

/* Floating tier — promote a card to it via `class="card kpi-floating"`. */
.card.kpi-floating {
  background: var(--depth-floating-overlay), var(--panel);
  box-shadow: var(--depth-floating-shadow);
  border-color: var(--depth-border-accent);
}
.card.kpi-floating::after {
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--depth-floating-edge) 25%,
    var(--depth-floating-edge) 75%,
    transparent 100%
  );
}

/* Flat tier — opt-out for grid cells / tight stacks. */
.card.kpi-flat {
  background: var(--panel);
  box-shadow: none;
}
.card.kpi-flat::after { display: none; }

/* Per-view accent rail along the top of every card.
   The color comes from --view-accent which is set per-view above,
   so cards in Supervisor look pink-tinted, Desk Assignment amber,
   Scheduler teal, Universal blue — without any per-view CSS rules. */
.view .card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--view-accent);
  border-radius: 14px 14px 0 0;
  opacity: 0.85;
  pointer-events: none;
  z-index: 1;
}

/* ============================================================
   Hover-lift — Phase 1 interaction layer.

   Every clickable surface (nav links in the sidebar, .btn buttons,
   .chip-action chips) gets a 1 px lift + accent-color glow on hover
   so the UI feels touch-responsive. Active state snaps back to flat.
   Focus-visible adds an accent outline for keyboard users.

   Animation duration is held to 180 ms so it feels snappy, not
   bouncy. Compatible with reduced-motion preferences via the global
   media query at the bottom.
   ============================================================ */
.btn,
.chip-action,
.app-sidebar .nav-link {
  transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
  will-change: auto;
}
.btn:not(:disabled):hover,
.chip-action:not(:disabled):hover,
.app-sidebar .nav-link:not(.active):hover {
  transform: translateY(var(--hover-lift-y));
  box-shadow: var(--hover-glow);
}
.btn:not(:disabled):active,
.chip-action:not(:disabled):active,
.app-sidebar .nav-link:active {
  transform: translateY(0);
}
.btn:focus-visible,
.chip-action:focus-visible,
.app-sidebar .nav-link:focus-visible {
  outline: 2px solid var(--view-accent);
  outline-offset: 2px;
}
/* Primary buttons get the stronger accent glow — they're the
   call-to-action surfaces and deserve the louder lift. */
.btn.primary:not(:disabled):hover {
  box-shadow: var(--hover-glow-accent);
}

/* Card hover is opt-in. Default .card stays put (cards aren't
   universally clickable). Add `.card.is-interactive` to opt in to
   the hover lift — used for clickable KPI tiles, notification
   items, etc. */
.card.is-interactive {
  cursor: pointer;
}
.card.is-interactive:hover {
  transform: translateY(var(--hover-lift-y));
  box-shadow: var(--depth-floating-shadow);
  border-color: var(--depth-border-accent);
}

/* Respect prefers-reduced-motion — kills transforms/shadows on
   hover for users who set the OS preference. */
@media (prefers-reduced-motion: reduce) {
  .btn,
  .chip-action,
  .app-sidebar .nav-link,
  .card,
  .card.is-interactive {
    transition: none !important;
  }
  .btn:not(:disabled):hover,
  .chip-action:not(:disabled):hover,
  .app-sidebar .nav-link:not(.active):hover,
  .card.is-interactive:hover {
    transform: none;
  }
}

/* ============================================================
   PHASE 2 — Typography & numeric hierarchy.

   Three weight tiers + tabular numerals on every numeric surface
   the existing CSS exposed. Tabular numerals keep digit widths
   stable as values update (otherwise "1.0%" → "10.0%" reflows the
   surrounding row). Hero scale promoted to 28-32 px on metric
   surfaces; labels caught at 0.68 rem with 0.06-0.08em tracking so
   they read as labels, not body text.

   The selector list catches every numeric surface that currently
   exists. New components can pick up tabular-nums by adding
   .num-tabular, or by living under .metric-value / .kpi-value etc.
   ============================================================ */
.metric-value,
.kpi-value,
.kpi-hero-value,
.star-d0-tile-value,
.star-d0-ring-pct,
.star-d0-ring-count,
.star-d0-ring-share,
.star-d0-context-value,
.notifications-bell-badge,
.notifications-bell-count,
.password-pill,
.topbar-clock,
.topbar-clock-primary,
.badge,
.num-tabular,
[class*="-count"][class$="ring"],
[class$="-tile-value"],
[class$="-stat-value"] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

/* Hero-scale metric — promote one critical number per card to read
   as the headline. Apply via class="metric-value kpi-hero" or
   directly to the headline node. */
.kpi-hero,
.metric-value.kpi-hero {
  font-size: clamp(1.75rem, 2.4vw, 2.1rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* Caps label utility — uniform treatment for the tiny ALL-CAPS
   labels that float above a metric value. */
.kpi-caps-label {
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  line-height: 1.2;
}

/* Promote the existing .metric-value class to the new hierarchy
   (slightly larger, tabular, tighter tracking). Cards that already
   used .metric-value pick this up automatically. */
.metric-value {
  font-size: clamp(1.5rem, 2vw, 1.85rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.05;
  margin: 8px 0 0;
}

/* ============================================================
   PHASE 3 — Inline visualization utilities.

   Generic sparkline/stacked-bar/ring host classes used inline next
   to a metric value. JS render helpers (renderInlineSparkline,
   renderKpiRing) target these wrappers. The CSS just sizes them;
   each helper injects an SVG into the host node.

   Reusable pattern lifted from the STAR D0 rings — the same SVG +
   stroke-dasharray approach scaled down.
   ============================================================ */
.inline-viz {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.inline-viz-host {
  display: inline-block;
  line-height: 0;
}
.inline-viz-sparkline {
  width: 64px;
  height: 22px;
}
.inline-viz-sparkline path {
  fill: none;
  stroke: var(--view-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 4px var(--view-accent-soft));
}
.inline-viz-sparkline-fill {
  fill: var(--view-accent);
  opacity: 0.08;
  stroke: none;
}
/* Severity dots — replace plain badges where a one-glyph status
   indicator works better than a word. */
.severity-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.05);
}
.severity-dot.is-good      { background: #4dd58a; }
.severity-dot.is-info      { background: var(--view-accent); }
.severity-dot.is-watch     { background: #f5cc4a; }
.severity-dot.is-bad       { background: #ff6b76; }
.severity-dot.is-pending   { background: #ff9c4a; }

/* Progress bar (Time Clock today, etc.) — minimal, lit edge,
   thin track. */
.kpi-progress {
  position: relative;
  height: 6px;
  border-radius: 3px;
  background: var(--ring-track, rgba(140, 175, 230, 0.12));
  overflow: hidden;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}
.kpi-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--view-accent), var(--view-accent-soft));
  border-radius: 3px;
  transition: width 0.4s ease-out;
  box-shadow: 0 0 8px var(--view-accent-soft);
}

/* ============================================================
   PHASE 4 — Page-specific accents.

   Login screen: subtle radial gradient backdrop on the login screen
   itself (independent of body bg since the login overlay sits
   above), plus a faint glow under the BETA banner pill.

   Scheduler: today-date tile picks up a stronger accent glow so it
   reads at a glance.

   Other pages picked up the Phase 1 treatment automatically.
   ============================================================ */
.login-screen {
  background:
    radial-gradient(800px 600px at 50% 30%, var(--view-accent-soft) 0%, transparent 60%),
    radial-gradient(900px 700px at 100% 100%, rgba(255, 82, 123, 0.06) 0%, transparent 60%),
    var(--bg);
}
html[data-theme="light"] .login-screen {
  background:
    radial-gradient(600px 500px at 50% 30%, rgba(31, 116, 223, 0.06) 0%, transparent 65%),
    var(--bg);
}

/* BETA banner — give the gradient a faint downward glow so it
   anchors visually below the topbar without a hard line. */
.beta-banner {
  box-shadow: 0 8px 24px rgba(255, 82, 123, 0.18);
}
html[data-theme="light"] .beta-banner {
  box-shadow: 0 4px 12px rgba(255, 82, 123, 0.12);
}

/* Scheduler today-date highlight. Existing schedule renders use
   .is-today or .schedule-today markers (the schedule code adds these
   to whichever cells/rows represent today). Apply a subtle accent
   inner glow so the eye anchors on "today" within a Day/Week view. */
.is-today,
.schedule-today,
.schedule-cell.is-today,
.schedule-tile.is-today {
  position: relative;
  box-shadow:
    inset 0 0 0 1px var(--view-accent),
    0 0 12px var(--view-accent-soft);
}

/* Active nav link in the sidebar — bump the accent rail from 2px
   inset to a left-side gradient + outer glow so it reads as the
   selected route, not just a slightly different background. */
.app-sidebar .nav-link.active {
  background: linear-gradient(
    90deg,
    var(--view-accent-soft) 0%,
    var(--chip-bg) 40%
  );
  box-shadow:
    inset 3px 0 0 var(--view-accent),
    0 0 12px var(--view-accent-soft);
}

/* ============================================================
   PHASE 5 — "Mission Control" cohesion layer.

   Eight moves that unify every page under one visual language.
   Builds on Phases 1-4: depth tokens, hover-lift, typography, and
   inline-viz scaffolding all stay in place; this layer adds the
   geometric framing and persistent motion that ties surfaces
   together. Reversible by removing this block.
   ============================================================ */

/* MOVE 1 — Topbar HUD restructure ------------------------------ */
.app-topbar {
  /* Override the simple flex-end layout from earlier — three-zone
     grid (left clocks / center title / right KPIs+user) with the
     center stretching to fill. */
  display: grid !important;
  grid-template-columns: minmax(200px, auto) 1fr minmax(200px, auto);
  align-items: center;
  gap: 16px;
  min-height: 56px;
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.topbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.topbar-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-width: 0;
  text-align: center;
}
.topbar-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar-sub {
  margin: 0;
  font-size: 0.68rem;
  color: var(--muted);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: flex-end;
}
.topbar-kpis {
  display: flex;
  align-items: center;
  gap: 8px;
}
.topbar-kpi {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.75rem;
  white-space: nowrap;
}
.topbar-kpi-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
/* Topbar pending-requests dot. Decoupled from the STAR D0 --star-d0-pending
   token (which was repurposed for Pending Concurrence = purple) — the
   topbar dot is a separate concept (supervisor's pending-requests count)
   and stays in the alert-orange family. */
.topbar-kpi-dot.is-pending { background: #ff9c4a; box-shadow: 0 0 6px rgba(255, 156, 74, 0.6); }
.topbar-kpi-dot.is-info    { background: var(--view-accent); box-shadow: 0 0 6px var(--view-accent-soft); }
.topbar-kpi-value {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.topbar-kpi-label {
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.topbar-live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #4dd58a;
  box-shadow: 0 0 8px rgba(77, 213, 138, 0.7);
  animation: live-pulse 1.5s ease-in-out infinite;
  flex-shrink: 0;
}
.topbar-accent-sweep {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--view-accent) 50%,
    transparent 100%
  );
  opacity: 0.6;
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: topbar-sweep 6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes topbar-sweep {
  0%   { background-position: -40% 0; }
  60%  { background-position: 140% 0; }
  100% { background-position: 140% 0; }
}
@media (max-width: 720px) {
  .app-topbar { grid-template-columns: 1fr auto; }
  .topbar-center { display: none; }
  .topbar-kpis { display: none; }
}

/* MOVE 2 — Sidebar icons + breathing active glow --------------- */
/* `:not([hidden])` so a `hidden` attribute on a nav-link (the way
   renderPageAccess gates page access for dispatchers) still wins
   over the flex layout below. Without that guard, `display: flex`
   would override the global `[hidden] { display: none !important }`
   and every page would be visible regardless of permission. */
.app-sidebar .nav-link:not([hidden]) {
  display: flex;
  align-items: center;
  gap: 10px;
}
.nav-link-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--muted);
  transition: color 0.18s ease, transform 0.18s ease;
}
.app-sidebar .nav-link:hover .nav-link-icon {
  color: var(--text);
}
.app-sidebar .nav-link.active .nav-link-icon {
  color: var(--view-accent);
}
.nav-link-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-sidebar .nav-link.active {
  animation: nav-breathe 3s ease-in-out infinite;
}
@keyframes nav-breathe {
  0%, 100% {
    box-shadow:
      inset 3px 0 0 var(--view-accent),
      0 0 10px var(--view-accent-soft);
  }
  50% {
    box-shadow:
      inset 3px 0 0 var(--view-accent),
      0 0 18px var(--view-accent-soft);
  }
}

/* MOVE 3 — Corner-bracket framing on floating cards ----------- */
.card.kpi-floating {
  /* Need an extra layer of pseudo-element for the bottom corners.
     Top corners ride on existing ::before (accent rail) — bottom
     corners use a new ::after pattern. The L-shape comes from
     gradient backgrounds masked to the corners with multiple
     background-image layers. */
  background-image:
    /* top-left bracket */
    linear-gradient(to right, var(--view-accent) 12px, transparent 12px),
    linear-gradient(to bottom, var(--view-accent) 12px, transparent 12px),
    /* top-right bracket */
    linear-gradient(to left, var(--view-accent) 12px, transparent 12px),
    linear-gradient(to bottom, var(--view-accent) 12px, transparent 12px),
    /* bottom-left bracket */
    linear-gradient(to right, var(--view-accent) 12px, transparent 12px),
    linear-gradient(to top, var(--view-accent) 12px, transparent 12px),
    /* bottom-right bracket */
    linear-gradient(to left, var(--view-accent) 12px, transparent 12px),
    linear-gradient(to top, var(--view-accent) 12px, transparent 12px),
    /* preserved depth overlay */
    var(--depth-floating-overlay),
    var(--panel);
  background-repeat: no-repeat;
  background-size:
    /* horizontal arm widths × bracket arm thickness */
    12px 1.5px,    /* TL horizontal */
    1.5px 12px,    /* TL vertical */
    12px 1.5px,    /* TR horizontal */
    1.5px 12px,    /* TR vertical */
    12px 1.5px,    /* BL horizontal */
    1.5px 12px,    /* BL vertical */
    12px 1.5px,    /* BR horizontal */
    1.5px 12px,    /* BR vertical */
    100% 100%,
    100% 100%;
  background-position:
    6px 6px,         /* TL h */
    6px 6px,         /* TL v */
    calc(100% - 6px) 6px,  /* TR h */
    calc(100% - 6px) 6px,  /* TR v */
    6px calc(100% - 6px),  /* BL h */
    6px calc(100% - 6px),  /* BL v */
    calc(100% - 6px) calc(100% - 6px), /* BR h */
    calc(100% - 6px) calc(100% - 6px), /* BR v */
    0 0,
    0 0;
}

/* MOVE 4 — Monospace digits for codes & identifiers ----------- */
/* Codes, IDs, flight numbers — anything that reads as a fixed
   identifier rather than a live numeric metric — switches to a
   tech mono. Live metric values stay in Inter (tabular nums). */
.mono-code,
.mono-id,
.flight-number,
.desk-id-chip,
.flight-row-code,
.username-display,
.topbar-clock,
.topbar-clock-primary,
.topbar-kpi-value,
.password-pill,
input[name="flightNumber"],
input[name="username"],
[data-mono="code"] {
  font-family: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, Consolas, monospace;
  font-feature-settings: "tnum", "calt" 0;
  letter-spacing: 0.01em;
}

/* MOVE 5 — Live-data pulse indicator -------------------------- */
.is-live {
  animation: live-pulse 1.5s ease-in-out infinite;
}
@keyframes live-pulse {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.15); }
}
.live-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #4dd58a;
  box-shadow: 0 0 6px rgba(77, 213, 138, 0.7);
  margin-right: 6px;
  vertical-align: middle;
  animation: live-pulse 1.5s ease-in-out infinite;
}

/* MOVE 6 — Gradient dividers --------------------------------- */
/* Convert solid border-top: 1px lines on common section dividers
   to fading-edge gradients. Applied via utility class so existing
   .sidebar-section, .star-d0-context-divider, etc. opt in.
   Selectors covering the most-visited dividers; per-component CSS
   inside those modules continues to work unchanged. */
.divider-soft,
.sidebar-section,
.dialog-actions,
.sidebar-foot,
.card .card-header + hr,
.title-row + hr,
hr.soft {
  border-color: transparent;
  background-image: linear-gradient(
    90deg,
    transparent 0%,
    var(--line) 20%,
    var(--line) 80%,
    transparent 100%
  );
  background-repeat: no-repeat;
}
.sidebar-section {
  background-position: 0 100%;
  background-size: 100% 1px;
  border-bottom: 0 !important;
}
hr.soft {
  height: 1px;
  background-position: center;
  background-size: 100% 1px;
  border: 0;
  margin: 16px 0;
}

/* MOVE 7 — Unified spacing rhythm ----------------------------- */
.card {
  padding: 16px;  /* was 12px */
}
.view {
  /* Section-to-section breathing room consistent across views. */
  gap: 16px;
}
.title-row {
  margin-bottom: 16px;
}

/* MOVE 8 — Page-load entrance animation ----------------------- */
/* When a view becomes active, fade it in over 250 ms with a soft
   scale-in. JS adds .is-entering on view activation; CSS handles
   the animation. The accent line under the topbar (move 1) plays
   its sweep on the same beat, so the eye sees: bar pulse + content
   slide-in simultaneously. */
.view.is-entering {
  animation: view-enter 0.28s ease-out both;
}
.view.is-entering .card {
  animation: card-rise 0.32s ease-out both;
  animation-delay: 60ms;
}
@keyframes view-enter {
  0%   { opacity: 0; transform: translateY(4px); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes card-rise {
  0%   { opacity: 0; transform: translateY(8px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* Reduced motion respects user preference — kill all the ambient
   animations + the entrance, keep static depth. */
@media (prefers-reduced-motion: reduce) {
  .topbar-accent-sweep,
  .topbar-live-dot,
  .live-dot,
  .is-live,
  .app-sidebar .nav-link.active,
  .view.is-entering,
  .view.is-entering .card {
    animation: none !important;
  }
}

.custom-layout-enabled .custom-widget {
  position: relative;
}

.widget-drag-handle {
  position: absolute;
  top: 8px;
  right: 24px;
  z-index: 2;
  border: 1px dashed #4362a5;
  background: var(--chip-bg);
  color: #9db6e5;
  border-radius: 999px;
  padding: 2px 8px;
  font-size: 0.68rem;
  line-height: 1.3;
  cursor: grab;
}

.widget-edit-handle {
  position: absolute;
  top: 8px;
  right: 6rem;
  z-index: 2;
  border: 1px dashed #4362a5;
  background: var(--chip-bg);
  color: #9db6e5;
  border-radius: 999px;
  padding: 2px 8px;
  font-size: 0.68rem;
  line-height: 1.3;
  cursor: pointer;
}

.widget-edit-handle:hover,
.widget-drag-handle:hover {
  border-color: #7aa2ff;
  color: #d6e3ff;
}

/* Rearrangeable cards: keep title row clear of absolute Reorder; align header badges with Reorder sizing. */
.custom-widget .card-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 12px;
  padding-right: 9.5rem;
}

.custom-widget .card-header h3 {
  margin: 0;
  min-width: 0;
}

.custom-widget .card-header > :not(h3) {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
}

.custom-widget .card-header .badge {
  box-sizing: border-box;
  padding: 2px 8px;
  font-size: 0.68rem;
  line-height: 1.3;
  border-radius: 999px;
  white-space: nowrap;
  justify-self: end;
}

.custom-widget .card-header.schedule-card-header {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto;
  row-gap: 10px;
}

.custom-widget .card-header.schedule-card-header h3 {
  grid-column: 1;
  grid-row: 1;
}

.custom-widget .card-header.schedule-card-header > :not(h3) {
  grid-column: 1;
  grid-row: 2;
  justify-self: stretch;
}

.schedule-card-header-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
}

.custom-widget.dragging {
  opacity: 0.55;
  outline: 1px dashed #7aa2ff;
}

.custom-widget.widget-resize-active {
  outline: 1px solid #5b8cff;
  user-select: none;
}

.widget-custom-content {
  margin-top: 12px;
  min-height: 0;
  color: #d9e6ff;
}

.widget-custom-content.empty:not([contenteditable="true"]) {
  display: none;
}

.widget-custom-content[contenteditable="true"] {
  display: block;
  min-height: 80px;
  padding: 10px;
  border: 1px dashed #4362a5;
  border-radius: 10px;
  background: rgba(15, 26, 59, 0.85);
  outline: none;
}

.widget-custom-content[contenteditable="true"]:focus {
  border-color: #7aa2ff;
  box-shadow: 0 0 0 2px rgba(122, 162, 255, 0.16);
}

.widget-content-editing .card-header h3[contenteditable="true"],
.widget-content-editing h3[contenteditable="true"] {
  padding: 3px 6px;
  border: 1px dashed #4362a5;
  border-radius: 8px;
  outline: none;
}

.widget-content-editing .card-header h3[contenteditable="true"]:focus,
.widget-content-editing h3[contenteditable="true"]:focus {
  border-color: #7aa2ff;
  box-shadow: 0 0 0 2px rgba(122, 162, 255, 0.16);
}

.widget-custom-content[contenteditable="true"]:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
}

.widget-resize-handle {
  position: absolute;
  right: 24px;
  bottom: 6px;
  z-index: 2;
  width: 20px;
  height: 20px;
  padding: 0;
  margin: 0;
  border: 1px solid #4362a5;
  border-radius: 4px;
  background: linear-gradient(
    135deg,
    transparent 45%,
    rgba(67, 98, 165, 0.35) 45%,
    rgba(67, 98, 165, 0.35) 55%,
    transparent 55%
  );
  cursor: nwse-resize;
  box-sizing: border-box;
  touch-action: none;
}

.widget-resize-handle:hover {
  border-color: #7aa2ff;
  background-color: rgba(15, 26, 59, 0.9);
}

.metric-value {
  font-size: 1.6rem;
  margin: 8px 0 0;
  font-weight: 700;
}

.muted {
  color: var(--muted);
}

.grid-two {
  margin-top: 12px;
  display: grid;
  grid-template-columns: 1fr 1.5fr;
  gap: 12px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 7px;
}

.tabs {
  display: flex;
  gap: 8px;
}

.tab {
  background: var(--chip-bg);
  border: 1px solid var(--line);
  /* Brighter text so the prev/next arrow glyphs (← →) and short
     labels stand out against the navy panel. The previous #a8bee6
     was muddy enough that supervisors reported the arrows being
     hard to spot at a glance. */
  color: #e6efff;
  border-radius: 8px;
  padding: 6px 10px;
  cursor: pointer;
}

.tab:hover {
  /* Visible hover affordance — the previous styling had no hover
     state, so users couldn't tell the arrows were even buttons
     until they clicked. Subtle blue lift matches the theme. */
  background: var(--chip-bg);
  border-color: #4a6cae;
  color: #ffffff;
}

.tab.active {
  background: var(--blue);
  color: #fff;
}

.link-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.dash-link {
  display: block;
  text-decoration: none;
  color: #d9e6ff;
  padding: 10px;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.dash-link small {
  display: block;
  color: var(--muted);
  margin-top: 4px;
}

/* FAA 121.465 duty-time violation styling. Red border + small ⚠
   glyph in the top-right corner of the pill. Tooltip on the pill
   itself carries the full violation message + recommendation. */
.shift-pill.is-duty-violation,
.week-shift-row.is-duty-violation {
  border-color: rgba(232, 96, 102, 0.85);
  box-shadow: 0 0 0 1px rgba(232, 96, 102, 0.55) inset;
}
.shift-pill .duty-flag {
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: 0.78rem;
  color: #ffb6b6;
  pointer-events: none;
}
.shift-pill.is-duty-violation { position: relative; }

/* Edit mode for the link grids — Main Dispatch Links + Supervisor
   Links share the same chrome. The grid switches to a single column
   so each row has room for the four inputs + remove + drag handle. */
.link-grid--editing {
  display: flex !important;
  flex-direction: column;
  gap: 8px;
}
.link-grid-actions {
  display: flex;
  gap: 6px;
}
.link-grid-edit-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: grab;
}
.link-grid-edit-row.link-grid-dragging {
  opacity: 0.5;
}
.link-grid-edit-row.link-grid-drag-over {
  border-color: var(--breeze-blue);
  box-shadow: 0 0 0 1px var(--breeze-blue) inset;
}
.link-grid-drag-handle {
  flex: 0 0 auto;
  font-size: 1.05rem;
  color: var(--muted);
  cursor: grab;
  user-select: none;
  letter-spacing: -2px;
  padding: 6px 6px;
  margin: -4px 0;
  border-radius: 6px;
  /* lets a finger drag the handle on touch without the page scrolling */
  touch-action: none;
}
.link-grid-drag-handle:active { cursor: grabbing; }
.link-grid-drag-handle:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }
.link-grid-edit-inputs {
  display: grid;
  grid-template-columns: 1.2fr 1.6fr 1fr auto;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  align-items: center;
}
.link-grid-edit-inputs input[type="text"] {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 5px 8px;
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
  min-width: 0;
}
.link-grid-edit-inputs input[type="text"].link-grid-error {
  border-color: rgba(232, 96, 102, 0.7);
  background: rgba(232, 96, 102, 0.08);
}
.link-grid-newtab {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.74rem;
  color: var(--muted);
  margin: 0;
  white-space: nowrap;
}
.link-grid-newtab input { margin: 0; }
.link-grid-remove {
  flex: 0 0 auto;
  padding: 0 8px;
  color: #ffb6b6;
}
.link-grid-add-row {
  display: grid;
  grid-template-columns: 1.2fr 1.6fr 1fr auto auto;
  gap: 6px;
  padding: 8px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  align-items: center;
  background: rgba(255, 255, 255, 0.02);
}
.link-grid-add-row input[type="text"] {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 5px 8px;
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
  min-width: 0;
}
@media (max-width: 760px) {
  /* Stack inputs on narrow viewports so the row doesn't truncate. */
  .link-grid-edit-inputs,
  .link-grid-add-row {
    grid-template-columns: 1fr;
  }
}

#coverageChart {
  width: 100%;
  height: 280px;
  border-radius: 12px;
  background: var(--panel-deep);
}

.list {
  margin: 0;
  padding: 0 0 0 16px;
}

.list li {
  margin: 5px 0;
}

textarea {
  min-height: 160px;
}

.actions-row {
  margin-top: 10px;
}

.btn {
  border: 0;
  border-radius: 10px;
  padding: 10px 14px;
  cursor: pointer;
}

.btn.primary {
  background: var(--blue);
  color: #fff;
}

.table {
  width: 100%;
  border-collapse: collapse;
}

/* Dispatcher point summary: fixed layout + clip so the card width follows grid columns (table was painting past the cell). */
.custom-widget .table.table-point-summary {
  table-layout: fixed;
  width: 100%;
  max-width: 100%;
  min-width: 0;
}

.custom-widget--has-point-summary {
  overflow-x: clip;
}

.custom-widget .table {
  table-layout: fixed;
  /* Tables inside widgets always span the available width so
     resizing the widget wider stretches the columns instead of
     leaving empty margin to the right of the table. */
  width: 100%;
}

/* Canvas elements (Chart.js coverage chart, etc.) inside a widget
   must fill the available width so a wider widget makes the chart
   actually bigger. Chart.js's responsive option resizes on parent
   change, but the canvas needs `display: block` + width: 100% to
   participate in layout properly. */
.custom-widget canvas {
  max-width: 100%;
  display: block;
}

/* Generic image / iframe / pre fill — keep them from overflowing
   while still consuming available width. */
.custom-widget pre,
.custom-widget .super-state-pre {
  max-width: 100%;
  width: 100%;
  box-sizing: border-box;
}

.table th,
.table td {
  border-bottom: 1px solid var(--line);
  text-align: left;
  padding: 6px 5px;
  font-size: 0.86rem;
  overflow-wrap: anywhere;
  word-break: normal;
}

.table th {
  color: #8ea4ce;
  font-weight: 500;
}

.controls-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  align-items: end;
}

.badge {
  border-radius: 999px;
  padding: 6px 10px;
  border: 1px solid var(--line);
}

.badge.allowed {
  color: #c4ffed;
  border-color: #1f855f;
}

.badge.denied {
  color: #ffd4d9;
  border-color: #a1404f;
}

/* Fixed-height schedule panel: the toolbar + collapsed hours head stay
   put at the top while the calendar scrolls inside #scheduleViewport.
   The head is a flex SIBLING above the scroll region (not a pinned
   overlay), so the top calendar row is never hidden — that's the fix for
   the cut-off first row across every view. */
.schedule-card {
  display: flex;
  flex-direction: column;
  max-height: min(82vh, calc(100vh - var(--topbar-h, 56px) - 96px));
  overflow: hidden;
}

.schedule-sticky-head {
  flex: 0 0 auto;
  margin: 0 0 6px;
}

.schedule-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.sched-period-label {
  font-size: 0.85rem;
  color: var(--muted);
  margin-left: 4px;
}

/* The calendar viewport is the card's scroll region for EVERY view, so
   the head never overlaps the grid and the top row stays visible. */
.schedule-viewport {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: auto;
  max-height: none;
  /* Teal surface for the calendar so it reads as its own zone instead
     of blending into the navy app shell. */
  background: linear-gradient(180deg, #0a1f25 0%, #0c2a32 100%);
  border: 1px solid #1c3b46;
  border-radius: 10px;
  padding: 6px;
}
.schedule-viewport.schedule-viewport--day,
.schedule-viewport.schedule-viewport--week,
.schedule-viewport.schedule-viewport--twoWeek,
.schedule-viewport.schedule-viewport--month {
  max-height: none;
  overflow-y: auto;
}

/* Card stays bounded (was min-height: max-content, which forced it to
   grow to content and reintroduced whole-page scroll). The internal
   viewport scroll handles overflow now. */
.schedule-card.schedule-card--day,
.schedule-card.schedule-card--month,
.schedule-card.schedule-card--week,
.schedule-card.schedule-card--twoWeek {
  min-height: 0;
}

.schedule-grid {
  display: grid;
  gap: 6px;
  width: 100%;
  min-width: 0;
}

/* Day: single column, no horizontal scroll */
.schedule-grid.view-day {
  grid-template-columns: minmax(0, 1fr);
}

/* Week: one row of seven columns, each column fills viewport height */
.schedule-grid.view-week {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-template-rows: 1fr;
  height: 100%;
  align-items: stretch;
  gap: 4px;
}

/* Two-week: 7 columns × 2 rows. Same per-cell density as week view but
   shows fourteen days at once. The viewport scrolls vertically if the
   cells need more room. */
.schedule-grid.view-twoWeek {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-auto-rows: minmax(180px, 1fr);
  align-items: stretch;
  gap: 4px;
}

.slot.week-col {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 4px 5px;
  border-radius: 8px;
}

.slot.week-col h4 {
  flex: 0 0 auto;
  margin: 0 0 4px;
  font-size: 0.72rem;
  line-height: 1.2;
  color: #a9bcde;
}

.week-col-shifts {
  flex: 0 0 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* No column-internal scroll — the schedule card grows to fit so
     every shift in every column is visible at once. */
  overflow: visible;
  padding-right: 2px;
}

.week-shift-row {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--panel-soft);
  border: 1px solid var(--line);
  border-left-width: 3px;
  /* Density pass: two tight lines (name/time, status/Z). Group + note
     moved to the hover tooltip, so the row no longer needs a third line. */
  font-size: 0.76rem;
  line-height: 1.18;
  text-align: left;
  color: #dce8ff;
  cursor: default;
  overflow: hidden;
  min-height: 28px;
}

.week-shift-row.supervisor-editable {
  cursor: pointer;
}

.week-shift-row.status-scheduled {
  border-left-color: var(--green);
}

.week-shift-row.status-callout,
.week-shift-row.status-sick {
  border-left-color: var(--red);
}

.week-shift-row.status-pto,
.week-shift-row.status-sick-pto,
.week-shift-row.status-callout-pto {
  border-left-color: #c9a227;
}

.week-shift-row.status-late {
  border-left-color: #f5a524;
}

.week-shift-row--empty {
  flex: 0 0 auto;
  opacity: 0.65;
  border-left-color: transparent;
  min-height: auto;
}

.week-shift-main {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.week-shift-meta {
  font-size: 0.85em;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.week-availability,
.day-availability {
  flex: 0 0 auto;
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px dashed var(--line);
  font-size: clamp(0.55rem, 0.8vw, 0.7rem);
  color: #9ecbff;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Day-view variant — the day pill list isn't space-constrained the way
   week / 2wk / month cells are, so we let it breathe a bit. */
.slot:not(.week-col):not(.other-month):not(.month-cell) > .day-availability {
  font-size: 0.78rem;
  padding: 6px 8px;
  background: rgba(122, 162, 255, 0.08);
  border: 1px solid rgba(122, 162, 255, 0.25);
  border-radius: 8px;
  margin-top: 8px;
  white-space: normal;
}

/* Month cells are tight — clip and truncate. */
.slot.other-month .day-availability,
.schedule-grid.view-month .slot .day-availability {
  font-size: 0.6rem;
  margin-top: 2px;
  padding-top: 2px;
}

.week-col-actions {
  flex: 0 0 auto;
  margin-top: 4px;
}

.week-col-actions .tab {
  font-size: 0.65rem;
  padding: 3px 6px;
}

/* Month: 7 columns × 6 rows. Rows size to their tallest cell (auto)
   instead of sharing height with `1fr`, so the dow header row doesn't
   inflate to match the day-cell rows when the card is in
   `min-height: max-content` mode (which it now is — see schedule-card
   :has() rule above). The 110px floor moves onto the day-cell .slot
   itself so date rows still keep a sensible minimum even when empty. */
.schedule-grid.view-month {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-auto-rows: auto;
}

.schedule-grid.view-month .slot {
  min-height: 110px;
}

.schedule-grid.view-month .month-dow {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6f83a9;
  padding: 4px 2px;
  min-height: auto;
  background: transparent;
  border: 0;
}

.slot {
  /* Day cells use a teal slate distinct from the surrounding navy
     widgets. Border picks up the same teal family so the grid reads
     cohesive. Today / blackout / week-col hover all override these
     base values via more-specific rules below. */
  background: #122e36;
  border: 1px solid #1f4350;
  border-radius: 10px;
  padding: 8px;
  min-width: 0;
  overflow: hidden;
}

.slot.other-month {
  opacity: 0.42;
  background: #0e2128;
}

.slot h4 {
  margin: 0 0 6px;
  font-size: 0.85rem;
  color: #a9bcde;
}

.slot p {
  margin: 0;
  font-size: 0.82rem;
  color: #dce8ff;
}

.sched-hint {
  margin: 0 0 8px;
  font-size: 0.82rem;
  /* Sits above the calendar viewport. Solid card background +
     position: relative + z-index keep it on its own row so no
     amount of grid overflow in week / 2-week / month views can
     paint pills on top of the hint text. */
  position: relative;
  z-index: 1;
  background: var(--card);
  padding: 2px 0;
}

.shift-pill {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Density pass: tighter than before so day/week/month fit far more on
     screen, while staying readable. Notes/group moved to the hover
     tooltip so every pill is a clean two lines. */
  font-size: 0.8rem;
  line-height: 1.2;
  padding: 3px 6px;
  margin: 2px 0;
  border-radius: 6px;
  background: var(--panel-soft);
  border: 1px solid var(--line);
  color: #dce8ff;
  cursor: default;
  text-align: left;
  width: 100%;
  min-width: 0;
}

/* Month variant — slim two-line tile so a 6-week grid stays compact. */
.shift-pill--month {
  font-size: 0.72rem;
  line-height: 1.15;
  padding: 2px 5px;
  margin: 1px 0;
  border-radius: 4px;
}
.shift-pill--month .shift-meta { font-size: 0.64rem; }

.shift-pill.supervisor-editable {
  cursor: pointer;
}

.shift-pill.status-scheduled {
  border-left: 3px solid var(--green);
}

.shift-pill.status-callout,
.shift-pill.status-sick {
  border-left: 3px solid var(--red);
}

.shift-pill.status-pto,
.shift-pill.status-sick-pto,
.shift-pill.status-callout-pto {
  border-left: 3px solid #c9a227;
}

.shift-pill.status-late {
  border-left: 3px solid #f5a524;
}

/* Swap- and Offer-origin shifts render in purple so the schedule
   tells the story at a glance: this dispatcher has this shift because
   it was traded to / offered to them by another dispatcher. The
   purple left bar overrides whatever status color the shift would
   otherwise have (almost always status-scheduled by the time it gets
   here, since you can only swap a working day). The auto-generated
   history note lives in the shift's `note` field and is visible in
   the shift dialog and via the pill's tooltip. */
.shift-pill.is-origin-swap,
.shift-pill.is-origin-offer {
  border-left: 3px solid #9b6dff;
  background: linear-gradient(
    90deg,
    rgba(155, 109, 255, 0.08) 0%,
    rgba(155, 109, 255, 0.02) 70%,
    transparent 100%
  );
}

.shift-pill .shift-meta {
  display: block;
  color: var(--muted);
  font-size: 0.72rem;
}

.month-day-num {
  font-weight: 700;
  font-size: 0.85rem;
  color: #a9bcde;
}

.month-shifts {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* No cap — month cells grow as tall as their shift list. The schedule
     card itself uses min-height: max-content for view-month, so the
     overall card grows along with the tallest cell. */
  overflow: visible;
}

/* Bucket section headers in the calendar.
   Used in day / week / 2-week / month views to label the
   Dispatchers / Trainers / Supervisors / Open groups. The
   --week / --month modifiers tighten the spacing for compact
   cells; the unmodified base is sized for the day view. */
.day-section-title {
  display: flex;
  align-items: center;
  gap: 6px;
  /* Density pass: the old 10px top margin per header was a big chunk of
     the dead space — trimmed hard. */
  margin: 5px 0 2px;
  padding: 2px 7px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
  border-left: 3px solid var(--view-accent, var(--blue));
  color: var(--text);
}

.day-section-title:first-child {
  margin-top: 0;
}

/* Day view pill list — one uniform vertical gap for every section header
   and pill, so the spacing reads evenly instead of leaning on each flex
   pill's own (non-collapsing) margins. Scoped to day view only; week uses
   .slot.week-col and month uses .month-shifts, both untouched. */
.day-shift-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.day-shift-list > .shift-pill { margin: 0; }
.day-shift-list > p { margin: 0; }
/* A little extra air above each section header (Dispatchers / Trainers /…)
   so groups stay visually distinct, but none below — the flex gap handles
   the space down to the first pill. The first header sits flush. */
.day-shift-list > .day-section-title { margin: 5px 0 0; }
.day-shift-list > .day-section-title:first-child { margin-top: 0; }

/* Section label takes the row; the Select button sits at the far right. */
.day-section-title-label { flex: 1 1 auto; min-width: 0; }

/* Per-section + per-day "Select" controls feed the multi-select bulk-edit
   bar. Kept tiny and unobtrusive; supervisor-only (added in JS). */
.section-select-all,
.day-select-all {
  flex: 0 0 auto;
  font: inherit;
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(193, 221, 255, 0.12);
  border: 1px solid rgba(193, 221, 255, 0.35);
  color: #cfe1ff;
  cursor: pointer;
}
.section-select-all:hover,
.day-select-all:hover {
  background: rgba(193, 221, 255, 0.22);
}
.day-select-all {
  display: inline-block;
  margin: 2px 0 0;
}
.day-select-all--compact {
  font-size: 0.56rem;
  padding: 0 5px;
  margin-left: 6px;
}

/* "+N more" affordance in capped month cells — opens the day view. */
.month-more-btn {
  width: 100%;
  margin-top: 2px;
  padding: 1px 5px;
  font-size: 0.62rem;
  font-weight: 600;
  text-align: left;
  background: rgba(255, 255, 255, 0.05);
  border: 1px dashed var(--line);
  border-radius: 4px;
  color: #9ecbff;
  cursor: pointer;
}
.month-more-btn:hover {
  background: rgba(255, 255, 255, 0.10);
}

.day-section-title--dispatcher {
  border-left-color: var(--breeze-blue);
  color: #cfe1ff;
}

/* Trainers stays green — it matches the Training shift category color
   so trainer-section pills and training-pair shifts read as related. */
.day-section-title--trainer {
  border-left-color: #3cb371;
  color: #c4ffed;
}

.day-section-title--supervisor {
  border-left-color: var(--breeze-pink);
  color: #ffd2dd;
}

/* Classroom section — purple tint (matches the classroom shift category
   default color #bc6cf0). Surfaces dispatchers/trainers in scheduled
   classroom training that day. They're off the floor and excluded
   from desk assignments. */
.day-section-title--classroom {
  border-left-color: #bc6cf0;
  color: #e8d4ff;
}

.day-section-title--open {
  border-left-color: var(--breeze-tan);
  color: #ffe8cd;
}

/* Off-duty sections — pinned to the bottom of each day cell in
   SECTION_DEFS. Three distinct accent colors so supervisors scanning
   the day can tell at a glance whether an absence was planned (PTO,
   amber), unexpected (Sick, red), or a no-show / called-off shift
   (Callout, orange). The pills themselves keep their existing
   status-derived styling — these rules only color the section header. */
.day-section-title--pto {
  border-left-color: #b58a3c;
  color: #f7dfa6;
}

.day-section-title--sick {
  border-left-color: var(--red);
  color: #ffd1d9;
}

.day-section-title--callout {
  border-left-color: #f08c3a;
  color: #ffd9b8;
}

/* Compact variant for week / 2-week columns — tighter padding and
   smaller text so the bucket labels don't crowd out the pills. */
.day-section-title--week {
  margin: 6px 0 2px;
  padding: 2px 6px;
  font-size: 0.66rem;
  letter-spacing: 0.05em;
}

/* Even more compact for month cells — labels become a thin tag. */
.day-section-title--month {
  margin: 4px 0 1px;
  padding: 1px 5px;
  font-size: 0.6rem;
  letter-spacing: 0.04em;
  border-left-width: 2px;
  border-radius: 4px;
}

.request-form {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  align-items: end;
}

.request-form-stack {
  grid-template-columns: 1fr;
  align-items: stretch;
}

/* ---- Request form: calendar label + selection count ---- */
.request-cal-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  color: var(--muted, #8a8f99);
  margin-top: 4px;
}
.request-cal-count {
  font-size: 11px;
  font-weight: 500;
  padding: 1px 7px;
  border-radius: 999px;
  background: transparent;
  color: var(--muted, #8a8f99);
  transition: background-color 0.15s, color 0.15s;
}
.request-cal-count.has-selection {
  background: var(--accent, #4aa3ff);
  color: #001633;
}

/* ---- Request form: workday calendar ---- */
.request-workday-cal {
  display: block;
  padding: 8px 10px 10px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--line, rgba(255, 255, 255, 0.08));
  border-radius: 8px;
  font-size: 12px;
}
/* Compact variant — fixed-height cells so the calendar stays a
   modest-sized date picker even when the card it lives in is wide.
   Constrained to ~340px so a stretched card doesn't blow the calendar
   up to dominate the view, but big enough that the dead space on
   either side isn't comical. */
.request-workday-cal-compact {
  padding: 8px 10px 10px;
  font-size: 12px;
  max-width: 340px;
  margin: 0 auto;
}
.request-workday-cal-compact .rwc-header {
  margin-bottom: 6px;
}
.request-workday-cal-compact .rwc-cell {
  font-size: 12px;
  font-weight: 600;
  height: 32px;
  aspect-ratio: auto;
  border-radius: 4px;
}
.request-workday-cal-compact .rwc-grid {
  gap: 3px;
}
.request-workday-cal-compact .rwc-month-name {
  font-size: 13px;
}
.request-workday-cal-compact .rwc-nav {
  width: 24px;
  height: 24px;
  font-size: 15px;
}
.request-workday-cal-compact .rwc-dow {
  font-size: 10px;
  padding: 2px 0;
  letter-spacing: 0;
}
.request-workday-cal-compact .rwc-actions {
  margin-top: 6px;
}
.request-workday-cal-compact .rwc-clear {
  font-size: 11px;
  padding: 3px 8px;
}
.request-workday-cal-compact .rwc-legend {
  font-size: 10px;
  gap: 4px 10px;
  margin-top: 6px;
  line-height: 1.3;
}
.request-workday-cal-compact .rwc-swatch {
  width: 10px;
  height: 10px;
}
.request-workday-cal .rwc-empty {
  text-align: center;
  color: var(--muted, #8a8f99);
  padding: 12px 6px;
  font-style: italic;
}
.request-workday-cal .rwc-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.request-workday-cal .rwc-month-name {
  font-weight: 600;
  font-size: 13px;
}
.request-workday-cal .rwc-nav {
  width: 26px;
  height: 26px;
  border: 1px solid var(--line, rgba(255, 255, 255, 0.12));
  border-radius: 4px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
}
.request-workday-cal .rwc-nav:hover {
  background: rgba(255, 255, 255, 0.06);
}
.request-workday-cal .rwc-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}
.request-workday-cal .rwc-dow {
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted, #8a8f99);
  text-transform: uppercase;
  padding: 2px 0;
}
.request-workday-cal .rwc-cell {
  aspect-ratio: 1 / 1;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  padding: 0;
  line-height: 1;
}
.request-workday-cal .rwc-cell:hover {
  border-color: var(--accent, #4aa3ff);
}
/* Read-only mode (used on the partner calendar during an Offer
   request — purely informational so D1 can confirm D2 isn't already
   working that day). Renders as a static div instead of a button,
   so we strip the cursor + hover affordances. */
.request-workday-cal .rwc-cell.is-readonly {
  cursor: default;
  pointer-events: none;
}
.request-workday-cal .rwc-cell.is-readonly:hover {
  border-color: transparent;
}
.request-workday-cal .rwc-cell.is-readonly.is-off:hover {
  border: 1px dashed rgba(255, 255, 255, 0.10);
}
.request-workday-cal .rwc-cell.is-other-month {
  opacity: 0.35;
}
.request-workday-cal .rwc-cell.is-off {
  background: transparent;
  border: 1px dashed rgba(255, 255, 255, 0.10);
  color: var(--muted, #8a8f99);
}
.request-workday-cal .rwc-cell.is-work {
  background: rgba(64, 200, 134, 0.22);
  border-color: rgba(64, 200, 134, 0.55);
  color: #fff;
}
.request-workday-cal .rwc-cell.is-pto {
  background: rgba(255, 196, 0, 0.22);
  border-color: rgba(255, 196, 0, 0.55);
  color: #fff;
}
.request-workday-cal .rwc-cell.is-sick,
.request-workday-cal .rwc-cell.is-callout {
  background: rgba(255, 107, 123, 0.22);
  border-color: rgba(255, 107, 123, 0.55);
  color: #fff;
}
.request-workday-cal .rwc-cell.is-today {
  outline: 1px solid var(--accent, #4aa3ff);
  outline-offset: -2px;
}
/* Selection ring — survives whatever the cell's underlying status
   color is (work/PTO/sick/off), so a selected work day still reads as
   "work + selected" instead of replacing one signal with the other. */
.request-workday-cal .rwc-cell.is-selected {
  outline: 2px solid var(--accent, #4aa3ff);
  outline-offset: -2px;
  color: #fff;
  font-weight: 700;
  box-shadow: 0 0 0 1px rgba(74, 163, 255, 0.4) inset;
}
.request-workday-cal .rwc-cell.is-selected.is-off {
  background: rgba(74, 163, 255, 0.18);
}

/* Clear-selection footer button. */
.request-workday-cal .rwc-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 6px;
}
.request-workday-cal .rwc-clear {
  font-size: 10px;
  padding: 3px 8px;
  border: 1px solid var(--line, rgba(255, 255, 255, 0.12));
  background: transparent;
  color: var(--muted, #8a8f99);
  border-radius: 4px;
  cursor: pointer;
}
.request-workday-cal .rwc-clear:hover {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  border-color: var(--accent, #4aa3ff);
}

/* Legend swatch for the "Selected" key. */
.request-workday-cal .rwc-swatch.is-selected {
  background: transparent;
  border: 2px solid var(--accent, #4aa3ff);
}
.request-workday-cal .rwc-legend {
  margin-top: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  font-size: 10px;
  color: var(--muted, #8a8f99);
}
.request-workday-cal .rwc-legend span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.request-workday-cal .rwc-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  border: 1px solid transparent;
}
.request-workday-cal .rwc-swatch.is-work {
  background: rgba(64, 200, 134, 0.5);
  border-color: rgba(64, 200, 134, 0.7);
}
.request-workday-cal .rwc-swatch.is-pto {
  background: rgba(255, 196, 0, 0.5);
  border-color: rgba(255, 196, 0, 0.7);
}
.request-workday-cal .rwc-swatch.is-sick {
  background: rgba(255, 107, 123, 0.5);
  border-color: rgba(255, 107, 123, 0.7);
}
.request-workday-cal .rwc-swatch.is-off {
  border: 1px dashed rgba(255, 255, 255, 0.25);
}

.banner {
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid #1f855f;
  background: rgba(29, 212, 161, 0.08);
  color: #c4ffed;
  margin-bottom: 12px;
  font-size: 0.88rem;
}

.banner.hidden {
  display: none;
}

.inline-link {
  color: #9ecbff;
  margin-left: 8px;
}

.btn.secondary {
  background: var(--chip-bg);
  color: #dce8ff;
  border: 1px solid var(--line);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.btn.danger {
  background: #5a1a2a;
  color: #ffd4d9;
}

.actions-split {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.small {
  font-size: 0.82rem;
}

.supervisor-intro {
  margin-top: 0;
}

.request-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 280px;
  overflow: auto;
}

.custom-widget .request-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.request-item {
  padding: 10px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--chip-bg);
  font-size: 0.88rem;
}

.request-item .tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 0.72rem;
  margin-right: 6px;
  background: var(--panel-soft);
  color: #bfd0ef;
}

.request-status {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.7rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  border: 1px solid;
}

.request-status-pending {
  border-color: #6b6033;
  background: #2a2415;
  color: #f3d68b;
}

.request-status-approved {
  border-color: #2f6b3f;
  background: #142a1c;
  color: #9be0a8;
}

.request-status-denied {
  border-color: #6b2d4a;
  background: #2a1322;
  color: #f7c1d4;
}

.request-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}

.btn.btn-xs {
  padding: 3px 10px;
  font-size: 0.74rem;
}

.request-queue-tabs .tab {
  padding: 3px 10px;
  font-size: 0.76rem;
}

.request-queue-tabs .tab-count {
  display: inline-block;
  margin-left: 4px;
  padding: 0 6px;
  background: rgba(122, 162, 255, 0.18);
  border-radius: 999px;
  font-size: 0.68rem;
  color: #cfd9ff;
}

.request-queue-tabs .tab.active .tab-count {
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
}

.request-decided-by {
  display: block;
  margin-top: 4px;
  font-size: 0.72rem;
  color: var(--muted);
  font-style: italic;
}

.request-decision-comment {
  display: block;
  margin-top: 4px;
  padding: 6px 8px;
  background: rgba(122, 162, 255, 0.08);
  border-left: 2px solid rgba(122, 162, 255, 0.45);
  border-radius: 4px;
  font-size: 0.78rem;
  color: #cfd9ff;
  white-space: pre-wrap;
  word-break: break-word;
}

.request-comment-dialog {
  max-width: 460px;
}

/* ---- Junior Assignment Log widget ---- */
.junior-log-add-form {
  display: grid;
  grid-template-columns: minmax(180px, 1.4fr) minmax(140px, 1fr) auto;
  gap: 8px 12px;
  align-items: end;
  margin-bottom: 12px;
}

.junior-log-add-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0;
}

.junior-log-add-form select {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 6px 8px;
  font: inherit;
}

.junior-log-table-wrap {
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: auto;
  /* Default cap when the wrap renders outside a resizable widget
     (e.g. inline reports or older dialogs). Inside a widget the
     .custom-widget override below removes the ceiling and flexes
     it to fill the available height. */
  max-height: 520px;
}

.custom-widget .junior-log-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  /* Flex item with min-height:0 in a flex column: shrinks/grows to
     fill the parent .widget-scroll-body. Removing the px ceiling
     here is what lets the table actually use the extra vertical
     space when the supervisor resizes the widget. */
  max-height: none;
}

.junior-log-table {
  width: 100%;
  font-size: 0.82rem;
}

.junior-log-table th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}

.junior-log-table .junior-log-pos-col {
  width: 32px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.junior-log-table .junior-log-pos {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.junior-log-table .junior-log-name {
  font-weight: 500;
  color: var(--text);
}

.junior-log-table .junior-log-name-orphan {
  color: var(--muted);
  font-style: italic;
}

.junior-log-table .junior-log-reason {
  font-size: 0.78rem;
  color: #cfd9ff;
}

.junior-log-table .junior-log-reason .reason-tag {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 0.7rem;
  border: 1px solid;
}

.junior-log-table .reason-tag.reason-junior {
  background: rgba(255, 200, 100, 0.12);
  border-color: rgba(255, 200, 100, 0.45);
  color: #ffd28d;
}

.junior-log-table .reason-tag.reason-pickup {
  background: rgba(122, 162, 255, 0.12);
  border-color: rgba(122, 162, 255, 0.35);
  color: #cfd9ff;
}

.junior-log-table .junior-log-actions {
  white-space: nowrap;
}

.junior-log-table .junior-log-actions .btn {
  padding: 2px 8px;
  font-size: 0.7rem;
  margin-right: 4px;
}

.junior-log-table .junior-log-actions .btn-icon {
  /* Compact arrow buttons for manual reorder. Square-ish so the four
     position controls (▲▲ ▲ ▼ ▼▼) line up neatly without dominating
     the row. Triangles use Geometric Shapes block glyphs so they
     render in every system font.

     The .btn base class only resets `font: inherit` and `border: 0`
     — no background — so without an explicit fill these inherit the
     OS native button style (light gray on most platforms). The
     light-color triangles I set with `color: #e6efff` then vanished
     into that light background. Forcing a dark navy fill matching
     the rest of the dashboard keeps the contrast predictable across
     macOS / Windows / Linux. */
  width: 28px;
  padding: 2px 0;
  text-align: center;
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.1;
  margin-right: 2px;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  color: #e6efff;
}
.junior-log-table .junior-log-actions .btn-icon:hover:not(:disabled) {
  background: var(--chip-bg);
  border-color: #4a6cae;
}
.junior-log-table .junior-log-actions .btn-icon:disabled {
  /* Override the global .btn-icon:disabled opacity so the disabled
     end-of-list buttons still read as buttons rather than washing
     to invisibility — supervisors need to see all four position
     slots to understand the row's bounds. */
  opacity: 0.45;
  background: var(--field-bg);
  cursor: not-allowed;
}

/* The "all the way to top/bottom" buttons stack two triangles. Give
   them a slightly wider footprint and tighten the letter-spacing so
   the two glyphs read as a single paired indicator instead of two
   separate triangles. */
.junior-log-table .junior-log-actions .junior-log-icon-double {
  width: 36px;
  letter-spacing: -2px;
}

.junior-log-table .junior-log-actions .btn-icon:disabled {
  /* Raised the floor from 0.35 → 0.55 so a disabled position button
     is still legible (you can read which arrow it is) instead of
     fading to near-invisible against the row. */
  opacity: 0.55;
  cursor: not-allowed;
}

.junior-log-table .junior-log-acted-ts {
  display: block;
  font-size: 0.66rem;
  color: var(--muted);
  margin-top: 1px;
}

.junior-log-table tr:nth-child(odd) {
  background: rgba(255, 255, 255, 0.01);
}

/* ---- Draft mode controls (topbar) ---- */
.draft-controls {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 4px;
  border-left: 1px solid var(--line);
  margin-left: 4px;
}

/* Relocated into the topbar user area (next to Switch user) so Publish is
   reachable without opening the collapsed Command Center sidebar. Compact
   the chips and let them wrap so the cluster never crowds the user card /
   notification bell when several drafts are pending. */
.topbar-right .topbar-draft-controls {
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 360px;
}
.topbar-right .topbar-draft-controls .chip {
  padding: 3px 8px;
  font-size: 0.72rem;
  white-space: nowrap;
}

/* "Apply immediately" toggle — pressed = override is ON (live edits).
   The status chip to its left is the primary indicator of mode; this
   button is the action. */
.draft-toggle[aria-pressed="true"] {
  background: rgba(224, 68, 91, 0.22);
  border-color: rgba(224, 68, 91, 0.55);
  color: #ffb3bf;
}

.draft-toggle[aria-pressed="true"]::before {
  content: "⚡ ";
}

/* Status chip — calm amber when staging, urgent red when live edits
   are on. Not interactive itself; the toggle to the right is. */
.draft-status {
  letter-spacing: 0.02em;
}

.draft-status.draft-status-drafting {
  background: rgba(255, 200, 100, 0.16);
  border-color: rgba(255, 200, 100, 0.45);
  color: #ffd28d;
}

.draft-status.draft-status-live {
  background: rgba(224, 68, 91, 0.18);
  border-color: rgba(224, 68, 91, 0.55);
  color: #ffb3bf;
  font-weight: 600;
}

.draft-count {
  background: rgba(255, 200, 100, 0.18);
  border-color: rgba(255, 200, 100, 0.45);
  color: #ffd28d;
}

.draft-publish {
  background: rgba(94, 181, 255, 0.16);
  border-color: rgba(94, 181, 255, 0.55);
  color: #d2ecff;
}

.draft-publish:hover {
  background: rgba(94, 181, 255, 0.3);
}

.draft-discard {
  background: rgba(224, 68, 91, 0.14);
  border-color: rgba(224, 68, 91, 0.55);
  color: #ffb3bf;
}

.draft-discard:hover {
  background: rgba(224, 68, 91, 0.28);
}

/* Drafted entities — dashed amber outline + small DRAFT pill so a
   supervisor can spot what's pending review. Reuses the amber palette
   from the open-shift inline pill since both convey "not committed". */
.shift-pill.is-draft,
.week-shift-row.is-draft {
  border-style: dashed;
  border-color: rgba(255, 200, 100, 0.7) !important;
  background: rgba(255, 200, 100, 0.08);
}

.draft-pill-tag {
  display: inline-block;
  margin-left: 4px;
  padding: 0 5px;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  background: rgba(255, 200, 100, 0.22);
  border: 1px solid rgba(255, 200, 100, 0.6);
  color: #ffd28d;
  border-radius: 4px;
  vertical-align: middle;
}

.blackout-row.is-draft,
.open-shift-row.is-draft {
  border-style: dashed;
  border-color: rgba(255, 200, 100, 0.7);
  background: rgba(255, 200, 100, 0.08);
}

.request-item.is-draft {
  border-style: dashed;
  border-color: rgba(255, 200, 100, 0.7);
  background: rgba(255, 200, 100, 0.06);
}

/* ---- My Shifts widget (Universal + Scheduler) ---- */
.my-shifts-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
  padding-right: 2px;
}

.custom-widget .my-shifts-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.my-shifts-empty {
  color: var(--muted);
  font-size: 0.85rem;
  padding: 16px 4px;
  text-align: center;
}

/* Pending shift offers, rendered above the upcoming-shifts list on
   the My Shifts widget. The container has its own background so the
   offer block reads as a distinct call-to-action separate from the
   informational shift rows below it. Each row pairs a summary with
   Accept / Decline buttons. */
.my-shifts-offers {
  background: rgba(31, 116, 223, 0.10);
  border: 1px solid rgba(31, 116, 223, 0.35);
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 10px;
}
.my-shifts-offers-head {
  margin: 0 0 6px;
  font-size: 0.82rem;
  color: var(--text);
}
.my-shift-offer {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid rgba(31, 116, 223, 0.18);
}
.my-shift-offer:first-of-type { border-top: 0; }
.my-shift-offer.is-void { opacity: 0.7; }
.my-shift-offer-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}
.my-shift-offer-body strong {
  font-size: 0.88rem;
}
.my-shift-offer-actions {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

.my-shift-row {
  display: grid;
  grid-template-columns: minmax(110px, auto) auto 1fr auto;
  gap: 6px 10px;
  align-items: center;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: 8px;
  background: #122e36;
  color: var(--text);
  font-size: 0.84rem;
  text-align: left;
  cursor: pointer;
  transition: background 120ms ease;
}

.my-shift-row:hover {
  background: #173741;
}

.my-shift-row.is-today {
  border-color: rgba(94, 181, 255, 0.65);
  box-shadow: inset 3px 0 0 #5eb5ff;
}

.my-shift-row .my-shift-date {
  font-weight: 600;
  color: var(--text);
}

.my-shift-row .my-shift-date small {
  display: block;
  font-weight: 400;
  font-size: 0.7rem;
  color: var(--muted);
  margin-top: 1px;
}

.my-shift-row .my-shift-time {
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--text);
}

.my-shift-row .my-shift-time small {
  display: block;
  font-size: 0.7rem;
  color: var(--muted);
  margin-top: 1px;
}

.my-shift-row .my-shift-context {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
  align-items: center;
  font-size: 0.78rem;
  color: var(--muted);
  min-width: 0;
}

.my-shift-row .my-shift-context .shift-desk-label,
.my-shift-row .my-shift-context .shift-group {
  margin: 0;
}

.my-shift-row .my-shift-note {
  display: block;
  flex: 1 1 100%;
  margin-top: 2px;
  font-size: 0.74rem;
  font-style: italic;
  color: #c0d4ff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.my-shift-row .my-shift-status-pill {
  font-size: 0.66rem;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.my-shift-row .my-shift-status-pill.status-scheduled {
  border-color: #1f6b4a;
  background: rgba(29, 212, 161, 0.12);
  color: #9be0c3;
}

.my-shift-row .my-shift-status-pill.status-callout,
.my-shift-row .my-shift-status-pill.status-sick {
  border-color: #6b2d4a;
  background: rgba(255, 107, 123, 0.12);
  color: #ffb3bf;
}

.my-shift-row .my-shift-status-pill.status-pto,
.my-shift-row .my-shift-status-pill.status-sick-pto,
.my-shift-row .my-shift-status-pill.status-callout-pto {
  border-color: #6b6033;
  background: rgba(255, 200, 100, 0.14);
  color: #f3d68b;
}

.my-shift-row .my-shift-status-pill.status-late {
  border-color: #7a5028;
  background: rgba(255, 168, 84, 0.12);
  color: #ffc88a;
}

/* ---- Today highlight (current Mountain-Time day) ---- */
.slot.is-today {
  /* Two-tone accent: a glowing top border in cyan + a faint cyan tint
     on the cell so the day stands apart from neighbors at a glance.
     Sits behind .is-blackout's diagonal stripes — both can apply at
     once if today happens to be a blackout day. Background tint moves
     into the teal family alongside the rest of the calendar surface
     so today doesn't suddenly turn navy. */
  border: 1px solid #5eb5ff;
  box-shadow:
    inset 0 3px 0 0 #5eb5ff,
    0 0 0 1px rgba(94, 181, 255, 0.35);
  background-color: #163f4a;
}

.day-today-badge {
  display: inline-block;
  font-size: 0.66rem;
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(94, 181, 255, 0.22);
  border: 1px solid rgba(94, 181, 255, 0.55);
  color: #d2ecff;
  letter-spacing: 0.04em;
  margin-top: 4px;
  font-weight: 600;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Day-view variant — slightly larger and a touch more padding so it
   matches the bigger cell density. */
.slot:not(.week-col):not(.other-month) > .day-today-badge {
  font-size: 0.75rem;
  padding: 3px 10px;
  margin-top: 8px;
}

/* ---- Blackout cells (PTO not allowed) ---- */
.slot.is-blackout {
  /* Diagonal red stripes layered behind the cell's own background.
     The stripes are subtle enough that shift pills still read clearly
     but visible enough that a supervisor scanning for PTO conflicts
     can't miss them. */
  background-image: repeating-linear-gradient(
    -45deg,
    rgba(224, 68, 91, 0.06) 0,
    rgba(224, 68, 91, 0.06) 8px,
    rgba(224, 68, 91, 0.16) 8px,
    rgba(224, 68, 91, 0.16) 16px
  );
  background-color: #1a0f1c;
}

.day-blackout-badge {
  display: inline-block;
  font-size: 0.66rem;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(224, 68, 91, 0.18);
  border: 1px solid rgba(224, 68, 91, 0.55);
  color: #ffb3bf;
  letter-spacing: 0.02em;
  margin-top: 2px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.day-blackout-badge::before {
  content: "🚫 ";
}

/* Day-view variant — slightly larger to match the day cell's overall
   density. */
.slot:not(.week-col):not(.other-month) > .day-blackout-badge {
  font-size: 0.78rem;
  padding: 4px 10px;
  margin-top: 8px;
  display: inline-block;
}

/* Reminder chip on calendar cells (payday, timesheets, custom). Color
   comes inline from the reminder; this just sets the shape + spacing.
   Mirrors the blackout badge so day/week/month cells read consistently. */
.day-reminder-badge {
  display: inline-block;
  font-size: 0.64rem;
  padding: 1px 7px;
  border-radius: 999px;
  letter-spacing: 0.02em;
  margin-top: 2px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.slot:not(.week-col):not(.other-month) > .day-reminder-badge {
  font-size: 0.76rem;
  padding: 3px 10px;
  margin-top: 6px;
}

/* Day-view header chips (Today / Payday + other reminders / Blackout) all
   share ONE size, vertical position, line-height, and a consistent
   horizontal gap — so when more than one shows on the same day (e.g.
   Today + Payday) they line up cleanly instead of one sitting 2px lower
   than the other. This rule comes after the individual ones above and
   has equal specificity, so it wins on source order. */
.slot:not(.week-col):not(.other-month) > .day-today-badge,
.slot:not(.week-col):not(.other-month) > .day-reminder-badge,
.slot:not(.week-col):not(.other-month) > .day-blackout-badge {
  font-size: 0.75rem;
  line-height: 1.3;
  padding: 3px 10px;
  margin-top: 8px;
  margin-right: 6px;
  vertical-align: middle;
}

/* Widget-level "work-in-progress" treatment. Matches the calendar's
   blackout-day visual cue without masking the data: a thick striped
   left edge + striped top band frame the card so the flagged-state
   reads from across the room, while the data area (table / form /
   chart) keeps its normal contrast. Implemented with two pseudo-
   elements anchored to the card's edges; the inner widget content
   is unaffected.

   Layered on top of the standard card chrome — the .is-wip class
   just adds these decorations without overriding `background`,
   `border`, etc. Removing `is-wip` from the HTML restores the
   plain card visually with no leftover styling. */
.card.is-wip {
  position: relative;
  /* Tint the card's edge with a red glow so the corners read as
     "flagged" even at a glance. */
  border-color: rgba(224, 68, 91, 0.75) !important;
  box-shadow:
    0 0 0 1px rgba(224, 68, 91, 0.35),
    0 0 24px rgba(224, 68, 91, 0.18),
    var(--card-shadow, 0 6px 18px rgba(0, 0, 0, 0.25));
}

/* Striped LEFT-edge ribbon — 12px wide vertical bar with the same
   diagonal stripe pattern the calendar uses for blackout days. Sits
   inside the card, anchored to the left edge, full height. Doesn't
   block clicks. Always visible regardless of what's in the body. */
.card.is-wip::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 12px;
  background-image: repeating-linear-gradient(
    -45deg,
    rgba(224, 68, 91, 0.45) 0,
    rgba(224, 68, 91, 0.45) 6px,
    rgba(224, 68, 91, 0.85) 6px,
    rgba(224, 68, 91, 0.85) 12px
  );
  border-radius: 14px 0 0 14px;
  pointer-events: none;
  z-index: 2;
}

/* Striped TOP band — 8px tall, full width, same stripe pattern. The
   per-view accent rail (::before on .view .card) lives at the top
   too at 2px; we sit just below it so both are visible. Combined
   with the left ribbon, the card reads as bracketed in red from
   two sides. */
.card.is-wip::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 8px;
  background-image: repeating-linear-gradient(
    -45deg,
    rgba(224, 68, 91, 0.45) 0,
    rgba(224, 68, 91, 0.45) 6px,
    rgba(224, 68, 91, 0.85) 6px,
    rgba(224, 68, 91, 0.85) 12px
  );
  border-radius: 14px 14px 0 0;
  pointer-events: none;
  z-index: 2;
}

/* Promote the inline pill in the WIP card header so it reads at
   widget scale — bigger, brighter, and bolder than the calendar
   pill which has to share space with shift content. */
.card.is-wip .day-blackout-badge.widget-wip-badge,
.card.is-wip > .card-header .day-blackout-badge {
  font-size: 0.85rem;
  padding: 4px 14px;
  letter-spacing: 0.08em;
  background: rgba(224, 68, 91, 0.4);
  border-color: rgba(224, 68, 91, 1);
  color: #fff5f6;
  text-transform: uppercase;
  font-weight: 800;
  box-shadow:
    0 0 14px rgba(224, 68, 91, 0.55),
    inset 0 0 6px rgba(255, 255, 255, 0.1);
}

/* WIP flag toggle button — mounted on every widget by
   mountWipFlagButton(). Sits in the top-right corner to the LEFT
   of the drag handle (which lives at right: 24px and is ~30px
   wide, occupying right: 24-54px). We anchor to right: 70px so
   there's a clean 16px gap between the WIP toggle's right edge
   and the drag handle's left edge — no visual overlap, no
   accidentally clicking the wrong control. Faint when inactive
   (stays out of the way until the user hovers the widget), bright
   red when the widget IS marked WIP. The button sits ABOVE the
   WIP top-band stripe (z-index: 4) so it remains clickable even
   on flagged widgets where the stripe overlay would otherwise
   intercept the pointer. */
.widget-wip-toggle {
  position: absolute;
  top: 8px;
  right: 70px;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  background: rgba(214, 227, 255, 0.04);
  border: 1px solid rgba(214, 227, 255, 0.18);
  color: rgba(214, 227, 255, 0.55);
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 4;
  opacity: 0;
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease,
    border-color 0.15s ease, box-shadow 0.15s ease;
}
/* Reveal on widget hover so the corner stays uncluttered when
   you're not actively curating WIP markers. */
.custom-widget:hover .widget-wip-toggle {
  opacity: 1;
}
.widget-wip-toggle:hover {
  background: rgba(224, 68, 91, 0.18);
  border-color: rgba(224, 68, 91, 0.6);
  color: #ffd1d8;
}
/* Active state — widget IS flagged. Button stays visible and red
   even without hover so you can tell at a glance which widgets are
   currently marked. */
.widget-wip-toggle.is-active {
  opacity: 1;
  background: rgba(224, 68, 91, 0.4);
  border-color: rgba(224, 68, 91, 1);
  color: #fff5f6;
  box-shadow: 0 0 10px rgba(224, 68, 91, 0.45);
}
.widget-wip-toggle.is-active:hover {
  background: rgba(224, 68, 91, 0.55);
}

/* ---- Blackout admin panel (Supervisor Dashboard) ---- */
.blackout-add-form {
  display: grid;
  grid-template-columns: minmax(120px, 1fr) minmax(120px, 1fr) minmax(160px, 2fr) auto;
  gap: 8px 10px;
  align-items: end;
  margin-bottom: 10px;
}

.blackout-add-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0;
}

.blackout-add-form input {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 6px 8px;
  font: inherit;
}

.blackout-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 200px;
  overflow: auto;
}

.blackout-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  align-items: center;
  padding: 6px 10px;
  border: 1px solid rgba(224, 68, 91, 0.45);
  border-radius: 8px;
  background: rgba(224, 68, 91, 0.06);
  font-size: 0.85rem;
}

.blackout-row .blackout-label {
  font-weight: 500;
  color: var(--text);
}

.blackout-row .blackout-range {
  color: var(--muted);
  font-size: 0.78rem;
}

/* Reminders card (scheduler) — list of payday / timesheet / custom
   reminders with a color swatch, recurrence summary, and next date. */
.reminders-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
  max-height: 240px;
  overflow: auto;
}
.reminder-row {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
}
.reminder-row-swatch {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 1px solid rgba(255, 255, 255, 0.25);
}
.reminder-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.reminder-row-main strong {
  font-size: 0.86rem;
}
.reminder-row-actions {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}

/* Reminder dialog — recurrence-specific fields toggle via JS (display
   none/flex); the notify fieldset groups the bell options. */
.reminder-notify-fieldset {
  margin: 4px 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.reminder-notify-offsets {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 6px 0 10px;
}

.blackout-row .blackout-actions {
  margin-left: auto;
}

.blackout-row .btn {
  padding: 2px 10px;
  font-size: 0.74rem;
}

.blackout-empty {
  color: var(--muted);
  font-size: 0.85rem;
  padding: 4px 0;
}

/* ---- Shift colors panel ---- */
.shift-colors-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 10px;
}

.shift-colors-row {
  display: grid;
  /* Five columns, one per child: color swatch, label input, hex
     code, usage count, remove button. Without all five tracks the
     last two children wrap to a second row and the remove button
     stretches into the gap, producing what looks like a stray
     translucent bar under the row. */
  grid-template-columns: 36px minmax(0, 1fr) 110px auto auto;
  gap: 10px;
  align-items: center;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--field-bg);
}

.shift-colors-row input[type="color"] {
  width: 36px;
  height: 30px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
}

.shift-colors-row input[type="text"] {
  background: var(--panel-deep);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--text);
  padding: 6px 8px;
  font: inherit;
  font-size: 0.85rem;
}

.shift-colors-row .shift-colors-hex {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.75rem;
  color: var(--muted);
  text-align: right;
}

.shift-colors-usage {
  white-space: nowrap;
  font-size: 0.7rem;
  color: var(--breeze-tan);
}

.shift-colors-remove {
  font-size: 1rem;
  font-weight: 600;
  color: #ff8a90;
  padding: 0 6px;
}
.shift-colors-remove:disabled {
  color: var(--muted);
  opacity: 0.5;
  cursor: not-allowed;
}

/* Add-category row — color picker + label + Add button on one
   line, mirrors the look of the existing category rows. */
.shift-colors-add-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.shift-colors-add-row input[type="color"] {
  width: 36px;
  height: 32px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--chip-bg);
  padding: 2px;
  cursor: pointer;
}
.shift-colors-add-row input[type="text"] {
  flex: 1 1 auto;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px 10px;
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
}

.request-comment-dialog textarea {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
  font-size: 0.85rem;
  resize: vertical;
  min-height: 70px;
  width: 100%;
  box-sizing: border-box;
}

.shift-dialog {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 0;
  background: var(--panel);
  color: var(--text);
  max-width: 420px;
  width: calc(100vw - 32px);
}

/* Wide variant for dialogs with multi-column tables (Manage roles
   permission matrix). 420px is too narrow for a roles-by-permissions
   grid + the role-management table — at 420 the form fields
   collapse onto each other and the permission matrix scrolls
   horizontally even at small role counts. */
.shift-dialog.dialog-wide {
  max-width: 960px;
}

/* The copy-shift dialog hosts a 3-month inline picker side-by-side,
   so it needs to be substantially wider than the default 420px to
   show all three months at once. 760px fits three ~220px month panes
   plus padding/gaps comfortably; on narrower viewports the calc()
   keeps a 32px viewport margin. */
#shiftCopyDialog {
  max-width: 760px;
}

.shift-dialog::backdrop {
  background: rgba(0, 0, 0, 0.55);
}

.shift-dialog h3 {
  margin: 0 0 8px;
}

.shift-dialog form {
  padding: 16px;
}

.shift-dialog label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 10px;
  font-size: 0.88rem;
  color: var(--muted);
}

.dialog-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
  justify-content: flex-end;
}

.import-dialog {
  max-width: 520px;
}

.import-summary {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 10px 0 12px;
  line-height: 1.55;
}

.import-summary strong {
  color: var(--text);
}

.import-summary .import-stat {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}

.import-summary .import-stat + .import-stat {
  margin-top: 2px;
}

.import-issues {
  background: #2a1322;
  border: 1px solid #6b2d4a;
  color: #f7c1d4;
  border-radius: 10px;
  padding: 8px 10px;
  margin-bottom: 12px;
  font-size: 0.82rem;
  max-height: 140px;
  overflow: auto;
}

.import-issues ul {
  margin: 4px 0 0;
  padding-left: 18px;
}

.import-checkbox {
  flex-direction: row !important;
  align-items: center;
  gap: 8px !important;
}

.import-checkbox input {
  width: auto;
}

/* Section header rows on Today's desk assignments — Dispatchers /
   Trainers / Supervisors, mirroring the Scheduler's bucket sections.
   Each header is a single-cell row spanning all columns. */
.desk-assign-section td {
  padding: 6px 10px !important;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  background: rgba(31, 116, 223, 0.08);
  border-top: 1px solid var(--line);
  border-left: 3px solid var(--breeze-blue);
}

.desk-assign-section--trainer td {
  border-left-color: #3cb371;
  background: rgba(60, 179, 113, 0.08);
}

.desk-assign-section--supervisor td {
  border-left-color: var(--breeze-pink);
  background: rgba(255, 82, 123, 0.08);
}

/* Classroom section — purple tint, mirrors the .day-section-title--classroom
   color so the scheduler and Today's Desk Assignments widget read as
   the same group. Dispatchers in classroom training are visible but
   visually flagged as off-the-floor. */
.desk-assign-section--classroom td {
  border-left-color: #bc6cf0;
  background: rgba(188, 108, 240, 0.08);
}

/* Jump Seating section pulls its color from the live
   state.shiftCategories entry for "jumpseat" via inline styles set in
   renderDeskAssignments() — that way changing the color in the Shift
   Colors panel propagates here automatically. CSS only carries the
   shared chrome (padding, font, border-top); colors come from JS. */

.desk-assign-section td .muted {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}

/* Flight assignments printout dialog. Wider than other dialogs so
   the per-desk tables breathe. Switches to a flat sheet view when
   printing — see @media print at the bottom of this file. */
.assignment-print-dialog {
  max-width: 980px;
  width: 95vw;
  padding: 18px 22px;
}

.assignment-print-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.assignment-print-header h3 {
  margin: 0 0 4px;
}

.assignment-print-actions {
  display: flex;
  gap: 8px;
}

.assignment-print-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  padding: 10px 14px;
  background: rgba(31, 116, 223, 0.08);
  border: 1px solid rgba(193, 221, 255, 0.25);
  border-radius: 10px;
  margin-bottom: 14px;
  font-size: 0.85rem;
  color: var(--text);
}

.assignment-print-summary strong {
  color: var(--breeze-cool);
}

.assignment-print-body {
  max-height: 60vh;
  overflow-y: auto;
}

.assignment-print-desk {
  margin-bottom: 18px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-deep);
}

.assignment-print-desk-head {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}

.assignment-print-desk-head h4 {
  margin: 0;
  font-size: 0.98rem;
  color: var(--breeze-cool);
}

.assignment-print-desk-head h4 .muted {
  color: var(--muted);
  font-weight: 400;
}

/* Title row: bold desk id + dispatcher name, kept on one line. */
.apd-titleblock {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.apd-titleblock .apd-dispatcher {
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--text);
}

/* Meta row: each fact as a small stacked label/value chip so the header
   reads as an organized grid instead of one clumped line. */
/* Aligned grid so labels/values line up in tidy columns across rows
   instead of the ragged flex-wrap they used to. auto-fit keeps it
   responsive — columns reflow by available width. */
.apd-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 8px 20px;
  margin: 0;
  align-items: start;
}
.apd-field {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}
.apd-field dt {
  margin: 0;
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}
.apd-field dd {
  margin: 0;
  font-size: 0.82rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
/* Passdown is the operationally important one — give it the tan accent
   so it stands out among the desk facts. */
.apd-field--passdown dd {
  color: var(--breeze-tan);
  font-weight: 700;
}

/* Subtle "runway" note next to First release — how long after on-duty the
   dispatcher's first release is due. Muted by default; turns red when the
   runway is under the expected 30-min minimum (a "shouldn't happen" flag). */
.apd-lead {
  margin-left: 4px;
  padding: 0 5px;
  border-radius: 999px;
  font-size: 0.66rem;
  font-weight: 500;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  cursor: help;
}
.apd-lead--tight {
  color: #ffb6c0;
  background: rgba(224, 68, 91, 0.20);
  border-color: rgba(224, 68, 91, 0.55);
  font-weight: 700;
}

.assignment-print-empty {
  margin: 0;
}

.assignment-print-table {
  width: 100%;
  font-size: 0.8rem;
}

.assignment-print-table th,
.assignment-print-table td {
  padding: 4px 8px;
  border-bottom: 1px solid #14224a;
  text-align: left;
}

.assignment-print-table th {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.assignment-print-unassigned {
  border-color: rgba(255, 82, 123, 0.35);
  background: rgba(255, 82, 123, 0.05);
}

.assignment-print-unassigned .assignment-print-desk-head h4 {
  color: var(--breeze-pink);
}

/* Forced-assignment indicators. The auto-assigner places every
   flight, but if the cap-respecting passes can't find a clean home,
   the over-cap fallback assigns it anyway and tags it with
   `forced: true`. The summary pill counts them; the per-row badge
   marks the specific over-cap rows. */
.assignment-print-forced-pill {
  background: rgba(255, 212, 170, 0.15);
  color: var(--breeze-tan);
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 212, 170, 0.35);
}

.assignment-print-forced-pill strong {
  color: var(--breeze-tan) !important;
}

/* Quiet gray pill for "N deferred to next cycle" — flights that
   were unassigned on this run but eligible for tomorrow's day-shift
   dispatchers, so they'll get picked up when the supervisor runs the
   next cycle. Styled lower-key than the over-cap pill since this
   isn't an action item; it's information. */
.assignment-print-deferred-pill {
  background: rgba(140, 150, 170, 0.15);
  color: var(--muted);
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid rgba(140, 150, 170, 0.35);
}
.assignment-print-deferred-pill strong {
  color: var(--text) !important;
}
.assignment-print-deferred-note h4 {
  color: var(--muted);
  font-weight: 500;
}

.forced-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(255, 212, 170, 0.18);
  color: var(--breeze-tan);
  border: 1px solid rgba(255, 212, 170, 0.35);
  vertical-align: middle;
}

.row-forced td {
  background: rgba(255, 212, 170, 0.04);
}

/* Manage Roles modal layout. Sections stack vertically with
   breathing room; the permissions matrix gets generous padding
   between cells so it reads as a grid instead of a wall of
   checkboxes. The previous bunched-up layout came from the form
   using a tight CSS Grid (auto-fit minmax 160px) at 420px modal
   width — switching to a wider dialog plus block-flowing form
   labels fixes it. */
.roles-manage-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.roles-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.roles-section-title {
  margin: 0;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: #d6e3ff;
}

/* Stretch the form's grid layout in the dialog to one column for
   the small inputs (label / admin / bucket) so each gets its own
   line with comfortable spacing instead of squishing into a 2-3
   column track. The general .user-accounts-form is grid-auto-fit
   for compact lists; here we override it back to a clean stack. */
.shift-dialog.dialog-wide .user-accounts-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.shift-dialog.dialog-wide .user-accounts-form > .actions-row {
  margin-top: 4px;
}

/* Permissions matrix table. Roles down the side, permissions
   across. Centered checkboxes; vertical headers for the permission
   columns so we can fit 8+ permissions without forcing a 1200px
   modal. Locked rows (admin roles) get a subtle tint so it's clear
   their permissions are inherited from the master Admin flag. */
/* Transposed permission matrix.
   Permissions are rows (full label + description on the left);
   roles are columns. Section dividers ("Page access" / "Read-only
   views" / "Edit / actions") render as full-width tinted rows
   between groups. */
.roles-perm-table {
  width: 100%;
  border-collapse: collapse;
}

/* Leftmost label column carries the permission name + description
   stacked. Width is generous so descriptions can wrap nicely. */
.roles-perm-label-col {
  text-align: left !important;
  width: 320px;
  min-width: 240px;
  padding: 10px 14px 10px 12px !important;
  vertical-align: top;
}
.roles-perm-table .roles-perm-label-col {
  font-weight: 500;
  color: #d6e3ff;
}
.roles-perm-label {
  display: block;
  font-weight: 600;
  font-size: 0.85rem;
  color: #ecf2ff;
  letter-spacing: 0.01em;
}
.roles-perm-desc {
  display: block;
  font-weight: 400;
  font-size: 0.72rem;
  line-height: 1.35;
  margin-top: 2px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}

/* Role column headers — two-line: role label + Admin/Standard pill. */
.roles-perm-table thead th {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
  text-align: center;
  padding: 10px 8px;
  vertical-align: bottom;
  white-space: normal;
  border-bottom: 1px solid rgba(193, 221, 255, 0.18);
}
.roles-perm-role-head .roles-perm-role-head-name {
  font-weight: 700;
  font-size: 0.82rem;
  text-transform: none;
  color: #d6e3ff;
  letter-spacing: 0;
  line-height: 1.3;
}
.roles-perm-role-head .roles-perm-role-head-status {
  display: inline-block;
  margin-top: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  background: rgba(214, 227, 255, 0.06);
  color: var(--muted);
}
.roles-perm-role-head.is-admin {
  background: rgba(244, 183, 96, 0.06);
}
.roles-perm-role-head.is-admin .roles-perm-role-head-status {
  background: rgba(244, 183, 96, 0.18);
  color: var(--breeze-tan);
}

/* Body rows — alternating subtle stripe + section-tinted left edge. */
.roles-perm-table tbody td {
  text-align: center;
  padding: 8px 8px;
  vertical-align: middle;
  border-bottom: 1px solid rgba(193, 221, 255, 0.06);
}
.roles-perm-table tbody th.roles-perm-label-col {
  border-bottom: 1px solid rgba(193, 221, 255, 0.06);
}
.roles-perm-row:hover td,
.roles-perm-row:hover th.roles-perm-label-col {
  background: rgba(193, 221, 255, 0.04);
}
.roles-perm-cell.is-admin {
  background: rgba(244, 183, 96, 0.04);
}

.roles-perm-table input[type="checkbox"] {
  width: 17px;
  height: 17px;
  cursor: pointer;
  accent-color: #5da6ff;
}
.roles-perm-cell.is-implied input[type="checkbox"] {
  accent-color: var(--breeze-tan);
  opacity: 0.7;
}

/* Edit cells blocked by the readOnlyAll master toggle render with
   a faint red/amber wash + a slash-through cursor on hover, so the
   supervisor can see at a glance which cells are locked off. */
.roles-perm-cell.is-blocked {
  background: rgba(215, 105, 87, 0.06);
}
.roles-perm-cell.is-blocked input[type="checkbox"] {
  opacity: 0.35;
  cursor: not-allowed;
}

/* Section divider rows — full-width tinted band with bold all-caps
   label. The `--page` / `--view` / `--edit` modifier colors the
   left border + label so the three groups are visually distinct
   without relying on color alone. */
.roles-perm-section-row th {
  text-align: left;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--muted);
  padding: 14px 14px 6px 12px !important;
  background: rgba(193, 221, 255, 0.04);
  border-top: 1px solid rgba(193, 221, 255, 0.12);
  border-bottom: 1px solid rgba(193, 221, 255, 0.12);
  border-left: 4px solid var(--muted);
}
.roles-perm-section--master th {
  color: #d8a6f0;
  border-left-color: #b16ad6;
  background: rgba(177, 106, 214, 0.07);
}
.roles-perm-section--page th {
  color: #8fb6ff;
  border-left-color: #5da6ff;
  background: rgba(93, 166, 255, 0.06);
}
.roles-perm-section--view th {
  color: #b9d18a;
  border-left-color: #8bc06b;
  background: rgba(139, 192, 107, 0.06);
}
.roles-perm-section--edit th {
  color: var(--breeze-tan);
  border-left-color: #f4b760;
  background: rgba(244, 183, 96, 0.06);
}

/* Permission row left edge picks up a faint section accent so the
   group association is visible all the way down each row. */
.roles-perm-row--master th.roles-perm-label-col { border-left: 3px solid rgba(177, 106, 214, 0.4); padding-left: 9px !important; }
.roles-perm-row--page  th.roles-perm-label-col { border-left: 3px solid rgba(93, 166, 255, 0.35); padding-left: 9px !important; }
.roles-perm-row--view  th.roles-perm-label-col { border-left: 3px solid rgba(139, 192, 107, 0.35); padding-left: 9px !important; }
.roles-perm-row--edit  th.roles-perm-label-col { border-left: 3px solid rgba(244, 183, 96, 0.35); padding-left: 9px !important; }

/* Admin toggle in the Roles table — checkbox + label sitting
   inline. Label flips between "Admin" (when checked) and
   "Standard" so the supervisor sees the current state without
   leaning on color alone. */
.role-admin-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}
.role-admin-toggle input[type="checkbox"] {
  width: 14px;
  height: 14px;
  cursor: pointer;
}
.role-admin-toggle-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: #d6e3ff;
}
.role-admin-toggle input:checked ~ .role-admin-toggle-label {
  color: var(--breeze-tan);
}

/* Manage Roles save status pill + Save button row. Pill shrinks
   when idle so it doesn't take dead space; flex auto-margin pushes
   the buttons to the right edge of the actions row. */
.roles-manage-actions {
  align-items: center;
}
.roles-manage-actions #rolesManageSaveStatus {
  margin-right: auto;
  /* Inherits .save-status base — but we need the dialog-context
     variant to not render a leading dot when idle. */
}
.roles-manage-actions .save-status--idle {
  display: none;
}

/* ---------------------------------------------------------------------------
   Super User Dashboard widgets. Four cards: System stats, Role audit,
   State inspector, Data tools. Visually distinct from Supervisor cards
   only via the danger-button styling on Factory reseed and the muted
   monospace pre block in the inspector — otherwise they reuse the
   common .card / .table machinery so the dashboard reads as one piece.
   --------------------------------------------------------------------------- */
.super-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.super-stat {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 6px 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.super-stat-label {
  font-size: 0.72rem;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.super-stat-value {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.super-roles-wrap {
  max-height: 100%;
  overflow: auto;
}
.super-roles-table th,
.super-roles-table td {
  text-align: center;
  font-size: 0.78rem;
  padding: 4px 6px;
}
.super-roles-table th:first-child,
.super-roles-table td:first-child {
  text-align: left;
}
.super-roles-on { color: #6dd0ff; font-weight: 700; }
.super-roles-off { color: var(--muted); opacity: 0.4; }

/* Auto sign-out (idle timeout) per-user override card. Pill palette
   mirrors the meaning of each state:
     • on        — auto sign-out is ACTIVE for this user (orange/amber,
                   matching the watch tone used elsewhere)
     • off       — auto sign-out is DISABLED for this user (green,
                   matching the "approved" / "done" tone)
     • force-on  — explicit override pushing the dispatcher INTO the
                   timer (red border — overriding role default)
     • force-off — explicit override REMOVING the dispatcher from the
                   timer (purple border — overriding role default)
     • neutral   — no override set; "role default" placeholder
   Pills share the same rounded shape and font weight as password-pill
   so the Employees and Auto Sign-out tables read at the same glance. */
.super-security-pill {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid;
  white-space: nowrap;
}
.super-security-pill--on {
  border-color: #6b6033;
  background: #2a2415;
  color: #f3d68b;
}
.super-security-pill--off {
  border-color: #2f6b3f;
  background: #142a1c;
  color: #9be0a8;
}
.super-security-pill--force-on {
  border-color: #6b2f2f;
  background: #2a1414;
  color: #ff9e9e;
}
.super-security-pill--force-off {
  border-color: #5a2f6b;
  background: #1f1428;
  color: #c8a8f0;
}
.super-security-pill--neutral {
  border-color: rgba(140, 175, 230, 0.25);
  background: rgba(10, 16, 41, 0.4);
  color: var(--muted);
  font-style: italic;
}
.super-security-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  white-space: nowrap;
}
.super-security-toggle,
.super-security-reset {
  font-size: 0.78rem;
}
.super-security-row.is-disabled-account {
  opacity: 0.55;
}

.super-state-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: end;
  margin-bottom: 8px;
}
.super-state-controls label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
  flex: 1 1 240px;
}
.super-state-controls input[type="search"] {
  height: 30px;
  padding: 4px 8px;
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
  font-size: 0.84rem;
}
.super-state-controls-actions {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}
.super-state-pre {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 10px;
  margin: 0;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.74rem;
  line-height: 1.4;
  color: #cdd6ff;
  white-space: pre;
  overflow: auto;
  flex: 1;
  min-height: 200px;
  max-height: 100%;
}

/* Diagonal red stripes on the Data tools card — same pattern blackout
   day cells use on the calendar — as an at-a-glance caution flag.
   Import / Factory reseed both replace the entire snapshot, and Export
   pulls every persisted field, so the card warrants a different visual
   weight than the routine tool cards next to it. */
.super-data-card {
  background-image: repeating-linear-gradient(
    -45deg,
    rgba(224, 68, 91, 0.06) 0,
    rgba(224, 68, 91, 0.06) 8px,
    rgba(224, 68, 91, 0.16) 8px,
    rgba(224, 68, 91, 0.16) 16px
  );
  background-color: #1a0f1c;
  border-color: rgba(224, 68, 91, 0.4);
}
.super-data-card .card-header h3 {
  color: #ffd0d0;
}
.super-data-card .card-header h3::before {
  /* Tiny warning glyph next to the title — reinforces the caution
     theme without taking up any extra layout space. */
  content: "⚠ ";
  color: rgba(255, 159, 67, 0.95);
  font-weight: 700;
}

.super-data-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0;
}
.super-data-status-ok { color: #6dd0ff; }
.super-data-status-err { color: #ffb6b6; }

/* Make the .danger button on Factory reseed visually distinct so it's
   harder to misclick. Reuses the same red palette as the existing
   bulk-shift conflict chip. */
.btn.danger {
  background: rgba(232, 96, 102, 0.22);
  border-color: rgba(232, 96, 102, 0.55);
  color: #ffd0d0;
}
.btn.danger:hover {
  background: rgba(232, 96, 102, 0.35);
  border-color: rgba(232, 96, 102, 0.75);
}

/* Server-side backup widget. Same shape as the security log table —
   filter row up top, scrollable table below. The Type column shows a
   small pill badge so scheduled vs manual vs pre-restore is readable
   at a glance. */
.super-backup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 8px 0;
}
.super-backup-wrap {
  max-height: 100%;
  overflow: auto;
}
.super-backup-table th,
.super-backup-table td {
  font-size: 0.82rem;
  padding: 6px 8px;
  vertical-align: middle;
}
.super-backup-actions-cell {
  white-space: nowrap;
  text-align: right;
}
.super-backup-actions-cell .btn-mini {
  margin-left: 4px;
}
.super-backup-type {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.super-backup-type--scheduled {
  background: rgba(31, 116, 223, 0.22);
  color: #b6d6ff;
  border: 1px solid rgba(31, 116, 223, 0.5);
}
.super-backup-type--manual {
  background: rgba(155, 95, 208, 0.22);
  color: #d8c4ff;
  border: 1px solid rgba(155, 95, 208, 0.5);
}
.super-backup-type--prerestore {
  background: rgba(255, 159, 67, 0.22);
  color: #ffc89a;
  border: 1px solid rgba(255, 159, 67, 0.55);
}

/* Schedule editor that lives between the actions row and the
   backup list. Compact fieldset with rows of label + input pairs;
   the custom-hours row toggles via [hidden]. The summary span on
   the save row carries the "Next fire ~…" hint. */
.super-backup-schedule {
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 6px;
  padding: 8px 10px 10px;
  margin: 6px 0 10px;
  background: rgba(255, 255, 255, 0.025);
}
.super-backup-schedule legend {
  padding: 0 6px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.7rem;
}
.super-backup-schedule-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 6px 0;
}
.super-backup-schedule-label {
  font-size: 0.8rem;
  color: var(--text-muted, #9aa3b2);
  min-width: 140px;
}
.super-backup-schedule-input {
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: inherit;
  border-radius: 4px;
  padding: 4px 8px;
  font-size: 0.85rem;
  min-width: 140px;
}
.super-backup-schedule-num {
  min-width: 70px;
  max-width: 90px;
}
.super-backup-schedule-custom .super-backup-schedule-input {
  flex: 1 1 220px;
  max-width: 320px;
}

/* The slim-snapshot row in the backup list. Same pill style as the
   type badges but uses the warning palette so it's clear at a
   glance that this row is post-purge (time-clock only). */
.super-backup-type--slim {
  background: rgba(120, 200, 130, 0.18);
  color: #b8e8c1;
  border: 1px solid rgba(120, 200, 130, 0.45);
}

/* API tokens widget. Same shape as the backup table — top-of-card
   action row, scrollable table below, status pill column, per-row
   revoke button. The one-time-display dialog uses a monospace
   readout so the pasted token value reads cleanly. */
.super-tokens-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 8px 0;
}
.super-tokens-wrap {
  max-height: 100%;
  overflow: auto;
}
.super-tokens-table th,
.super-tokens-table td {
  font-size: 0.82rem;
  padding: 6px 8px;
  vertical-align: middle;
}
.super-tokens-actions-cell {
  white-space: nowrap;
  text-align: right;
}
.super-token-status {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.super-token-status--active {
  background: rgba(60, 179, 113, 0.20);
  color: #b8e6c9;
  border: 1px solid rgba(60, 179, 113, 0.5);
}
.super-token-status--revoked {
  background: rgba(232, 96, 102, 0.20);
  color: #ffc4c4;
  border: 1px solid rgba(232, 96, 102, 0.5);
}
.super-token-status--expired {
  background: rgba(255, 159, 67, 0.20);
  color: #ffc89a;
  border: 1px solid rgba(255, 159, 67, 0.5);
}

/* One-time-display dialog readout. The token's a long string, so
   the monospace block needs to wrap and be selectable for the
   "select-and-copy" fallback when the Clipboard API fails. */
.super-token-label-readout {
  margin: 8px 0 4px;
  font-size: 0.86rem;
  color: var(--text);
}
.super-token-readout {
  display: flex;
  align-items: stretch;
  gap: 6px;
  margin: 8px 0 12px;
}
.super-token-readout code {
  flex: 1;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.84rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 10px;
  word-break: break-all;
  color: #cdd6ff;
  user-select: all;
}
.super-token-readout .btn-mini {
  flex: 0 0 auto;
  align-self: flex-start;
}

/* Security log widget (Supervisor Dashboard). Filter row sits at
   the top with date / type / actor controls; the table below
   flex-fills the widget body so the wider the supervisor drags
   the card, the more rows they see. */
.security-log-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
  align-items: end;
  margin-bottom: 10px;
}
.security-log-controls label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
}
.security-log-controls input[type="search"],
.security-log-controls input[type="date"],
.security-log-controls select {
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 8px;
  font-size: 0.85rem;
  min-width: 0;
}
.security-log-controls-actions {
  display: inline-flex;
  gap: 8px;
  margin-left: auto;
}

.security-log-table-wrap {
  border: 1px solid var(--line);
  border-radius: 10px;
  max-height: 360px;
  overflow: auto;
}
.custom-widget .security-log-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.security-log-table {
  width: 100%;
  font-size: 0.82rem;
}
.security-log-table thead th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}

/* Color-coded event-type pill so a supervisor can scan the column
   visually. Login/logout neutral, role/permission pink-ish, password
   warm tan, desk teal, mutation generic blue. Falls back to a
   neutral pill for any unknown type so adding new event types
   never produces an unstyled cell. */
.security-log-type {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: rgba(122, 162, 255, 0.18);
  color: #cfe0ff;
  border: 1px solid rgba(122, 162, 255, 0.35);
  white-space: nowrap;
}
.security-log-type--login,
.security-log-type--logout {
  background: rgba(193, 221, 255, 0.12);
  color: #d6e3ff;
  border-color: rgba(193, 221, 255, 0.3);
}
.security-log-type--password-change {
  background: rgba(255, 212, 170, 0.18);
  color: var(--breeze-tan);
  border-color: rgba(255, 212, 170, 0.4);
}
.security-log-type--role-add,
.security-log-type--role-remove,
.security-log-type--role-admin-change,
.security-log-type--role-permission-change,
.security-log-type--user-role-change {
  background: rgba(255, 82, 123, 0.18);
  color: #ffd2dd;
  border-color: rgba(255, 82, 123, 0.4);
}
.security-log-type--desk-add,
.security-log-type--desk-remove,
.security-log-type--desk-update,
.security-log-type--desk-assignment-run {
  background: rgba(31, 188, 196, 0.18);
  color: #b8f0f3;
  border-color: rgba(31, 188, 196, 0.35);
}

/* Time clock widget — Clock in / Clock out plus today's session
   list. Status row at the top shows a tinted dot + name + elapsed
   so the dispatcher knows at a glance whether they're on the
   clock. Today's table flex-fills the widget body (the
   .custom-widget override is already in place at the top of the
   file via the .ops-table-wrap rule). */
.time-clock-card .card-header h3 {
  margin: 0;
}

/* Supervisor time-clock widget — live status board for every
   dispatcher. Status pills color-code who's on the clock vs off
   vs not scheduled, so a supervisor can scan the column visually. */
.supervisor-time-clock-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}
.supervisor-time-clock-wrap {
  max-height: 60vh;
  overflow: auto;
}
.supervisor-time-clock-table th,
.supervisor-time-clock-table td {
  padding: 6px 8px;
  vertical-align: middle;
}
.time-clock-status-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid;
}
.time-clock-status-pill.is-open {
  background: rgba(60, 179, 113, 0.30);
  color: #b6f0cd;
  border-color: rgba(60, 179, 113, 0.7);
}
.time-clock-status-pill.is-done {
  background: rgba(31, 116, 223, 0.28);
  color: #cfe0ff;
  border-color: rgba(31, 116, 223, 0.65);
}
.time-clock-status-pill.is-pending {
  background: rgba(255, 159, 67, 0.30);
  color: #ffd2a3;
  border-color: rgba(255, 159, 67, 0.65);
}
.time-clock-status-pill.is-off {
  background: rgba(140, 150, 170, 0.18);
  color: #b8c7e7;
  border-color: rgba(140, 150, 170, 0.5);
}
.time-clock-note-cell {
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Supervisor-edited / supervisor-created time clock rows render in
   red so the dispatcher sees their times were adjusted, the
   supervisor scanning the live board can spot every fixed row, and
   payroll can audit at a glance. Strong color, not just a tint, so
   it's unambiguous. */
tr.time-clock-row-edited td,
tr.time-clock-row-edited td * {
  color: #ff8a8a !important;
}
tr.time-clock-row-edited td strong {
  color: #ffb6b6 !important;
}

/* FAA 121.465 duty-time compliance widget on the Supervisor
   Dashboard. List view of every dispatcher rule violation in the
   selected window (7 / 14 / 30 / 60 days). Errors first, warnings
   second; severity column carries a color-coded pill. Empty state
   uses a soft success tint so a clean list still feels positive. */
.duty-compliance-card .card-header { gap: 8px; }
.duty-compliance-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.duty-compliance-window {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--muted);
}
.duty-compliance-window select {
  font-size: 0.82rem;
}
.duty-compliance-wrap {
  max-height: 60vh;
  overflow: auto;
}
.duty-compliance-table th,
.duty-compliance-table td {
  padding: 6px 8px;
  vertical-align: top;
}
.duty-compliance-table td.duty-rec {
  font-size: 0.8rem;
  color: var(--muted);
  max-width: 360px;
  white-space: normal;
}
.duty-compliance-table td.duty-msg {
  max-width: 320px;
  white-space: normal;
}
.duty-compliance-table tr.is-error td {
  background: rgba(232, 96, 102, 0.08);
}
.duty-compliance-empty {
  padding: 14px 12px;
  text-align: center;
  color: #b6f0cd;
  background: rgba(60, 179, 113, 0.10);
  border: 1px solid rgba(60, 179, 113, 0.35);
  border-radius: 6px;
  font-size: 0.85rem;
}
.duty-severity-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.70rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid;
  white-space: nowrap;
}
.duty-severity-pill.is-error {
  background: rgba(232, 96, 102, 0.30);
  color: #ffc8cb;
  border-color: rgba(232, 96, 102, 0.7);
}
.duty-severity-pill.is-warning {
  background: rgba(255, 159, 67, 0.28);
  color: #ffd2a3;
  border-color: rgba(255, 159, 67, 0.65);
}
.duty-rule-pill {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.70rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: rgba(140, 150, 170, 0.15);
  color: #cfd9f0;
  border: 1px solid rgba(140, 150, 170, 0.3);
  white-space: nowrap;
}
/* Warn / danger badge variants used by the duty-compliance widget
   header. Mirrors the existing `.badge.allowed` / `.badge.denied`
   pair with an amber tint for warnings-only state. */
.badge.warn {
  color: #ffd2a3;
  border-color: rgba(255, 159, 67, 0.65);
}
.badge.danger {
  color: #ffc8cb;
  border-color: rgba(232, 96, 102, 0.7);
}

/* Daily flight coverage check widget. Lives in two places (Supervisor
   Dashboard rolling lookahead + Desk Assignment cycle strip), both
   driven by computeDailyCoverage(). Each row carries a status pill per
   pool (mainline / charter), a demand/capacity ratio, and any
   recommendation strings as a small bulleted list. */
.daily-flight-check-card .card-header { gap: 8px; }
.daily-flight-check-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.daily-flight-check-window {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--muted);
}
.daily-flight-check-window select {
  font-size: 0.82rem;
}
.daily-flight-check-wrap {
  max-height: 60vh;
  overflow: auto;
}
/* When the widget is in custom-layout mode the supervisor can drag
   the bottom-right handle to resize. Drop the static max-height,
   flex-grow into whatever vertical space the resized card gives us,
   and keep the wrap the only scroll surface so the rest of the card
   chrome (header + intro paragraph + window selector) stays pinned.
   Mirrors the exact pattern used by .security-log-table-wrap,
   .desk-assign-table-wrap, and the other primary-content tables. */
.custom-widget .daily-flight-check-wrap {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.daily-flight-check-table th,
.daily-flight-check-table td {
  padding: 6px 8px;
  vertical-align: top;
}
/* Sticky header so the column labels stay visible when the supervisor
   resizes the widget tall enough to scroll within the wrap. Same
   pattern as .security-log-table thead th. */
.daily-flight-check-table thead th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}
.daily-flight-check-table tr.is-action td {
  background: rgba(232, 96, 102, 0.08);
}
.daily-flight-check-table tr.is-watch td {
  background: rgba(255, 159, 67, 0.06);
}
.daily-flight-check-cell {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.daily-flight-check-counts {
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
}
.daily-flight-check-util {
  font-variant-numeric: tabular-nums;
}
.daily-flight-check-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.70rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid;
  white-space: nowrap;
}
.daily-flight-check-pill.is-ok {
  background: rgba(60, 179, 113, 0.20);
  color: #b6f0cd;
  border-color: rgba(60, 179, 113, 0.6);
}
.daily-flight-check-pill.is-watch {
  background: rgba(255, 159, 67, 0.28);
  color: #ffd2a3;
  border-color: rgba(255, 159, 67, 0.65);
}
.daily-flight-check-pill.is-action {
  background: rgba(232, 96, 102, 0.30);
  color: #ffc8cb;
  border-color: rgba(232, 96, 102, 0.7);
}
.daily-flight-check-rec {
  max-width: 480px;
  white-space: normal;
}
.daily-flight-check-rec-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.daily-flight-check-rec-list li {
  font-size: 0.80rem;
  line-height: 1.35;
}
.daily-flight-check-rec-tag {
  display: inline-block;
  margin-right: 6px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid;
  vertical-align: 1px;
}
.daily-flight-check-rec-tag.is-mainline {
  background: rgba(110, 193, 255, 0.20);
  color: #cfe7ff;
  border-color: rgba(110, 193, 255, 0.5);
}
.daily-flight-check-rec-tag.is-charter {
  background: rgba(95, 188, 208, 0.22);
  color: #c5ecf3;
  border-color: rgba(95, 188, 208, 0.5);
}
.daily-flight-check-empty {
  padding: 14px 12px;
  text-align: center;
  color: var(--muted);
  background: rgba(140, 150, 170, 0.08);
  border: 1px dashed rgba(140, 150, 170, 0.35);
  border-radius: 6px;
  font-size: 0.85rem;
}

/* "How to read this" expandable legend. Hidden by default, opens on
   click. Definition list pairs each term with a plain-language
   explanation; live pill / tag samples make the color codes
   self-documenting. Keeps the in-page real estate small while still
   giving supervisors a one-click reference for what the numbers mean. */
.daily-flight-check-help {
  margin: 4px 0 10px;
  padding: 0;
  font-size: 0.82rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 26, 59, 0.5);
}
.daily-flight-check-help[open] {
  background: rgba(15, 26, 59, 0.8);
}
.daily-flight-check-help summary {
  cursor: pointer;
  padding: 6px 12px;
  font-weight: 600;
  color: var(--text);
  list-style: revert;
  user-select: none;
}
.daily-flight-check-help summary:hover {
  color: #cfe7ff;
}
.daily-flight-check-legend {
  margin: 0;
  padding: 4px 12px 12px;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 14px;
  align-items: start;
}
.daily-flight-check-legend dt {
  font-weight: 600;
  color: #cfd9f0;
  white-space: nowrap;
  padding-top: 2px;
}
.daily-flight-check-legend dd {
  margin: 0;
  color: var(--muted);
  line-height: 1.45;
}
.daily-flight-check-legend dd code {
  background: rgba(140, 150, 170, 0.18);
  border: 1px solid rgba(140, 150, 170, 0.3);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.78rem;
  color: #cfe7ff;
}
.daily-flight-check-legend-sublist {
  margin: 6px 0 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.daily-flight-check-legend-sublist li {
  line-height: 1.4;
}

/* Bulk Add Shifts dialog. Three fieldsets: Dispatchers (with
   per-row shift overrides), Dates (mode-switching pattern), and
   Preview (live table with conflict / blackout / PTO flags +
   per-row Skip checkboxes). */
/* `.bulk-shift-issue` is deliberately retained — the right-click
   copy / reassign dialogs (which replaced the Bulk Add Shifts UI)
   reuse these flag-chip styles for "existing shift", "blackout",
   and "PTO" warning labels. The other .bulk-shift-* rules are gone. */
.bulk-shift-issue {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 600;
  margin-right: 4px;
  background: rgba(255, 159, 67, 0.18);
  color: #ffb37a;
  border: 1px solid rgba(255, 159, 67, 0.45);
}
.bulk-shift-issue--conflict {
  background: rgba(232, 96, 102, 0.18);
  color: #ffb6b6;
  border-color: rgba(232, 96, 102, 0.5);
}
.bulk-shift-issue--blackout {
  background: rgba(176, 24, 32, 0.4);
  color: #ffd4d4;
  border-color: rgba(255, 59, 72, 0.6);
}
.bulk-shift-issue--pto {
  background: rgba(155, 95, 208, 0.22);
  color: #d8c4ff;
  border-color: rgba(155, 95, 208, 0.5);
}

/* Right-click context menu on shift pills.
   The menu is absolutely positioned in the viewport (the JS code
   calculates x/y from the contextmenu event). We intentionally use
   position: fixed so the menu is anchored to the viewport regardless
   of the scroll position of whatever calendar/grid the shift lives in.
   z-index is high enough to clear modal backdrops would-be siblings,
   but the menu is hidden whenever a shift dialog is open. */
.shift-context-menu {
  position: fixed;
  z-index: 2000;
  min-width: 180px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shift-context-menu[hidden] { display: none; }
.shift-context-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 7px 10px;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  border: none;
  font-size: 0.88rem;
  cursor: pointer;
}
.shift-context-menu-item:hover,
.shift-context-menu-item:focus-visible {
  background: rgba(255, 255, 255, 0.07);
  outline: none;
}
.shift-context-menu-item--danger { color: #ffb6b6; }
.shift-context-menu-item--danger:hover,
.shift-context-menu-item--danger:focus-visible {
  background: rgba(232, 96, 102, 0.18);
}
.shift-context-menu-divider {
  height: 1px;
  background: var(--line);
  margin: 4px 2px;
}

/* Copy-to-dates dialog: each chosen date renders as a chip with a
   remove button and an "include anyway" checkbox that appears when
   the date is flagged (existing shift / blackout / PTO). The chip
   layout is column-stack with date + flags row on top so wrapping
   works on narrow viewports without the override checkbox getting
   pushed off-screen. */
.shift-copy-add-hint {
  margin: 0 0 8px;
}

/* Multi-month inline date picker. Three months render side-by-side
   on wide viewports and stack vertically on narrow ones via flex-wrap.
   The header row carries the prev / next arrows and the visible-range
   label. */
.shift-copy-cal-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.shift-copy-cal-range {
  flex: 1;
  text-align: center;
  font-weight: 600;
  font-size: 0.86rem;
  color: var(--text);
}
.shift-copy-cal-nav .btn-mini {
  height: 28px;
  padding: 0 10px;
  flex: 0 0 auto;
}
.shift-copy-months {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 12px;
  user-select: none;
}
.shift-copy-month {
  flex: 1 1 220px;
  min-width: 200px;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px;
}
.shift-copy-month-head {
  text-align: center;
  font-weight: 600;
  font-size: 0.84rem;
  margin-bottom: 6px;
  color: var(--text);
}
.shift-copy-month-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  margin-bottom: 4px;
}
.shift-copy-month-weekdays span {
  text-align: center;
  font-size: 0.7rem;
  color: var(--muted);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.shift-copy-month-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
/* Each date is a button so it's keyboard-focusable and gets the
   browser's native focus ring. Hover/active states are subtle so
   they don't fight with the selected/today/source highlights. */
.shift-copy-cal-cell {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--text);
  font: inherit;
  font-size: 0.78rem;
  padding: 4px 0;
  cursor: pointer;
  text-align: center;
  min-height: 26px;
}
.shift-copy-cal-cell:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.07);
}
.shift-copy-cal-cell.is-out {
  color: var(--muted);
  opacity: 0.45;
}
.shift-copy-cal-cell.is-weekend:not(.is-out) {
  background: rgba(255, 255, 255, 0.025);
}
.shift-copy-cal-cell.is-today {
  border-color: var(--accent, #1f74df);
  font-weight: 700;
}
.shift-copy-cal-cell.is-selected {
  background: rgba(31, 116, 223, 0.45);
  color: #fff;
  border-color: rgba(31, 116, 223, 0.85);
  font-weight: 600;
}
.shift-copy-cal-cell.is-selected:hover {
  background: rgba(31, 116, 223, 0.6);
}
.shift-copy-cal-cell.is-source {
  background: rgba(255, 159, 67, 0.18);
  border-color: rgba(255, 159, 67, 0.5);
  color: #ffb37a;
  cursor: not-allowed;
  font-weight: 600;
}
.shift-copy-cal-cell:disabled { cursor: not-allowed; }
.shift-copy-chips {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 260px;
  overflow-y: auto;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.18);
  margin-bottom: 10px;
}
.shift-copy-chips:empty::before {
  content: "No dates added yet — pick a date above to copy this shift to.";
  color: var(--muted);
  font-size: 0.82rem;
  padding: 8px;
}
.shift-copy-chip {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px 8px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  border-radius: 6px;
}
.shift-copy-chip-head {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.shift-copy-chip-date {
  font-weight: 600;
  font-size: 0.86rem;
}
.shift-copy-chip-remove {
  margin-left: auto;
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 2px 6px;
  border-radius: 4px;
}
.shift-copy-chip-remove:hover { color: #ffb6b6; background: rgba(232, 96, 102, 0.15); }
.shift-copy-chip-include {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0;
}
.shift-copy-chip-include input { margin: 0; }
.shift-copy-summary {
  font-size: 0.82rem;
  color: var(--muted);
  margin: 4px 0 8px;
}

.btn.btn-mini {
  padding: 3px 10px;
  font-size: 0.78rem;
}
.dialog-actions-spacer {
  flex: 1 1 auto;
}
.time-clock-report-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 12px;
  margin-bottom: 12px;
}
.time-clock-report-controls label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
}
.time-clock-report-wrap {
  max-height: 60vh;
  overflow: auto;
}
.time-clock-report-table th,
.time-clock-report-table td {
  padding: 6px 8px;
  vertical-align: middle;
}
.time-clock-status {
  margin: 6px 0 10px;
}
.time-clock-status-line {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.92rem;
}
.time-clock-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}
.time-clock-dot--on {
  background: #3cb371;
  box-shadow: 0 0 0 3px rgba(60, 179, 113, 0.18);
  animation: time-clock-dot-pulse 1.6s ease-in-out infinite;
}
.time-clock-dot--off {
  background: #6b7a99;
}
@keyframes time-clock-dot-pulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(60, 179, 113, 0.12); }
  50%      { box-shadow: 0 0 0 5px rgba(60, 179, 113, 0.32); }
}

.time-clock-actions {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.time-clock-today {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 0;
}
.custom-widget .time-clock-today {
  flex: 1 1 auto;
  min-height: 0;
}
.time-clock-today-title {
  margin: 0;
  font-size: 0.88rem;
  letter-spacing: 0.02em;
  color: #d6e3ff;
}
.time-clock-today-wrap {
  border: 1px solid var(--line);
  border-radius: 10px;
  max-height: 200px;
  overflow: auto;
}
.custom-widget .time-clock-today-wrap {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.time-clock-today-table {
  width: 100%;
  font-size: 0.85rem;
}
.time-clock-on-clock {
  color: #3cb371;
  font-weight: 600;
}

/* Badge variant — "On the clock" pill turns green so the badge
   alone reads the dispatcher's state without scanning to the
   status line. */
.badge-on-clock {
  background: rgba(60, 179, 113, 0.18);
  color: #b6f0c9;
  border-color: rgba(60, 179, 113, 0.4);
}

/* Time clock archive dialog — wider so the 5-column table breathes,
   plus filter row at the top and summary line above the table. */
.time-clock-archive-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
  align-items: end;
  margin-bottom: 10px;
}
.time-clock-archive-controls label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
}
.time-clock-archive-controls input[type="date"],
.time-clock-archive-controls select {
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 8px;
  font-size: 0.85rem;
}
.time-clock-archive-controls-actions {
  display: inline-flex;
  gap: 8px;
  margin-left: auto;
}
.time-clock-archive-summary {
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.time-clock-archive-summary strong {
  color: #d6e3ff;
}
.time-clock-archive-table-wrap {
  border: 1px solid var(--line);
  border-radius: 10px;
  max-height: 420px;
  overflow: auto;
}
.time-clock-archive-table thead th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}

/* STAR D0 Tracker — supervisor/trainer-only daily on-time
   scoreboard. Layout mirrors the Excel workbook: title, summary
   tiles, legend, editable flight table. Color palette is the
   user's spec (different from the Excel defaults):
     D0                                     → green (good)
     >15 Early · pending concurrence        → orange (action needed)
     >15 Early · YES concurrence            → amber (acceptable)
     >15 Early · NO concurrence             → dark red (bad)
     Late                                   → light red (bad-ish)
   Each row gets a left-edge color rail driven by the row class
   plus a tinted background that paints the entire row when the
   result is finalized. */

/* The STAR D0 page uses normal block flow inside the active view.
   Earlier this rule was `display: flex; height: 100%` so the card
   would fill the main content area, but the parent section's
   parent (<main>) has no defined height and `height: 100%` on a
   non-sized parent collapses to 0 — that left the page rendering
   only the title + help text + footer with everything between
   them eaten by collapsed flex children.
   Falling back to natural block flow + a fixed-height table-wrap
   gets a reliable layout that scrolls when the table overflows.
   The page itself can scroll if the summary tiles + table push
   past the viewport. */
/* Natural flow layout. The card holds: header, help, tiles, legend,
   table, actions. Each sits in its own row at its natural height.
   The table is part of the card directly — no nested scroll
   container, no resize handles. The page scrolls when the table
   pushes content past the viewport. Single scroll surface. */
#starD0View .star-d0-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.star-d0-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  /* Subtle divider so the header reads as its own band above the
     densely-packed help/tiles/legend/table stack. */
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(31, 116, 223, 0.18);
}
.star-d0-header h3 {
  margin: 0;
}
.star-d0-date-row {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-left: auto;
  font-size: 0.85rem;
}
/* Export / Print actions group at the right end of the date row.
   Small left-margin + faint divider line so they read as a separate
   "tools" group without crowding the navigation controls. */
.star-d0-date-row-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 14px;
  padding-left: 14px;
  border-left: 1px solid var(--line);
}
.star-d0-date-row label {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
}
.star-d0-date-row input[type="date"] {
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 8px;
  font-size: 0.85rem;
}
.star-d0-date-label {
  margin-left: 6px;
  font-style: italic;
}

/* Help/instructions block — small, muted, sits inside its own
   pill so it doesn't visually merge with the date row or the
   tiles below it. */
.star-d0-help {
  margin: 0;
  padding: 10px 14px;
  background: rgba(31, 116, 223, 0.06);
  border: 1px solid rgba(31, 116, 223, 0.18);
  border-radius: 8px;
  line-height: 1.5;
}

/* ===========================================================
   STAR D0 — ring-based summary (replaces the legacy 8 flat tiles).
   Two-tier layout: a hero ring (% headline) + context tile on top,
   followed by a row of 5 small status rings. Pure SVG strokes, no
   chart library. Animated arc-draw on render.
   =========================================================== */

/* Status palette — drives every ring's stroke color and the hero %
   text color. Tokenized so the rollout to other pages reuses the
   same names. Dark-theme values; light-theme overrides below. */
:root {
  --star-d0-good:    #4dd58a;
  --star-d0-watch:   #f5cc4a;
  --star-d0-bad:     #1F74DF;  /* Breeze Blue — "No Concurrence" (brand-aligned, distinct from red) */
  --star-d0-bad-dark:#ff3b48;  /* red — "Late" */
  --star-d0-pending: #a855f7;  /* vivid purple — "Pending Concurrence" (awaiting crew sign-off) */
  --star-d0-neutral: rgba(140, 175, 230, 0.7);
  --ring-track:      rgba(140, 175, 230, 0.12);
  --kpi-card-shadow: 0 10px 28px rgba(0, 0, 0, 0.32);
  --kpi-ring-glow:   0 0 24px rgba(80, 130, 220, 0.10);
}
/* (Light-theme token overrides for STAR D0 tokens removed — see top
   of file. Dark values above apply universally now.) */

/* Hero-row container: ring on the left, context on the right. */
.star-d0-summary {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.star-d0-summary-grid {
  display: grid;
  grid-template-columns: minmax(180px, auto) 1fr;
  gap: 18px;
  align-items: stretch;
}
@media (max-width: 720px) {
  .star-d0-summary-grid {
    grid-template-columns: 1fr;
  }
}

.star-d0-hero,
.star-d0-context,
.star-d0-status-rings .star-d0-ring--small {
  position: relative;
  padding: 18px 20px;
  border-radius: 14px;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.04),
    rgba(255, 255, 255, 0.00) 60%,
    rgba(0, 0, 0, 0.06)
  ),
  var(--panel);
  border: 1px solid var(--line);
  box-shadow: var(--kpi-card-shadow);
  overflow: hidden;
}
.star-d0-hero::before,
.star-d0-context::before,
.star-d0-status-rings .star-d0-ring--small::before {
  /* Faint 1px highlight along the top edge — gives the card a
     subtle glassmorphic raised feel. */
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.00),
    rgba(255, 255, 255, 0.12) 30%,
    rgba(255, 255, 255, 0.12) 70%,
    rgba(255, 255, 255, 0.00)
  );
  pointer-events: none;
}
html[data-theme="light"] .star-d0-hero::before,
html[data-theme="light"] .star-d0-context::before,
html[data-theme="light"] .star-d0-status-rings .star-d0-ring--small::before {
  display: none;
}

/* Hero ring sizing + centered overlay. */
.star-d0-hero {
  display: flex;
  align-items: center;
  justify-content: center;
}
.star-d0-ring {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.star-d0-ring-host {
  position: relative;
  display: inline-block;
}
.star-d0-ring-svg {
  display: block;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.25));
}
html[data-theme="light"] .star-d0-ring-svg {
  filter: drop-shadow(0 2px 4px rgba(20, 30, 60, 0.08));
}
.star-d0-ring-track {
  stroke: var(--ring-track);
}
.star-d0-ring-arc {
  /* Animated arc draw: pre-state has dash 0, target stored in
     --target-dash. Removing .is-pre transitions to the target. */
  stroke-dasharray: 0 99999;
  transition: stroke-dasharray 0.25s ease-out;
}
.star-d0-ring-arc:not(.is-pre) {
  stroke-dasharray: var(--target-dash);
}
.star-d0-ring-arc.is-good      { stroke: var(--star-d0-good); }
.star-d0-ring-arc.is-watch     { stroke: var(--star-d0-watch); }
.star-d0-ring-arc.is-bad       { stroke: var(--star-d0-bad); }
.star-d0-ring-arc.is-bad-dark  { stroke: var(--star-d0-bad-dark); }
.star-d0-ring-arc.is-pending   { stroke: var(--star-d0-pending); }
.star-d0-ring-arc.is-neutral   { stroke: var(--star-d0-neutral); }

/* Centered label stack inside each ring's donut hole. Subtle inner
   glow (dark theme only) keeps the center feeling "lit". */
.star-d0-ring-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  pointer-events: none;
  text-align: center;
}
.star-d0-ring--hero .star-d0-ring-center::before {
  content: "";
  position: absolute;
  inset: 18%;
  border-radius: 50%;
  box-shadow: var(--kpi-ring-glow);
  pointer-events: none;
}
.star-d0-ring-pct {
  font-size: 1.95rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.star-d0-ring-pct.is-good      { color: var(--star-d0-good); }
.star-d0-ring-pct.is-watch     { color: var(--star-d0-watch); }
.star-d0-ring-pct.is-bad       { color: var(--star-d0-bad); }
.star-d0-ring-pct-label {
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}

/* Small-ring count + share-of-total stack. */
.star-d0-ring-count {
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.star-d0-ring-share {
  font-size: 0.66rem;
  color: var(--muted);
  font-weight: 600;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}
.star-d0-ring-label {
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  text-align: center;
  max-width: 120px;
  line-height: 1.2;
}

/* Context tile (Status + Total flights + Today's distribution).
   Three sections stacked top→bottom: header (Status + Total side by
   side), divider, then a stacked-bar distribution with a compact
   legend so the dead vertical space gets put to work. */
.star-d0-context {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 12px;
}
.star-d0-context-top {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.star-d0-context-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.star-d0-context-label {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}
.star-d0-context-value {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.star-d0-context-value.is-done {
  color: var(--star-d0-good);
}
.star-d0-context-divider {
  height: 1px;
  background: linear-gradient(
    90deg,
    rgba(255,255,255,0.00),
    rgba(255,255,255,0.10) 35%,
    rgba(255,255,255,0.10) 65%,
    rgba(255,255,255,0.00)
  );
}
html[data-theme="light"] .star-d0-context-divider {
  background: linear-gradient(
    90deg,
    rgba(20,30,60,0.00),
    rgba(20,30,60,0.10) 35%,
    rgba(20,30,60,0.10) 65%,
    rgba(20,30,60,0.00)
  );
}

/* Distribution row — header + horizontal stacked bar + legend.
   Each segment's flex-grow is set inline to its count, so segment
   widths are proportional to their share of "with result" total. */
.star-d0-context-dist {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.star-d0-context-dist-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.star-d0-context-dist-total {
  font-size: 0.7rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.star-d0-dist-bar {
  display: flex;
  height: 12px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--ring-track);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.18);
}
.star-d0-dist-seg {
  display: block;
  height: 100%;
  transition: flex-grow 0.25s ease-out;
}
.star-d0-dist-seg.is-good      { background: var(--star-d0-good); }
.star-d0-dist-seg.is-watch     { background: var(--star-d0-watch); }
.star-d0-dist-seg.is-bad       { background: var(--star-d0-bad); }
.star-d0-dist-seg.is-bad-dark  { background: var(--star-d0-bad-dark); }
.star-d0-dist-seg.is-pending   { background: var(--star-d0-pending); }
.star-d0-dist-empty {
  display: block;
  width: 100%;
  height: 100%;
}

/* Legend chips — colored dot + label + count. Wraps under the bar
   on narrow viewports. */
.star-d0-dist-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
}
.star-d0-dist-legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.74rem;
  color: var(--muted);
}
.star-d0-dist-legend-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.star-d0-dist-legend-dot.is-good      { background: var(--star-d0-good); }
.star-d0-dist-legend-dot.is-watch     { background: var(--star-d0-watch); }
.star-d0-dist-legend-dot.is-bad       { background: var(--star-d0-bad); }
.star-d0-dist-legend-dot.is-bad-dark  { background: var(--star-d0-bad-dark); }
.star-d0-dist-legend-dot.is-pending   { background: var(--star-d0-pending); }
.star-d0-dist-legend-text {
  color: var(--text);
}
.star-d0-dist-legend-count {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* Status rings row — auto-fit responsive grid. */
.star-d0-status-rings {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
}
.star-d0-ring--small {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 16px 12px 14px;
}
.star-d0-tile {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(31, 116, 223, 0.08);
  padding: 6px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.star-d0-tile-label {
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}
.star-d0-tile-value {
  font-size: 1.25rem;
  font-weight: 700;
  color: #d6e3ff;
  line-height: 1.1;
}
.star-d0-tile--status.is-done .star-d0-tile-value {
  color: #6ee0a3;
}
.star-d0-tile--d0 {
  background: rgba(60, 179, 113, 0.28);
  border-color: rgba(60, 179, 113, 0.7);
}
.star-d0-tile--d0 .star-d0-tile-value { color: #b6f0cd; }
.star-d0-tile--early-yes {
  background: rgba(240, 196, 58, 0.28);
  border-color: rgba(240, 196, 58, 0.7);
}
.star-d0-tile--early-yes .star-d0-tile-value { color: #ffe28a; }
.star-d0-tile--early-pending {
  background: rgba(168, 85, 247, 0.32);
  border-color: rgba(168, 85, 247, 0.75);
}
.star-d0-tile--early-pending .star-d0-tile-value { color: #e0c8ff; }
.star-d0-tile--early-no {
  background: rgba(31, 116, 223, 0.36);
  border-color: rgba(31, 116, 223, 0.85);
}
.star-d0-tile--early-no .star-d0-tile-value { color: #bfdbfe; }
.star-d0-tile--late {
  background: rgba(176, 24, 32, 0.50);
  border-color: rgba(255, 59, 72, 0.75);
}
.star-d0-tile--late .star-d0-tile-value { color: #ffd4d4; }
.star-d0-tile--pct {
  background: rgba(0, 22, 51, 0.72);
  border-color: rgba(193, 221, 255, 0.45);
}
.star-d0-tile--pct .star-d0-tile-value { color: #C1DDFF; }

/* Legend strip — quick-reference color chips below the summary.
   Sits in its own subtle band so it visually separates the tiles
   above from the editable grid below. */
.star-d0-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 12px;
  background: rgba(10, 31, 37, 0.6);
  border: 1px solid rgba(31, 116, 223, 0.18);
  border-radius: 8px;
}
.star-d0-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  border: 1px solid;
}
.star-d0-chip--d0 {
  background: rgba(60, 179, 113, 0.42);
  color: #d6f7e3;
  border-color: rgba(60, 179, 113, 0.9);
}
.star-d0-chip--early-yes {
  background: rgba(240, 196, 58, 0.42);
  color: #fff1b8;
  border-color: rgba(240, 196, 58, 0.9);
}
.star-d0-chip--early-no {
  background: rgba(31, 116, 223, 0.48);
  color: #bfdbfe;
  border-color: rgba(31, 116, 223, 0.95);
}
.star-d0-chip--early-pending {
  background: rgba(168, 85, 247, 0.46);
  color: #e0c8ff;
  border-color: rgba(168, 85, 247, 0.9);
}
.star-d0-chip--late {
  background: rgba(176, 24, 32, 0.62);
  color: #ffe6e6;
  border-color: rgba(255, 59, 72, 0.9);
}

/* Table — every cell is editable except Min vs ETD and Result
   which compute live. Inputs strip their default chrome and
   inherit the row tint from the row class. The table sits
   directly on the card; no inner scroll wrapper.

   `table-layout: fixed` is REQUIRED — without it the browser
   sizes columns by their auto-content width. Most cells contain
   empty <input>/<textarea> elements which have no natural
   max-content, so columns collapse to ~0 px and stack the
   visible cells on top of each other. With fixed layout the
   <colgroup> below dictates each column's width directly. */
.star-d0-table {
  width: 100%;
  font-size: 0.85rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
}
/* Sortable column headers — click to sort. Cursor + hover cue make
   the affordance obvious; the active column gets the accent color and
   shows a ▲/▼ arrow (painted by updateStarD0SortIndicators). */
.star-d0-table th.star-d0-sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.star-d0-table th.star-d0-sortable:hover {
  color: var(--accent, #4aa3ff);
}
.star-d0-table th.star-d0-sortable.is-sorted {
  color: var(--accent, #4aa3ff);
}
.star-d0-table th .star-d0-sort-ind {
  font-size: 0.7em;
  letter-spacing: -1px;
}
/* Column widths — match the order of <th> in the thead:
   #, Flight #, City Pair, ETD, Block Out, Min vs ETD, Result,
   Concurrence, Remarks. Percentages so the table balances across
   the full card width on any viewport rather than bunching all
   the data columns to the left and giving Remarks a giant
   single-line trough. The fixed minimums via `min-width` on the
   small columns prevent collapse on very narrow screens. */
.star-d0-table > colgroup col:nth-child(1) { width: 3%;  min-width: 40px;  }
.star-d0-table > colgroup col:nth-child(2) { width: 8%;  min-width: 80px;  }
.star-d0-table > colgroup col:nth-child(3) { width: 10%; min-width: 90px;  }
.star-d0-table > colgroup col:nth-child(4) { width: 7%;  min-width: 70px;  }
.star-d0-table > colgroup col:nth-child(5) { width: 8%;  min-width: 90px;  }
.star-d0-table > colgroup col:nth-child(6) { width: 9%;  min-width: 90px;  }
.star-d0-table > colgroup col:nth-child(7) { width: 10%; min-width: 110px; }
.star-d0-table > colgroup col:nth-child(8) { width: 13%; min-width: 130px; }
.star-d0-table > colgroup col:nth-child(9) { width: 32%; min-width: 240px; }
.star-d0-table thead th {
  position: sticky;
  /* `-1px` instead of `0` so the th covers the sub-pixel sliver
     that some browsers leave between the sticky boundary and the
     viewport top. With `top: 0`, scrolling sometimes shows ~1 px
     of the row-color stripe peeking above the header band. */
  top: -1px;
  background: var(--panel);
  /* Bumped from 1 to 5 so the sticky thead reliably paints over
     anything else with default stacking, including any
     pseudo-elements or focused-input shadows that might otherwise
     bleed through. */
  z-index: 5;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 10px 8px;
  text-align: left;
  /* Thicker bottom edge so the header reads as a clear band, plus
     a top inset shadow that fills the same -1 px we offset above
     so there's no visible gap when the user scrolls into the
     stuck position. */
  border-bottom: 1px solid var(--line);
  box-shadow: 0 -1px 0 #0d142f;
}
.star-d0-table td {
  /* Roomier vertical padding so each row breathes without
     visually fusing into the next. Combined with line-height
     1.35 the table reads more like a spreadsheet than a
     compressed grid. */
  padding: 6px 8px;
  vertical-align: middle;
  line-height: 1.35;
}
/* Row-number column — narrow, muted, monospace so the supervisor
   can scan up/down the grid like the Excel row labels. */
.star-d0-rownum-col,
.star-d0-rownum {
  width: 36px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  color: var(--muted);
  background: rgba(193, 221, 255, 0.04);
  user-select: none;
}

.star-d0-input {
  width: 100%;
  background: transparent;
  color: var(--text);
  border: 1px solid transparent;
  border-radius: 4px;
  /* Tightest reasonable input padding — keeps the cell height at
     ~20px so we hit 35+ visible rows. The focus ring still draws
     cleanly because it's a 1px outline rather than a thick border. */
  padding: 1px 5px;
  font: inherit;
  font-size: 0.85rem;
  line-height: 1.15;
}
.star-d0-input:hover {
  border-color: rgba(193, 221, 255, 0.18);
}
.star-d0-input:focus {
  outline: none;
  border-color: var(--blue);
  background: var(--chip-bg);
  box-shadow: 0 0 0 1px var(--blue);
}
.star-d0-input--time {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.05em;
  text-align: center;
  max-width: 80px;
}
.star-d0-input--flight {
  max-width: 100px;
  font-weight: 600;
}
.star-d0-input--pair {
  max-width: 110px;
  text-transform: uppercase;
}
/* Remarks is a textarea so multi-line notes can wrap and the cell
   grows to fit. Two layers of auto-grow:
   1. `field-sizing: content` (Chrome 123+, Safari 18.4+) — native
      auto-grow with zero JS in modern browsers.
   2. `autoGrowStarD0Remarks()` JS fallback — sets the textarea's
      explicit height after computing scrollHeight, used in browsers
      without field-sizing (Firefox today). Box-sizing matches the
      page's global border-box default so the math doesn't disagree
      across mixed contexts. */
textarea.star-d0-input--remarks {
  display: block;
  width: 100%;
  resize: none;
  overflow: hidden;
  min-height: 28px;
  line-height: 1.4;
  font-family: inherit;
  white-space: pre-wrap;
  word-break: break-word;
  box-sizing: border-box;
  field-sizing: content;
  /* Match the surrounding row padding so the textarea visually
     aligns with adjacent inputs/values. */
  padding: 4px 6px;
}
.star-d0-row td.star-d0-cell--remarks {
  vertical-align: top;
}
/* Remarks column already absorbs remaining width via the colgroup
   col:nth-child(9) `width: auto` rule above — no extra td/th rule
   needed under `table-layout: fixed`. */
.star-d0-conc {
  width: 72px;
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 2px 6px;
  font-size: 0.82rem;
  cursor: pointer;
}
.star-d0-conc:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.star-d0-min,
.star-d0-result {
  font-variant-numeric: tabular-nums;
  text-align: center;
  font-weight: 600;
  font-size: 0.85rem;
}
.star-d0-remove {
  font-size: 1rem;
  font-weight: 600;
  padding: 2px 8px;
  color: #ff8a90;
}
.star-d0-empty {
  text-align: center;
  padding: 22px;
}

/* Row-level color rules — paint the whole row so a glance down
   the table reads as a status board. The left-edge gets a thick
   accent rail and the row background sits at high enough opacity
   to clearly stand out against the dark navy table chrome. */
.star-d0-row td:first-child {
  border-left: 6px solid transparent;
}
.star-d0-row--d0 {
  background: rgba(60, 179, 113, 0.32);
}
.star-d0-row--d0 td:first-child { border-left-color: #3cb371; }
.star-d0-row--early-yes {
  background: rgba(240, 196, 58, 0.34);
}
.star-d0-row--early-yes td:first-child { border-left-color: #f0c43a; }
.star-d0-row--early-pending {
  background: rgba(168, 85, 247, 0.40);
}
.star-d0-row--early-pending td:first-child { border-left-color: #a855f7; }
.star-d0-row--early-no {
  background: rgba(31, 116, 223, 0.42);
}
.star-d0-row--early-no td:first-child { border-left-color: #1F74DF; }
.star-d0-row--late {
  background: rgba(176, 24, 32, 0.62);
}
.star-d0-row--late td:first-child { border-left-color: #ff3b48; }

/* Color the result cell text per state too — bolder visual
   reinforcement for the supervisor scanning the column. Bumped
   to higher-contrast tones that still read on the colored row
   background. */
.star-d0-row--d0 .star-d0-result {
  color: #b6f0cd;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}
.star-d0-row--early-yes .star-d0-result {
  color: #ffe28a;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}
.star-d0-row--early-pending .star-d0-result {
  color: #e0c8ff;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}
.star-d0-row--early-no .star-d0-result {
  color: #bfdbfe;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.45);
}
.star-d0-row--late .star-d0-result {
  color: #ffeaea;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.45);
}

.star-d0-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  /* Top divider so the Export / Print buttons don't visually fuse
     with the resize-frame above them. */
  padding-top: 12px;
  border-top: 1px solid rgba(31, 116, 223, 0.18);
}
.star-d0-actions .muted {
  margin-left: auto;
}

/* Print — single-table sheet, black on white, hide everything
   except the STAR view. Mirrors the assignment-print pattern.
   Earlier version used `body.printing-stard0 > *:not(#starD0View)`
   which assumed #starD0View was a DIRECT body child. It isn't —
   the section sits inside .app-shell > main > section. The
   "hide direct body children except #starD0View" rule was therefore
   hiding .app-shell (which contains everything) and the print came
   out blank. Switched to an explicit hide-list of just the chrome
   we don't want to print: nav bars, sidebar, topbar, sticky pieces,
   plus every OTHER .view inside .main so only the STAR D0 section
   renders. */
@media print {
  body.printing-stard0 nav,
  body.printing-stard0 .sidebar,
  body.printing-stard0 .sidebar-backdrop,
  body.printing-stard0 .topbar,
  body.printing-stard0 .topbar-nav,
  body.printing-stard0 .top-nav,
  body.printing-stard0 .hamburger-btn,
  body.printing-stard0 #loginScreen,
  body.printing-stard0 .view:not(#starD0View) {
    display: none !important;
  }
  body.printing-stard0 .app-shell,
  body.printing-stard0 .main {
    display: block !important;
    margin: 0 !important;
    padding: 0 !important;
    min-height: 0 !important;
  }
  body.printing-stard0 #starD0View {
    display: block !important;
    margin-top: 0 !important;
  }
  body.printing-stard0 #starD0View .star-d0-card {
    background: white !important;
    color: black !important;
    border: 0 !important;
    padding: 0 !important;
    box-shadow: none !important;
  }
  body.printing-stard0 .star-d0-actions,
  body.printing-stard0 .star-d0-remove,
  body.printing-stard0 .star-d0-date-row button {
    display: none !important;
  }
  body.printing-stard0 .star-d0-table,
  body.printing-stard0 .star-d0-table th,
  body.printing-stard0 .star-d0-table td {
    background: white !important;
    color: black !important;
    border-color: #999 !important;
  }
  body.printing-stard0 .star-d0-input {
    background: transparent !important;
    border: 0 !important;
    color: black !important;
  }
  body.printing-stard0 .star-d0-tile {
    background: #f4f4f4 !important;
    color: black !important;
    border-color: #999 !important;
  }
  body.printing-stard0 .star-d0-tile-label,
  body.printing-stard0 .star-d0-tile-value {
    color: black !important;
  }
  body.printing-stard0 {
    height: auto !important;
    overflow: visible !important;
  }
}

/* Passdown report dialog. Minimal text-dump dialog for the
   "DD: NN" desk-flight summary supervisors paste into shift
   passdowns. Monospace font in the textarea so the colon-aligned
   counts read like a column. Status pill flexes left of the action
   buttons so the dialog action row reads "[Copied to clipboard]
   [Copy] [Close]" left-to-right. */
.passdown-text {
  width: 100%;
  background: var(--field-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92rem;
  line-height: 1.45;
  resize: vertical;
  min-height: 200px;
  margin-bottom: 10px;
}
.passdown-text:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 2px var(--view-accent-soft, rgba(58, 111, 240, 0.25));
}
.passdown-status {
  margin-right: auto;
  font-size: 0.82rem;
  color: var(--breeze-tan);
  align-self: center;
}

/* Inline action links in the Operations control panel's Employees
   block header — "Manage roles…" and "Merge users…". Visually
   subordinate to the block title so they read as quiet shortcuts
   rather than competing controls. Right-aligned so they sit in the
   block header's flow without pushing the title around. */
.ops-block-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}
.ops-block-actions {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--muted);
}
.ops-block-actions .btn-link {
  font-size: inherit;
  color: var(--muted);
}
.ops-block-actions .btn-link:hover {
  color: #cfe0ff;
}
.ops-block-actions-sep {
  color: var(--line);
}

/* Per-row Edit/Remove cluster on the extended Employees table —
   needs a little spacing so the two link buttons don't run together. */
.ops-emp-actions .btn-link + .btn-link {
  margin-left: 10px;
}

/* Attention pulse on action buttons. Used by the Reassignment plan
   button when a fresh callout-driven diff is ready and the
   supervisor hasn't viewed it yet. Subtle so it reads as "there's
   something here" without being alarmist; clears the moment the
   dialog opens. */
.btn-attn {
  border-color: var(--breeze-tan);
  box-shadow: 0 0 0 2px rgba(255, 212, 170, 0.18);
  animation: btn-attn-pulse 1.6s ease-in-out infinite;
}

@keyframes btn-attn-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(255, 212, 170, 0.12); }
  50%      { box-shadow: 0 0 0 4px rgba(255, 212, 170, 0.32); }
}

/* Reassignment toast — momentary banner that surfaces a sticky-mode
   redistribution. Appears at the top of the Desk Assignment view, fades
   in via .visible, auto-dismisses after 30s. Uses the same Breeze tan
   accent family as the attention pulse so the visual language is
   consistent: "supervisor, look here." */
.reassignment-toast {
  display: none;
  align-items: center;
  gap: 12px;
  margin: 8px 0 12px;
  padding: 10px 14px;
  border-radius: 8px;
  background: linear-gradient(
    90deg,
    rgba(255, 212, 170, 0.18),
    rgba(255, 212, 170, 0.08)
  );
  border: 1px solid rgba(255, 212, 170, 0.55);
  color: #f4e6d2;
  font-size: 0.88rem;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
  animation: reassign-toast-in 0.35s ease-out;
}

.reassignment-toast.visible {
  display: flex;
}

/* Dispatcher notification toast — fixed-positioned floating toast.
   Shows over any view (Scheduler, Universal Dashboard, etc.) when a
   new personal-inbox notification arrives. Auto-hides after 6 s. */
.dispatcher-notif-toast {
  position: fixed;
  top: 18px;
  right: 18px;
  z-index: 9999;
  display: none;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  min-width: 280px;
  max-width: 420px;
  border-radius: 10px;
  background: linear-gradient(
    135deg,
    rgba(36, 80, 162, 0.96),
    rgba(28, 56, 116, 0.96)
  );
  border: 1px solid rgba(140, 180, 240, 0.55);
  color: #f0f4ff;
  font-size: 0.88rem;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.32);
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 0.28s ease-out, transform 0.28s ease-out;
}
.dispatcher-notif-toast.visible {
  display: flex;
  opacity: 1;
  transform: translateY(0);
}
.dispatcher-notif-toast-icon {
  font-size: 1.3rem;
  line-height: 1;
  flex-shrink: 0;
}
.dispatcher-notif-toast-body {
  flex: 1;
  min-width: 0;
}
.dispatcher-notif-toast-title {
  font-weight: 600;
  margin-bottom: 2px;
  color: #ffffff;
}
.dispatcher-notif-toast-detail {
  font-size: 0.82rem;
  color: rgba(240, 244, 255, 0.85);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dispatcher-notif-toast-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.dispatcher-notif-toast-close {
  background: transparent;
  border: none;
  color: rgba(240, 244, 255, 0.7);
  cursor: pointer;
  font-size: 1rem;
  padding: 4px 8px;
  border-radius: 4px;
}
.dispatcher-notif-toast-close:hover {
  background: rgba(255, 255, 255, 0.1);
  color: #ffffff;
}

/* ----------------------------------------------------------------------
   Unified toast stack. Bottom-right fixed container; pushToast() appends
   .toast nodes into it. column-reverse so the newest toast sits nearest
   the corner and older ones stack upward. The container ignores pointer
   events; individual toasts re-enable them so their buttons stay
   clickable without the empty stack area blocking clicks underneath.
   ---------------------------------------------------------------------- */
.toast-stack {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 9999;
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-end;
  gap: 10px;
  pointer-events: none;
  max-width: 440px;
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  min-width: 280px;
  max-width: 420px;
  border-radius: 10px;
  /* Default (info) — the same blue gradient as the legacy notif toast. */
  background: linear-gradient(
    135deg,
    rgba(36, 80, 162, 0.96),
    rgba(28, 56, 116, 0.96)
  );
  border: 1px solid rgba(140, 180, 240, 0.55);
  color: #f0f4ff;
  font-size: 0.88rem;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.32);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.28s ease-out, transform 0.28s ease-out;
}
.toast.visible {
  opacity: 1;
  transform: translateY(0);
}
.toast--warn {
  background: linear-gradient(
    135deg,
    rgba(176, 120, 36, 0.96),
    rgba(120, 80, 24, 0.96)
  );
  border-color: rgba(255, 212, 140, 0.6);
  color: #fff4e2;
}
.toast--error {
  background: linear-gradient(
    135deg,
    rgba(176, 48, 64, 0.96),
    rgba(120, 28, 40, 0.96)
  );
  border-color: rgba(255, 160, 170, 0.6);
  color: #ffeaed;
}
.toast--success {
  background: linear-gradient(
    135deg,
    rgba(36, 142, 96, 0.96),
    rgba(24, 96, 66, 0.96)
  );
  border-color: rgba(150, 240, 200, 0.6);
  color: #e8fff4;
}
.toast-icon {
  font-size: 1.3rem;
  line-height: 1;
  flex-shrink: 0;
}
.toast-body {
  flex: 1;
  min-width: 0;
}
.toast-title {
  font-weight: 600;
  margin-bottom: 2px;
  color: #ffffff;
}
.toast-detail {
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.85);
  /* Allow up to two lines, then ellipsize — shift/clock summaries can
     run a little long. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.toast-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.toast-close {
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  font-size: 1rem;
  padding: 4px 8px;
  border-radius: 4px;
}
.toast-close:hover {
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
}

/* Keep a card clear of the sticky topbar when a notification's "Open"
   action scrolls to it (scrollIntoView block:start). Without this the
   card's header lands underneath the sticky bar. */
.card {
  scroll-margin-top: 96px;
}

@keyframes reassign-toast-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.reassignment-toast-icon {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 212, 170, 0.28);
  color: var(--breeze-tan, #ffd4aa);
  font-size: 1.1rem;
  font-weight: 700;
}

.reassignment-toast-body {
  flex: 1 1 auto;
  min-width: 0;
}

.reassignment-toast-title {
  font-weight: 700;
  font-size: 0.92rem;
  color: #fff;
  margin-bottom: 2px;
}

.reassignment-toast-detail {
  color: rgba(244, 230, 210, 0.85);
  font-size: 0.82rem;
}

.reassignment-toast-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
}

.reassignment-toast-close {
  background: transparent;
  border: 0;
  color: rgba(244, 230, 210, 0.7);
  font-size: 1rem;
  line-height: 1;
  padding: 4px 6px;
  border-radius: 4px;
  cursor: pointer;
}

.reassignment-toast-close:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
}

/* Right-click "Mark as callout / no-show" menu — floating absolute
   panel positioned at the cursor on contextmenu over a desk-assign
   row. Looks like a native context menu without the OS chrome:
   compact, dark, slight shadow, two action buttons + a header strip
   identifying which dispatcher we're about to mark out. */
.desk-assign-ctx-menu {
  position: fixed;
  z-index: 9999;
  min-width: 200px;
  padding: 4px 0;
  background: var(--surface-2);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  color: #f4e6d2;
  font-size: 0.86rem;
}

.desk-assign-ctx-menu[hidden] {
  display: none;
}

.desk-assign-ctx-menu-header {
  padding: 8px 12px;
  font-weight: 600;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  margin-bottom: 4px;
}

.desk-assign-ctx-menu-header .muted {
  color: rgba(244, 230, 210, 0.55);
  font-weight: 400;
  font-size: 0.78rem;
  margin-left: 4px;
}

.desk-assign-ctx-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  padding: 8px 14px;
  color: inherit;
  font-size: inherit;
  cursor: pointer;
}

.desk-assign-ctx-menu-item:hover {
  background: rgba(255, 212, 170, 0.16);
  color: #fff;
}

.desk-assign-ctx-menu-item.is-danger {
  color: #ff8a8a;
}

.desk-assign-ctx-menu-item.is-danger:hover {
  background: rgba(255, 80, 80, 0.18);
  color: #fff;
}

.desk-assign-ctx-menu-sep {
  height: 1px;
  margin: 4px 8px;
  background: rgba(255, 255, 255, 0.08);
}

/* Faint hover affordance on right-clickable rows so the user notices
   they're interactive even before they try the contextmenu. */
tr.desk-assign-row {
  cursor: context-menu;
}

tr.desk-assign-row:hover td {
  background: rgba(255, 255, 255, 0.03);
}

/* Click-to-expand affordance — pointer cursor on rows that can
   reveal their per-desk flight list. Wins over the context-menu
   cursor when both classes apply (later rule, same specificity). */
tr.desk-assign-expandable {
  cursor: pointer;
}

tr.desk-assign-expandable.is-expanded td:first-child::before {
  content: "▾ ";
  color: var(--breeze-tan, #ffd4aa);
  font-size: 0.7rem;
}

tr.desk-assign-expandable:not(.is-expanded) td:first-child::before {
  content: "▸ ";
  color: rgba(244, 230, 210, 0.45);
  font-size: 0.7rem;
}

/* Per-desk flight detail row — sits inside the same table, spans all
   six columns, contains a compact sub-table of the flights currently
   assigned to that desk. Background is slightly inset from the parent
   so the relationship is visually clear. */
tr.desk-assign-detail > td.desk-assign-detail-cell {
  padding: 0;
  background: rgba(255, 255, 255, 0.02);
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.desk-assign-detail-wrap {
  padding: 8px 12px 12px 28px;
}

/* Stats line at the top of the inline expand row — mirrors the
   "N flights · peak X @ HH:MM Z" summary the Show Assignments
   dialog renders above each desk's flight table. */
.desk-assign-detail-summary {
  margin-bottom: 6px;
  color: rgba(244, 230, 210, 0.7);
}

.desk-assign-detail-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
}

.desk-assign-detail-table th {
  text-align: left;
  font-weight: 600;
  color: rgba(244, 230, 210, 0.65);
  padding: 4px 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.desk-assign-detail-table td {
  padding: 4px 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.03);
  color: rgba(244, 230, 210, 0.9);
}

.desk-assign-detail-table tr:last-child td {
  border-bottom: 0;
}

/* Card subtitle — single short line that sits between the .card-header
   row and the descriptive paragraph. Used by Today's desk assignments
   widgets to show the date(s) the data is for. Inherits .muted .small
   for color/size; the rule here just nudges the spacing so it doesn't
   crowd the H3 above or the description below. */
.card-subtitle {
  margin: -4px 0 8px;
  line-height: 1.35;
}

.card-subtitle strong {
  color: rgba(244, 230, 210, 0.85);
  font-weight: 600;
}

.card-subtitle-sep {
  margin: 0 6px;
  opacity: 0.5;
}

/* ============================================================
   Dispatch Suggested Passdown — read-only grid (Universal) +
   editable day-tabbed editor (Desk Assignments).
   ============================================================ */

.passdown-guide-grid-wrap {
  overflow-x: auto;
}

.passdown-guide-grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
}

.passdown-guide-grid th {
  background: rgba(31, 116, 223, 0.14);
  color: #cfe1ff;
  font-weight: 600;
  padding: 6px 8px;
  text-align: center;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.passdown-guide-grid td {
  padding: 4px 8px;
  text-align: center;
  border: 1px solid rgba(255, 255, 255, 0.05);
  color: rgba(244, 230, 210, 0.9);
  white-space: nowrap;
}

.passdown-guide-grid td.passdown-empty {
  background: rgba(0, 0, 0, 0.12);
}

.passdown-arrow {
  color: var(--breeze-tan, #ffd4aa);
  font-weight: 700;
  margin: 0 2px;
}

.passdown-guide-tabs {
  display: flex;
  gap: 4px;
  margin: 8px 0;
  flex-wrap: wrap;
}

.passdown-guide-tabs .tab.is-active {
  background: rgba(255, 212, 170, 0.18);
  color: #fff;
  border-color: var(--breeze-tan, #ffd4aa);
}

.passdown-guide-editor {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
}

.passdown-edit-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.passdown-edit-row input {
  background: rgba(0, 0, 0, 0.20);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 4px;
  color: inherit;
  padding: 5px 8px;
  font: inherit;
}

.passdown-edit-from {
  width: 70px;
  flex: 0 0 auto;
}

.passdown-edit-to {
  flex: 1 1 auto;
  min-width: 0;
}

.passdown-edit-delete {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid rgba(255, 122, 122, 0.4);
  color: #ff8a8a;
  border-radius: 4px;
  width: 28px;
  height: 28px;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
}

.passdown-edit-delete:hover {
  background: rgba(255, 80, 80, 0.18);
  color: #fff;
}

/* ------------------------------------------------------------------ */
/* Schedule-adjusted passdown panel (Universal Dashboard).            */
/* ------------------------------------------------------------------ */
.passdown-adjusted {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.10);
}
.passdown-adjusted-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.passdown-adjusted-head h4 {
  margin: 0;
  font-size: 0.95rem;
}
.passdown-adjusted-datelabel {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
  color: var(--muted, #9aa3b2);
}
.passdown-adjusted-date {
  background: rgba(0, 0, 0, 0.20);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 4px;
  color: inherit;
  padding: 4px 6px;
  font: inherit;
}
.passdown-adjusted-summary {
  margin: 6px 0 8px;
}
.passdown-adjusted-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-height: 320px;
  overflow-y: auto;
}
.passdown-adjusted-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 4px 8px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.03);
  font-size: 0.86rem;
}
.passdown-adjusted-row.is-reroute { background: rgba(255, 196, 58, 0.10); }
.passdown-adjusted-row.is-dropped { background: rgba(224, 68, 91, 0.08); }
.passdown-adjusted-row.is-unresolved { background: rgba(224, 68, 91, 0.12); }

/* Out (missing) desk code — red + struck through so it's obviously gone. */
.passdown-out {
  color: #ff8a8a;
  font-weight: 700;
  text-decoration: line-through;
  text-decoration-thickness: 2px;
}
.passdown-adjusted-row .passdown-from { font-weight: 700; min-width: 38px; }
.passdown-adjusted-row .passdown-to { font-weight: 600; }
.passdown-adjusted-empty { list-style: none; padding: 6px 4px; }

.passdown-adjusted-legend {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}
.passdown-tag {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.4;
  padding: 1px 7px;
  border-radius: 999px;
  white-space: nowrap;
}
.passdown-tag--reroute   { background: rgba(255, 196, 58, 0.18); color: #ffd98a; border: 1px solid rgba(255, 196, 58, 0.4); }
.passdown-tag--dropped   { background: rgba(255, 255, 255, 0.06); color: #c2c8d2; border: 1px solid rgba(255, 255, 255, 0.18); }
.passdown-tag--out       { background: rgba(224, 68, 91, 0.20); color: #ffb6c0; border: 1px solid rgba(224, 68, 91, 0.55); }
.passdown-tag--double    { background: rgba(95, 188, 208, 0.18); color: #9fe0ef; border: 1px solid rgba(95, 188, 208, 0.45); }
.passdown-tag--cover     { background: rgba(60, 179, 113, 0.18); color: #9be9b9; border: 1px solid rgba(60, 179, 113, 0.45); }
.passdown-tag--unresolved{ background: rgba(224, 68, 91, 0.20); color: #ffb6c0; border: 1px solid rgba(224, 68, 91, 0.5); }

/* Date-range fieldset inside the shift add/edit dialog. Add-mode only;
   hidden in Edit-mode (a single shift record is being edited). Holds the
   optional End date + a per-day-of-week toggle row so supervisors can
   create a classroom-training week, several days of same-hours coverage,
   etc. in one submit. */
/* Per-person scheduled-hours summary under the schedule toolbar. A
   compact, wrapping row of name + hours chips that re-totals for the
   active view (day/week/2wk/month). */
.schedule-hours-summary {
  margin: 0 0 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
}
.schedule-hours-summary[hidden] {
  display: none;
}
/* Header doubles as the collapse/expand toggle — one readable line. */
.sched-hours-toggle {
  display: flex;
  align-items: baseline;
  gap: 6px;
  width: 100%;
  padding: 7px 10px;
  background: transparent;
  border: none;
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
  text-align: left;
  cursor: pointer;
}
.sched-hours-toggle:hover { background: rgba(255, 255, 255, 0.04); border-radius: 8px; }
.sched-hours-caret {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 0.7rem;
}
/* Expanded per-person breakdown — aligned grid with dotted leaders so the
   eye tracks each name to its hours instead of the rows blending together.
   Sorted high-to-low, generous column gap, capped height + own scroll. */
.sched-hours-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0 30px;
  max-height: 168px;
  overflow: auto;
  padding: 4px 10px 8px;
}
.sched-hours-cell {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 4px 2px;
  font-size: 0.82rem;
  line-height: 1.4;
  white-space: nowrap;
}
.sched-hours-name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text);
}
/* Dotted leader between name and hours — the key readability fix. */
.sched-hours-leader {
  flex: 1 1 auto;
  min-width: 12px;
  position: relative;
  top: -0.3em;
  border-bottom: 1px dotted rgba(160, 188, 222, 0.45);
}
.sched-hours-val {
  flex: 0 0 auto;
  font-weight: 700;
  color: #e6f0ff;
  font-variant-numeric: tabular-nums;
}
.sched-hours-pto {
  color: #ffd98a;
  font-weight: 500;
  font-size: 0.9em;
}

/* Grouped sections in the shift add/edit dialog. Each chunk of related
   fields (Who & when / Shift details / Training / Note) gets a small
   uppercase header and a divider so the form reads as steps instead of
   one long column. */
.shift-form-section {
  margin: 0 0 14px;
  padding: 0 0 12px;
  border-bottom: 1px solid var(--line);
}
.shift-form-section:last-of-type {
  border-bottom: 0;
  margin-bottom: 8px;
  padding-bottom: 0;
}
.shift-form-section-title {
  margin: 0 0 8px;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 700;
  color: var(--muted);
}
.shift-repeat-toggle {
  margin-top: 6px;
}

/* Inline duty-time banner (replaces the old alert/confirm popups). */
.shift-edit-warning {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: 8px;
}
.shift-edit-warning[hidden] {
  display: none;
}
.shift-edit-warning.is-error {
  background: rgba(224, 68, 91, 0.12);
  border: 1px solid rgba(224, 68, 91, 0.5);
  border-left: 3px solid #e0445b;
}
.shift-edit-warning.is-warn {
  background: rgba(240, 196, 58, 0.12);
  border: 1px solid rgba(240, 196, 58, 0.5);
  border-left: 3px solid #f0c43a;
}
.shift-edit-warning-title {
  font-weight: 700;
  font-size: 0.85rem;
  margin-bottom: 4px;
}
.shift-edit-warning-list {
  margin: 4px 0 8px;
  padding-left: 18px;
  font-size: 0.82rem;
  line-height: 1.45;
}

/* Action row — destructive group (Move to open / Remove) on the left,
   safe group (Cancel / Save) pushed to the right so a delete can't be
   fat-fingered next to Save. */
.shift-dialog-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.shift-dialog-actions-danger {
  display: flex;
  gap: 8px;
}
.shift-dialog-actions-main {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

.shift-range-fieldset {
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 6px;
  padding: 8px 12px;
  margin: 8px 0;
}

.shift-range-fieldset[hidden] {
  display: none;
}

.shift-range-fieldset legend {
  padding: 0 6px;
  font-size: 0.82rem;
  color: rgba(244, 230, 210, 0.85);
}

.shift-range-dow {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  align-items: center;
  margin-top: 6px;
}

.shift-range-dow-label {
  margin-right: 4px;
  color: rgba(244, 230, 210, 0.7);
}

.shift-range-dow-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
}

.shift-range-dow-toggle input {
  margin: 0;
}

.shift-range-dow-toggle:has(input:checked) {
  background: rgba(255, 212, 170, 0.16);
  border-color: rgba(255, 212, 170, 0.45);
  color: #ffd4aa;
}

.btn-mini {
  padding: 2px 8px;
  font-size: 0.72rem;
}

/* ============================================================
   Multi-select bulk action bar + selected-state indicator.
   Surfaced when ≥1 shift is selected via Cmd/Shift-click on any
   selectable surface (Scheduler pills, Open Shifts rows, Today's
   Desk Assignments rows).
   ============================================================ */

.multi-select-action-bar {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 18px;
  border-radius: 10px;
  background: var(--surface-2);
  border: 1px solid rgba(255, 212, 170, 0.55);
  color: #f4e6d2;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  font-size: 0.88rem;
  z-index: 9000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease-out, transform 0.18s ease-out;
}

.multi-select-action-bar.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

.multi-select-action-bar[hidden] {
  display: none;
}

.multi-select-summary strong {
  color: var(--breeze-tan, #ffd4aa);
  font-size: 0.96rem;
  margin-right: 4px;
}

.multi-select-summary .muted {
  color: rgba(244, 230, 210, 0.6);
  margin-left: 6px;
}

.multi-select-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* "Mark as ▾" dropdown inside the multi-select action bar. The wrap is
   the positioning context; the menu floats above the bar (bar sits at
   the bottom of the viewport, so the menu opens upward). */
.multi-select-mark-wrap {
  position: relative;
}
.multi-select-mark-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  min-width: 180px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  background: var(--panel, #1b2330);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  z-index: 70;
}
.multi-select-mark-menu[hidden] {
  display: none;
}
.multi-select-mark-item {
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--text);
  padding: 7px 10px;
  border-radius: 5px;
  font-size: 0.85rem;
  cursor: pointer;
}
.multi-select-mark-item:hover {
  background: rgba(255, 255, 255, 0.08);
}

/* Bulk edit dialog — each opt-in field group. The leading "Apply X"
   checkbox gates whether the field below is written across the
   selection. */
.bulk-edit-field {
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bulk-edit-field > .import-checkbox:first-child {
  font-weight: 600;
}

.multi-select-close {
  background: transparent;
  border: 0;
  color: rgba(244, 230, 210, 0.7);
  font-size: 1rem;
  line-height: 1;
  padding: 4px 8px;
  border-radius: 4px;
  cursor: pointer;
}

.multi-select-close:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
}

/* Selected-state visual indicator on every selectable surface. Uses
   a 2px breeze-tan outline + slight tint so the selection reads at a
   glance against the schedule's colorful pill backgrounds. */
.is-multi-selected {
  outline: 2px solid var(--breeze-tan, #ffd4aa) !important;
  outline-offset: 1px;
  background-color: rgba(255, 212, 170, 0.12) !important;
}

.shift-pill.is-multi-selected,
.week-shift-row.is-multi-selected {
  outline: 2px solid var(--breeze-tan, #ffd4aa) !important;
  box-shadow: 0 0 0 1px rgba(255, 212, 170, 0.3);
}

tr.is-multi-selected td {
  background-color: rgba(255, 212, 170, 0.10);
}

.open-shift-row.is-multi-selected {
  outline: 2px solid var(--breeze-tan, #ffd4aa);
  outline-offset: 1px;
  background: rgba(255, 212, 170, 0.08);
}

/* ============================================================
   EDCT / Route Requests widgets — dispatcher form on Universal
   Dashboard + supervisor decision panel on Supervisor Dashboard.
   ============================================================ */

.edct-route-tabs {
  display: flex;
  gap: 8px;
  margin: 8px 0 12px;
}

.edct-route-tabs .tab.is-active {
  background: rgba(255, 212, 170, 0.18);
  color: #fff;
  border-color: var(--breeze-tan, #ffd4aa);
}

.edct-route-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 8px;
}

.edct-route-form-grid > .span-2 {
  grid-column: span 2;
}

.edct-route-form-grid label,
.edct-route-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.82rem;
  color: rgba(244, 230, 210, 0.85);
}

.edct-route-form label > input,
.edct-route-form label > textarea,
.edct-route-form-grid label > input,
.edct-route-form-grid label > textarea {
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 4px;
  color: inherit;
  padding: 6px 8px;
  font: inherit;
}

.edct-route-form label > textarea,
.edct-route-form-grid label > textarea {
  resize: vertical;
  font-family: inherit;
}

.edct-route-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 320px;
  overflow-y: auto;
}

.edct-route-list--supervisor {
  max-height: 480px;
}

.edct-route-item {
  padding: 10px 12px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.03);
  border-left: 3px solid rgba(255, 255, 255, 0.15);
}

.edct-route-item--pending {
  border-left-color: var(--breeze-tan, #ffd4aa);
  background: rgba(255, 212, 170, 0.06);
}

.edct-route-item--approved {
  border-left-color: #6dd47e;
  background: rgba(109, 212, 126, 0.05);
}

.edct-route-item--rejected {
  border-left-color: #ff7a7a;
  background: rgba(255, 122, 122, 0.05);
}

.edct-route-item-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.edct-route-item-detail {
  margin-bottom: 4px;
}

.edct-route-item-status {
  margin-top: 6px;
}

.edct-route-detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
  margin: 6px 0;
}

.edct-route-detail-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 6px 0;
}

.edct-route-detail-stack code {
  background: rgba(0, 0, 0, 0.25);
  padding: 4px 6px;
  border-radius: 3px;
  font-size: 0.78rem;
  word-break: break-word;
  display: inline-block;
  margin-top: 2px;
}

.edct-route-notes {
  margin: 4px 0;
  padding: 4px 8px;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 4px;
  font-style: italic;
}

.edct-route-decide {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.edct-route-decide-approve,
.edct-route-decide-reject {
  display: flex;
  gap: 6px;
  align-items: stretch;
}

.edct-route-decide input,
.edct-route-decide textarea {
  flex: 1;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 4px;
  color: inherit;
  padding: 6px 8px;
  font: inherit;
}

.edct-route-decide textarea {
  resize: vertical;
  font-family: inherit;
  min-height: 32px;
}

.edct-route-decided {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.edct-route-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.edct-route-pill--pending {
  background: rgba(255, 212, 170, 0.20);
  color: #ffd4aa;
  border: 1px solid rgba(255, 212, 170, 0.45);
}

.edct-route-pill--approved {
  background: rgba(109, 212, 126, 0.20);
  color: #b4f0bd;
  border: 1px solid rgba(109, 212, 126, 0.45);
}

.edct-route-pill--rejected {
  background: rgba(255, 122, 122, 0.20);
  color: #ffb0b0;
  border: 1px solid rgba(255, 122, 122, 0.45);
}

.edct-route-type-badge {
  display: inline-block;
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  margin-right: 6px;
}

.edct-route-type-badge--edct {
  background: rgba(122, 162, 255, 0.20);
  color: #cfe0ff;
  border: 1px solid rgba(122, 162, 255, 0.45);
}

.edct-route-type-badge--route {
  background: rgba(186, 122, 255, 0.20);
  color: #e0cfff;
  border: 1px solid rgba(186, 122, 255, 0.45);
}

.badge-attn {
  background: rgba(255, 212, 170, 0.22);
  color: #ffd4aa;
}

/* Desk Assignments widget header actions — wrapping container that
   keeps the two action buttons (+ Add shift, Passdown) clear of the
   WIP toggle and drag handle absolutes that float over the card's
   top-right corner. Without the right-padding, the rightmost button
   would sit under the WIP flag icon and be hard to click.
   - margin-left: auto pushes the group to the right edge
   - padding-right reserves space for WIP toggle (~70px) + drag (~30px) */
.desk-assign-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  padding-right: 110px;
}

/* "Partial" pill — surfaced inline next to a dispatcher's name in the
   Desk Assignments widget when their shift end differs from their
   desk template's local end time. Same visual weight as the
   .next-day-badge so it reads as informational, not warning. Amber
   tint distinguishes it from the cool-blue next-day badge. */
.desk-assign-partial-pill {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(255, 195, 130, 0.20);
  color: #ffd9a8;
  border: 1px solid rgba(255, 195, 130, 0.45);
  vertical-align: middle;
}

/* Next-day badge — operating-date column on the assignment printout
   tags any flight whose calendar date is past the cycle's first day
   (e.g. a 0500 Z leg that's actually 5/01 in a cycle starting 4/30).
   Cool blue accent so it's clearly informational, not warning-level
   like the over-cap badge. */
.next-day-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(122, 162, 255, 0.18);
  color: #cfe0ff;
  border: 1px solid rgba(122, 162, 255, 0.4);
  vertical-align: middle;
}

/* Subtle row tint for next-day flights so a scan down the desk's
   list still reads chronologically: same-day rows on the standard
   surface, next-day rows pick up a cool blue wash. */
.row-next-day td {
  background: rgba(122, 162, 255, 0.06);
}

/* Charter flights — a badge on the printout next to the flight
   number for any 6000-series leg (or SSIM service-type "C"). The
   pink accent matches the existing C-service pill so supervisors
   spot charter flights quickly even on a black-and-white printout. */
.charter-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(255, 82, 123, 0.18);
  color: var(--breeze-pink);
  border: 1px solid rgba(255, 82, 123, 0.35);
  vertical-align: middle;
}

/* Transcontinental flight badge — visual cue that the assigner
   placed this flight near the START of the dispatcher's FA window
   (rather than the end) because it's a long-haul. Same visual
   treatment as charter, different hue (cool blue) so the two are
   easy to distinguish in the per-desk printout. */
.transcon-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(93, 166, 255, 0.16);
  color: #8fb6ff;
  border: 1px solid rgba(93, 166, 255, 0.4);
  vertical-align: middle;
}

/* Print mode — when the user clicks the dialog's Print button we
   add `printing-assignments` to <body> and fire window.print().
   These rules hide everything except the dialog content and flatten
   the styling so the printed output is a clean black-on-white sheet. */
@media print {
  /* Hide everything except whichever assignment-print dialog is
     currently open (assignment or reassignment). The :modal
     pseudo-class isn't universal yet, so we use [open] which is
     reliably set on the active <dialog>. */
  body.printing-assignments > *:not(#assignmentPrintDialog):not(#reassignmentPrintDialog) {
    display: none !important;
  }
  body.printing-assignments #assignmentPrintDialog:not([open]),
  body.printing-assignments #reassignmentPrintDialog:not([open]) {
    display: none !important;
  }
  body.printing-assignments #assignmentPrintDialog,
  body.printing-assignments #reassignmentPrintDialog {
    display: block !important;
    position: static !important;
    border: 0 !important;
    box-shadow: none !important;
    margin: 0 !important;
    padding: 0 !important;
    max-width: 100% !important;
    width: 100% !important;
    /* Without an explicit height reset the native <dialog>
       inherits the user-agent's modal-mode max-height (~80vh on
       Chrome/Safari) which clips the printout to whatever happened
       to be on-screen when the supervisor clicked Print. */
    max-height: none !important;
    height: auto !important;
    overflow: visible !important;
    background: white !important;
    color: black !important;
  }
  body.printing-assignments #assignmentPrintDialog form,
  body.printing-assignments #reassignmentPrintDialog form {
    background: white !important;
    color: black !important;
    max-height: none !important;
    overflow: visible !important;
  }
  /* The dialog body is the actual culprit — `.assignment-print-body`
     uses `max-height: 60vh; overflow-y: auto` so the supervisor
     can scroll the on-screen preview. During print we need to drop
     both so the full content flows down the page. */
  body.printing-assignments .assignment-print-body,
  body.printing-assignments .assignment-print-summary {
    max-height: none !important;
    overflow: visible !important;
  }
  /* Inner per-desk wrappers and tables: defensively unclip in
     case a future tweak adds a max-height somewhere. */
  body.printing-assignments .assignment-print-desk,
  body.printing-assignments .assignment-print-table,
  body.printing-assignments .assignment-print-table tbody {
    max-height: none !important;
    overflow: visible !important;
  }
  /* Hint to the print engine to size to actual content, not viewport
     height. Prevents a single tall table from getting cropped. */
  body.printing-assignments {
    height: auto !important;
    overflow: visible !important;
  }
  /* Mirror flag — JS sets `printing-assignments` on both <body> and
     <html> so this selector works without `:has()`. */
  html.printing-assignments {
    height: auto !important;
    overflow: visible !important;
  }
  body.printing-assignments .assignment-print-actions,
  body.printing-assignments #assignmentPrintClose,
  body.printing-assignments #assignmentPrintRerun,
  body.printing-assignments #assignmentPrintBtn,
  body.printing-assignments #reassignmentPrintClose,
  body.printing-assignments #reassignmentPrintBtn {
    display: none !important;
  }
  body.printing-assignments .assignment-print-desk,
  body.printing-assignments .assignment-print-summary,
  body.printing-assignments .assignment-print-table,
  body.printing-assignments .assignment-print-table th,
  body.printing-assignments .assignment-print-table td {
    background: white !important;
    color: black !important;
    border-color: #999 !important;
  }
  body.printing-assignments .assignment-print-desk-head h4,
  body.printing-assignments .assignment-print-desk-head h4 .muted,
  body.printing-assignments .apd-field dt,
  body.printing-assignments .muted {
    color: #444 !important;
  }
  body.printing-assignments .apd-titleblock .apd-dispatcher,
  body.printing-assignments .apd-field dd,
  body.printing-assignments .apd-field--passdown dd {
    color: black !important;
  }
  body.printing-assignments .assignment-print-desk-head {
    border-bottom-color: #999 !important;
  }
  body.printing-assignments .assignment-print-summary strong {
    color: black !important;
  }
  body.printing-assignments .assignment-print-desk {
    page-break-inside: avoid;
  }
}

/* Desk Schedule widget on the Universal Dashboard.
   Mirrors the printed reference table — cool-blue bordered grid with
   a centered date-range header and a column-header row beneath. The
   numeric desk identifiers stand out in bold; cells flip to inline
   inputs when a supervisor toggles edit mode. */
.desk-schedule-actions {
  display: flex;
  gap: 8px;
  margin: 0 0 10px;
}

.desk-schedule-table-wrap {
  border: 1px solid rgba(193, 221, 255, 0.4);
  border-radius: 10px;
  overflow: auto;
  max-height: 420px;
}

.custom-widget .desk-schedule-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.desk-schedule-table {
  width: 100%;
  font-size: 0.85rem;
  border-collapse: collapse;
}

.desk-schedule-table thead tr {
  background: var(--chip-bg);
}

.desk-schedule-header {
  text-align: center !important;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--breeze-cool);
  padding: 8px 10px !important;
  background: rgba(31, 116, 223, 0.15);
  border-bottom: 1px solid rgba(193, 221, 255, 0.35);
}

.desk-schedule-table th {
  padding: 8px 10px;
  text-align: center;
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
}

.desk-schedule-table td {
  padding: 6px 10px;
  text-align: center;
  border-bottom: 1px solid #14224a;
}

.desk-schedule-table tbody tr:last-child td {
  border-bottom: 0;
}

.desk-schedule-table tbody tr:hover {
  background: rgba(31, 116, 223, 0.06);
}

.desk-schedule-input {
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 0.82rem;
  text-align: center;
  width: 100%;
  max-width: 160px;
}

.desk-schedule-input--id {
  max-width: 90px;
  font-weight: 600;
}

.desk-schedule-input:focus {
  outline: none;
  border-color: var(--breeze-blue);
  box-shadow: 0 0 0 2px rgba(31, 116, 223, 0.25);
}

.desk-schedule-remove {
  margin-left: 6px;
  color: var(--breeze-pink);
  font-weight: 700;
  font-size: 1rem;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0 6px;
}

.desk-schedule-remove:hover {
  color: #ff8294;
}

.desk-schedule-add {
  margin-left: 6px;
  padding: 4px 12px;
  font-size: 0.78rem;
}

#deskScheduleAddRow td {
  background: rgba(31, 116, 223, 0.06);
}

/* Flight schedule list — browses imported SSIM legs by date.
   Top control row, then a scrollable table with sticky header. */
.flight-list-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: end;
  margin-bottom: 10px;
}

.flight-list-controls > label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.75rem;
  color: var(--muted);
  min-width: 160px;
}

.flight-list-controls input[type="date"],
.flight-list-controls input[type="search"] {
  background: var(--chip-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 0.85rem;
}

.flight-list-controls input:focus {
  outline: none;
  border-color: var(--view-accent, var(--blue));
  box-shadow: 0 0 0 2px var(--view-accent-soft);
}

.flight-list-svc {
  display: flex;
  gap: 4px;
  align-self: end;
  margin-bottom: 1px;
}

.flight-list-svc .tab {
  padding: 6px 10px;
  font-size: 0.75rem;
}

.flight-list-empty {
  padding: 14px 0;
  text-align: center;
}

.flight-list-body {
  max-height: 360px;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-deep);
}

.custom-widget .flight-list-body {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

/* Today's Desk Assignments table wrapper — same flex-fill pattern as
   the other primary-content tables. Outside a widget the wrapper is
   transparent (no border, no scroll); inside a widget it expands to
   the available height and gets its own scrollbar so the supervisor
   can always see column headers while scrolling through dispatchers. */
.desk-assign-table-wrap {
  /* Defaults outside a widget context — natural height, table flows
     in normal block layout. */
}

.custom-widget .desk-assign-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

/* Today's working shifts widget — Universal Dashboard "who's here
   today" roster grouped by role bucket. Same flex-fill behavior as
   the desk-assign table so the wrap grows with the widget. Section
   header row mirrors the scheduler's bucket sectioning so the two
   views read the same way. */
.todays-shifts-wrap {
  max-height: 60vh;
  overflow: auto;
}
.custom-widget .todays-shifts-wrap {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.todays-shifts-table th,
.todays-shifts-table td {
  padding: 6px 8px;
  vertical-align: middle;
}
.todays-shifts-table thead th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}
.todays-shifts-section-row td.todays-shifts-section-head {
  background: rgba(26, 139, 157, 0.18);
  color: #cfe7ff;
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 6px 8px;
  border-top: 1px solid rgba(110, 193, 255, 0.25);
  border-bottom: 1px solid rgba(110, 193, 255, 0.25);
}

/* Coverage Trend "How to read this" expandable panel — same shape
   as the Daily Flight Coverage Check widget's help panel so the
   two read consistently. */
.coverage-trend-help {
  margin: 4px 0 10px;
  padding: 0;
  font-size: 0.82rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 26, 59, 0.5);
}
.coverage-trend-help[open] {
  background: rgba(15, 26, 59, 0.8);
}
.coverage-trend-help summary {
  cursor: pointer;
  padding: 6px 12px;
  font-weight: 600;
  color: var(--text);
  list-style: revert;
  user-select: none;
}
.coverage-trend-help summary:hover {
  color: #cfe7ff;
}
.coverage-trend-legend {
  margin: 0;
  padding: 4px 12px 12px;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 14px;
  align-items: start;
}
.coverage-trend-legend dt {
  font-weight: 600;
  color: #cfd9f0;
  white-space: nowrap;
  padding-top: 2px;
}
.coverage-trend-legend dd {
  margin: 0;
  color: var(--muted);
  line-height: 1.45;
}
.coverage-legend-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 2px;
  vertical-align: -1px;
  margin-right: 2px;
  border: 1px solid rgba(255, 255, 255, 0.15);
}
.coverage-legend-swatch.is-ok    { background: #1dd4a1; }
.coverage-legend-swatch.is-watch { background: #f5a524; }
.coverage-legend-swatch.is-out   { background: #ff6b7b; }

/* Coverage Trend chart wrapper + per-bar hover tooltip. The wrapper
   provides a positioned ancestor for the absolutely-placed tooltip
   so the tooltip can follow the cursor without escaping the card.
   Tooltip is hidden by default; the mousemove handler in
   renderCoverageChart unhides it and writes the position. */
.coverage-chart-wrap {
  position: relative;
}
.coverage-tooltip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  background: rgba(15, 26, 59, 0.96);
  border: 1px solid rgba(110, 193, 255, 0.45);
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  padding: 8px 10px;
  min-width: 240px;
  max-width: 340px;
  font-size: 0.78rem;
  color: var(--text);
  line-height: 1.4;
}
.coverage-tooltip[hidden] {
  display: none;
}
.coverage-tooltip-head {
  font-weight: 700;
  color: #cfe7ff;
  font-size: 0.82rem;
  margin-bottom: 4px;
  border-bottom: 1px solid rgba(110, 193, 255, 0.25);
  padding-bottom: 3px;
}
.coverage-tooltip-row {
  margin: 3px 0;
}
.coverage-tooltip-row .coverage-legend-swatch {
  margin-right: 4px;
}
.coverage-tooltip-stat strong {
  color: #cfe7ff;
}
.coverage-tooltip-breakdown {
  padding-left: 18px;
  margin-top: -2px;
}
.coverage-tooltip-target-derivation {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed rgba(110, 193, 255, 0.25);
  font-style: italic;
  line-height: 1.4;
}

.flight-list-table {
  width: 100%;
  font-size: 0.82rem;
}

.flight-list-table thead {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--chip-bg);
}

.flight-list-table th {
  padding: 8px 10px;
  text-align: left;
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
}

.flight-list-table td {
  padding: 6px 10px;
  border-bottom: 1px solid #14224a;
  vertical-align: middle;
}

.flight-list-table tbody tr:hover {
  background: rgba(31, 116, 223, 0.06);
}

/* Service-type pills. J=Scheduled (blue), C=Charter (pink),
   P=Positioning (tan). Echoes the brand palette. */
.flight-svc {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  border: 1px solid;
}
.flight-svc-J {
  border-color: rgba(31, 116, 223, 0.55);
  color: var(--breeze-cool);
  background: rgba(31, 116, 223, 0.12);
}
.flight-svc-C {
  border-color: rgba(255, 82, 123, 0.55);
  color: #ffd2dd;
  background: rgba(255, 82, 123, 0.10);
}
.flight-svc-P {
  border-color: rgba(255, 212, 170, 0.55);
  color: var(--breeze-tan);
  background: rgba(255, 212, 170, 0.10);
}

/* Flight-schedule status panel on the Desk Assignment view.
   Compact summary box that shows the currently-loaded SSIM file. */
.flight-schedule-status {
  margin: 8px 0 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--field-bg);
  border: 1px solid var(--line);
  line-height: 1.4;
}

.flight-schedule-status strong {
  color: var(--text);
  font-size: 0.92rem;
}

/* Multi-SSIM file list. Each row pairs a filename + meta line with a
   per-file Remove button so the operator can drop a single file
   without nuking the whole schedule. Soonest-to-expire sorts to the
   top; the per-row "expires today" / "N days left" tag is rendered
   inline by the panel. */
.flight-files-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.flight-file-row {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 6px 8px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--line);
  border-radius: 6px;
}
.flight-file-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.flight-file-row-main strong {
  font-size: 0.84rem;
}
.flight-file-row .btn-mini {
  flex: 0 0 auto;
}
/* Expired file row — amber left border + inline EXPIRED tag so the
   row reads as needing attention even after the top banner is dismissed. */
.flight-file-row--expired {
  border-left: 3px solid #f08c3a;
  background: rgba(240, 140, 58, 0.08);
}
.flight-file-expired-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  border-radius: 4px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #1a1206;
  background: #f08c3a;
  vertical-align: middle;
}

/* Expired-SSIM warning banner. Amber-tinted callout that appears above
   the file list when one or more loaded schedules have passed their
   validTo. Replaces the old silent auto-prune — the supervisor decides
   whether to remove. */
.flight-expired-warning {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 10px;
  padding: 8px 10px;
  background: rgba(240, 140, 58, 0.12);
  border: 1px solid rgba(240, 140, 58, 0.5);
  border-left: 3px solid #f08c3a;
  border-radius: 6px;
}
.flight-expired-warning[hidden] {
  display: none;
}
.flight-expired-warning-text {
  flex: 1;
  min-width: 200px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.flight-expired-warning-text strong {
  color: #ffcaa0;
  font-size: 0.84rem;
}
.flight-expired-warning-actions {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

/* Training fieldset on the shift edit dialog. Same chrome as the
   import-conflict fieldset but with a green tint to echo the
   Training shift category color. */
.shift-training-fieldset {
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid #2f6b3f;
  border-radius: 10px;
  background: #0f1f17;
  color: var(--text);
}

.shift-training-fieldset legend {
  padding: 0 6px;
  font-size: 0.82rem;
  color: #9be0a8;
  font-weight: 600;
}

.shift-training-fieldset > label {
  display: block;
  margin-top: 8px;
}

.shift-training-fieldset > label:first-of-type {
  margin-top: 0;
}

/* Conflict-resolution fieldset on the import dialog. Mirrors
   .import-groups-fieldset chrome but uses a warmer warn-amber tint
   so it reads as a destructive choice that needs attention. */
.import-conflict-fieldset {
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid #6b6033;
  border-radius: 10px;
  background: #1f1a0d;
  color: var(--text);
}

.import-conflict-fieldset legend {
  padding: 0 6px;
  font-size: 0.82rem;
  color: #f3d68b;
  font-weight: 600;
}

.import-conflict-hint {
  margin: 0 0 10px !important;
}

.import-conflict-hint strong {
  color: #f3d68b;
}

.import-radio {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 10px;
  margin: 6px 0;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--field-bg);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.import-radio:hover {
  border-color: #7aa2ff;
}

.import-radio input[type="radio"] {
  width: auto;
  margin-top: 2px;
  accent-color: var(--view-accent, var(--blue));
}

/* JS toggles `.is-checked` on the label whenever its radio flips
   (see syncImportRadioLabels). Replaces the previous `:has(input
   :checked)` rule, which doesn't work in Safari < 15.4 / Firefox
   < 121. */
.import-radio.is-checked {
  border-color: var(--view-accent, var(--blue));
  background: #122e36;
}

.import-radio strong {
  display: inline;
  margin-right: 4px;
}

.import-radio em {
  color: #ffd4d9;
  font-style: normal;
  font-size: 0.8rem;
}

.import-groups-fieldset {
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--field-bg);
  color: var(--text);
}

.import-groups-fieldset legend {
  padding: 0 6px;
  font-size: 0.82rem;
  color: var(--muted);
}

.import-groups-hint {
  margin: 0 0 8px;
}

.import-groups-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 4px 12px;
  max-height: 160px;
  overflow: auto;
}

.import-group-toggle {
  display: flex !important;
  align-items: center;
  gap: 6px !important;
  margin: 0 !important;
  font-size: 0.82rem;
  color: var(--text) !important;
}

.import-group-toggle input {
  width: auto;
}

.import-group-count {
  color: var(--muted);
  font-size: 0.74rem;
  margin-left: auto;
}

.import-groups-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}

.import-groups-actions .tab {
  padding: 2px 10px;
  font-size: 0.75rem;
}

/* ---- Open shifts panel ---- */
.open-shift-add-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px 10px;
  align-items: end;
  margin-bottom: 12px;
}

.open-shift-add-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0;
}

.open-shift-add-form input {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 6px 8px;
  font: inherit;
}

.open-shift-add-form button {
  align-self: end;
  grid-column: 1 / -1;
  justify-self: start;
}

.open-shifts-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.open-shifts-empty {
  color: var(--muted);
  font-size: 0.85rem;
  padding: 8px 0;
}

.open-shift-day-group {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  padding: 8px 10px;
}

.open-shift-day-group h4 {
  margin: 0 0 6px;
  font-size: 0.82rem;
  color: #cfd9ff;
  letter-spacing: 0.02em;
}

.open-shift-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  align-items: center;
  padding: 6px 0;
  border-top: 1px dashed rgba(122, 162, 255, 0.18);
}

.open-shift-row:first-of-type {
  border-top: 0;
}

.open-shift-meta {
  flex: 1 1 200px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  align-items: center;
  font-size: 0.85rem;
}

.open-shift-meta strong {
  font-weight: 600;
  color: var(--text);
}

.open-shift-meta .open-shift-group-tag {
  font-size: 0.72rem;
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(122, 162, 255, 0.12);
  border: 1px solid rgba(122, 162, 255, 0.25);
  color: #cfd9ff;
}

.open-shift-meta .open-shift-label {
  font-size: 0.78rem;
  color: var(--muted);
  font-style: italic;
}

.open-shift-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
  align-items: center;
}

.open-shift-actions select {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 4px 6px;
  font: inherit;
  font-size: 0.78rem;
  min-width: 130px;
}

.open-shift-actions .btn {
  padding: 4px 10px;
  font-size: 0.76rem;
}

/* Inline "open shift" pill — same shape as a regular shift pill but with
   a dashed outline and faded amber treatment so it reads as "available,
   not assigned". The .shift-pill.open and .week-shift-row.open variants
   reuse the existing layouts so spacing matches the surrounding cells. */
.shift-pill.open,
.week-shift-row.open {
  background: rgba(255, 200, 100, 0.06);
  border: 1.5px dashed rgba(255, 200, 100, 0.55);
  border-left-width: 3px;
  border-left-style: dashed;
  color: #ffd28d;
  opacity: 0.92;
  cursor: pointer;
}

.shift-pill.open:hover,
.week-shift-row.open:hover {
  opacity: 1;
  background: rgba(255, 200, 100, 0.13);
  border-color: rgba(255, 200, 100, 0.85);
}

.shift-pill.open .shift-meta,
.week-shift-row.open .week-shift-meta {
  color: #ffba6b;
}

.shift-pill.open .shift-desk-label,
.week-shift-row.open .shift-desk-label {
  background: rgba(255, 200, 100, 0.16);
  border-color: rgba(255, 200, 100, 0.45);
  color: #ffd28d;
}

.open-pickup-hint {
  display: block;
  font-size: 0.66rem;
  color: rgba(255, 200, 100, 0.85);
  margin-top: 2px;
  letter-spacing: 0.02em;
}

/* Open-shift action dialog — slightly wider than the shift edit dialog
   so the assign-to-dispatcher select + Junior button fit on one row. */
.open-shift-dialog {
  max-width: 460px;
}

.open-shift-dialog .open-shift-summary {
  background: rgba(255, 200, 100, 0.08);
  border: 1px dashed rgba(255, 200, 100, 0.55);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 4px 0 14px;
  color: #ffd28d;
  font-size: 0.88rem;
  line-height: 1.45;
}

.open-shift-dialog .open-shift-summary strong {
  color: #ffe8c0;
}

/* Per-day "N open" chip on week + month cells. Click target stays on the
   wrap so double-click-to-day still works without conflict. */
.day-open-badge {
  display: inline-block;
  font-size: 0.66rem;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(255, 200, 100, 0.14);
  border: 1px solid rgba(255, 200, 100, 0.4);
  color: #ffd28d;
  letter-spacing: 0.02em;
  margin-top: 2px;
}

.slot.week-col .day-open-badge,
.slot .day-open-badge {
  align-self: flex-start;
}

/* ---- Group label on shift cells in the calendar ---- */
.shift-group {
  display: block;
  font-size: 0.7rem;
  color: #a9bcde;
  font-weight: 500;
  letter-spacing: 0.02em;
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shift-pill .shift-group {
  margin-top: 2px;
}

.week-shift-row .shift-group {
  font-size: 0.66rem;
  margin-top: 1px;
}

.shift-pill .shift-group,
.week-shift-row .shift-group {
  /* Tone the group apart from the status meta line so users can see at a
     glance which team a shift belongs to. */
  background: rgba(122, 162, 255, 0.12);
  border: 1px solid rgba(122, 162, 255, 0.25);
  border-radius: 999px;
  padding: 1px 8px;
  width: fit-content;
}

.tab-import {
  margin-left: 4px;
  border-color: #4362a5;
  color: #cfd9ff;
}

.tab-import:hover {
  border-color: #7aa2ff;
  background: var(--chip-bg);
}

/* Primary "Add shift" button in the schedule toolbar — accent-filled so
   it reads as the main action next to the neutral nav/filter tabs. */
.tab-add {
  margin-left: 4px;
  background: var(--breeze-blue);
  border-color: var(--breeze-blue);
  color: #fff;
  font-weight: 600;
}
.tab-add:hover {
  background: #2f86ff;
  border-color: #2f86ff;
}

/* Filter toggle (e.g. Hide PTO). Visually identical to other tabs but uses
   aria-pressed to mark the on/off state, which the existing .tab.active
   styles latch on to via the matching attribute selector below. */
.tab-toggle[aria-pressed="true"] {
  background: #2a1322;
  border-color: #6b2d4a;
  color: #f7c1d4;
}

.tab-toggle[aria-pressed="true"]::before {
  content: "✓ ";
}

/* Inline shift note shown on each calendar pill / row. Small italic
   text so it reads as context, not a primary label. The leading icon
   doubles as a "this shift has a note" affordance even when the text
   ends up ellipsized in tight cells. */
.shift-note {
  display: block;
  margin-top: 2px;
  font-size: 0.66rem;
  font-style: italic;
  color: #c0d4ff;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.shift-pill .shift-note,
.week-shift-row .shift-note {
  /* Leading note icon — faint enough to read as decoration, but visible
     enough to signal "expand the shift to see the full note". */
  position: relative;
  padding-left: 14px;
}

.shift-pill .shift-note::before,
.week-shift-row .shift-note::before {
  content: "📝";
  position: absolute;
  left: 0;
  top: 0;
  font-size: 0.7rem;
  font-style: normal;
  opacity: 0.85;
}

/* Shift-edit dialog textarea (the new Note field). Reuses the same
   look as the request-comment dialog's textarea. */
#shiftEditNote {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
  font-size: 0.85rem;
  resize: vertical;
  min-height: 50px;
  width: 100%;
  box-sizing: border-box;
}

/* Desk label badge prepended to shifts in the calendar. Matches the
   colored shorthand the user already recognizes from Microsoft Shifts
   ("04", "C5", "1430"). */
.shift-desk-label {
  display: inline-block;
  font-weight: 600;
  background: rgba(154, 200, 255, 0.18);
  border: 1px solid rgba(154, 200, 255, 0.35);
  border-radius: 4px;
  padding: 0 5px;
  margin-right: 5px;
  font-size: 0.92em;
  letter-spacing: 0.02em;
  color: #dceaff;
}

.shift-pill .shift-desk-label,
.week-shift-row .shift-desk-label {
  /* Sit flush with the surrounding text — these are inside a span/button
     where line-height is already tight. */
  vertical-align: baseline;
}

/* ---- Login screen ---- */
.login-screen {
  position: fixed;
  inset: 0;
  background: radial-gradient(circle at top, #11204a 0%, #050a1a 80%);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 24px;
}

.login-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 24px;
  width: min(380px, 100%);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.4);
}

.brand-login {
  margin-bottom: 18px;
}

.login-card form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.login-card label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--muted);
}

.login-card input[type="text"],
.login-card input[type="password"] {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  padding: 10px 12px;
  font: inherit;
}

.login-card input:focus {
  outline: 1px solid #5b7ad6;
  outline-offset: 1px;
}

.login-card .btn.primary {
  margin-top: 4px;
}

.login-error {
  color: #f7c1d4;
  background: #2a1322;
  border: 1px solid #6b2d4a;
  padding: 8px 10px;
  border-radius: 8px;
  margin: 0;
  font-size: 0.82rem;
}

.login-hint {
  margin: 0;
}

.chip-action {
  cursor: pointer;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  color: var(--text);
}

.chip-action:hover {
  border-color: var(--blue);
  background: var(--panel-soft);
  color: var(--text);
}

/* ---- User accounts panel (Supervisor Dashboard) ---- */
.user-accounts-card .actions-split {
  margin-top: 4px;
}

.user-accounts-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}

.user-accounts-form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.82rem;
  color: var(--muted);
  margin: 0;
}

.user-accounts-form input,
.user-accounts-form select {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
}

.user-accounts-form .actions-split {
  grid-column: 1 / -1;
  display: flex;
  gap: 8px;
}

.user-accounts-table-wrap {
  max-height: none;
}

.password-pill {
  display: inline-block;
  font-size: 0.72rem;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid;
}

/* Inline role select on the user-accounts table.
   Each row has one of these so a supervisor can change a role
   without leaving the table. The role-{value} class colors the
   border + text so the role is glanceable at the row level
   (matches the badge palette used elsewhere — purple = manager,
   teal = supervisor, blue-grey = dispatcher). */
.inline-role-select {
  appearance: none;
  -webkit-appearance: none;
  background: var(--chip-bg);
  color: #d6e3ff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 4px 22px 4px 10px;
  font-size: 0.78rem;
  font-weight: 500;
  cursor: pointer;
  /* Custom caret (no native dropdown arrow). Brighter color (#cfe0ff
     instead of #8fa4c8) and slightly larger triangle (6px vs 5px)
     so the dropdown affordance is obvious at a glance. */
  background-image: linear-gradient(45deg, transparent 50%, #cfe0ff 50%),
                    linear-gradient(135deg, #cfe0ff 50%, transparent 50%);
  background-position: calc(100% - 12px) 52%, calc(100% - 7px) 52%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  transition: border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

.inline-role-select:hover {
  border-color: #7aa2ff;
}

.inline-role-select:focus {
  outline: none;
  border-color: var(--view-accent);
  box-shadow: 0 0 0 2px var(--view-accent-soft);
}

.inline-role-select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.inline-role-select.role-supervisor {
  border-color: rgba(255, 82, 123, 0.5); /* Sunset Pink */
  color: #ffd2dd;
}
.inline-role-select.role-manager {
  border-color: rgba(193, 221, 255, 0.55); /* Cool Breeze Blue */
  color: var(--breeze-cool);
}
.inline-role-select.role-trainer {
  border-color: rgba(255, 212, 170, 0.55); /* Sunrise Tan */
  color: var(--breeze-tan);
}
.inline-role-select.role-dispatcher {
  border-color: var(--line);
  color: #cfe1ff;
}

/* ----- Multi-role + extras chip display on the Employees table ----- */
.user-roles-cell {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}
.user-role-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 500;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  color: var(--text);
  line-height: 1.4;
}
.user-role-chip.is-primary {
  font-weight: 600;
  background: var(--chip-bg);
  border-color: var(--blue);
}
.user-role-chip-star {
  color: #f6c34a;
  font-size: 0.72rem;
  line-height: 1;
}
.user-role-chip.role-supervisor { color: #ffd8a8; border-color: rgba(214, 134, 60, 0.55); }
.user-role-chip.role-manager    { color: #e0c0ff; border-color: rgba(160, 95, 200, 0.55); }
.user-role-chip.role-trainer    { color: #b6f0c1; border-color: rgba(80, 160, 100, 0.55); }
.user-role-chip.role-dispatcher { color: #cfe1ff; border-color: rgba(80, 130, 200, 0.45); }
.user-role-chip.role-unknown    { color: var(--muted); border-style: dashed; }
.user-role-extras-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  border-radius: 999px;
  font-size: 0.7rem;
  background: rgba(150, 200, 255, 0.10);
  border: 1px solid rgba(150, 200, 255, 0.35);
  color: #c8def5;
  cursor: help;
}
.user-roles-edit-link {
  margin-left: 2px;
  font-size: 0.78rem;
}

/* ----- Roles & Permissions editor dialog ----- */
.user-roles-edit-section {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.user-roles-edit-section h4 {
  margin: 0 0 4px;
  font-size: 0.92rem;
  color: var(--text);
}
.user-roles-edit-role-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 6px;
}
.user-roles-edit-role-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--chip-bg);
}
.user-roles-edit-role-toggle,
.user-roles-edit-role-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.85rem;
}
.user-roles-edit-role-admin {
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #f6c34a;
  margin-left: 4px;
}
.user-roles-edit-role-primary input:disabled + span {
  color: var(--muted);
  opacity: 0.5;
}
.user-roles-edit-perm-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 6px;
  max-height: 320px;
  overflow-y: auto;
  padding-right: 4px;
}
.user-roles-edit-perm-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.user-roles-edit-perm-section-heading {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.user-roles-edit-perm-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.84rem;
  padding: 4px 6px;
  border-radius: 6px;
}
.user-roles-edit-perm-row:hover {
  background: var(--chip-bg);
}
.user-roles-edit-perm-row.is-redundant {
  opacity: 0.6;
}
.user-roles-edit-perm-redundant {
  font-size: 0.72rem;
  color: var(--muted);
  font-style: italic;
  margin-left: 4px;
}

.password-pill.password-default {
  border-color: #6b6033;
  background: #2a2415;
  color: #f3d68b;
}

.password-pill.password-set {
  border-color: #2f6b3f;
  background: #142a1c;
  color: #9be0a8;
}

/* Disabled-account rows in the Employees table read dimmed so a
   supervisor scanning the roster sees at a glance which accounts
   are locked out. Name cell gets a "(disabled)" suffix in the
   markup. */
.ops-emp-row.is-disabled-account {
  opacity: 0.55;
}
.ops-emp-row.is-disabled-account td {
  text-decoration-color: rgba(255, 255, 255, 0.3);
}

.ops-panel {
  margin-bottom: 12px;
}

.ops-panel-intro {
  margin-top: 0;
}

.ops-grid {
  display: grid;
  /* Column count follows the widget's own width (auto-fit), not the viewport,
     so the panel collapses to a single column when the widget is narrow even
     on a wide screen. */
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin-top: 10px;
}

.ops-block-title {
  margin: 0 0 8px;
  font-size: 0.88rem;
  color: #bfd0ef;
}

.ops-inline-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: flex-end;
  margin-bottom: 10px;
}

.ops-inline-form input,
.ops-inline-form select {
  flex: 1 1 120px;
  min-width: 100px;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
}

.ops-table-wrap {
  max-height: 200px;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: 10px;
}

/* Per-day desk rotation — narrow checkbox columns under the
   compound "In rotation (per day)" header. Each checkbox sits in
   its own minimal-width column so all seven fit without forcing the
   adjacent time-input columns to compress. */
.ops-desks-table .ops-desks-days-header {
  text-align: center;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  padding-bottom: 2px;
}
.ops-desks-table .ops-desks-day-row th {
  text-align: center;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted);
  padding: 2px 4px;
  width: 22px;
}
.ops-desks-day-cell {
  text-align: center;
  width: 22px;
  padding: 2px 0 !important;
}
.ops-desks-day-cell input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}

.ops-employees-bulk-bar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}

.ops-employees-bulk-bar .btn {
  padding: 4px 12px;
  font-size: 0.78rem;
}

.ops-emp-check-col {
  width: 28px;
  padding-right: 0 !important;
}

.ops-emp-check-col input,
[data-emp-check] {
  width: auto;
  cursor: pointer;
}

[data-emp-check]:disabled {
  cursor: not-allowed;
}

@container (max-width: 560px) {
  .custom-widget .card-header {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 8px;
    padding-top: 26px;
    padding-right: 0;
  }

  .custom-widget .card-header > :not(h3) {
    grid-column: 1;
    grid-row: auto;
    justify-self: start;
  }

  .custom-widget .card-header .badge {
    justify-self: start;
  }

  .custom-widget .schedule-card-header-tools,
  .custom-widget .schedule-toolbar,
  .custom-widget .tabs,
  .custom-widget .actions-split {
    align-items: stretch;
    justify-content: flex-start;
  }

  .custom-widget .tabs {
    flex-wrap: wrap;
  }

  .custom-widget .tab,
  .custom-widget .btn,
  .custom-widget .btn.secondary {
    white-space: normal;
  }

  .custom-widget .link-grid {
    grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  }

  .custom-widget .controls-grid,
  .custom-widget .request-form {
    grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
  }

  .custom-widget .request-form-stack {
    grid-template-columns: 1fr;
  }

  .custom-widget .ops-grid {
    grid-template-columns: 1fr;
  }

  .custom-widget .schedule-grid.view-week,
  .custom-widget .schedule-grid.view-month {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
    height: auto;
  }

  .custom-widget .schedule-viewport.schedule-viewport--week {
    height: auto;
    min-height: 220px;
    overflow-y: auto;
  }
}

@container (max-width: 360px) {
  .widget-scroll-body {
    padding-right: 28px;
  }

  .widget-drag-handle {
    right: 14px;
  }

  /* Push Edit far enough left that it can't bump into the Reorder pill on a
     narrow phone viewport (Reorder ≈ 70px wide at right: 14px → edge ≈ 84px).
     6.5rem ≈ 104px keeps a clean ~20px gap. */
  .widget-edit-handle {
    right: 6.5rem;
  }

  .widget-resize-handle {
    right: 14px;
  }

  .custom-widget .card-header {
    padding-top: 52px;
  }

  .custom-widget .table th,
  .custom-widget .table td {
    padding: 6px 4px;
    font-size: 0.78rem;
  }
}

/* Operations Control Panel — when the widget is given an explicit height,
   make the inner Employees / Desks tables grow to fill the available space
   instead of staying capped at 200px. The chain (scroll-body → ops-grid →
   ops-block → ops-table-wrap) all needs flex/min-height: 0 so the height
   actually propagates. */
#opsControlPanel.custom-widget > .widget-scroll-body {
  display: flex;
  flex-direction: column;
}

#opsControlPanel .ops-grid {
  flex: 1 1 auto;
  min-height: 0;
  /* When stacked into one column on a narrow widget, share remaining height
     between the two blocks instead of letting one swallow it all. */
  grid-auto-rows: minmax(0, 1fr);
}

#opsControlPanel .ops-block {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

#opsControlPanel .ops-table-wrap {
  flex: 1 1 auto;
  min-height: 80px;
  max-height: none;
}

.table-compact th,
.table-compact td {
  padding: 6px 5px;
  font-size: 0.82rem;
}

.ops-desk-cap {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  font-size: 0.85rem;
  color: var(--muted);
  max-width: 200px;
}

.ops-desk-cap input {
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
}

.ops-hours-input {
  width: 100%;
  max-width: 140px;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 4px 6px;
  font: inherit;
  font-size: 0.8rem;
}

.avail-form {
  margin-bottom: 12px;
}

.availability-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.85rem;
}

.availability-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.availability-item .btn-link {
  background: none;
  border: 0;
  color: var(--red);
  cursor: pointer;
  font: inherit;
  text-decoration: underline;
  padding: 0;
}

@media (max-width: 1200px) {
  /* Sidebar drawer behavior is constant across breakpoints — no
     special-casing here anymore since the sidebar is always a slide-in
     drawer triggered by the hamburger button. */

  .metrics-row,
  .grid-two,
  .controls-grid,
  .request-form {
    grid-template-columns: 1fr;
  }


  .schedule-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---------------------------------------------------------------------------
   Native control visibility on the dark navy theme.

   Browser default styling for date / time / month picker icons,
   number spinners, and search-clear buttons is dark-on-dark on every
   Chromium-family browser — supervisors reported the calendar icons
   on every date input were "hard to see." These rules invert/brighten
   the affected glyphs at the user-agent level so every input gets the
   fix automatically (no per-input class needed).

   Two techniques in play:
     - filter: invert(...) brightness(...) for raster icons baked into
       the user-agent shadow DOM (the calendar/clock icons).
     - color overrides for any caret/glyph the UA renders as text.

   Hover/focus also bumps brightness so the affordance reads as
   interactive.
   --------------------------------------------------------------------------- */

input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="week"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  /* The default icon is near-black; invert + brighten flips it to a
     bright steel-blue that pops on the navy backgrounds without
     looking neon. Slight cursor + opacity tweak so the icon reads
     as clickable. */
  filter: invert(0.9) brightness(1.4) sepia(0.15) hue-rotate(180deg);
  opacity: 0.95;
  cursor: pointer;
  padding: 2px;
  border-radius: 3px;
  transition: opacity 0.15s ease, background-color 0.15s ease;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover,
input[type="month"]::-webkit-calendar-picker-indicator:hover,
input[type="week"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
  background-color: rgba(122, 162, 255, 0.18);
}

/* Number-input spinners. Default UA arrows are also dark; these
   rules nudge contrast and add a soft hover background so it's
   obvious which arrow you're about to click. */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  filter: invert(0.85) brightness(1.3);
  opacity: 0.9;
  cursor: pointer;
}
input[type="number"]::-webkit-inner-spin-button:hover,
input[type="number"]::-webkit-outer-spin-button:hover {
  opacity: 1;
}

/* Search input clear ("×") button. */
input[type="search"]::-webkit-search-cancel-button {
  filter: invert(0.9) brightness(1.4);
  opacity: 0.85;
  cursor: pointer;
}
input[type="search"]::-webkit-search-cancel-button:hover {
  opacity: 1;
}

/* Color picker swatch — keep the inner color visible, brighten the
   border so the control itself reads as a control. */
input[type="color"] {
  border-color: #4a6cae;
}

/* Native <select> caret (the arrow in the corner). Some selects in
   the app use appearance:none + a custom caret, but plain selects
   inherit the UA caret which can be muddy on dark inputs. Tinting
   the accent-color steers Chromium toward our palette and brightens
   the caret without breaking accessibility. */
select {
  accent-color: #7aa2ff;
}

/* Telephone / generic text inputs that use ::-webkit-clear-button
   (some browsers expose this on mobile keyboards) — keep them in the
   same bright family. */
input::-webkit-clear-button {
  filter: invert(0.9) brightness(1.4);
  opacity: 0.85;
}

/* ====================================================================
   UI REFRESH PILOT — Universal Dashboard + menu, light/dark theming
   --------------------------------------------------------------------
   Two themes (dark = default, light = the clean reference look),
   selected by `data-theme` on <html> (set pre-paint by the inline
   script in index.html, flipped by the topbar toggle wired in app.js).

   Scope: this pilot themes the topbar MENU and the Universal Dashboard
   only. Other views' content keeps its existing look — so the theme
   tokens below are defined ONLY on `.topbar` and on
   `#universalView.refresh-layout`, never globally.

   Token model: each token block sets both the pilot's own `--ud-*`
   tokens and the app's existing `--text/--muted/--line/--panel`
   variables (scoped), so component rules that already read those keep
   working in both themes. Component rules below are written ONCE using
   the tokens — only the token VALUES differ per theme.
   ==================================================================== */

/* --- Universal Dashboard theme tokens -------------------------------
   The Universal Dashboard's refresh-layout keeps its own `--ud-*`
   token set (it's a bespoke grid with its own surfaces). These now
   ride the global `data-theme` and only need to be scoped to the
   dashboard view — the topbar is gone (replaced by the sidebar, which
   is themed straight off the global vars below). */
html[data-theme="dark"] #universalView.refresh-layout {
  --ud-surface: #0f1320;
  --ud-card: #181d2b;
  --ud-card-border: #2a3142;
  --ud-card-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 12px 26px -14px rgba(0, 0, 0, 0.7);
  --ud-text: #e9ecf4;
  --ud-text-2: #99a3b7;
  --ud-text-3: #6c7589;
  --ud-line: #2a3142;
  --ud-fill: #232a3a;
  --ud-fill-hover: #2c3446;
  --ud-fill-border: #343c50;
  --ud-accent: #4d9bff;
  --ud-on-accent: #ffffff;
  --ud-chart-panel: rgba(0, 0, 0, 0.22);
  /* re-point the app's existing vars (scoped) so var(--text) etc.
     inside the dashboard resolve to the dashboard's themed values */
  --text: #e9ecf4;
  --muted: #99a3b7;
  --line: #2a3142;
  --panel: #181d2b;
  --panel-soft: #232a3a;
}
html[data-theme="light"] #universalView.refresh-layout {
  --ud-surface: #f4f4f6;
  --ud-card: #ffffff;
  --ud-card-border: #e8e8ee;
  --ud-card-shadow: 0 1px 2px rgba(17, 17, 34, 0.04), 0 8px 18px -10px rgba(17, 17, 34, 0.10);
  --ud-text: #1c1c26;
  --ud-text-2: #6c6c78;
  --ud-text-3: #9a9aa6;
  --ud-line: #e8e8ee;
  --ud-fill: #f1f1f4;
  --ud-fill-hover: #e8e8ed;
  --ud-fill-border: #e4e4ea;
  --ud-accent: #1f74df;
  --ud-on-accent: #ffffff;
  --ud-chart-panel: #141d3d;
  --text: #1c1c26;
  --muted: #6c6c78;
  --line: #e8e8ee;
  --panel: #ffffff;
  --panel-soft: #f6f6f9;
}

/* ====================================================================
   APP LAYOUT — left sidebar + main content
   --------------------------------------------------------------------
   The nav moved out of the old topbar into a persistent left sidebar.
   Sidebar + main sit in a flex `.app-layout`. Everything here themes
   straight off the global vars (--panel, --line, --text, --muted,
   --chip-bg, --field-bg, --blue), so it follows the data-theme toggle
   automatically in both light and dark.
   ==================================================================== */
.app-layout {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}
.app-sidebar {
  flex: 0 0 248px;
  width: 248px;
  align-self: stretch;
  position: sticky;
  top: 0;
  /* Use dvh (dynamic viewport height) so the sidebar fits the actually-
     visible viewport on mobile (address-bar shrink) and on desktop with
     a sticky BETA banner pushing the layout down. The override below
     subtracts the banner height when the banner is present. */
  height: 100dvh;
  /* Lets the sidebar scroll its own content if it ever overflows
     rather than clipping items behind the viewport edge — fixes the
     "Saved badge cut off at the bottom" bug. */
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 18px 14px;
  background: var(--panel);
  border-right: 1px solid var(--line);
  overflow-y: auto;
}
.app-sidebar .brand-sidebar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 8px;
  padding: 4px 8px 14px;
  border-bottom: 1px solid var(--line);
}
.app-sidebar .brand-sidebar .brand-mark {
  width: 34px;
  height: 34px;
  border-radius: 9px;
}
.app-sidebar .brand-sidebar h1 { margin: 0; font-size: 0.98rem; color: var(--text); }
.app-sidebar .brand-sidebar p { margin: 1px 0 0; font-size: 0.7rem; color: var(--muted); }

/* --- Content-column header: clocks (left) + user switcher (right) --- */
.app-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* bleed to the edges of .main's 18px padding, then re-pad, so the
     header spans the full content width and sits flush at the top */
  margin: -18px -18px 14px;
  padding: 11px 18px;
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
/* Live clocks in the topbar — Zulu is the primary read (slightly
   larger + bolder), Mountain is the secondary local reference, and
   the basis chip is reference info shown small + muted so it doesn't
   compete with the live times. Wrap is allowed on narrow viewports so
   the user switcher never collides with them. */
.topbar-clocks {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  min-width: 0;
}
.topbar-clock {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--text);
  padding: 5px 10px;
  background: var(--chip-bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  white-space: nowrap;
}
.topbar-clock-primary {
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text);
  border-color: var(--blue);
  background: linear-gradient(
    180deg,
    rgba(73, 134, 228, 0.10),
    rgba(73, 134, 228, 0.02)
  );
}
.topbar-clock-basis {
  font-size: 0.7rem;
  color: var(--muted);
  padding: 4px 8px;
  border-radius: 6px;
  background: transparent;
  border: 1px dashed var(--line);
  white-space: nowrap;
}
@media (max-width: 720px) {
  /* On narrow screens, hide the basis chip and the Mountain clock to
     keep the topbar single-row. Zulu stays since it's the operational
     reference. */
  .topbar-clock-basis,
  .topbar-clocks #clockMountain {
    display: none;
  }
}
.user-switcher {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 8px 4px 4px;
}
.user-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--breeze-white);
  background: linear-gradient(135deg, var(--breeze-pink), var(--breeze-blue));
}
.user-switcher-select {
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 0.84rem;
  padding: 4px 6px 4px 2px;
  cursor: pointer;
  max-width: 220px;
}
.user-switcher-select:disabled {
  cursor: default;
  color: var(--muted);
}

/* ============================================================
   Topbar user area — three elements aligned right-to-right.
   • .topbar-switch-user  — supervisor-only Switch User select
   • .user-card-wrap      — read-only user card with dropdown menu
   • .notifications-bell  — relocated from the sidebar
   The flex gap matches the topbar-kpis gap so the four
   right-side groups (KPIs, switch, card, bell) read evenly.
   ============================================================ */

.topbar-switch-user {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px 4px 12px;
  font-size: 0.78rem;
}
.topbar-switch-user .switch-user-label {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.66rem;
  font-weight: 600;
}
.topbar-switch-user .user-switcher-select {
  font-size: 0.82rem;
  max-width: 180px;
}
.topbar-switch-user[hidden] { display: none; }

/* User card + dropdown menu */
.user-card-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.user-card {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px 4px 4px;
  cursor: pointer;
  color: inherit;
  font: inherit;
  transition: background 120ms ease, border-color 120ms ease;
}
.user-card:hover,
.user-card[aria-expanded="true"] {
  background: rgba(31, 116, 223, 0.08);
  border-color: rgba(31, 116, 223, 0.40);
}
.user-card-name {
  font-size: 0.84rem;
  font-weight: 500;
  white-space: nowrap;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.user-card-caret {
  color: var(--muted);
  font-size: 0.7rem;
  margin-left: 2px;
  transition: transform 120ms ease;
}
.user-card[aria-expanded="true"] .user-card-caret {
  transform: rotate(180deg);
}

.user-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 180px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px;
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.40);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.user-menu[hidden] { display: none; }
.user-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  font-size: 0.86rem;
  padding: 8px 12px;
  border-radius: 6px;
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.user-menu-item:hover,
.user-menu-item:focus-visible {
  background: rgba(255, 255, 255, 0.06);
  outline: none;
}

/* Notifications bell when it lives in the topbar (no longer the
   sidebar-chip styling). Keep the existing bell size/badge rules
   below at line ~10171 — this only adjusts container behavior. */
.topbar-right .notifications-bell {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  color: inherit;
}
.topbar-right .notifications-bell:hover {
  background: rgba(31, 116, 223, 0.08);
  border-color: rgba(31, 116, 223, 0.40);
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-height: 0;
}
.sidebar-section {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 14px 4px 6px;
  margin: 0 6px 4px;
  border-bottom: 1px solid var(--line);
}
.sidebar-section:first-child { padding-top: 4px; }
/* Sidebar nav links — override the legacy .nav-link base styles and
   the old gradient hover/active treatment. */
.app-sidebar .nav-link {
  width: 100%;
  text-align: left;
  border: 0;
  margin: 0;
  padding: 9px 12px;
  border-radius: 9px;
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--muted);
  background: transparent;
  box-shadow: none;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.app-sidebar .nav-link:hover {
  background: var(--chip-bg);
  color: var(--text);
  /* box-shadow intentionally omitted — Phase 1 hover-lift adds an
     accent-color glow on :not(.active):hover; setting box-shadow:none
     here would override it. */
}
.app-sidebar .nav-link.active {
  /* Background + box-shadow now come from the Phase 4 .nav-link.active
     rule earlier in the cascade (gradient fill + accent rail + outer
     glow). Keep typography/color treatment here. */
  color: var(--text);
  font-weight: 600;
}

.sidebar-foot {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* .sidebar-clocks moved to .topbar-clocks in the topbar — see CSS
   block near .app-topbar. Old sidebar-clocks rules removed; the IDs
   #clockZulu / #clockMountain / #clockBasis still exist (and the JS
   updateClocks() still targets them) but they live in the topbar now. */
.sidebar-foot .draft-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.sidebar-foot-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.sidebar-foot-actions .save-status {
  flex: 1 1 auto;
  justify-content: center;
}
.sidebar-foot-actions #themeToggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 7px 9px;
}
.sidebar-foot-actions #themeToggle svg { width: 15px; height: 15px; display: block; }

/* Main content column — fills the rest, scrolls independently. */
.app-layout > .main {
  flex: 1 1 auto;
  min-width: 0;
  padding: 18px;
}

/* Narrow screens — stack the sidebar above the content. */
@media (max-width: 860px) {
  .app-layout { flex-direction: column; }
  .app-sidebar {
    flex: none;
    width: 100%;
    height: auto;
    position: static;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .sidebar-nav { flex-direction: row; flex-wrap: wrap; }
  .sidebar-foot { margin-top: 12px; }
}

/* ================  UNIVERSAL DASHBOARD  ============================
   A clean dashboard: a work-surface "stage" carrying white/dark cards
   in a 12-column grid with equal-height rows (align-items: stretch),
   each card a flex column whose main region fills — so same-row cards
   line up and there's no dead space between or inside cards. A
   max-height cap keeps a long table from blowing out a row band; it
   scrolls inside its card instead. setupFixedLayoutForView() in app.js
   sets the DOM order; the grid flows in that order. */

#universalView.refresh-layout.active {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-items: stretch;
  gap: 11px;
  width: 100%;
  margin-top: 12px;
  padding: 14px;
  border-radius: 16px;
  background: var(--ud-surface);
}

/* --- Title block ---------------------------------------------------- */
#universalView.refresh-layout > .title-row {
  grid-column: 1 / -1;
  align-self: start;
  margin: 0 2px 0;
}
#universalView.refresh-layout .title-row h2 {
  color: var(--ud-text);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.2px;
}
#universalView.refresh-layout .title-row h2::before {
  background: var(--ud-accent);
  box-shadow: none;
}
#universalView.refresh-layout .title-row p { color: var(--ud-text-2); }

/* --- Cards ---------------------------------------------------------- */
#universalView.refresh-layout > .card {
  display: flex;
  flex-direction: column;
  /* safety cap so one long table can't blow out a whole row band; the
     content region below scrolls within this. No min-height — cards
     stay as compact as their content allows. */
  max-height: 400px;
  background: var(--ud-card);
  border: 1px solid var(--ud-card-border);
  border-radius: 12px;
  padding: 13px 15px;
  box-shadow: var(--ud-card-shadow);
}
#universalView.refresh-layout > .card::before { display: none; }
#universalView.refresh-layout .card-header { margin-bottom: 3px; }
#universalView.refresh-layout .card-header h3 {
  color: var(--ud-text);
  font-size: 13.5px;
  font-weight: 600;
}
#universalView.refresh-layout .card > p.muted,
#universalView.refresh-layout .card .card-subtitle {
  color: var(--ud-text-3);
  font-size: 11.5px;
  margin: 0 0 6px;
  line-height: 1.4;
}

/* --- Composition: 12-column grid, equal-height rows -----------------
   DOM order is set by setupFixedLayoutForView(). Spans sum to 12/band:
     Band 1 : Desk Assignments (8) - My Shifts (4)
     Band 2 : Time Clock (4) - EDCT (4) - Dispatch Links (4)
     Band 3 : Passdown grid (full width)
     Band 4 : Open Shifts (6) - Coverage Trend (6)
     Band 5 : Junior Log (full width — Desk Schedule card removed) */
#universalView.refresh-layout > #deskAssignmentsCardUniversal { grid-column: span 8; }
#universalView.refresh-layout > #myShiftsUniversalCard        { grid-column: span 4; }
#universalView.refresh-layout > #timeClockCard               { grid-column: span 4; }
#universalView.refresh-layout > #edctRouteRequestsCard       { grid-column: span 4; }
#universalView.refresh-layout > #mainLinksCard               { grid-column: span 4; }
/* Full-width band on its own — let it grow to fit the 7-day grid AND the
   schedule-adjusted panel instead of capping at 400px (which made the
   adjusted rows spill below the card). The adjusted list keeps its own
   320px scroll cap so the card can't grow without bound. */
#universalView.refresh-layout > #passdownGuideCardUniversal  { grid-column: 1 / -1; max-height: none; }
#universalView.refresh-layout > #openShiftsUniversalCard     { grid-column: span 6; }
#universalView.refresh-layout > #coverageTrendCardUniversal  { grid-column: span 6; }
#universalView.refresh-layout > #juniorLogCard              { grid-column: 1 / -1; }

/* --- Content fill: the main region of each card flexes to fill the
   stretched card, so equal-height rows have no internal dead space and
   long tables scroll within the card instead of overflowing. */
#universalView.refresh-layout #deskAssignmentsCardUniversal .desk-assign-table-wrap,
#universalView.refresh-layout #myShiftsUniversalCard .my-shifts-list,
#universalView.refresh-layout #timeClockCard .time-clock-today,
#universalView.refresh-layout #edctRouteRequestsCard .edct-route-my-requests,
#universalView.refresh-layout #mainLinksCard .link-grid,
#universalView.refresh-layout #passdownGuideCardUniversal .passdown-guide-grid-wrap,
#universalView.refresh-layout #openShiftsUniversalCard .open-shifts-list,
#universalView.refresh-layout #juniorLogCard .junior-log-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
#universalView.refresh-layout .time-clock-today-wrap,
#universalView.refresh-layout .edct-route-list { overflow: auto; }
#universalView.refresh-layout .edct-route-list { max-height: none; }

/* --- Component theming (token-based, works in both themes) --------- */
#universalView.refresh-layout .table th { color: var(--ud-text-2); }
#universalView.refresh-layout .junior-log-table th,
#universalView.refresh-layout .desk-schedule-table thead tr,
#universalView.refresh-layout .desk-schedule-table th {
  background: var(--ud-fill);
  color: var(--ud-text-2);
}
#universalView.refresh-layout .passdown-guide-grid th {
  background: var(--ud-fill);
  color: var(--ud-text-2);
}
#universalView.refresh-layout .passdown-guide-grid td {
  color: var(--ud-text);
  border-color: var(--ud-line);
}
#universalView.refresh-layout .passdown-guide-grid td.passdown-empty {
  background: var(--ud-fill);
}
#universalView.refresh-layout input,
#universalView.refresh-layout select,
#universalView.refresh-layout textarea,
#universalView.refresh-layout .junior-log-add-form select {
  background: var(--ud-fill);
  color: var(--ud-text);
  border: 1px solid var(--ud-fill-border);
}
#universalView.refresh-layout .btn:not(.primary):not(.danger) {
  background: var(--ud-fill);
  color: var(--ud-text);
  border: 1px solid var(--ud-fill-border);
}
#universalView.refresh-layout .btn:not(.primary):not(.danger):hover {
  background: var(--ud-fill-hover);
}
#universalView.refresh-layout .tab {
  background: var(--ud-fill);
  border: 1px solid var(--ud-fill-border);
  color: var(--ud-text-2);
}
#universalView.refresh-layout .tab:hover {
  background: var(--ud-fill-hover);
  color: var(--ud-text);
}
#universalView.refresh-layout .tab.active,
#universalView.refresh-layout .tab.is-active {
  background: var(--ud-accent);
  border-color: var(--ud-accent);
  color: var(--ud-on-accent);
}
#universalView.refresh-layout .badge {
  background: var(--ud-fill);
  color: var(--ud-text-2);
  border-color: var(--ud-fill-border);
}
#universalView.refresh-layout .open-shift-day-group {
  background: var(--ud-fill);
  border-color: var(--ud-line);
}
#universalView.refresh-layout .open-shift-day-group h4 { color: var(--ud-text); }
#universalView.refresh-layout .dash-link {
  background: var(--ud-fill);
  color: var(--ud-text);
  border-color: var(--ud-line);
}
#universalView.refresh-layout .dash-link:hover { background: var(--ud-fill-hover); }
#universalView.refresh-layout .my-shift-row:hover { background: var(--ud-fill); }
#universalView.refresh-layout .edct-route-item { background: var(--ud-fill); }

/* Coverage chart is drawn for a dark backdrop — seat it in an inset
   panel (dark in both themes) so it stays legible. */
#universalView.refresh-layout #coverageTrendCardUniversal .coverage-chart-wrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ud-chart-panel);
  border-radius: 12px;
  padding: 10px;
  /* Safety net: keep the canvas inside the wrap so a too-small wrap
     can't let the chart spill upward into the "How to read this"
     legend. The chart tooltip uses position:absolute INSIDE this
     wrap and is short enough to stay within bounds, so clipping
     here doesn't cut it off in practice. */
  overflow: hidden;
}
/* Cap the canvas's displayed height to its wrap so the aspect-ratio
   height can't exceed available space when the wrap is short. */
#universalView.refresh-layout #coverageTrendCardUniversal .coverage-chart-wrap > canvas {
  max-height: 100%;
}
#universalView.refresh-layout canvas { max-width: 100%; height: auto; display: block; }

/* When the "How to read this" panel is expanded, the help legend grows
   tall enough that the default 400px card cap squeezes the chart-wrap
   below the canvas's natural display height. Bump the card's max-height
   to give the chart room to draw at full size without overflowing the
   wrap (which was causing the canvas to visually cover the help panel). */
#universalView.refresh-layout > #coverageTrendCardUniversal:has(.coverage-trend-help[open]) {
  max-height: 720px;
}

/* --- Light-theme-only semantic touch-ups ---------------------------
   These pills/badges/tags carry meaning-coded colors tuned for a dark
   surface; on the light cards they need darker, higher-contrast tones.
   Dark theme keeps the originals (they read fine on the dark cards). */
html[data-theme="light"] #universalView.refresh-layout .badge.allowed {
  background: #e6f6ee; color: #1f8a5c; border-color: #bfe6d1;
}
html[data-theme="light"] #universalView.refresh-layout .badge.denied,
html[data-theme="light"] #universalView.refresh-layout .badge.danger {
  background: #fdebed; color: #c33b48; border-color: #f4ccd1;
}
html[data-theme="light"] #universalView.refresh-layout .badge.warn {
  background: #fdf1e0; color: #b3791f; border-color: #f1ddbb;
}
html[data-theme="light"] #universalView.refresh-layout .my-shift-status-pill.status-scheduled { color: #1c7a52; }
html[data-theme="light"] #universalView.refresh-layout .my-shift-status-pill.status-callout,
html[data-theme="light"] #universalView.refresh-layout .my-shift-status-pill.status-sick { color: #c0394b; }
html[data-theme="light"] #universalView.refresh-layout .my-shift-status-pill.status-pto { color: #9a7b1e; }
html[data-theme="light"] #universalView.refresh-layout .my-shift-status-pill.status-late { color: #b5651d; }
html[data-theme="light"] #universalView.refresh-layout .my-shift-row .my-shift-note { color: #5a5a66; }
html[data-theme="light"] #universalView.refresh-layout .junior-log-table .junior-log-reason { color: #44444e; }
html[data-theme="light"] #universalView.refresh-layout .junior-log-table .reason-tag.reason-junior { color: #9a7b1e; }
html[data-theme="light"] #universalView.refresh-layout .junior-log-table .reason-tag.reason-pickup { color: #2f5da3; }
html[data-theme="light"] #universalView.refresh-layout .time-clock-status-pill.is-open { color: #1c7a52; }
html[data-theme="light"] #universalView.refresh-layout .time-clock-status-pill.is-done { color: #1f5fb0; }
html[data-theme="light"] #universalView.refresh-layout .time-clock-status-pill.is-pending { color: #b3791f; }
html[data-theme="light"] #universalView.refresh-layout .time-clock-status-pill.is-off { color: #5c5c68; }
html[data-theme="light"] #universalView.refresh-layout .open-shift-meta .open-shift-group-tag {
  background: #eef3fc; border-color: #d3e3f8; color: #2f5da3;
}

/* --- Narrow viewports ---------------------------------------------- */
@media (max-width: 1100px) {
  #universalView.refresh-layout.active { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  #universalView.refresh-layout > #deskAssignmentsCardUniversal,
  #universalView.refresh-layout > #passdownGuideCardUniversal,
  #universalView.refresh-layout > #openShiftsUniversalCard,
  #universalView.refresh-layout > #juniorLogCard,
  #universalView.refresh-layout > #coverageTrendCardUniversal,
  #universalView.refresh-layout > #deskScheduleCard { grid-column: span 6; }
  #universalView.refresh-layout > #myShiftsUniversalCard,
  #universalView.refresh-layout > #timeClockCard,
  #universalView.refresh-layout > #edctRouteRequestsCard,
  #universalView.refresh-layout > #mainLinksCard { grid-column: span 3; }
}
@media (max-width: 680px) {
  #universalView.refresh-layout.active { grid-template-columns: 1fr; padding: 14px; }
  #universalView.refresh-layout > .card { grid-column: 1 / -1; max-height: none; }
}

/* ====================================================================
   FIXED LAYOUT + REORDER — non-Universal dashboards
   --------------------------------------------------------------------
   The drag/resize grid is retired. The Universal Dashboard keeps its
   hand-composed grid (the #universalView.refresh-layout rules above);
   every OTHER dashboard gets this generic responsive auto-fill grid.
   Widget ORDER is the only layout customization — set per dashboard
   via the Customize button + the reorder dialog.
   ==================================================================== */
/* Single-column stack: every card spans the full content width. This
   gives wide content (permission matrices, JSON dumps, big tables)
   the room it needs, eliminates the dead space a multi-column grid
   leaves between mismatched-height cards, and makes "reorder" a clean
   one-dimensional list. */
.refresh-layout.active:not(#universalView) {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  margin-top: 12px;
  padding: 14px;
  border-radius: 16px;
  background: var(--bg);
}
.refresh-layout:not(#universalView) > .card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 13px 15px;
  margin: 0;
  /* keep the card at the column width even when its content is wide —
     wide inner elements scroll within themselves (rules below) rather
     than stretching the card or spilling outside it */
  min-width: 0;
  overflow: hidden;
}
/* Wide inner content scrolls inside the card instead of overflowing:
   table wrappers scroll horizontally, the JSON inspector and any <pre>
   scroll both ways. */
.refresh-layout:not(#universalView) .ops-table-wrap,
.refresh-layout:not(#universalView) [class$="-table-wrap"],
.refresh-layout:not(#universalView) [class*="-table-wrap "] {
  overflow: auto;
  max-width: 100%;
}
.refresh-layout:not(#universalView) pre,
.refresh-layout:not(#universalView) .super-state-pre {
  overflow: auto;
  max-width: 100%;
}
/* The State Inspector dumps the entire app state as JSON — without a
   cap the card runs a mile long and buries every widget below it.
   Cap the height so the JSON scrolls inside a fixed-size box. */
.refresh-layout:not(#universalView) .super-state-pre {
  max-height: 420px;
}
/* The Role x Permission audit matrix has ~20+ permission columns —
   let the cells keep their text on one line so the table reaches its
   natural width and scrolls inside its wrapper, instead of crushing
   every header down to one letter per line. */
.refresh-layout:not(#universalView) .super-roles-table th,
.refresh-layout:not(#universalView) .super-roles-table td {
  white-space: nowrap;
}

/* ====================================================================
   SUPERVISOR DASHBOARD — compact masonry (no dead space)
   --------------------------------------------------------------------
   The generic non-universal layout above stacks every card full width
   in a single flex column — very tall, lots of scrolling, wide dead
   space beside the narrow cards. A fixed 12-column grid still leaves
   gaps because (a) the supervisor cards have wildly different natural
   heights and (b) the order is user-customizable (the Customize
   reorder dialog persists a per-user order), so any hand-packed
   band/span plan breaks the moment a card is moved.

   To get NO dead space regardless of order we use a CSS multi-column
   masonry: cards flow into balanced columns and pack tight with no
   vertical gaps. Two things make it clean:
     1. Each card is height-capped and its table/list region scrolls
        INSIDE the card (rules below). Nothing balloons — the 30-row
        employee roster, the duty/flight tables, etc. all scroll within
        a normal-height card instead of creating a giant one.
     2. The genuinely wide tables span every column (column-span: all)
        so they stay readable; the narrow cards masonry-pack around
        them. Customize/reorder still works — column flow follows DOM
        order. ID specificity + later source order beat the generic
        :not(#universalView) rules above. */
#supervisorView.refresh-layout.active {
  display: block;
  column-count: 2;
  column-gap: 11px;
}
/* Title block and the wide tables span all columns. */
#supervisorView.refresh-layout.active > .title-row {
  column-span: all;
  margin-bottom: 11px;
}

/* Compact typography to match the Universal Dashboard's denser cards —
   tighter header margin and smaller heading / intro text, less dead
   space inside each card. Colors are left to the existing dark tokens. */
#supervisorView.refresh-layout .card-header { margin-bottom: 4px; }
#supervisorView.refresh-layout .card-header h3 {
  font-size: 13.5px;
  font-weight: 600;
}
#supervisorView.refresh-layout .card > p.muted {
  font-size: 11.5px;
  margin: 0 0 6px;
  line-height: 1.4;
}

/* Every card: a height-capped flex column that never splits across a
   column break. The cap turns long content into an internal scroll
   (region rules below) instead of a giant card. margin-bottom is the
   vertical gap between stacked cards (multicol has no row gap). */
#supervisorView.refresh-layout.active > .card {
  display: flex;
  flex-direction: column;
  max-height: 360px;
  margin: 0 0 11px;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}

/* Only the genuinely wide, many-column data tables span all columns —
   they need the full width to stay readable. Keeping this set small
   means fewer masonry breaks, so the remaining cards pack into the
   columns evenly (each column-span card fragments the flow into a
   segment that has to balance on its own). They also get a bit more
   height since they're the data-heavy widgets. */
#supervisorView.refresh-layout.active > #opsDesksPanel,
#supervisorView.refresh-layout.active > #dutyComplianceCard,
#supervisorView.refresh-layout.active > #dailyFlightCheckCard,
#supervisorView.refresh-layout.active > #supervisorTimeClockCard {
  column-span: all;
  max-height: 460px;
}

/* The Dispatcher Point System table scrolls inside its own wrapper
   (.point-summary-wrap), capped to ~10 rows so a full dispatcher roster
   doesn't make the card huge. The card itself sizes to that wrapper. */
#supervisorView.refresh-layout.active > #dispatcherPointCard { max-height: none; }
#supervisorView.refresh-layout .point-summary-wrap {
  /* header (~30px) + ~10 rows (~29px each) before scrolling */
  max-height: 330px;
  overflow-y: auto;
}
/* Keep the column headers visible while the roster scrolls. */
#supervisorView.refresh-layout .point-summary-wrap thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
}

/* --- Inner content fill: each card's primary table/list region flexes
   to fill the capped card and scrolls within it, so the card body never
   overflows and there's no dead space below short content. */
#supervisorView.refresh-layout > #briefingCard .briefing-body,
#supervisorView.refresh-layout > #requestQueueSupervisorCard .request-list,
#supervisorView.refresh-layout > #supervisorTasksCard .list,
#supervisorView.refresh-layout > #supervisorLinksCard .link-grid,
#supervisorView.refresh-layout > #passDownLogCard textarea,
#supervisorView.refresh-layout > #dutyComplianceCard .ops-table-wrap,
#supervisorView.refresh-layout > #dailyFlightCheckCard .ops-table-wrap,
#supervisorView.refresh-layout > #blackoutCard .blackout-list,
#supervisorView.refresh-layout > #remindersCard .reminders-list,
#supervisorView.refresh-layout > #supRemindersCard .reminders-list,
#supervisorView.refresh-layout > #shiftColorsCard .shift-colors-list,
#supervisorView.refresh-layout > #supervisorTimeClockCard .ops-table-wrap,
#supervisorView.refresh-layout > #edctRouteRequestsSupervisorCard .edct-route-list,
#supervisorView.refresh-layout > #bidSeniorityCard .bid-seniority-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  overflow: auto;
}

/* User settings / Desks panels: the ops-block wraps the scrolling table,
   so the flex chain (card → ops-block → ops-table-wrap) all needs to
   grow + allow shrink for the table to scroll inside the capped card.
   This also overrides the older max-height:none on #opsControlPanel. */
#supervisorView.refresh-layout > #opsControlPanel .ops-block,
#supervisorView.refresh-layout > #opsDesksPanel .ops-block {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#supervisorView.refresh-layout > #opsControlPanel .ops-table-wrap,
#supervisorView.refresh-layout > #opsDesksPanel .ops-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  overflow: auto;
}

/* Narrow viewports: collapse to a single masonry column. */
@media (max-width: 900px) {
  #supervisorView.refresh-layout.active { column-count: 1; }
}

/* ====================================================================
   OTHER CARD DASHBOARDS — same compact masonry (Super User, Daily Ops
   Brief, Desk Assignment, Scheduler)
   --------------------------------------------------------------------
   Same multi-column masonry as the Supervisor Dashboard so every
   non-Universal dashboard reads the same way: cards pack into balanced
   columns with no vertical dead space, the genuinely wide widgets (big
   audit/log tables, the desk tables, the scheduler calendar) span all
   columns, and the narrow cards masonry-pack around them.

   Unlike the Supervisor rules, these cards are NOT height-capped — they
   keep their natural height so nothing is clipped. Tall tables already
   scroll inside their own .ops-table-wrap / [*-table-wrap] containers
   (rules earlier in this file), which keeps any one card from growing
   without bound. Card order still follows the DOM / Customize order. */
#superUserView.refresh-layout.active,
#dailyOpsBriefView.refresh-layout.active,
#deskAssignmentView.refresh-layout.active,
#schedulerView.refresh-layout.active {
  display: block;
  column-count: 2;
  column-gap: 11px;
}
#superUserView.refresh-layout.active > .title-row,
#dailyOpsBriefView.refresh-layout.active > .title-row,
#deskAssignmentView.refresh-layout.active > .title-row,
#schedulerView.refresh-layout.active > .title-row {
  column-span: all;
  margin-bottom: 11px;
}

/* Compact typography to match the Universal / Supervisor dashboards. */
#superUserView.refresh-layout .card-header,
#dailyOpsBriefView.refresh-layout .card-header,
#deskAssignmentView.refresh-layout .card-header,
#schedulerView.refresh-layout .card-header { margin-bottom: 4px; }
#superUserView.refresh-layout .card-header h3,
#dailyOpsBriefView.refresh-layout .card-header h3,
#deskAssignmentView.refresh-layout .card-header h3,
#schedulerView.refresh-layout .card-header h3 {
  font-size: 13.5px;
  font-weight: 600;
}
#superUserView.refresh-layout .card > p.muted,
#dailyOpsBriefView.refresh-layout .card > p.muted,
#deskAssignmentView.refresh-layout .card > p.muted,
#schedulerView.refresh-layout .card > p.muted {
  font-size: 11.5px;
  margin: 0 0 6px;
  line-height: 1.4;
}

/* Each card: natural height, never split across a column break, with a
   bottom margin as the vertical gap (multicol has no row gap). */
#superUserView.refresh-layout.active > .card,
#dailyOpsBriefView.refresh-layout.active > .card,
#deskAssignmentView.refresh-layout.active > .card,
#schedulerView.refresh-layout.active > .card {
  margin: 0 0 11px;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}

/* Wide widgets span all columns so their tables / calendar keep width.
   Super User: stats grid + the two widest audit tables.
   Desk Assignment: flight list, cycle-coverage strip, desk table.
   Scheduler: the calendar + the full-width request inbox. */
#superUserView.refresh-layout.active > #superStatsCard,
#superUserView.refresh-layout.active > #superRolesCard,
#superUserView.refresh-layout.active > #securityLogCard,
#deskAssignmentView.refresh-layout.active > #flightListCard,
#deskAssignmentView.refresh-layout.active > #dailyFlightCheckStripCard,
#deskAssignmentView.refresh-layout.active > #deskAssignmentsCard,
#schedulerView.refresh-layout.active > .schedule-card,
#schedulerView.refresh-layout.active > #dispatcherRequestInboxCard {
  column-span: all;
}

/* Narrow viewports: collapse each to a single masonry column. */
@media (max-width: 900px) {
  #superUserView.refresh-layout.active,
  #dailyOpsBriefView.refresh-layout.active,
  #deskAssignmentView.refresh-layout.active,
  #schedulerView.refresh-layout.active { column-count: 1; }
}

/* EDCT / Route Requests (Universal Dashboard) — the submission form is
   taller than the card's max-height. Let the whole card scroll as one
   block instead of spilling its inputs out the bottom. */
#universalView.refresh-layout > #edctRouteRequestsCard {
  display: block;
  overflow-y: auto;
}

/* Customize button — top-right of every dashboard's title row. */
.refresh-layout > .title-row { position: relative; }
.customize-layout-btn {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: 0.76rem;
  font-weight: 600;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--chip-bg);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.customize-layout-btn:hover {
  background: var(--panel-soft);
  color: var(--text);
  border-color: var(--blue);
}
.customize-layout-btn svg { width: 13px; height: 13px; flex: none; }

/* Reorder dialog — drag-and-drop list of the active dashboard's
   widgets. Box chrome comes from the shared .shift-dialog class; these
   rules only style the list and its draggable rows. */
.widget-order-list {
  list-style: none;
  margin: 12px 0 4px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 52vh;
  overflow-y: auto;
}
.widget-order-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--chip-bg);
  color: var(--text);
  font-size: 0.86rem;
  user-select: none;
}
.widget-order-item.is-dragging {
  opacity: 0.45;
  border-color: var(--blue);
}
/* The grip is the drag handle. touch-action:none lets a finger drag it on
   iPad without the page scrolling; the rest of the row stays scrollable. */
.widget-order-grip {
  color: var(--muted);
  font-size: 1.2rem;
  line-height: 1;
  letter-spacing: -2px;
  flex: none;
  cursor: grab;
  touch-action: none;
  padding: 8px 6px;
  margin: -8px 2px -8px -4px;
  border-radius: 6px;
}
.widget-order-grip:active { cursor: grabbing; }
.widget-order-grip:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }
.widget-order-name { min-width: 0; flex: 1 1 auto; }

/* Show/hide toggle on the right of each row in the Customize dialog. */
.widget-order-show {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--muted);
  flex: none;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
  user-select: none;
}
.widget-order-show:hover {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
}
.widget-order-show input[type="checkbox"] {
  cursor: pointer;
  margin: 0;
}
.widget-order-show input[type="checkbox"]:checked + span {
  color: var(--text);
  font-weight: 500;
}

/* Hidden-card style. Applied to dashboard widgets the supervisor has
   chosen to hide via the Customize dialog. We use display:none so the
   card occupies zero space, but the card stays in the DOM so its
   listeners and cached state survive a hide→show toggle. */
.is-widget-hidden {
  display: none !important;
}

/* ====================================================================
   Supervisor notifications — bell + popover panel
   --------------------------------------------------------------------
   The bell lives in the sidebar footer (hidden for non-supervisors).
   Click toggles a fixed-position popover with Open / Completed tabs.
   Each row has an icon (kind-colored), title, summary, age, and either
   "Open" + "Mark completed" buttons (open rows) or a completion stamp
   ("by Sarah · 2h ago") for completed rows.
   ==================================================================== */
.notifications-bell { position: relative; padding: 7px 9px; }
.notifications-bell svg { width: 15px; height: 15px; display: block; }
.notifications-bell-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  border-radius: 999px;
  padding: 0 4px;
  background: #e24b4a;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  box-shadow: 0 0 0 2px var(--panel);
}

.notifications-panel {
  /* Anchored to the top-right since the bell moved from the sidebar
     foot to the topbar user area. Previously: bottom: 16px; left: 260px;
     (anchored to the old bell at the bottom of the 260 px sidebar).
     New anchor: just below the topbar, flush to the right edge with a
     16 px inset that lines up roughly under the bell. */
  position: fixed;
  top: 72px;
  right: 16px;
  width: 380px;
  max-height: 72vh;
  z-index: 1000;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.15);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  color: var(--text);
}
.notifications-panel[hidden] { display: none !important; }
.notifications-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 11px 14px 8px;
  border-bottom: 1px solid var(--line);
}
.notifications-panel-head h3 { margin: 0; font-size: 0.96rem; }
.notifications-panel-close {
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 6px;
}
.notifications-panel-close:hover { color: var(--text); background: var(--chip-bg); }

.notifications-tabs { display: flex; gap: 4px; padding: 8px 12px 0; }
.notifications-tab {
  background: transparent;
  border: 0;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 6px 10px;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.notifications-tab:hover { color: var(--text); background: var(--chip-bg); }
.notifications-tab.is-active { color: var(--text); background: var(--chip-bg); }
.notifications-tab-count {
  font-size: 0.7rem;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--field-bg);
  color: var(--muted);
}
.notifications-tab.is-active .notifications-tab-count {
  background: var(--blue);
  color: #fff;
}

.notifications-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.notifications-empty {
  color: var(--muted);
  font-size: 0.84rem;
  text-align: center;
  padding: 24px 12px;
}
.notification-item {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) auto;
  gap: 9px;
  align-items: start;
  padding: 9px 10px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--chip-bg);
}
.notification-item--completed { opacity: 0.7; }
.notification-item-icon {
  width: 22px;
  height: 22px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  font-size: 0.72rem;
  font-weight: 700;
  color: #fff;
  background: var(--blue);
}
.notification-item-icon[data-kind="request"] { background: #1f74df; }
.notification-item-icon[data-kind="edct"] { background: #6a5acd; }
.notification-item-icon[data-kind="callout"] { background: #c33b48; }
.notification-item-icon[data-kind="faa"] { background: #b3791f; }
.notification-item-icon[data-kind="coverage"] { background: #1f8a5c; }
.notification-item-body { min-width: 0; }
.notification-item-title {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.notification-item-summary {
  font-size: 0.76rem;
  color: var(--muted);
  margin: 2px 0 0;
  line-height: 1.35;
}
.notification-item-meta { font-size: 0.7rem; color: var(--muted); margin-top: 3px; }
.notification-item-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex: none;
}
.notification-item-open,
.notification-item-complete,
.notification-item-approve,
.notification-item-deny {
  border: 1px solid var(--line);
  background: var(--field-bg);
  color: var(--text);
  padding: 4px 9px;
  border-radius: 7px;
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.notification-item-open:hover,
.notification-item-complete:hover { border-color: var(--blue); }
.notification-item-complete.is-primary {
  background: var(--blue);
  color: #fff;
  border-color: var(--blue);
}
/* Approve / Deny on the supervisor's request notifications. Approve
   matches the primary blue treatment so it's the obvious affirmative
   action; Deny gets a red outline that turns solid on hover so it's
   clearly the destructive path without screaming for attention by
   default. */
.notification-item-approve.is-primary {
  background: var(--green, #22c55e);
  color: #001a0d;
  border-color: var(--green, #22c55e);
}
.notification-item-approve.is-primary:hover {
  filter: brightness(1.08);
}
.notification-item-deny {
  color: var(--red, #ff6b7b);
  border-color: var(--red, #ff6b7b);
}
.notification-item-deny:hover {
  background: var(--red, #ff6b7b);
  color: #fff;
}
.notification-completion {
  font-size: 0.7rem;
  color: var(--muted);
  white-space: nowrap;
  text-align: right;
  line-height: 1.3;
}
.notification-completion strong { color: var(--text); font-weight: 600; }

@media (max-width: 860px) {
  .notifications-panel {
    /* On narrow screens, center horizontally and overlay near the
       top of the viewport so it doesn't collide with the (now
       likely-wrapped) topbar. Clears `right` because we set `left`. */
    top: 72px;
    right: auto;
    left: 50%;
    transform: translateX(-50%);
    width: calc(100vw - 24px);
    max-width: 420px;
  }
}

/* ============================================================
   PHASE 6 — Glass UI + login-screen brand backdrop.

   Dark-theme only. Cards, the sidebar, and the topbar all become
   translucent frosted panels so the Breeze Dispatch brand SVG that
   sits on the body background reads through every surface uniformly
   instead of only showing in the gaps between cards. Tables, inputs,
   dialogs stay opaque so the content they hold remains crisp.

   Login screen gets a layered version of the same treatment: the
   brand SVG fills the screen behind the form; the login card is
   itself a glass panel sitting over the brand. The existing brand
   "A" mark + "Breeze Aerodesk / Dispatch Center" text stays in
   place (Option B from the design proposal).

   Light theme stays clean (no glass). Users with the OS
   "Reduce transparency" preference get an opaque fallback.
   ============================================================ */

html[data-theme="dark"] .card,
html:not([data-theme]) .card {
  /* Ultra-translucency glass — 28% panel + 30 px blur + 200%
     saturate. Brand watermark reads clearly through every card.
     The heavy blur is what keeps text readable: it dissolves any
     local cyan/amber detail in the SVG into a smooth navy color
     wash that white body text wins easily against. */
  background-color: rgba(13, 20, 47, 0.28);
  background-blend-mode: normal;
  backdrop-filter: blur(30px) saturate(200%);
  -webkit-backdrop-filter: blur(30px) saturate(200%);
  border-color: rgba(255, 255, 255, 0.12);
}
/* Keep the depth-overlay gradient on top of the translucency. Phase
   1's .card rule sets background-image to the depth overlay; that
   stays, just sits over the translucent base color now. */

html[data-theme="dark"] .card.kpi-floating,
html:not([data-theme]) .card.kpi-floating {
  /* Floating tier — slightly more opaque so the hero KPIs read as
     the "front" layer above default cards. 35% panel + 34 px blur. */
  background-color: rgba(13, 20, 47, 0.35);
  backdrop-filter: blur(34px) saturate(200%);
  -webkit-backdrop-filter: blur(34px) saturate(200%);
  border-color: rgba(255, 255, 255, 0.16);
}

/* Opt-outs that need an opaque backing to stay readable. */
html[data-theme="dark"] .card.kpi-flat,
html:not([data-theme]) .card.kpi-flat {
  background-color: var(--panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Tables — opaque rows so the dense data is legible. */
html[data-theme="dark"] .card .table tbody tr,
html[data-theme="dark"] .card table tbody tr,
html:not([data-theme]) .card .table tbody tr,
html:not([data-theme]) .card table tbody tr {
  background: rgba(13, 20, 47, 0.85);
}

/* Form inputs — opaque so the typed text reads cleanly. */
html[data-theme="dark"] .card input,
html[data-theme="dark"] .card textarea,
html[data-theme="dark"] .card select,
html:not([data-theme]) .card input,
html:not([data-theme]) .card textarea,
html:not([data-theme]) .card select {
  background-color: var(--field-bg);
}

/* Sidebar — heavy glass strip on the left edge. The whole left rail
   feels frosted now; the brand backdrop bleeds clearly through to
   the nav links. Stronger blur (32 px) compensates for the 30%
   opacity so the icon + label on each nav-link stays sharp. */
html[data-theme="dark"] .app-sidebar,
html:not([data-theme]) .app-sidebar {
  background-color: rgba(8, 17, 38, 0.30);
  backdrop-filter: blur(32px) saturate(200%);
  -webkit-backdrop-filter: blur(32px) saturate(200%);
  border-right: 1px solid rgba(255, 255, 255, 0.06);
}

/* Topbar HUD — glass strip across the top. The accent-sweep line
   underneath stays at full opacity since it's an animation cue. */
html[data-theme="dark"] .app-topbar,
html:not([data-theme]) .app-topbar {
  background-color: rgba(6, 10, 27, 0.40);
  backdrop-filter: blur(30px) saturate(200%);
  -webkit-backdrop-filter: blur(30px) saturate(200%);
}

/* Dialogs / modals — keep opaque. The dialog backdrop overlay
   already darkens the page behind the modal; layering glass on top
   of glass would be muddy and reduce text contrast. */
html[data-theme="dark"] dialog.shift-dialog,
html:not([data-theme]) dialog.shift-dialog {
  background-color: var(--panel);
  backdrop-filter: none;
}

/* Scheduler view — opt out of glass on the dense schedule cards.
   Their grid + per-cell rendering would compete with the watermark
   for visual attention. Keep them opaque so the calendar reads as
   a clean reference grid. */
html[data-theme="dark"] #schedulerView .card,
html:not([data-theme]) #schedulerView .card {
  background-color: var(--panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: var(--depth-border);
}

/* LIGHT THEME — no glass. Clean opaque panels on a bright work
   surface. (No selectors needed — the cascade lets the original
   .card rule win because the dark-theme overrides above are scoped
   to html[data-theme="dark"].) */

/* REDUCED TRANSPARENCY — accessibility fallback. Users who set the
   OS "Reduce transparency" preference get an opaque UI regardless
   of theme. */
@media (prefers-reduced-transparency: reduce) {
  .card,
  .card.kpi-floating,
  .app-sidebar,
  .app-topbar {
    background-color: var(--panel) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* ============================================================
   LOGIN SCREEN — brand backdrop + glass form.

   The brand SVG fills the entire login screen as a fixed-cover
   image, layered behind two soft radial glows that tint the scene
   with the view's accent color. The existing .login-card becomes a
   frosted glass panel centered over the brand; the existing "A"
   brand mark and "Breeze Aerodesk / Dispatch Center" text stay
   inside the card (Option B).

   Light theme skips the SVG since the brand colors clash with a
   bright bg — just keeps the original radial-gradient login bg.
   ============================================================ */

/* Full-bleed Breeze Dispatch SVG. The new SVG (v2) carries the
   compass + wordmark on the left/center AND a built-in elliptical
   "form area" on the right (a faint blue glow at SVG coords
   1530, 540 with radius ~320×340). The login card is anchored to
   that glow zone via JS-set CSS variables — see bootstrap.js. */
html[data-theme="dark"] .login-screen,
html:not([data-theme]) .login-screen {
  background-color: #050a1a;
  background-image:
    /* Accent radial — gentle color wash */
    radial-gradient(800px 600px at 50% 35%, var(--view-accent-soft) 0%, transparent 60%),
    /* Edge vignette to fade the SVG into bg at the viewport edges */
    radial-gradient(ellipse 130% 110% at 50% 50%, transparent 28%, rgba(5, 10, 26, 0.45) 80%, #050a1a 100%),
    /* The Breeze Dispatch SVG */
    url("../assets/breeze-dispatch-logo.svg");
  background-size: auto, auto, contain;
  background-position: center, center, center center;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

/* Login card — fully transparent. The new SVG bakes a soft blue
   glow into the form area, so the card needs no chrome of its own:
   no background, no border, no shadow. The form inputs sit directly
   on the SVG's glow zone. A very slight backdrop-filter is kept
   only to soften the watermark text behind the inputs, not to
   create a visible card surface. */
html[data-theme="dark"] .login-card,
html:not([data-theme]) .login-card {
  background-color: transparent;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  border: none;
  box-shadow: none;
  padding: 0;
}
/* Bump muted text on the login card the same way we did on the
   dashboard cards — at 40% opacity the brand bleeds through, so
   secondary text needs a contrast lift to stay readable. */
html[data-theme="dark"] .login-card .muted,
html:not([data-theme]) .login-card .muted,
html[data-theme="dark"] .login-card label,
html:not([data-theme]) .login-card label {
  color: #d6e1f3;
}

/* Reduced transparency / light theme — opaque login card. */
@media (prefers-reduced-transparency: reduce) {
  .login-card {
    background-color: var(--panel) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* ============================================================
   Form-area-anchored login card.

   The new Breeze Dispatch SVG has a built-in elliptical "form area"
   on the right side (a soft blue glow centered at SVG coords
   1530, 540 with radius 320×340). The login card is absolutely
   positioned over that glow zone using CSS variables that
   bootstrap.js writes on load and on every resize:
     --login-form-x  →  form-area center, left position in px
     --login-form-y  →  form-area center, top position in px
     --login-form-w  →  ideal card width given the SVG's render size

   The card itself has no chrome (transparent background, no
   border) so the form appears to live INSIDE the SVG's glow zone.

   Below 1024 px the card falls back to a small inset on the right
   side of the screen so the SVG and form don't overlap into an
   unusable layout on phones.
   ============================================================ */
.login-screen {
  position: fixed;
}
@media (min-width: 1024px) {
  .login-screen {
    display: block;
    padding: 0;
  }
  .login-card {
    position: absolute;
    left: var(--login-form-x, 80%);
    top: var(--login-form-y, 50%);
    transform: translate(-50%, -50%);
    width: var(--login-form-w, 340px);
    max-width: var(--login-form-w, 340px);
  }
}

/* Gentle fade-in for the login card. Replaces the previous
   seven-variant random entrance — distracting against the brand
   backdrop. .enter-fade is added by applyRandomLoginEntrance()
   in app.js when the login screen first appears AND on every
   sign-out so the fade plays again on the next login. */
.login-card.enter-fade {
  animation: login-fade-in 0.35s ease-out both;
}
@keyframes login-fade-in {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

/* ============================================================
   PHASE 6.1 — Readability boost for glass-on-brand surfaces.

   The Phase 6 glass treatment dropped panel opacity to 55-65% so
   the brand watermark shows through more clearly. To keep
   secondary text legible on the now-busier backdrop, this block:
     • Bumps `.muted` text on .card from var(--muted) (#8fa4c8) to
       a slightly brighter shade so hint lines under headers stay
       readable.
     • Adds a thin text-shadow on body text inside cards so glyph
       edges don't dissolve into the bg color wash.
   Light theme keeps its current opaque cards, so neither rule
   applies there.
   ============================================================ */
html[data-theme="dark"] .card .muted,
html:not([data-theme]) .card .muted,
html[data-theme="dark"] .card .small.muted,
html:not([data-theme]) .card .small.muted {
  color: #d1e0f5;  /* Bumped again for the 28%-opacity glass */
}
html[data-theme="dark"] .card,
html:not([data-theme]) .card {
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.18);
}
/* Strip the text-shadow off form inputs (it would smudge typed text)
   and from large headings (already heavy weight, doesn't need it). */
html[data-theme="dark"] .card input,
html[data-theme="dark"] .card textarea,
html[data-theme="dark"] .card select,
html[data-theme="dark"] .card h1,
html[data-theme="dark"] .card h2,
html:not([data-theme]) .card input,
html:not([data-theme]) .card textarea,
html:not([data-theme]) .card select,
html:not([data-theme]) .card h1,
html:not([data-theme]) .card h2 {
  text-shadow: none;
}

/* ============================================================
   PHASE 7 — Login entrance variants + Universal Dashboard puzzle
   assembly.

   Login: six entrance keyframes; JS picks one at random when the
   login screen appears (showLoginScreen → applyRandomLoginEntrance).
   Same destination (centered card) every time, different path
   to get there.

   Universal Dashboard puzzle: when activateView('universal') fires,
   JS marks the view with .puzzle-active and tags every .card with
   .puzzle-piece + a random --puzzle-delay (0–600 ms). CSS keyframe
   handles the fade + slide-up. The Phase 5 card-rise on .is-entering
   is suppressed inside .puzzle-active so the two animations don't
   compose.

   Both systems respect prefers-reduced-motion.
   ============================================================ */

/* Common card transform origin for entrance animations — center
   means the scale/rotate happens around the card's middle. */
.login-card {
  transform-origin: center center;
  will-change: transform, opacity;
}

/* --- Login entrance variants ---
   Dramatically longer durations (800–1100 ms) and bigger travel
   distances so each variant feels distinct and unmistakably
   different from the others. All easings overshoot slightly so
   the card "lands" with a small back-and-forth settle. */
.login-card.enter-slide-up {
  animation: login-slide-up 0.85s cubic-bezier(0.34, 1.56, 0.64, 1.0) both;
}
.login-card.enter-slide-down {
  animation: login-slide-down 0.85s cubic-bezier(0.34, 1.56, 0.64, 1.0) both;
}
.login-card.enter-slide-left {
  animation: login-slide-left 0.85s cubic-bezier(0.34, 1.56, 0.64, 1.0) both;
}
.login-card.enter-slide-right {
  animation: login-slide-right 0.85s cubic-bezier(0.34, 1.56, 0.64, 1.0) both;
}
.login-card.enter-scale-in {
  animation: login-scale-in 0.95s cubic-bezier(0.22, 1.0, 0.36, 1.0) both;
}
.login-card.enter-rotate-in {
  animation: login-rotate-in 1.00s cubic-bezier(0.22, 1.0, 0.36, 1.0) both;
}
.login-card.enter-zoom-blur {
  animation: login-zoom-blur 1.10s cubic-bezier(0.22, 1.0, 0.36, 1.0) both;
}
@keyframes login-slide-up {
  0%   { opacity: 0; transform: translateY(220px) scale(0.92); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes login-slide-down {
  0%   { opacity: 0; transform: translateY(-220px) scale(0.92); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes login-slide-left {
  0%   { opacity: 0; transform: translateX(300px) scale(0.92); }
  100% { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes login-slide-right {
  0%   { opacity: 0; transform: translateX(-300px) scale(0.92); }
  100% { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes login-scale-in {
  0%   { opacity: 0; transform: scale(0.12); }
  55%  { opacity: 1; transform: scale(1.08); }
  80%  { transform: scale(0.97); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes login-rotate-in {
  0%   { opacity: 0; transform: rotate(-15deg) scale(0.55); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: rotate(0deg) scale(1); }
}
@keyframes login-zoom-blur {
  0%   { opacity: 0; transform: scale(1.5); filter: blur(20px); }
  60%  { opacity: 1; filter: blur(0); }
  100% { opacity: 1; transform: scale(1); filter: blur(0); }
}

/* --- Universal Dashboard puzzle cascade --- */
.view.puzzle-active .card.puzzle-piece {
  animation: puzzle-fall 0.48s cubic-bezier(0.18, 1.0, 0.32, 1.0) both;
  animation-delay: var(--puzzle-delay, 0ms);
  /* will-change is set ON the cards being animated only — not
     globally — so the browser only promotes them while they're
     animating. */
  will-change: transform, opacity;
}
@keyframes puzzle-fall {
  0% {
    opacity: 0;
    transform: translateY(-24px) scale(0.92);
    filter: blur(6px);
  }
  60% {
    opacity: 1;
    filter: blur(0);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0);
  }
}
/* Suppress the Phase 5 .is-entering card-rise while the puzzle is
   running — otherwise both animations would compose and the cards
   would jitter. The puzzle cascade owns Universal's entrance now. */
.view.puzzle-active.is-entering .card {
  animation: puzzle-fall 0.48s cubic-bezier(0.18, 1.0, 0.32, 1.0) both;
  animation-delay: var(--puzzle-delay, 0ms);
}

@media (prefers-reduced-motion: reduce) {
  .login-card.enter-slide-up,
  .login-card.enter-slide-down,
  .login-card.enter-slide-left,
  .login-card.enter-slide-right,
  .login-card.enter-scale-in,
  .login-card.enter-rotate-in,
  .login-card.enter-zoom-blur,
  .view.puzzle-active .card.puzzle-piece,
  .view.puzzle-active.is-entering .card {
    animation: none !important;
  }
}

/* ====================================================================
   BID CYCLES — scoped under #bidAppView so these rules can't bleed
   into the rest of Aerodesk. The Bid module is identity-gated to
   Super User only; the renderer is src/modules/bid/view.js.
   ==================================================================== */
#bidAppView {
  --bid-accent: var(--breeze-tan, #FFD4AA);
  --bid-accent-soft: rgba(255, 212, 170, 0.18);
  --bid-card: var(--panel);
  --bid-card-border: var(--line);
  --bid-fill: var(--field-bg);
  --bid-fill-border: var(--line);
  --bid-text: var(--text);
  --bid-text-2: var(--muted);
}

#bidAppView .bid-subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 16px;
  padding: 4px;
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 10px;
}
#bidAppView .bid-tab {
  background: transparent;
  border: 1px solid transparent;
  color: var(--bid-text-2);
  padding: 7px 14px;
  border-radius: 7px;
  cursor: pointer;
  font-size: 13px;
  font-family: inherit;
  transition: all 0.15s ease;
}
#bidAppView .bid-tab:hover { color: var(--bid-text); background: rgba(255,255,255,0.04); }
#bidAppView .bid-tab.active {
  color: var(--bid-text);
  background: var(--bid-accent-soft);
  border-color: var(--bid-accent);
}

#bidAppView .bid-sub { display: none; }
#bidAppView .bid-sub.active { display: block; }

#bidAppView .bid-card {
  background: var(--bid-card);
  border: 1px solid var(--bid-card-border);
  border-radius: 12px;
  padding: 18px 20px;
  margin-bottom: 16px;
}
#bidAppView .bid-card h3 {
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bid-accent);
}
#bidAppView .bid-help {
  margin: 0 0 14px;
  font-size: 13px;
  color: var(--bid-text-2);
  line-height: 1.55;
}
#bidAppView .bid-help strong { color: var(--bid-text); }
#bidAppView .bid-help code {
  background: var(--bid-fill);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 12px;
}

#bidAppView .bid-workflow-card {
  background: linear-gradient(135deg, var(--bid-accent-soft), transparent);
  border: 1px solid var(--bid-accent);
  border-radius: 12px;
  padding: 16px 20px;
  margin-bottom: 16px;
  font-size: 13px;
}
#bidAppView .bid-workflow-card.compact { padding: 12px 16px; }
#bidAppView .bid-workflow-card strong { color: var(--bid-accent); }
#bidAppView .bid-workflow-card em { font-style: normal; color: var(--bid-accent); }
#bidAppView .bid-workflow-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bid-accent);
  margin-bottom: 12px;
}
#bidAppView .bid-workflow-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
}
#bidAppView .bid-wf-step {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: rgba(0,0,0,0.2);
  border: 1px solid var(--bid-card-border);
  border-radius: 8px;
  padding: 10px 12px;
}
#bidAppView .bid-wf-num {
  display: inline-flex;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--bid-accent);
  color: #001633;
  font-size: 12px;
  font-weight: 700;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
#bidAppView .bid-wf-step strong { display: block; font-size: 13px; margin-bottom: 2px; }
#bidAppView .bid-wf-sub { color: var(--bid-text-2); font-size: 11px; line-height: 1.4; }

#bidAppView .bid-form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-bottom: 12px;
}
#bidAppView .bid-checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  font-size: 13px;
  color: var(--bid-text);
  cursor: pointer;
  user-select: none;
}
#bidAppView .bid-checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
  margin: 0;
  flex: none;
}
#bidAppView .bid-form-row label {
  display: block;
  font-size: 11px;
  color: var(--bid-text-2);
  margin-bottom: 4px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
#bidAppView .bid-form-row input,
#bidAppView .bid-form-row select {
  width: 100%;
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  color: var(--bid-text);
  padding: 8px 10px;
  border-radius: 6px;
  font-family: inherit;
  font-size: 13px;
}

#bidAppView .bid-btn-row { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
#bidAppView .bid-btn-primary, #bidAppView .bid-btn-secondary, #bidAppView .bid-btn-danger {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 16px;
  border-radius: 6px;
  border: 1px solid transparent;
  cursor: pointer;
  font-weight: 500;
}
#bidAppView .bid-btn-primary {
  background: var(--bid-accent);
  color: #001633;
}
#bidAppView .bid-btn-primary:hover { filter: brightness(1.08); }
#bidAppView .bid-btn-primary:disabled,
#bidAppView .bid-btn-secondary:disabled,
#bidAppView .bid-btn-danger:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
}
#bidAppView .bid-btn-primary:disabled:hover,
#bidAppView .bid-btn-secondary:disabled:hover,
#bidAppView .bid-btn-danger:disabled:hover {
  filter: none;
  background: var(--bid-accent);
}

/* Brief outline-flash on a card that just got scrolled into view via
   a notification click — draws the eye to the card the supervisor or
   dispatcher just navigated to (the "Requests pending your approval"
   card is the primary user). Auto-cleared by JS after ~1.4s. */
@keyframes aerodesk-card-flash {
  0%   { box-shadow: 0 0 0 0 rgba(74, 163, 255, 0); }
  20%  { box-shadow: 0 0 0 4px rgba(74, 163, 255, 0.55); }
  100% { box-shadow: 0 0 0 0 rgba(74, 163, 255, 0); }
}
.card-flash {
  animation: aerodesk-card-flash 1.4s ease-out;
}

/* ---- Editor stale-roster warning ---- */
#bidAppView .bid-stale-warning {
  padding: 12px 16px;
  margin: 8px 0 14px;
  background: rgba(255, 107, 123, 0.10);
  border: 1px solid rgba(255, 107, 123, 0.45);
  border-left: 4px solid var(--red, #ff6b7b);
  border-radius: 6px;
  color: var(--bid-text);
  font-size: 13px;
  line-height: 1.5;
}
#bidAppView .bid-stale-warning strong {
  color: var(--red, #ff6b7b);
}

/* ---- Block-editor +/- row actions ---- */
#bidAppView .bid-block-actions {
  white-space: nowrap;
  text-align: right;
}
#bidAppView .bid-block-actions button {
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  width: 26px;
  height: 26px;
  margin-left: 4px;
  border-radius: 4px;
  border: 1px solid var(--bid-fill-border);
  background: var(--bid-fill);
  color: var(--bid-text);
  cursor: pointer;
  line-height: 1;
}
#bidAppView .bid-block-actions button:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--bid-accent);
  color: var(--bid-accent);
}
#bidAppView .bid-block-actions .bid-block-rm:hover {
  border-color: var(--red, #ff6b7b);
  color: var(--red, #ff6b7b);
}
#bidAppView .bid-btn-secondary {
  background: var(--bid-fill);
  color: var(--bid-text);
  border-color: var(--bid-fill-border);
}
#bidAppView .bid-btn-secondary:hover { background: rgba(255,255,255,0.04); }
#bidAppView .bid-btn-danger {
  background: transparent;
  border-color: var(--red, #ff6b7b);
  color: var(--red, #ff6b7b);
}

#bidAppView .bid-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin-top: 8px;
}
#bidAppView .bid-table th, #bidAppView .bid-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--bid-card-border);
}
#bidAppView .bid-table th {
  font-weight: 500;
  font-size: 11px;
  color: var(--bid-text-2);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
#bidAppView .bid-table input[type="number"] {
  width: 56px;
  padding: 4px 6px;
  text-align: center;
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  color: var(--bid-text);
  border-radius: 4px;
  font-family: inherit;
  font-size: 12px;
}

#bidAppView .bid-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--bid-accent-soft);
  color: var(--bid-accent);
  font-size: 11px;
  font-weight: 600;
}
#bidAppView .bid-pill-lg {
  padding: 4px 12px;
  font-size: 13px;
  letter-spacing: 0.04em;
}

/* ---- Bid lines: block-grouped calendar grid ---- */
#bidAppView .bid-lines-container { margin-top: 12px; }
#bidAppView .bid-lines-grouped {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* "Dispatchers working each day" summary strip above the per-block
   grids. Mini bar chart so the supervisor can eyeball whether staffing
   tracks the busy days. The busiest day's bar is highlighted. */
#bidAppView .bid-daily-totals {
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 14px;
}
#bidAppView .bid-daily-totals-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--bid-text-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 10px;
}
#bidAppView .bid-daily-totals-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  align-items: end;
}
#bidAppView .bid-daily-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
#bidAppView .bid-daily-bar-wrap {
  width: 100%;
  height: 56px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
#bidAppView .bid-daily-bar {
  width: 60%;
  min-height: 2px;
  background: var(--bid-accent);
  border-radius: 3px 3px 0 0;
  opacity: 0.55;
  transition: opacity 0.15s;
}
#bidAppView .bid-daily-col.is-max .bid-daily-bar {
  opacity: 1;
}
#bidAppView .bid-daily-count {
  font-size: 14px;
  font-weight: 700;
  color: var(--bid-text);
}
#bidAppView .bid-daily-col.is-max .bid-daily-count {
  color: var(--bid-accent);
}
#bidAppView .bid-daily-day {
  font-size: 10px;
  font-weight: 600;
  color: var(--bid-text-2);
  text-transform: uppercase;
}
#bidAppView .bid-daily-totals-note {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--bid-fill-border);
  font-size: 11px;
  line-height: 1.45;
  color: var(--bid-text-2);
}
#bidAppView .bid-daily-totals-note.is-warn {
  color: #b45309;
  font-weight: 500;
}

/* ---- Setup: weekend-off controls divider + subhead ---- */
#bidAppView .bid-divider {
  border: none;
  border-top: 1px solid var(--bid-fill-border);
  margin: 14px 0 10px;
}
#bidAppView .bid-subhead {
  font-size: 13px;
  font-weight: 700;
  margin: 0 0 4px;
  color: var(--bid-text);
}

/* ---- Editor: weekends-off readout ---- */
#bidAppView .bid-weekend-stats {
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 14px;
}
#bidAppView .bid-weekend-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--bid-text-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 8px;
}
#bidAppView .bid-weekend-headline {
  font-size: 13px;
  color: var(--bid-text);
  margin-bottom: 8px;
}
#bidAppView .bid-weekend-bar {
  display: flex;
  width: 100%;
  height: 14px;
  border-radius: 7px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
}
#bidAppView .bid-weekend-seg { height: 100%; }
#bidAppView .bid-weekend-seg.off-both  { background: #34d399; }
#bidAppView .bid-weekend-seg.off-one   { background: #fbbf24; }
#bidAppView .bid-weekend-seg.work-both { background: #64748b; }
#bidAppView .bid-weekend-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 8px;
  font-size: 11px;
  color: var(--bid-text-2);
}
#bidAppView .bid-weekend-legend .sw {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 5px;
  vertical-align: middle;
}
#bidAppView .bid-weekend-legend .sw.off-both  { background: #34d399; }
#bidAppView .bid-weekend-legend .sw.off-one   { background: #fbbf24; }
#bidAppView .bid-weekend-legend .sw.work-both { background: #64748b; }
#bidAppView .bid-weekend-note {
  margin-top: 8px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--bid-text-2);
}
#bidAppView .bid-weekend-warn {
  color: #b45309;
  font-weight: 500;
}
#bidAppView .bid-weekend-hint {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--bid-fill-border);
  font-size: 11px;
  line-height: 1.45;
  color: var(--bid-text-2);
}
#bidAppView .bid-lines-block {
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 8px;
  padding: 12px 14px;
}
#bidAppView .bid-lines-block-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
#bidAppView .bid-lines-block-hours {
  font-size: 12px;
  color: var(--bid-text);
  font-weight: 500;
}
#bidAppView .bid-lines-block-count {
  font-size: 11px;
  color: var(--bid-text-2);
  margin-left: auto;
}
#bidAppView .bid-lines-grid {
  display: grid;
  grid-template-columns: 72px repeat(7, minmax(0, 1fr));
  gap: 4px 6px;
  align-items: center;
}
#bidAppView .bid-lines-grid-day {
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--bid-text-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 0 4px;
}
#bidAppView .bid-lines-grid-label {
  font-size: 12px;
  color: var(--bid-text-2);
  padding: 2px 0;
}
#bidAppView .bid-lines-grid-label strong {
  color: var(--bid-text);
  font-weight: 600;
}
#bidAppView .bid-lines-grid-cell {
  height: 22px;
  border-radius: 4px;
}
#bidAppView .bid-lines-grid-cell.work {
  background: var(--bid-accent);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06) inset;
}
#bidAppView .bid-lines-grid-cell.off {
  background: transparent;
  border: 1px dashed var(--bid-fill-border);
}
#bidAppView .bid-lines-grid-totals-label {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--bid-fill-border);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bid-text-2);
}
#bidAppView .bid-lines-grid-total {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--bid-fill-border);
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--bid-text);
}
#bidAppView .bid-empty-inline {
  padding: 16px;
  text-align: center;
  color: var(--bid-text-2);
  font-size: 12px;
  font-style: italic;
}
#bidAppView .bid-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  font-size: 11px;
  font-weight: 500;
  border-radius: 999px;
  background: var(--bid-fill);
  color: var(--bid-text-2);
  border: 1px solid var(--bid-fill-border);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
#bidAppView .bid-chip.state-posted  { color: var(--breeze-blue, #1F74DF); border-color: var(--breeze-blue, #1F74DF); }
#bidAppView .bid-chip.state-awarded { color: var(--green, #1dd4a1); border-color: var(--green, #1dd4a1); }

#bidAppView .bid-cycles-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}
#bidAppView .bid-cycle-card {
  background: var(--bid-card);
  border: 1px solid var(--bid-card-border);
  border-radius: 10px;
  padding: 14px 16px;
}
#bidAppView .bid-cycle-title {
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 6px;
}
#bidAppView .bid-cycle-meta {
  font-size: 12px;
  color: var(--bid-text-2);
  display: flex;
  gap: 10px;
  margin-top: 4px;
  flex-wrap: wrap;
}

#bidAppView .bid-stat-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
#bidAppView .bid-stat {
  flex: 1;
  min-width: 130px;
  background: var(--bid-card);
  border: 1px solid var(--bid-card-border);
  border-radius: 10px;
  padding: 14px;
}
#bidAppView .bid-stat-label {
  font-size: 11px;
  color: var(--bid-text-2);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}
#bidAppView .bid-stat-value {
  font-size: 22px;
  font-weight: 600;
  color: var(--bid-text);
}

#bidAppView .bid-empty-state {
  text-align: center;
  padding: 36px 28px;
  max-width: 640px;
  margin: 12px auto;
  background: var(--bid-card);
  border: 1px solid var(--bid-card-border);
  border-radius: 12px;
}
#bidAppView .bid-empty-icon { font-size: 38px; margin-bottom: 8px; opacity: 0.7; }
#bidAppView .bid-empty-state h3 {
  margin: 0 0 6px;
  font-size: 18px;
  color: var(--bid-text);
  text-transform: none;
  letter-spacing: 0;
}
#bidAppView .bid-empty-state p {
  color: var(--bid-text-2);
  font-size: 13px;
  line-height: 1.55;
  margin: 6px 0 14px;
}

#bidAppView .bid-pref-picker {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
#bidAppView .bid-pref-list {
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 8px;
  padding: 10px;
  min-height: 240px;
  max-height: 440px;
  overflow-y: auto;
}
#bidAppView .bid-pref-list h4 {
  margin: 0 0 8px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bid-text-2);
}
#bidAppView .bid-pref-item {
  background: var(--bid-card);
  border: 1px solid var(--bid-fill-border);
  border-radius: 6px;
  padding: 8px 10px;
  margin-bottom: 6px;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
#bidAppView .bid-rank-badge {
  background: var(--bid-accent);
  color: #001633;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  padding: 2px 6px;
  margin-right: 6px;
}

#bidAppView .bid-help-list { margin: 8px 0 0 18px; padding: 0; }
#bidAppView .bid-help-list li {
  margin-bottom: 8px;
  font-size: 13px;
  color: var(--bid-text-2);
  line-height: 1.55;
}
#bidAppView .bid-help-list li strong { color: var(--bid-text); }
#bidAppView .bid-glossary { margin: 0; }
#bidAppView .bid-glossary dt {
  font-size: 13px;
  font-weight: 600;
  color: var(--bid-accent);
  margin-top: 10px;
}
#bidAppView .bid-glossary dt:first-child { margin-top: 0; }
#bidAppView .bid-glossary dd {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--bid-text-2);
  line-height: 1.55;
}

/* ---- Seniority editor in Supervisor Dashboard ---- */
.bid-seniority-controls { margin: 6px 0 10px; }
.bid-seniority-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 360px;
  overflow-y: auto;
  padding-right: 6px;
}
.bid-seniority-row {
  display: grid;
  grid-template-columns: 70px 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 6px;
}
.bid-seniority-row .bid-seniority-rank {
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 4px 6px;
  border-radius: 4px;
  font-family: inherit;
  font-size: 13px;
  text-align: center;
}
.bid-seniority-name { font-size: 13px; }
.bid-seniority-row .btn-sm { padding: 3px 8px; font-size: 12px; }

/* ---- Bid demand preview (Setup view) ---- */
#bidAppView .bid-demand-preview { margin-top: 14px; }
#bidAppView .bid-demand-note {
  font-size: 12px;
  color: var(--bid-text-2);
  margin-bottom: 10px;
  padding: 8px 12px;
  background: var(--bid-fill);
  border-radius: 6px;
  border-left: 3px solid var(--bid-accent);
}
#bidAppView .bid-demand-bars {
  display: grid;
  grid-template-columns: repeat(24, 1fr);
  gap: 2px;
  align-items: end;
  height: 160px;
  padding: 6px;
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 8px;
}
#bidAppView .bid-demand-col {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: stretch;
  position: relative;
  min-width: 0;
  height: 100%;
}
#bidAppView .bid-demand-bar {
  position: relative;
  min-height: 2px;
  border-radius: 2px 2px 0 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin-top: 1px;
}
#bidAppView .bid-demand-bar.weekday { background: var(--bid-accent); }
#bidAppView .bid-demand-bar.weekend { background: rgba(193, 221, 255, 0.6); }
#bidAppView .bid-demand-bar span {
  font-size: 9px;
  color: #001633;
  font-weight: 700;
  padding-bottom: 1px;
}
#bidAppView .bid-demand-bar.weekend span { color: #001633; }
#bidAppView .bid-demand-tick {
  font-size: 9px;
  color: var(--bid-text-2);
  text-align: center;
  padding-top: 3px;
  white-space: nowrap;
}
#bidAppView .bid-demand-legend {
  display: flex;
  gap: 16px;
  margin-top: 8px;
  font-size: 11px;
  color: var(--bid-text-2);
}
#bidAppView .bid-legend-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  vertical-align: middle;
  margin-right: 4px;
}
#bidAppView .bid-legend-dot.weekday { background: var(--bid-accent); }
#bidAppView .bid-legend-dot.weekend { background: rgba(193, 221, 255, 0.6); }

/* ---- Bid Line Editor — coverage chart ---- */
#bidAppView .bid-coverage-legend {
  display: flex;
  gap: 16px;
  margin: 6px 0 10px;
  font-size: 11px;
  color: var(--bid-text-2);
}
#bidAppView .bid-legend-dot.demand { background: rgba(255, 212, 170, 0.45); border: 1px solid var(--bid-accent); }
#bidAppView .bid-legend-dot.cov    { background: var(--green, #1dd4a1); }
#bidAppView .bid-legend-dot.short  { background: var(--red, #ff6b7b); opacity: 0.7; }

#bidAppView .bid-coverage-chart {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 6px;
  align-items: center;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
#bidAppView .bid-cov-row { display: contents; }
#bidAppView .bid-cov-label {
  color: var(--bid-text-2);
  font-size: 10px;
}
#bidAppView .bid-cov-track {
  position: relative;
  height: 16px;
  background: rgba(0, 0, 0, 0.18);
  border-radius: 3px;
  overflow: hidden;
}
#bidAppView .bid-cov-demand {
  position: absolute;
  inset: 0 auto 0 0;
  background: rgba(255, 212, 170, 0.35);
  border-right: 1px solid var(--bid-accent);
}
#bidAppView .bid-cov-cov {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--green, #1dd4a1);
  opacity: 0.85;
}
#bidAppView .bid-cov-short {
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--red, #ff6b7b);
  opacity: 0.55;
}
#bidAppView .bid-cov-numbers {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 10px;
  font-weight: 600;
  color: var(--bid-text);
  text-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
#bidAppView .bid-cov-summary {
  grid-column: 1 / -1;
  padding: 8px 12px;
  margin-bottom: 8px;
  background: var(--bid-fill);
  border-radius: 6px;
  border-left: 3px solid var(--bid-accent);
  font-size: 12px;
}
#bidAppView .bid-cov-summary strong { color: var(--bid-text); }

/* ---- Bid demand visualization (Setup view, prominent placement) ---- */
#bidAppView #bidDemandCard h3 { color: var(--bid-accent); }

#bidAppView .bid-demand-highlights {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin: 8px 0 14px;
}
#bidAppView .bid-demand-hi-item {
  background: var(--bid-fill);
  border: 1px solid var(--bid-fill-border);
  border-radius: 8px;
  padding: 10px 12px;
}
#bidAppView .bid-demand-hi-item.wide { grid-column: span 2; }
#bidAppView .bid-demand-hi-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bid-text-2);
  margin-bottom: 4px;
}
#bidAppView .bid-demand-hi-value {
  font-size: 13px;
  font-weight: 600;
  color: var(--bid-text);
}

#bidAppView #bidDemandCard .bid-demand-bars {
  height: 200px;          /* taller — make the shape obvious */
  padding: 8px;
}
#bidAppView .bid-demand-col.is-peak .bid-demand-bar.weekday {
  background: linear-gradient(180deg, var(--breeze-pink, #FF527B) 0%, var(--bid-accent) 100%);
  box-shadow: 0 0 8px rgba(255, 82, 123, 0.4);
}
#bidAppView .bid-demand-col.is-peak .bid-demand-tick {
  color: var(--breeze-pink, #FF527B);
  font-weight: 700;
}
#bidAppView .bid-demand-tick-local {
  font-size: 9px;
  color: var(--bid-text-2);
  opacity: 0.7;
}
#bidAppView .bid-legend-dot.peak {
  background: linear-gradient(180deg, var(--breeze-pink, #FF527B) 0%, var(--bid-accent) 100%);
}

#bidAppView .bid-demand-recommend {
  margin-top: 14px;
  padding: 12px 14px;
  background: linear-gradient(135deg, var(--bid-accent-soft), transparent);
  border: 1px solid var(--bid-accent);
  border-radius: 8px;
}
#bidAppView .bid-demand-rec-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--bid-accent);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 8px;
}
#bidAppView .bid-demand-rec-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
#bidAppView .bid-demand-rec-chip {
  background: var(--bid-accent);
  color: #001633;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
}
#bidAppView .bid-demand-rec-note {
  font-size: 11px;
  color: var(--bid-text-2);
  line-height: 1.5;
}

/* ---- Bid demand explainer banner above the chart ---- */
#bidAppView .bid-demand-explainer {
  font-size: 12px;
  color: var(--bid-text-2);
  line-height: 1.55;
  padding: 10px 14px;
  background: var(--bid-fill);
  border-radius: 6px;
  border-left: 3px solid var(--bid-accent);
  margin: 10px 0 6px;
}
#bidAppView .bid-demand-explainer strong { color: var(--bid-text); }
#bidAppView .bid-demand-explainer-list {
  margin: 8px 0 8px;
  padding-left: 0;
  list-style: none;
}
#bidAppView .bid-demand-explainer-list li {
  position: relative;
  padding-left: 16px;
  margin: 6px 0;
}
#bidAppView .bid-demand-explainer-list li::before {
  content: "›";
  position: absolute;
  left: 4px;
  top: -1px;
  color: var(--bid-accent);
  font-weight: 700;
}
#bidAppView .bid-demand-explainer code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 3px;
  color: var(--bid-text);
}
#bidAppView .bid-demand-explainer em {
  display: inline-block;
  margin-top: 4px;
  color: var(--bid-text-2);
  font-style: italic;
}
#bidAppView .bid-demand-inline-swatch {
  display: inline-block;
  width: 11px;
  height: 11px;
  vertical-align: middle;
  margin: 0 3px 0 2px;
  border-radius: 2px;
}
#bidAppView .bid-demand-inline-swatch.weekday { background: var(--bid-accent); }
#bidAppView .bid-demand-inline-swatch.weekend { background: rgba(193, 221, 255, 0.6); }
#bidAppView .bid-demand-inline-swatch.peak {
  background: linear-gradient(180deg, var(--breeze-pink, #FF527B) 0%, var(--bid-accent) 100%);
}

/* ---- Bid demand chart — plain-language summary banner ---- */
#bidAppView .bid-demand-summary {
  padding: 16px 18px;
  margin: 6px 0 14px;
  background: linear-gradient(135deg, var(--bid-accent-soft), transparent);
  border: 1px solid var(--bid-accent);
  border-radius: 10px;
}
#bidAppView .bid-demand-summary-line {
  font-size: 15px;
  line-height: 1.55;
  color: var(--bid-text);
}
#bidAppView .bid-demand-summary-line strong { color: var(--bid-accent); }
#bidAppView .bid-demand-summary-sub {
  margin-top: 6px;
  font-size: 11px;
  color: var(--bid-text-2);
}
#bidAppView .bid-demand-hi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
#bidAppView .bid-demand-hi-sub {
  font-size: 10px;
  color: var(--bid-text-2);
  margin-top: 2px;
  text-transform: lowercase;
}
#bidAppView .bid-demand-hi-peakhours {
  font-size: 12px;
  line-height: 1.4;
  font-weight: 500;
}

/* ============================================================
   LOGIN — corner-anchored layout (Phase 7).

   Previously the login card was centered over the brand SVG and
   covered the Breeze wordmark/compass. This block:
     - Anchors the form to the upper-right of the screen instead.
     - Strips the heavy panel chrome (border, shadow, opaque bg)
       so the inputs look like they're floating directly on the
       brand backdrop.
     - Restyles inputs as translucent rounded fields that pick
       up the background through them.

   Light theme keeps the centered card (no SVG, no logo to cover),
   reduced-transparency mode also keeps a solid card for a11y.
   ============================================================ */
html[data-theme="dark"] .login-screen,
html:not([data-theme]) .login-screen {
  /* Anchor top-right with corner padding tuned to sit INSIDE the
     bracket-frame corner mark in the brand SVG. The bracket sits
     60 SVG-units in from each edge of the SVG (which is 1920×1080).
     With `contain`, the SVG is letterboxed on aspect-mismatched
     viewports — on a typical 16:10 laptop the SVG fills the width
     but leaves ~45 px of letterbox above and below, pushing the
     bracket top edge down to ~90 px from the viewport top.
     The top-padding lower bound of 88 px clears the bracket on
     16:10 laptops; the right-padding handles the same on the right
     side for any narrower-than-16:9 viewport. */
  align-items: flex-start;
  justify-content: flex-end;
  padding: clamp(88px, 9vh, 130px) clamp(72px, 6vw, 110px);
}

@media (max-width: 720px) {
  /* On narrow screens fall back to centered horizontally so the
     form doesn't squeeze into a corner that's barely wider than
     the inputs themselves. Still top-anchored to keep the logo
     visible underneath. */
  html[data-theme="dark"] .login-screen,
  html:not([data-theme]) .login-screen {
    justify-content: center;
    padding: 28px 18px;
  }
}

html[data-theme="dark"] .login-card,
html:not([data-theme]) .login-card {
  /* Strip the panel chrome. Card becomes a transparent container
     for the form fields. The blur on the inputs (below) gives the
     "floating on the backdrop" effect without a visible frame.
     Width caps so the form's left edge stays well inside the
     viewport — combined with the corner padding above, the entire
     form sits inside the bracket-frame corner mark. */
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  padding: 0;
  width: min(320px, calc(100vw - 160px));
}

/* Brand mark / "Breeze Aerodesk / Dispatch Center" inside the card.
   Reduced from the original 18px gap to keep the corner footprint
   compact, and lifted text shadows so the title reads against any
   bg color the SVG ends up showing through. */
html[data-theme="dark"] .brand-login,
html:not([data-theme]) .brand-login {
  margin-bottom: 16px;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
}
html[data-theme="dark"] .brand-login h1,
html:not([data-theme]) .brand-login h1 {
  color: var(--text);
}
html[data-theme="dark"] .brand-login p,
html:not([data-theme]) .brand-login p {
  color: rgba(214, 225, 243, 0.85);
}

/* Frosted-glass inputs that read AS part of the background.
   - Translucent dark fill so the brand SVG bleeds through subtly.
   - Localized blur on each input so text stays sharp without
     needing a card-level backdrop-filter.
   - Soft border in white-alpha keeps the field edge visible
     without the "panel" look. */
html[data-theme="dark"] .login-card input[type="text"],
html[data-theme="dark"] .login-card input[type="password"],
html:not([data-theme]) .login-card input[type="text"],
html:not([data-theme]) .login-card input[type="password"] {
  background: rgba(13, 20, 47, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 10px;
  color: var(--text);
  padding: 11px 14px;
  backdrop-filter: blur(12px) saturate(160%);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  transition: border-color 0.18s, background 0.18s;
}
html[data-theme="dark"] .login-card input:focus,
html:not([data-theme]) .login-card input:focus {
  outline: none;
  border-color: rgba(193, 221, 255, 0.75);   /* Breeze cool blue */
  background: rgba(13, 20, 47, 0.6);
}
html[data-theme="dark"] .login-card input::placeholder,
html:not([data-theme]) .login-card input::placeholder {
  color: rgba(214, 225, 243, 0.55);
}

/* Labels above the inputs — pure text on the backdrop, no chip. */
html[data-theme="dark"] .login-card label,
html:not([data-theme]) .login-card label {
  color: rgba(232, 240, 255, 0.85);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}

/* Sign in button stays solid (keeps clear affordance) but its
   shadow now reads on the backdrop, not on a card. */
html[data-theme="dark"] .login-card .btn.primary,
html:not([data-theme]) .login-card .btn.primary {
  box-shadow: 0 8px 24px rgba(31, 116, 223, 0.45);
}

/* Error pill keeps its dark background but loses the harsh outline
   so it reads as a floating callout, not a card-inside-card. */
html[data-theme="dark"] .login-error,
html:not([data-theme]) .login-error {
  background: rgba(42, 19, 34, 0.85);
  border: 1px solid rgba(107, 45, 74, 0.85);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Hint copy — readable on backdrop with shadow. */
html[data-theme="dark"] .login-hint,
html:not([data-theme]) .login-hint {
  color: rgba(214, 225, 243, 0.75);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}

/* Accessibility fallback — when the OS asks for reduced transparency,
   give the form a solid container again so users on those modes still
   get a readable layout. Top-right anchoring stays. */
@media (prefers-reduced-transparency: reduce) {
  html[data-theme="dark"] .login-card,
  html:not([data-theme]) .login-card {
    background: rgba(13, 20, 47, 0.92) !important;
    border: 1px solid rgba(255, 255, 255, 0.10) !important;
    padding: 22px !important;
    border-radius: 14px;
    box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55) !important;
  }
  html[data-theme="dark"] .login-card input[type="text"],
  html[data-theme="dark"] .login-card input[type="password"],
  html:not([data-theme]) .login-card input[type="text"],
  html:not([data-theme]) .login-card input[type="password"] {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* ============================================================
   Desk-assignment optimizer — morning toggle + recommendation panel.
   ============================================================ */
.da-autoopt-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  font-size: 0.82rem;
  color: var(--text);
  cursor: pointer;
}
.da-autoopt-toggle input[type="checkbox"] {
  width: auto;
  margin: 0;
  cursor: pointer;
}

.da-optimizer-panel {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-soft);
}
.da-optimizer-panel[hidden] { display: none; }

.da-opt-ok {
  font-size: 0.86rem;
  color: var(--green);
  font-weight: 600;
}
.da-opt-ok .muted { color: var(--muted); font-weight: 400; }

.da-opt-head {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 10px;
}

.da-opt-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
}
.da-opt-table {
  border-collapse: collapse;
  font-size: 0.8rem;
  min-width: 240px;
}
.da-opt-table th {
  text-align: left;
  color: var(--muted);
  font-weight: 600;
  padding: 2px 10px 6px 0;
  border-bottom: 1px solid var(--line);
}
.da-opt-table td {
  padding: 3px 10px 3px 0;
  color: var(--text);
  white-space: nowrap;
}
.da-opt-arrow { color: var(--muted); padding: 0 4px; }
.da-opt-new { color: var(--blue); font-weight: 700; }
.da-opt-better { color: var(--green); font-weight: 700; }
.da-opt-worse { color: var(--red); font-weight: 700; }

.da-opt-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

/* ============================================================
   Supervisor morning briefing card.
   ============================================================ */
.briefing-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.briefing-body {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  overflow-y: auto;
}
.briefing-clear {
  color: var(--green);
  font-weight: 600;
  font-size: 0.86rem;
  margin: 6px 0;
}
.briefing-item {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 9px;
  border-radius: 8px;
  background: var(--surface-2);
  border-left: 3px solid var(--line);
}
.briefing-item.briefing-critical { border-left-color: var(--red); }
.briefing-item.briefing-warn { border-left-color: var(--accent-tan); }
.briefing-item.briefing-info { border-left-color: var(--blue); }
.briefing-dot {
  font-size: 0.7rem;
  line-height: 1.5;
  flex: 0 0 auto;
}
.briefing-critical .briefing-dot { color: var(--red); }
.briefing-warn .briefing-dot { color: var(--accent-tan); }
.briefing-info .briefing-dot { color: var(--blue); }
.briefing-title {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text);
}
.briefing-detail {
  font-size: 0.78rem;
  color: var(--muted);
  margin-top: 1px;
}
.badge.badge-ok { background: rgba(29, 212, 161, 0.18); color: var(--green); }
.badge.badge-critical { background: rgba(255, 107, 123, 0.18); color: var(--red); }
.badge.badge-warn { background: rgba(255, 212, 170, 0.20); color: var(--accent-tan); }
.badge.badge-info { background: rgba(31, 116, 223, 0.18); color: var(--blue); }

/* ===================================================================
   Daily OPs Brief — Super-User-only dashboard
   =================================================================== */
.opsbrief-controls,
.opsbrief-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
}
.opsbrief-field-label {
  display: block;
  margin: 14px 0 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  text-transform: uppercase;
}
.opsbrief-opsplan {
  width: 100%;
  min-height: 120px;
  resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  line-height: 1.5;
  background: var(--field-bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
}
#opsBriefStatus,
#opsBriefRosterStatus,
#opsBriefDataSummary { font-size: 12px; }
#opsBriefStatus[data-kind="error"],
#opsBriefRosterStatus[data-kind="error"] { color: var(--red); }
#opsBriefStatus[data-kind="ok"] { color: var(--green); }
#opsBriefStatus[data-kind="busy"] { color: var(--blue); }

/* Region paragraph blocks — one editable paragraph per region. */
.opsbrief-regions {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 6px;
}
.opsbrief-region {
  border-left: 3px solid var(--view-accent);
  padding-left: 12px;
}
.opsbrief-region-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--view-accent);
  margin-bottom: 6px;
}
.opsbrief-region-text {
  width: 100%;
  min-height: 64px;
  resize: vertical;
  overflow: hidden;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
  background: var(--field-bg);
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 8px 10px;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.opsbrief-region-text:hover { border-color: var(--line); }
.opsbrief-region-text:focus {
  outline: none;
  border-color: var(--view-accent);
  background: var(--chip-bg);
}

/* Roster editor */
.opsbrief-roster-details { margin-top: 8px; }
.opsbrief-roster-details > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--view-accent);
  padding: 4px 0;
}
.opsbrief-roster-editor {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
}
.opsbrief-roster-row { display: flex; flex-direction: column; }
.opsbrief-roster-input,
.opsbrief-roster-textarea {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  color: var(--text);
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 10px;
}
.opsbrief-roster-textarea { resize: vertical; min-height: 44px; }

/* Daily OPs Brief — full combined paragraph + generation progress */
.opsbrief-full { margin: 4px 0 18px; }
.opsbrief-full-text { min-height: 120px; font-size: 14.5px; }
.opsbrief-regions-heading {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 4px 0 10px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
.opsbrief-progress { margin-top: 14px; }
.opsbrief-progress-track {
  width: 100%;
  height: 8px;
  background: var(--field-bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
}
.opsbrief-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--view-accent), var(--breeze-cool));
  transition: width 0.25s ease;
}
.opsbrief-progress-fill.is-done {
  background: var(--green);
  transition: width 0.3s ease;
}
.opsbrief-progress-meta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}
.opsbrief-progress-count { font-variant-numeric: tabular-nums; }

/* Daily OPs Brief — once-a-day warning banner (high visibility) */
.opsbrief-warning {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 20px;
  padding: 14px 18px;
  border-radius: 10px;
  background: rgba(255, 82, 123, 0.14);
  border: 1px solid var(--breeze-pink, #FF527B);
  box-shadow: 0 0 0 1px rgba(255, 82, 123, 0.25), 0 6px 18px rgba(255, 82, 123, 0.18);
  color: var(--text);
}
.opsbrief-warning-icon {
  font-size: 24px;
  line-height: 1;
  color: var(--breeze-pink, #FF527B);
  flex: 0 0 auto;
}
.opsbrief-warning-text { font-size: 14px; line-height: 1.5; }
.opsbrief-warning-text strong { color: var(--breeze-pink, #FF527B); }
html.skin-hud .opsbrief-warning {
  background: rgba(255, 82, 123, 0.16);
  box-shadow: 0 0 0 1px rgba(255, 82, 123, 0.4), 0 0 22px rgba(255, 82, 123, 0.25);
}

/* Daily OPs Brief — once-a-day run count + reason gate */
#opsBriefRunCount { margin-top: 8px; font-size: 12px; }
#opsBriefRunCount[data-kind="warn"] { color: var(--accent-tan, #FFD4AA); }
.opsbrief-reasongate {
  margin-top: 14px;
  padding: 14px 16px;
  border-radius: 10px;
  background: rgba(255, 212, 170, 0.10);
  border: 1px solid var(--accent-tan, #FFD4AA);
  box-shadow: 0 0 0 1px rgba(255, 212, 170, 0.22);
}
.opsbrief-reasongate .opsbrief-field-label { margin-top: 0; color: var(--accent-tan, #FFD4AA); }
html.skin-hud .opsbrief-reasongate {
  box-shadow: 0 0 0 1px rgba(255, 212, 170, 0.35), 0 0 18px rgba(255, 212, 170, 0.18);
}
